diff --git a/Ash_AWord.xcodeproj/project.pbxproj b/Ash_AWord.xcodeproj/project.pbxproj index 4e4f1fa..37d0017 100644 --- a/Ash_AWord.xcodeproj/project.pbxproj +++ b/Ash_AWord.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 018BA697D81ADF73A75A2345 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7B8559E65B4108EA2C3F343 /* libPods.a */; }; A95510921AC39E6A0006D16F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A95510911AC39E6A0006D16F /* main.m */; }; A95510951AC39E6A0006D16F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A95510941AC39E6A0006D16F /* AppDelegate.m */; }; A95510981AC39E6A0006D16F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A95510971AC39E6A0006D16F /* ViewController.m */; }; @@ -14,6 +15,24 @@ A955109D1AC39E6A0006D16F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A955109C1AC39E6A0006D16F /* Images.xcassets */; }; A95510A01AC39E6A0006D16F /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A955109E1AC39E6A0006D16F /* LaunchScreen.xib */; }; A95510AC1AC39E6A0006D16F /* Ash_AWordTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A95510AB1AC39E6A0006D16F /* Ash_AWordTests.m */; }; + A97C54CD1AC8F10F0038818A /* BaseViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54BD1AC8F10F0038818A /* BaseViewModel.m */; }; + A97C54CF1AC8F10F0038818A /* NSString+HXAddtions.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54C11AC8F10F0038818A /* NSString+HXAddtions.m */; }; + A97C54D01AC8F10F0038818A /* PropertyEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54C31AC8F10F0038818A /* PropertyEntity.m */; }; + A97C54D11AC8F10F0038818A /* RequireEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54C51AC8F10F0038818A /* RequireEngine.m */; }; + A97C54D21AC8F10F0038818A /* AFgzipResponseSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54C81AC8F10F0038818A /* AFgzipResponseSerializer.m */; }; + A97C54D31AC8F10F0038818A /* XiaoYuAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54CA1AC8F10F0038818A /* XiaoYuAPIClient.m */; }; + A97C54D41AC8F10F0038818A /* XMFishBaseCode.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54CC1AC8F10F0038818A /* XMFishBaseCode.m */; }; + A97C54DD1AC901980038818A /* MBProgressHUD+Util.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54DC1AC901970038818A /* MBProgressHUD+Util.m */; }; + A97C54E11AC91DA10038818A /* CommonUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = A97C54E01AC91DA10038818A /* CommonUtil.m */; }; + A9FAD4221AC922A5007ADC10 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9FAD4201AC922A5007ADC10 /* MainViewController.m */; }; + A9FAD4231AC922A5007ADC10 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9FAD4211AC922A5007ADC10 /* MainViewController.xib */; }; + A9FAD4271AC922BE007ADC10 /* NoteViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9FAD4251AC922BE007ADC10 /* NoteViewController.m */; }; + A9FAD4281AC922BE007ADC10 /* NoteViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9FAD4261AC922BE007ADC10 /* NoteViewController.xib */; }; + A9FAD42C1AC923E2007ADC10 /* MessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9FAD42A1AC923E2007ADC10 /* MessageViewController.m */; }; + A9FAD42D1AC923E2007ADC10 /* MessageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9FAD42B1AC923E2007ADC10 /* MessageViewController.xib */; }; + A9FAD4311AC923FC007ADC10 /* MyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9FAD42F1AC923FC007ADC10 /* MyViewController.m */; }; + A9FAD4321AC923FC007ADC10 /* MyViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9FAD4301AC923FC007ADC10 /* MyViewController.xib */; }; + A9FAD4351AC925AC007ADC10 /* LoginViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A9FAD4341AC925AC007ADC10 /* LoginViewModel.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -27,6 +46,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 93C9ED3E7E1041224C33AED5 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; A955108C1AC39E6A0006D16F /* Ash_AWord.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ash_AWord.app; sourceTree = BUILT_PRODUCTS_DIR; }; A95510901AC39E6A0006D16F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A95510911AC39E6A0006D16F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -40,6 +60,42 @@ A95510A51AC39E6A0006D16F /* Ash_AWordTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Ash_AWordTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A95510AA1AC39E6A0006D16F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A95510AB1AC39E6A0006D16F /* Ash_AWordTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Ash_AWordTests.m; sourceTree = ""; }; + A97C54BC1AC8F10F0038818A /* BaseViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseViewModel.h; sourceTree = ""; }; + A97C54BD1AC8F10F0038818A /* BaseViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseViewModel.m; sourceTree = ""; }; + A97C54C01AC8F10F0038818A /* NSString+HXAddtions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HXAddtions.h"; sourceTree = ""; }; + A97C54C11AC8F10F0038818A /* NSString+HXAddtions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HXAddtions.m"; sourceTree = ""; }; + A97C54C21AC8F10F0038818A /* PropertyEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyEntity.h; sourceTree = ""; }; + A97C54C31AC8F10F0038818A /* PropertyEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PropertyEntity.m; sourceTree = ""; }; + A97C54C41AC8F10F0038818A /* RequireEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RequireEngine.h; sourceTree = ""; }; + A97C54C51AC8F10F0038818A /* RequireEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RequireEngine.m; sourceTree = ""; }; + A97C54C71AC8F10F0038818A /* AFgzipResponseSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFgzipResponseSerializer.h; sourceTree = ""; }; + A97C54C81AC8F10F0038818A /* AFgzipResponseSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFgzipResponseSerializer.m; sourceTree = ""; }; + A97C54C91AC8F10F0038818A /* XiaoYuAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XiaoYuAPIClient.h; sourceTree = ""; }; + A97C54CA1AC8F10F0038818A /* XiaoYuAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XiaoYuAPIClient.m; sourceTree = ""; }; + A97C54CB1AC8F10F0038818A /* XMFishBaseCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMFishBaseCode.h; sourceTree = ""; }; + A97C54CC1AC8F10F0038818A /* XMFishBaseCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMFishBaseCode.m; sourceTree = ""; }; + A97C54D61AC8FB6C0038818A /* Common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; }; + A97C54D81AC8FCED0038818A /* PrefixHeader.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; + A97C54DB1AC901970038818A /* MBProgressHUD+Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MBProgressHUD+Util.h"; sourceTree = ""; }; + A97C54DC1AC901970038818A /* MBProgressHUD+Util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MBProgressHUD+Util.m"; sourceTree = ""; }; + A97C54DF1AC91DA10038818A /* CommonUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtil.h; sourceTree = ""; }; + A97C54E01AC91DA10038818A /* CommonUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtil.m; sourceTree = ""; }; + A9FAD41F1AC922A5007ADC10 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; + A9FAD4201AC922A5007ADC10 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + A9FAD4211AC922A5007ADC10 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = ""; }; + A9FAD4241AC922BE007ADC10 /* NoteViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoteViewController.h; sourceTree = ""; }; + A9FAD4251AC922BE007ADC10 /* NoteViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoteViewController.m; sourceTree = ""; }; + A9FAD4261AC922BE007ADC10 /* NoteViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NoteViewController.xib; sourceTree = ""; }; + A9FAD4291AC923E2007ADC10 /* MessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageViewController.h; sourceTree = ""; }; + A9FAD42A1AC923E2007ADC10 /* MessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessageViewController.m; sourceTree = ""; }; + A9FAD42B1AC923E2007ADC10 /* MessageViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageViewController.xib; sourceTree = ""; }; + A9FAD42E1AC923FC007ADC10 /* MyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyViewController.h; sourceTree = ""; }; + A9FAD42F1AC923FC007ADC10 /* MyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyViewController.m; sourceTree = ""; }; + A9FAD4301AC923FC007ADC10 /* MyViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MyViewController.xib; sourceTree = ""; }; + A9FAD4331AC925AC007ADC10 /* LoginViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewModel.h; sourceTree = ""; }; + A9FAD4341AC925AC007ADC10 /* LoginViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginViewModel.m; sourceTree = ""; }; + AD3C5DED2445FC2F0892A525 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + E7B8559E65B4108EA2C3F343 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -47,6 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 018BA697D81ADF73A75A2345 /* libPods.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -66,6 +123,8 @@ A955108E1AC39E6A0006D16F /* Ash_AWord */, A95510A81AC39E6A0006D16F /* Ash_AWordTests */, A955108D1AC39E6A0006D16F /* Products */, + BB5C06BBA9CE89A900ADC8EE /* Pods */, + D600413F4BCED5BE80E26E1F /* Frameworks */, ); sourceTree = ""; }; @@ -81,6 +140,13 @@ A955108E1AC39E6A0006D16F /* Ash_AWord */ = { isa = PBXGroup; children = ( + A97C54E21AC91FD70038818A /* Resource */, + A97C54D91AC9014D0038818A /* Vender */, + A97C54D51AC8F51E0038818A /* Config */, + A97C54BA1AC8F0A90038818A /* Utility */, + A97C54B71AC8F0370038818A /* ViewControllers */, + A97C54B81AC8F0370038818A /* ViewModels */, + A97C54B91AC8F0370038818A /* Views */, A95510931AC39E6A0006D16F /* AppDelegate.h */, A95510941AC39E6A0006D16F /* AppDelegate.m */, A95510961AC39E6A0006D16F /* ViewController.h */, @@ -97,6 +163,7 @@ isa = PBXGroup; children = ( A95510901AC39E6A0006D16F /* Info.plist */, + A97C54D81AC8FCED0038818A /* PrefixHeader.pch */, A95510911AC39E6A0006D16F /* main.m */, ); name = "Supporting Files"; @@ -119,6 +186,145 @@ name = "Supporting Files"; sourceTree = ""; }; + A97C54B71AC8F0370038818A /* ViewControllers */ = { + isa = PBXGroup; + children = ( + A9FAD41E1AC92293007ADC10 /* Main */, + ); + path = ViewControllers; + sourceTree = ""; + }; + A97C54B81AC8F0370038818A /* ViewModels */ = { + isa = PBXGroup; + children = ( + A9FAD4331AC925AC007ADC10 /* LoginViewModel.h */, + A9FAD4341AC925AC007ADC10 /* LoginViewModel.m */, + ); + path = ViewModels; + sourceTree = ""; + }; + A97C54B91AC8F0370038818A /* Views */ = { + isa = PBXGroup; + children = ( + ); + path = Views; + sourceTree = ""; + }; + A97C54BA1AC8F0A90038818A /* Utility */ = { + isa = PBXGroup; + children = ( + A97C54DE1AC91D990038818A /* Tool */, + A97C54BB1AC8F10F0038818A /* Network */, + ); + path = Utility; + sourceTree = ""; + }; + A97C54BB1AC8F10F0038818A /* Network */ = { + isa = PBXGroup; + children = ( + A97C54BC1AC8F10F0038818A /* BaseViewModel.h */, + A97C54BD1AC8F10F0038818A /* BaseViewModel.m */, + A97C54C01AC8F10F0038818A /* NSString+HXAddtions.h */, + A97C54C11AC8F10F0038818A /* NSString+HXAddtions.m */, + A97C54C21AC8F10F0038818A /* PropertyEntity.h */, + A97C54C31AC8F10F0038818A /* PropertyEntity.m */, + A97C54C41AC8F10F0038818A /* RequireEngine.h */, + A97C54C51AC8F10F0038818A /* RequireEngine.m */, + A97C54C61AC8F10F0038818A /* Serializer */, + A97C54C91AC8F10F0038818A /* XiaoYuAPIClient.h */, + A97C54CA1AC8F10F0038818A /* XiaoYuAPIClient.m */, + A97C54CB1AC8F10F0038818A /* XMFishBaseCode.h */, + A97C54CC1AC8F10F0038818A /* XMFishBaseCode.m */, + ); + path = Network; + sourceTree = ""; + }; + A97C54C61AC8F10F0038818A /* Serializer */ = { + isa = PBXGroup; + children = ( + A97C54C71AC8F10F0038818A /* AFgzipResponseSerializer.h */, + A97C54C81AC8F10F0038818A /* AFgzipResponseSerializer.m */, + ); + path = Serializer; + sourceTree = ""; + }; + A97C54D51AC8F51E0038818A /* Config */ = { + isa = PBXGroup; + children = ( + A97C54D61AC8FB6C0038818A /* Common.h */, + ); + path = Config; + sourceTree = ""; + }; + A97C54D91AC9014D0038818A /* Vender */ = { + isa = PBXGroup; + children = ( + A97C54DA1AC901970038818A /* MBProgressHUD */, + ); + path = Vender; + sourceTree = ""; + }; + A97C54DA1AC901970038818A /* MBProgressHUD */ = { + isa = PBXGroup; + children = ( + A97C54DB1AC901970038818A /* MBProgressHUD+Util.h */, + A97C54DC1AC901970038818A /* MBProgressHUD+Util.m */, + ); + path = MBProgressHUD; + sourceTree = ""; + }; + A97C54DE1AC91D990038818A /* Tool */ = { + isa = PBXGroup; + children = ( + A97C54DF1AC91DA10038818A /* CommonUtil.h */, + A97C54E01AC91DA10038818A /* CommonUtil.m */, + ); + path = Tool; + sourceTree = ""; + }; + A97C54E21AC91FD70038818A /* Resource */ = { + isa = PBXGroup; + children = ( + ); + path = Resource; + sourceTree = ""; + }; + A9FAD41E1AC92293007ADC10 /* Main */ = { + isa = PBXGroup; + children = ( + A9FAD41F1AC922A5007ADC10 /* MainViewController.h */, + A9FAD4201AC922A5007ADC10 /* MainViewController.m */, + A9FAD4211AC922A5007ADC10 /* MainViewController.xib */, + A9FAD4241AC922BE007ADC10 /* NoteViewController.h */, + A9FAD4251AC922BE007ADC10 /* NoteViewController.m */, + A9FAD4261AC922BE007ADC10 /* NoteViewController.xib */, + A9FAD4291AC923E2007ADC10 /* MessageViewController.h */, + A9FAD42A1AC923E2007ADC10 /* MessageViewController.m */, + A9FAD42B1AC923E2007ADC10 /* MessageViewController.xib */, + A9FAD42E1AC923FC007ADC10 /* MyViewController.h */, + A9FAD42F1AC923FC007ADC10 /* MyViewController.m */, + A9FAD4301AC923FC007ADC10 /* MyViewController.xib */, + ); + path = Main; + sourceTree = ""; + }; + BB5C06BBA9CE89A900ADC8EE /* Pods */ = { + isa = PBXGroup; + children = ( + 93C9ED3E7E1041224C33AED5 /* Pods.debug.xcconfig */, + AD3C5DED2445FC2F0892A525 /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + D600413F4BCED5BE80E26E1F /* Frameworks */ = { + isa = PBXGroup; + children = ( + E7B8559E65B4108EA2C3F343 /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -126,9 +332,11 @@ isa = PBXNativeTarget; buildConfigurationList = A95510AF1AC39E6A0006D16F /* Build configuration list for PBXNativeTarget "Ash_AWord" */; buildPhases = ( + DAEB2FA2A84A6068EB8BF453 /* Check Pods Manifest.lock */, A95510881AC39E6A0006D16F /* Sources */, A95510891AC39E6A0006D16F /* Frameworks */, A955108A1AC39E6A0006D16F /* Resources */, + 1A9F58AE9370E1C95033655F /* Copy Pods Resources */, ); buildRules = ( ); @@ -199,9 +407,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9FAD4321AC923FC007ADC10 /* MyViewController.xib in Resources */, A955109B1AC39E6A0006D16F /* Main.storyboard in Resources */, A95510A01AC39E6A0006D16F /* LaunchScreen.xib in Resources */, + A9FAD42D1AC923E2007ADC10 /* MessageViewController.xib in Resources */, A955109D1AC39E6A0006D16F /* Images.xcassets in Resources */, + A9FAD4281AC922BE007ADC10 /* NoteViewController.xib in Resources */, + A9FAD4231AC922A5007ADC10 /* MainViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -214,13 +426,60 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 1A9F58AE9370E1C95033655F /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DAEB2FA2A84A6068EB8BF453 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ A95510881AC39E6A0006D16F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9FAD4271AC922BE007ADC10 /* NoteViewController.m in Sources */, + A97C54D31AC8F10F0038818A /* XiaoYuAPIClient.m in Sources */, + A9FAD4311AC923FC007ADC10 /* MyViewController.m in Sources */, + A97C54DD1AC901980038818A /* MBProgressHUD+Util.m in Sources */, A95510981AC39E6A0006D16F /* ViewController.m in Sources */, + A97C54D21AC8F10F0038818A /* AFgzipResponseSerializer.m in Sources */, + A9FAD4221AC922A5007ADC10 /* MainViewController.m in Sources */, + A9FAD42C1AC923E2007ADC10 /* MessageViewController.m in Sources */, + A97C54CD1AC8F10F0038818A /* BaseViewModel.m in Sources */, + A97C54CF1AC8F10F0038818A /* NSString+HXAddtions.m in Sources */, A95510951AC39E6A0006D16F /* AppDelegate.m in Sources */, + A97C54D11AC8F10F0038818A /* RequireEngine.m in Sources */, + A97C54D01AC8F10F0038818A /* PropertyEntity.m in Sources */, + A97C54D41AC8F10F0038818A /* XMFishBaseCode.m in Sources */, + A97C54E11AC91DA10038818A /* CommonUtil.m in Sources */, + A9FAD4351AC925AC007ADC10 /* LoginViewModel.m in Sources */, A95510921AC39E6A0006D16F /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -297,7 +556,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -332,7 +591,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -341,9 +600,12 @@ }; A95510B01AC39E6A0006D16F /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 93C9ED3E7E1041224C33AED5 /* Pods.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + GCC_PREFIX_HEADER = Ash_AWord/PrefixHeader.pch; INFOPLIST_FILE = Ash_AWord/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -351,9 +613,12 @@ }; A95510B11AC39E6A0006D16F /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AD3C5DED2445FC2F0892A525 /* Pods.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + GCC_PREFIX_HEADER = Ash_AWord/PrefixHeader.pch; INFOPLIST_FILE = Ash_AWord/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -412,6 +677,7 @@ A95510B11AC39E6A0006D16F /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; A95510B21AC39E6A0006D16F /* Build configuration list for PBXNativeTarget "Ash_AWordTests" */ = { isa = XCConfigurationList; @@ -420,6 +686,7 @@ A95510B41AC39E6A0006D16F /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/Ash_AWord/Config/Common.h b/Ash_AWord/Config/Common.h new file mode 100644 index 0000000..43c78df --- /dev/null +++ b/Ash_AWord/Config/Common.h @@ -0,0 +1,54 @@ +// +// Common.h +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#ifndef Ash_AWord_Common_h +#define Ash_AWord_Common_h + +#ifdef DEBUG +#ifndef DLog +# define DLog(fmt, ...) {NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);} +#endif +#ifndef ELog +# define ELog(err) {if(err) DLog(@"%@", err)} +#endif +#else +#ifndef DLog +# define DLog(...) +#endif +#ifndef ELog +# define ELog(err) +#endif +#endif + + +/** + * 系统版本 + */ +#define iOSVersion ([[[UIDevice currentDevice] systemVersion] floatValue]) //系统版本 +#define iOS7AndLater ( [[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending ) + +#define isiPhone5 ([[UIScreen mainScreen] bounds].size.height == 568) +#define isiPhone4s ([[UIScreen mainScreen] bounds].size.height == 480) + +/** + * 获取当前window宽高 + */ +#define kScreenWidth ([[UIScreen mainScreen] bounds].size.width) +#define kScreenHeight ([[UIScreen mainScreen] bounds].size.height) + +#define kMainScreenFrame ([[UIScreen mainScreen] bounds]) + +#define kNetworkErrorTips @"网络连接失败" + +/** + * 接口地址 + */ + +#define Ash_AWord_API_URL @"mobi.yingyinglicai.com:8443/front" + +#endif diff --git a/Ash_AWord/Info.plist b/Ash_AWord/Info.plist index 2dcbbef..337fbc5 100644 --- a/Ash_AWord/Info.plist +++ b/Ash_AWord/Info.plist @@ -33,8 +33,6 @@ UISupportedInterfaceOrientations UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight diff --git a/Ash_AWord/PrefixHeader.pch b/Ash_AWord/PrefixHeader.pch new file mode 100644 index 0000000..e1795b9 --- /dev/null +++ b/Ash_AWord/PrefixHeader.pch @@ -0,0 +1,30 @@ +// +// PrefixHeader.pch +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#ifndef Ash_AWord_PrefixHeader_pch +#define Ash_AWord_PrefixHeader_pch + +// Include any system framework and library headers here that should be included in all compilation units. +// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. + +#import + +#ifndef __IPHONE_6_0 +#warning "This project uses features only available in iOS SDK 6.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import + #import + #import "Common.h" + #import "MBProgressHUD+Util.h" + #import "CommonUtil.h" +#endif + +#endif diff --git a/Ash_AWord/Utility/Network/BaseViewModel.h b/Ash_AWord/Utility/Network/BaseViewModel.h new file mode 100644 index 0000000..f6be791 --- /dev/null +++ b/Ash_AWord/Utility/Network/BaseViewModel.h @@ -0,0 +1,21 @@ +// +// BaseViewModel.h +// TheLifeCircle +// +// Created by xmfish on 14/12/30. +// Copyright (c) 2014年 小鱼网. All rights reserved. +// + +#import "MTLModel.h" +#import "MTLJSONAdapter.h" + +@interface BaseViewModel : MTLModel + +@property (nonatomic, readonly) NSNumber *errCode; +@property (nonatomic, readonly) NSString *errMessage; + +- (BOOL)success; + +- (BOOL)needLogin; + +@end diff --git a/Ash_AWord/Utility/Network/BaseViewModel.m b/Ash_AWord/Utility/Network/BaseViewModel.m new file mode 100644 index 0000000..ddc320f --- /dev/null +++ b/Ash_AWord/Utility/Network/BaseViewModel.m @@ -0,0 +1,38 @@ +// +// BaseViewModel.m +// TheLifeCircle +// +// Created by xmfish on 14/12/30. +// Copyright (c) 2014年 小鱼网. All rights reserved. +// + +#import "BaseViewModel.h" + +@implementation BaseViewModel ++ (NSDictionary *)JSONKeyPathsByPropertyKey { + return @{ + @"errCode": @"errCode", + @"errMessage": @"errMessage", + }; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error +{ + self = [super initWithDictionary:dictionaryValue error:error]; + if (self != nil) + { + } + return self; +} + +- (BOOL)success +{ + return ([self.errCode integerValue] == 0); +} + +- (BOOL)needLogin +{ + return ([self.errCode integerValue] == 1001); +} + +@end diff --git a/Ash_AWord/Utility/Network/NSString+HXAddtions.h b/Ash_AWord/Utility/Network/NSString+HXAddtions.h new file mode 100644 index 0000000..320aae4 --- /dev/null +++ b/Ash_AWord/Utility/Network/NSString+HXAddtions.h @@ -0,0 +1,22 @@ +// +// NSString+HXAddtions.h +// HXWeb +// +// Created by hufeng on 12-2-13. +// Copyright (c) 2012年 __MyCompanyName__. All rights reserved. +// + +#import + +@interface NSString (HXAddtions) + ++(NSString *) jsonStringWithDictionary:(NSDictionary *)dictionary; + ++(NSString *) jsonStringWithArray:(NSArray *)array; + ++(NSString *) jsonStringWithString:(NSString *) string; + ++(NSString *) jsonStringWithObject:(id) object; + ++(NSString *) jsonStringWithRTArray:(NSArray *)array; +@end \ No newline at end of file diff --git a/Ash_AWord/Utility/Network/NSString+HXAddtions.m b/Ash_AWord/Utility/Network/NSString+HXAddtions.m new file mode 100644 index 0000000..33c4576 --- /dev/null +++ b/Ash_AWord/Utility/Network/NSString+HXAddtions.m @@ -0,0 +1,87 @@ +// +// NSString+HXAddtions.m +// HXWeb +// +// Created by hufeng on 12-2-13. +// Copyright (c) 2012年 __MyCompanyName__. All rights reserved. +// + +#import "NSString+HXAddtions.h" + +@implementation NSString (HXAddtions) + ++(NSString *) jsonStringWithString:(NSString *) string{ + return [NSString stringWithFormat:@"\"%@\"", + [[string stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""] + ]; +} ++(NSString *) jsonStringWithArray:(NSArray *)array{ + NSMutableString *reString = [NSMutableString string]; + [reString appendString:@"["]; + NSMutableArray *values = [NSMutableArray array]; + for (id valueObj in array) { + NSString *value = [NSString jsonStringWithObject:valueObj]; + if (value) { + [values addObject:[NSString stringWithFormat:@"%@",value]]; + } + } + [reString appendFormat:@"%@",[values componentsJoinedByString:@","]]; + [reString appendString:@"]"]; + return reString; +} ++(NSString *) jsonStringWithDictionary:(NSDictionary *)dictionary{ + NSArray *keys = [dictionary allKeys]; + NSMutableString *reString = [NSMutableString string]; + [reString appendString:@"{"]; + NSMutableArray *keyValues = [NSMutableArray array]; + for (int i=0; i<[keys count]; i++) { + NSString *name = [keys objectAtIndex:i]; + id valueObj = [dictionary objectForKey:name]; + NSString *value = [NSString jsonStringWithObject:valueObj]; + if (value) { + [keyValues addObject:[NSString stringWithFormat:@"\"%@\":%@",name,value]]; + } + } + [reString appendFormat:@"%@",[keyValues componentsJoinedByString:@","]]; + [reString appendString:@"}"]; + return reString; +} ++(NSString *) jsonStringWithObject:(id) object{ + NSString *value = nil; + if (!object) { + return value; + } + + if ([object isKindOfClass:[NSString class]]) { + value = [NSString jsonStringWithString:object]; + }else if([object isKindOfClass:[NSDictionary class]]){ + value = [NSString jsonStringWithDictionary:object]; + }else if([object isKindOfClass:[NSArray class]]){ + value = [NSString jsonStringWithArray:object]; + }else if([object isKindOfClass:[NSNumber class]]){ + value = [NSString jsonStringWithString:[object stringValue]]; + } + return value; +} + ++(NSString *) jsonStringWithRTArray:(NSArray *)array{ + NSMutableString *reString = [[NSMutableString alloc] init]; + [reString appendString:@"{"]; + NSMutableArray *values = [[NSMutableArray alloc] initWithArray:array]; + for (int i=0; i<[values count]; i++) { + NSString *tempString = [values objectAtIndex:i]; + NSString *value = [NSString jsonStringWithString:tempString]; + if (i%2 == 0) { + [reString appendFormat:@"%@:",value]; + }else{ + if (i == [values count]-1) { + [reString appendFormat:@"%@",value]; + }else{ + [reString appendFormat:@"%@,",value]; + } + } + } + [reString appendFormat:@"}"]; + return reString; +} +@end \ No newline at end of file diff --git a/Ash_AWord/Utility/Network/PropertyEntity.h b/Ash_AWord/Utility/Network/PropertyEntity.h new file mode 100644 index 0000000..6466ecc --- /dev/null +++ b/Ash_AWord/Utility/Network/PropertyEntity.h @@ -0,0 +1,49 @@ +// +// PropertyEntity.h +// iOSFramework +// +// Created by xmfish on 14-9-22. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, HTTPRequestType) { + HTTPRequestTypeWithGET = 0, + HTTPRequestTypeWithPOST, + HTTPRequestTypeWithPOSTDATA, +}; + +@class responesOBJ,DCParserConfiguration; + +@interface proFile : NSObject + +@property (nonatomic, strong) NSData *data; +@property (nonatomic, strong) NSString *name; //参数名称 +@property (nonatomic, strong) NSString *filePath; +//@"image/jpeg" +@property (nonatomic, strong) NSString *mimeType; + +@property (nonatomic, strong) NSArray *img; + +@end + +@interface PropertyEntity : NSObject + +@property (nonatomic, assign) HTTPRequestType requireType; +@property (nonatomic, copy) NSString *reqURL; +@property (nonatomic, copy) NSString *url; +@property (nonatomic, strong) id pro; +@property (nonatomic, assign) Class responesOBJ; +@property (nonatomic, strong)DCParserConfiguration* config; + +@property (nonatomic, strong) proFile *pFile; +//@property (nonatomic, strong) NSArray *img; + +@property (nonatomic, assign) BOOL isCache; + +@property (nonatomic, copy) NSString *localJsonFileName; + +- (NSDictionary *)encodePro; + +@end diff --git a/Ash_AWord/Utility/Network/PropertyEntity.m b/Ash_AWord/Utility/Network/PropertyEntity.m new file mode 100644 index 0000000..84222af --- /dev/null +++ b/Ash_AWord/Utility/Network/PropertyEntity.m @@ -0,0 +1,36 @@ +// +// PropertyEntity.m +// iOSFramework +// +// Created by xmfish on 14-9-22. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import "PropertyEntity.h" +#import "XMFishBaseCode.h" + +@implementation proFile + +@end + +@implementation PropertyEntity + +- (NSDictionary *)encodePro +{ +// XMFishBaseCode *bc = [[XMFishBaseCode alloc] init]; +// +// [bc setToken:nil]; +// [bc setCallback:_url]; +// if (_pro) +// { +// [bc setParams:[NSMutableDictionary dictionaryWithDictionary:_pro]]; +// } +// +// NSMutableString *key = [[NSMutableString alloc]initWithString:[bc getBaseCode]]; +// [key stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +// NSDictionary *dic = @{@"mkey":key}; + + return _pro; +} + +@end diff --git a/Ash_AWord/Utility/Network/RequireEngine.h b/Ash_AWord/Utility/Network/RequireEngine.h new file mode 100644 index 0000000..ff50c19 --- /dev/null +++ b/Ash_AWord/Utility/Network/RequireEngine.h @@ -0,0 +1,22 @@ +// +// RequireEngine.h +// iOSFramework +// +// Created by xmfish on 14-9-22. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import +#import "XiaoYuAPIClient.h" + +@class PropertyEntity; + +typedef void (^CompletionBlock) (id viewModel); +typedef void (^FailedBlock) (NSError *error); + + +@interface RequireEngine : NSObject + ++ (AFHTTPRequestOperation *)requireWithProperty:(PropertyEntity *)proper completionBlock:(CompletionBlock)completionBlock failedBlock:(FailedBlock)failedBlock; + +@end diff --git a/Ash_AWord/Utility/Network/RequireEngine.m b/Ash_AWord/Utility/Network/RequireEngine.m new file mode 100644 index 0000000..bc51024 --- /dev/null +++ b/Ash_AWord/Utility/Network/RequireEngine.m @@ -0,0 +1,167 @@ +// +// RequireEngine.m +// iOSFramework +// +// Created by xmfish on 14-9-22. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import "RequireEngine.h" +#import "PropertyEntity.h" + +#import "MTLJSONAdapter.h" +#import "NSData+Godzippa.h" + +#import "EGOCache.h" + +@implementation RequireEngine + ++ (void)saveCacheWithAPIKey:(NSString *)apiKey property:(PropertyEntity *)proper data:(id)tweet{ + if (proper.isCache) { + SEL selector = NSSelectorFromString(@"errCode"); + IMP imp = [tweet methodForSelector:selector]; + NSString* (*func)(id, SEL) = (void *)imp; + NSString *tmpErrCode = func(tweet, selector); + DLog(@"%@", tmpErrCode); + + if ([tmpErrCode isKindOfClass:[NSString class]] && [tmpErrCode isEqualToString:@"0"]) { + [[EGOCache globalCache] setObject:tweet forKey:apiKey]; + + } + } +} + ++ (void)responseFromCacheWithAPIKey:(NSString *)apiKey property:(PropertyEntity *)proper completionBlock:(CompletionBlock)completionBlock{ + if (proper.isCache && [[EGOCache globalCache] hasCacheForKey:apiKey]) { + completionBlock([[EGOCache globalCache] objectForKey:apiKey]); + } +} + ++ (AFHTTPRequestOperation *)requireWithProperty:(PropertyEntity *)proper completionBlock:(CompletionBlock)completionBlock failedBlock:(FailedBlock)failedBlock{ + + NSString *apiKey = proper.url; + if (proper.pro && proper.pro[@"page"]) + { + apiKey = [apiKey stringByAppendingFormat:@"%@",proper.pro[@"page"]]; + } + + if (proper.requireType == HTTPRequestTypeWithGET) { + return [[XiaoYuAPIClient sharedClient] GET:[proper reqURL] + parameters:[proper url] ? [proper encodePro] : [proper pro] + success:^(AFHTTPRequestOperation * __unused task, id json) { + DLog(@"%@", json); + + //______________________________________ + #warning just 4 testing .... delete when release + NSURL *_localResourceFileURL = [[NSBundle mainBundle] URLForResource:proper.localJsonFileName ? proper.localJsonFileName : @"a" withExtension:@"json"]; + NSData *data = [NSData dataWithContentsOfURL:_localResourceFileURL]; + + if (data) { + json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:NULL]; + } + + DLog(@"%@", json); + //______________________________________ + + if(json) + { + NSError *error = nil; + id tweet = [MTLJSONAdapter modelOfClass:[proper.responesOBJ class] fromJSONDictionary:json error:&error]; + + if (completionBlock) + { + completionBlock(tweet); + } + + [self saveCacheWithAPIKey:apiKey property:proper data:tweet]; + } + else + { + + if (completionBlock) + { + completionBlock(nil); + } + } + } + failure:^(AFHTTPRequestOperation *__unused task, NSError *error) { + DLog(@"%@", error.description); + + if (failedBlock) + { + failedBlock(error); + }else + { + [MBProgressHUD errorHudWithView:nil label:kNetworkErrorTips hidesAfter:1.0]; + } + + [self responseFromCacheWithAPIKey:apiKey property:proper completionBlock:completionBlock]; + }]; + } else if(proper.requireType == HTTPRequestTypeWithPOST){ + return [[XiaoYuAPIClient sharedClient] POST:[proper reqURL] + parameters:[proper url] ? [proper encodePro] : [proper pro] + success:^(AFHTTPRequestOperation * __unused task, id JSON) { + + DLog(@"%@", JSON); + + NSError *error = nil; + id tweet = [MTLJSONAdapter modelOfClass:[proper.responesOBJ class] fromJSONDictionary:JSON error:&error]; + + if (completionBlock) { + completionBlock(tweet); + } + + [self saveCacheWithAPIKey:apiKey property:proper data:tweet]; + } + failure:^(AFHTTPRequestOperation *__unused task, NSError *error) { + NSLog(@"%@", error.description); + + if (failedBlock) { + failedBlock(error); + } + + [self responseFromCacheWithAPIKey:apiKey property:proper completionBlock:completionBlock]; + }]; + } else if(proper.requireType == HTTPRequestTypeWithPOSTDATA){ + return [[XiaoYuAPIClient sharedClient] POST:[proper reqURL] + parameters:[proper url] ? [proper encodePro] : [proper pro] + constructingBodyWithBlock:^(id formData) { + for (int i = 0; i < proper.pFile.img.count; i++) { + UIImage *uploadImage = proper.pFile.img[i]; + NSString *proName = [proper.pFile.name stringByAppendingFormat:@"%d", i]; +// NSData* data = UIImageJPEGRepresentation(uploadImage, 0.5); +// DLog(@"%ld kB",data.length/(8*1024)); + [formData appendPartWithFileData:UIImageJPEGRepresentation(uploadImage, 0.5) name:proper.pFile.img.count == 1 ? proper.pFile.name : proName fileName:[proName stringByAppendingString:@".jpg" ] mimeType:@"image/jpg"]; + } + } + success:^(AFHTTPRequestOperation * __unused task, id JSON) { + + DLog(@"%@", JSON); + + NSError *error = nil; + id tweet = [MTLJSONAdapter modelOfClass:[proper.responesOBJ class] fromJSONDictionary:JSON error:&error]; + + if (completionBlock) { + completionBlock(tweet); + } + + [self saveCacheWithAPIKey:apiKey property:proper data:tweet]; + } + failure:^(AFHTTPRequestOperation *__unused task, NSError *error) { + NSLog(@"%@", error.description); + + if (failedBlock) { + failedBlock(error); + } + + [self responseFromCacheWithAPIKey:apiKey property:proper completionBlock:completionBlock]; + }]; + } else { + NSAssert(proper.requireType == HTTPRequestTypeWithGET || proper.requireType == HTTPRequestTypeWithPOST, @"you must set requireType to GET or POST, or write something"); + return nil; + } +} + +@end diff --git a/Ash_AWord/Utility/Network/Serializer/AFgzipResponseSerializer.h b/Ash_AWord/Utility/Network/Serializer/AFgzipResponseSerializer.h new file mode 100644 index 0000000..a239e34 --- /dev/null +++ b/Ash_AWord/Utility/Network/Serializer/AFgzipResponseSerializer.h @@ -0,0 +1,23 @@ +// +// AFgzipResponseSerializer.h +// iOSFramework +// +// Created by aDu on 14-10-21. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import "AFURLResponseSerialization.h" + +@interface AFgzipResponseSerializer : AFHTTPResponseSerializer + +/** + The serializer used to generate requests to be compressed. + */ +@property (readonly, nonatomic, strong) id serializer; + +/** + Creates and returns an instance of `AFgzipRequestSerializer`, using the specified serializer to generate requests to be compressed. + */ ++ (instancetype)serializerWithSerializer:(id )serializer; + +@end diff --git a/Ash_AWord/Utility/Network/Serializer/AFgzipResponseSerializer.m b/Ash_AWord/Utility/Network/Serializer/AFgzipResponseSerializer.m new file mode 100644 index 0000000..9771f3e --- /dev/null +++ b/Ash_AWord/Utility/Network/Serializer/AFgzipResponseSerializer.m @@ -0,0 +1,122 @@ +// +// AFgzipResponseSerializer.m +// iOSFramework +// +// Created by aDu on 14-10-21. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import "AFgzipResponseSerializer.h" + +#import "NSData+Godzippa.h" + +@interface AFgzipResponseSerializer () +@property (readwrite, nonatomic, strong) id serializer; +@end + +@implementation AFgzipResponseSerializer + ++ (instancetype)serializerWithSerializer:(id)serializer { + AFgzipResponseSerializer *gzipSerializer = [self serializer]; + gzipSerializer.serializer = serializer; + + return gzipSerializer; +} + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error{ + + NSHTTPURLResponse *httpURLResponse = (NSHTTPURLResponse *)response; + NSDictionary *headers = httpURLResponse.allHeaderFields; + NSString *gzipStr = headers[@"Content-Encoding"]; +// NSString *identity = headers[@"Transfer-Encoding"]; + + NSError *serializationError = nil; + NSDictionary *json = nil; + + if ([gzipStr isEqualToString:@"gzip"]) { +#ifdef DEBUG + NSData *decompressedData = [data dataByGZipDecompressingDataWithError:nil]; + if (decompressedData) { + NSLog(@"%@", [NSString stringWithUTF8String:[decompressedData bytes]]); + + } +#endif + + if (!serializationError && data) { + NSError *decompressionError; + + NSData *decompressedData = [data dataByGZipDecompressingDataWithError:&decompressionError]; + + if (!decompressionError && decompressedData) { + NSError *serializationJSONError; + json = [NSJSONSerialization JSONObjectWithData:decompressedData + options:kNilOptions + error:&serializationJSONError]; + + if (!serializationJSONError && json) { + return json; + } else { + if (error) { + *error = serializationJSONError; + } + } + } else { + if (error) { + *error = decompressionError; + } + } + } else { + if (error) { + *error = serializationError; + } + } + + return json; + } else { + + NSError *serializationJSONError; + json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&serializationJSONError]; + + if (!serializationJSONError && json) { + return json; + } else { + if (error) { + *error = serializationJSONError; + } + } + + return json; + } +} + +#pragma mark - NSCoder + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super init]; + if (!self) { + return nil; + } + + self.serializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(serializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.serializer forKey:NSStringFromSelector(@selector(serializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFgzipResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.serializer = self.serializer; + + return serializer; +} + +@end diff --git a/Ash_AWord/Utility/Network/XMFishBaseCode.h b/Ash_AWord/Utility/Network/XMFishBaseCode.h new file mode 100644 index 0000000..9d1bb3b --- /dev/null +++ b/Ash_AWord/Utility/Network/XMFishBaseCode.h @@ -0,0 +1,27 @@ +// +// XMFishBaseCode.h +// FanLiKa +// +// Created by aDu on 14-10-23. +// Copyright (c) 2014年 xmfish. All rights reserved. +// + +#import + +@interface XMFishBaseCode : NSObject + +@property (nonatomic, strong, readwrite) NSString *token; +@property (nonatomic, strong, readwrite) NSString *callback; +@property (nonatomic, strong, readwrite) NSMutableDictionary *params; + +@property (nonatomic, strong, readwrite) NSString *uid; +@property (nonatomic, strong, readwrite) NSString *username; +@property (nonatomic, strong, readwrite) NSString *cityId; + +@property (nonatomic, strong, readwrite) NSString *mobile; + +- (NSString *)getBaseCode; + ++ (NSString *)getWkey; + +@end diff --git a/Ash_AWord/Utility/Network/XMFishBaseCode.m b/Ash_AWord/Utility/Network/XMFishBaseCode.m new file mode 100644 index 0000000..6ae479f --- /dev/null +++ b/Ash_AWord/Utility/Network/XMFishBaseCode.m @@ -0,0 +1,156 @@ +// +// XMFishBaseCode.m +// FanLiKa +// +// Created by aDu on 14-10-23. +// Copyright (c) 2014年 xmfish. All rights reserved. +// + +#import "XMFishBaseCode.h" +#import "NSString+HXAddtions.h" +#import "SecureUDID.h" +#import +@implementation XMFishBaseCode + +static char encodingTable[] = ""; + +- (id)init +{ + self = [super init]; + + return self; +} + +- (void)setParams:(NSMutableDictionary *)Params +{ + if (_params != Params) + { + _params = nil; + _params = Params; + } +} + +- (NSString *)getBaseCode +{ + + + NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; + + NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; + data[@"timestamp"] = [NSString stringWithFormat:@"%f", timeStamp]; + + + //app版本号 + NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; + if (appVersion) + { + [data setObject:appVersion forKey:@"appversion"]; + } + + //网络类型 + NSString *netWorkType = [NSString stringWithFormat:@"%d", [CommonUtil networkType]]; + [data setObject:netWorkType forKey:@"networktype"]; + + //设备类型 + NSString *deviceName = [CommonUtil getUtsName]; + if (deviceName) + { + [data setObject:deviceName forKey:@"devicename"]; + } + + //唯一的设备号,用于服务端识别游客 + NSString *domain = @"com.shengshi.ufun"; + NSString *key = @"xmfishufunkey"; + NSString *udid = [SecureUDID UDIDForDomain:domain usingKey:key]; + if (udid) + { + [data setObject:udid forKey:@"udid"]; + } + + DLog(@"Http param : %@", data); + return [NSString jsonStringWithDictionary:data]; +} + +- (NSData *)encode:(const uint8_t*) input length:(NSInteger) length +{ + NSInteger _dlen =((length + 2)/3 * 4)+1; + NSMutableData* tmpdata = [NSMutableData dataWithLength:_dlen]; + uint8_t* tmpchar = (uint8_t*)tmpdata.mutableBytes; + srand((unsigned)time(NULL)); + + for (NSInteger i = 0; i < length; i += 3) + { + NSInteger value = 0; + for (NSInteger j = i; j < (i + 3); j++) + { + value <<= 8; + if (j < length) + { + value |= (0xFF & input[j]); + } + } + NSInteger index = (i / 3) * 4; + tmpchar[index + 0] = encodingTable[(value >> 18) & 0x3F]; + tmpchar[index + 1] = encodingTable[(value >> 12) & 0x3F]; + if ((i + 1) < length) + { + tmpchar[index + 2] = encodingTable[(value >> 6) & 0x3F]; + } + else + { + tmpchar[index + 2] = 0; + _dlen--; + } + if ((i + 2) < length) + { + tmpchar[index + 3] = encodingTable[(value >> 0) & 0x3F]; + } + else + { + tmpchar[index + 3] = 0; + _dlen--; + } + } + int pos=1+ abs((rand()*1234))%60; //字符偏移量 + int add=encodingTable[pos]; + int step=((add%_dlen)%5)+4; + NSMutableData *_output = [NSMutableData dataWithLength:_dlen]; + uint8_t* output = (uint8_t*)_output.mutableBytes; + tmpchar[_dlen-1]=add; + for (int k = 0; k <= _dlen; k+=step) + { + int m=0; + if (k+step+1 > _dlen) + { + while (m < step && tmpchar[k+m] > 0) + { + output[k+m]=tmpchar[k+m]; + m++; + } + } + else + { + while (m < step && tmpchar[k+step-m-1] > 0) + { + output[k+m]=tmpchar[k+step-m-1]; + m++; + } + } + } + return _output; +} + +- (NSString *)encode:(NSString *)string +{ + NSData *rayBety=[string dataUsingEncoding:NSUTF8StringEncoding]; + NSData *newBety=[self encode:rayBety.bytes length:rayBety.length]; + NSString *newString=[[NSString alloc]initWithData:newBety encoding:NSUTF8StringEncoding]; + return newString; +} + ++ (NSString *)getWkey +{ + return [[[XMFishBaseCode alloc] init] getBaseCode]; +} + +@end diff --git a/Ash_AWord/Utility/Network/XiaoYuAPIClient.h b/Ash_AWord/Utility/Network/XiaoYuAPIClient.h new file mode 100644 index 0000000..6c8007c --- /dev/null +++ b/Ash_AWord/Utility/Network/XiaoYuAPIClient.h @@ -0,0 +1,15 @@ +// +// XiaoYuAPIClient.h +// iOSFramework +// +// Created by xmfish on 14-9-22. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import "AFHTTPRequestOperationManager.h" + +@interface XiaoYuAPIClient : AFHTTPRequestOperationManager + ++ (instancetype)sharedClient; ++ (NSString *)getApiBaseString; +@end diff --git a/Ash_AWord/Utility/Network/XiaoYuAPIClient.m b/Ash_AWord/Utility/Network/XiaoYuAPIClient.m new file mode 100644 index 0000000..feeb084 --- /dev/null +++ b/Ash_AWord/Utility/Network/XiaoYuAPIClient.m @@ -0,0 +1,50 @@ +// +// XiaoYuAPIClient.m +// iOSFramework +// +// Created by xmfish on 14-9-22. +// Copyright (c) 2014年 aDu. All rights reserved. +// + +#import "XiaoYuAPIClient.h" + +#import "AFgzipResponseSerializer.h" + +#import "AFURLRequestSerialization.h" + +@implementation XiaoYuAPIClient + + + ++ (instancetype)sharedClient +{ + static XiaoYuAPIClient *_sharedClient = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedClient = [[XiaoYuAPIClient alloc] initWithBaseURL:[NSURL URLWithString:[self getApiBaseString]]]; + _sharedClient.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; + + [_sharedClient.requestSerializer setValue:@"text/html" forHTTPHeaderField:@"Content-Type"]; + _sharedClient.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"]; + +// [_sharedClient.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; + +// [_sharedClient.requestSerializer setValue:@"identity" forHTTPHeaderField:@"Accept-Encoding"]; + +// _sharedClient.responseSerializer = [AFgzipResponseSerializer serializerWithSerializer:[AFHTTPResponseSerializer serializer]]; +// +// _sharedClient.requestSerializer = [AFJSONRequestSerializer serializer]; +// [_sharedClient.responseSerializer setValue:@"gizp" forKey:@"Content-Encoding"]; +// _sharedClient.responseSerializer.acceptableContentTypes = [_sharedClient.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"]; + + }); + + return _sharedClient; +} + ++ (NSString *)getApiBaseString +{ + return Ash_AWord_API_URL; +} + +@end diff --git a/Ash_AWord/Utility/Tool/CommonUtil.h b/Ash_AWord/Utility/Tool/CommonUtil.h new file mode 100644 index 0000000..b825e44 --- /dev/null +++ b/Ash_AWord/Utility/Tool/CommonUtil.h @@ -0,0 +1,61 @@ +// +// CommonUtil.h +// WechatPayDemo +// +// Created by Alvin on 3/22/14. +// Copyright (c) 2014 Alvin. All rights reserved. +// + +#import + +typedef enum +{ + kNetworkTypeNone = 0, + kNetworkType2G = 1, + kNetworkType3G = 2, + kNetworkType4G = 3, + kNetworkTypeLET = 4, + kNetworkTypeWiFi = 5, +} +NetworkType; + +@interface CommonUtil : NSObject + +//获取系统网络类型 ++ (NetworkType)networkType; + +//设备类型比如iPhone3,1(iphone4) ++ (NSString *)getUtsName; + ++ (NSString *)md5:(NSString *)input; + ++ (NSString *)sha1:(NSString *)input; + ++ (NSString *)getIPAddress:(BOOL)preferIPv4; + ++ (NSDictionary *)getIPAddresses; + +/** + * NSdate转为日历 + */ ++(NSDateComponents*)componentsWithDate:(NSDate*)date; ++(NSDateComponents*)componentsWithDate:(NSDate*)date; +/** + * NSdate相差天数 + */ ++(NSInteger)dayWithBeginDate:(NSDate*)beginDate withEndDate:(NSDate*)endDate; +/** + * NSdate返回与指定NSdate相差天数的新的NSDate + */ ++(NSDate*)newDateWithDate:(NSDate*)oldDate withOffSetDay:(NSInteger)day; +//判断是否同一天 ++(BOOL)isSameDay:(NSDate*)date1 date2:(NSDate*)date2; +@end + +@interface NSString (MD5Extensions) +- (NSString *)md5; +@end + +@interface NSData (MD5Extensions) +- (NSString*)md5; +@end diff --git a/Ash_AWord/Utility/Tool/CommonUtil.m b/Ash_AWord/Utility/Tool/CommonUtil.m new file mode 100644 index 0000000..370884f --- /dev/null +++ b/Ash_AWord/Utility/Tool/CommonUtil.m @@ -0,0 +1,224 @@ +// +// CommonUtil.m +// WechatPayDemo +// +// Created by Alvin on 3/22/14. +// Copyright (c) 2014 Alvin. All rights reserved. +// + +#import "CommonUtil.h" +#import +#include +#include +#include +#import + +#define IOS_CELLULAR @"pdp_ip0" +#define IOS_WIFI @"en0" +#define IP_ADDR_IPv4 @"ipv4" +#define IP_ADDR_IPv6 @"ipv6" + +@implementation CommonUtil + ++ (NetworkType)networkType +{ + NSArray *subviews = [[[[UIApplication sharedApplication] valueForKey:@"statusBar"] valueForKey:@"foregroundView"]subviews]; + NSNumber *dataNetworkItemView = nil; + + for (id subview in subviews) + { + if([subview isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]]) { + dataNetworkItemView = subview; + break; + } + } + + NetworkType type = (NetworkType)[[dataNetworkItemView valueForKey:@"dataNetworkType"] integerValue]; + return type; +} + +//设备类型比如iPhone3,1(iphone4) ++ (NSString *)getUtsName +{ + NSString *machine = nil; + struct utsname utsName; + int utsInt = uname(&utsName); + if (utsInt >= 0) + { + machine = [NSString stringWithCString:utsName.machine encoding:NSASCIIStringEncoding]; + } + return machine; +} + ++ (NSString *)md5:(NSString *)input +{ + const char *cStr = [input UTF8String]; + unsigned char digest[16]; + CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call + + NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; + + for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) + [output appendFormat:@"%02x", digest[i]]; + + return output; +} + ++ (NSString *)sha1:(NSString *)input +{ + const char *ptr = [input UTF8String]; + + int i =0; + int len = strlen(ptr); + Byte byteArray[len]; + while (i!=len) + { + unsigned eachChar = *(ptr + i); + unsigned low8Bits = eachChar & 0xFF; + + byteArray[i] = low8Bits; + i++; + } + + unsigned char digest[CC_SHA1_DIGEST_LENGTH]; + + CC_SHA1(byteArray, len, digest); + + NSMutableString *hex = [NSMutableString string]; + for (int i=0; i<20; i++) + [hex appendFormat:@"%02x", digest[i]]; + + NSString *immutableHex = [NSString stringWithString:hex]; + + return immutableHex; +} + ++ (NSString *)getIPAddress:(BOOL)preferIPv4 +{ + NSArray *searchArray = preferIPv4 ? + @[ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] : + @[ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ; + + NSDictionary *addresses = [self getIPAddresses]; + //NSLog(@"addresses: %@", addresses); + + __block NSString *address; + [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) + { + address = addresses[key]; + if(address) *stop = YES; + } ]; + return address ? address : @"0.0.0.0"; +} + ++ (NSDictionary *)getIPAddresses +{ + NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8]; + + // retrieve the current interfaces - returns 0 on success + struct ifaddrs *interfaces; + if(!getifaddrs(&interfaces)) { + // Loop through linked list of interfaces + struct ifaddrs *interface; + for(interface=interfaces; interface; interface=interface->ifa_next) { + if(!(interface->ifa_flags & IFF_UP) || (interface->ifa_flags & IFF_LOOPBACK)) { + continue; // deeply nested code harder to read + } + const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr; + if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) { + NSString *name = [NSString stringWithUTF8String:interface->ifa_name]; + char addrBuf[INET6_ADDRSTRLEN]; + if(inet_ntop(addr->sin_family, &addr->sin_addr, addrBuf, sizeof(addrBuf))) { + NSString *key = [NSString stringWithFormat:@"%@/%@", name, addr->sin_family == AF_INET ? IP_ADDR_IPv4 : IP_ADDR_IPv6]; + addresses[key] = [NSString stringWithUTF8String:addrBuf]; + } + } + } + // Free memory + freeifaddrs(interfaces); + } + + // The dictionary keys have the form "interface" "/" "ipv4 or ipv6" + return [addresses count] ? addresses : nil; +} ++(NSDateComponents*)componentsWithDate:(NSDate *)date +{ + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] ; + NSInteger unitFlags = NSYearCalendarUnit | + NSMonthCalendarUnit | + NSDayCalendarUnit | + NSWeekdayCalendarUnit | + NSHourCalendarUnit | + NSMinuteCalendarUnit | + NSSecondCalendarUnit; + NSDateComponents *componets = [[NSCalendar autoupdatingCurrentCalendar] components:NSWeekdayCalendarUnit fromDate:date]; + componets = [calendar components:unitFlags fromDate:date]; + return componets; +} ++(NSInteger)dayWithBeginDate:(NSDate *)beginDate withEndDate:(NSDate *)endDate +{ + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; + unsigned int unitFlag = NSDayCalendarUnit; + NSDateComponents *components = [calendar components:unitFlag fromDate:beginDate toDate:endDate options:0]; + return [components day] + 1; +} ++(NSDate*)newDateWithDate:(NSDate *)oldDate withOffSetDay:(NSInteger)day +{ + NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; + + NSDateComponents *comps = nil; + + comps = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:oldDate]; + + NSDateComponents *adcomps = [[NSDateComponents alloc] init]; + + [adcomps setYear:0]; + + [adcomps setMonth:0]; + + [adcomps setDay:day]; + return [calendar dateByAddingComponents:adcomps toDate:oldDate options:0]; +} ++(BOOL)isSameDay:(NSDate *)date1 date2:(NSDate *)date2 +{ + NSCalendar* calendar = [NSCalendar currentCalendar]; + unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit; + NSDateComponents* comp1 = [calendar components:unitFlags fromDate:date1]; + NSDateComponents* comp2 = [calendar components:unitFlags fromDate:date2]; + + return [comp1 day] == [comp2 day] && + [comp1 month] == [comp2 month] && + [comp1 year] == [comp2 year]; +} +@end + +@implementation NSString (MD5Extensions) +- (NSString *) md5 +{ + const char *cStr = [self UTF8String]; + unsigned char result[16]; + CC_MD5( cStr, strlen(cStr), result ); // This is the md5 call + return [NSString stringWithFormat: + @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], + result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], + result[12], result[13], result[14], result[15] + ]; +} +@end + +@implementation NSData (MD5Extensions) +- (NSString*)md5 +{ + unsigned char result[16]; + CC_MD5( self.bytes, self.length, result ); // This is the md5 call + return [NSString stringWithFormat: + @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], + result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], + result[12], result[13], result[14], result[15] + ]; +} +@end diff --git a/Ash_AWord/Vender/MBProgressHUD/MBProgressHUD+Util.h b/Ash_AWord/Vender/MBProgressHUD/MBProgressHUD+Util.h new file mode 100644 index 0000000..d66c85a --- /dev/null +++ b/Ash_AWord/Vender/MBProgressHUD/MBProgressHUD+Util.h @@ -0,0 +1,24 @@ +// +// MBProgressHUD+Util.h +// ManagerDemo +// +// Created by xmfish on 14-8-12. +// Copyright (c) 2014年 ash. All rights reserved. +// + +#import "MBProgressHUD.h" + +@interface MBProgressHUD (Util) +/** + * Finds all HUD subviews and returns them. + * + * @param view The view that is going to be searched. + * @return All found HUD views (array of MBProgressHUD objects). + */ + ++ (void)hudWithView:(UIView *)view label:(NSString *)msg; + ++ (void)errorHudWithView:(UIView *)view label:(NSString *)msg hidesAfter:(NSTimeInterval)delay; + ++ (void)checkHudWithView:(UIView *)view label:(NSString *)msg hidesAfter:(NSTimeInterval)delay; +@end diff --git a/Ash_AWord/Vender/MBProgressHUD/MBProgressHUD+Util.m b/Ash_AWord/Vender/MBProgressHUD/MBProgressHUD+Util.m new file mode 100644 index 0000000..81c8814 --- /dev/null +++ b/Ash_AWord/Vender/MBProgressHUD/MBProgressHUD+Util.m @@ -0,0 +1,66 @@ + +#import "MBProgressHUD+Util.h" + +static MBProgressHUD *activityHUD = nil; +@implementation MBProgressHUD(Util) + + + + ++ (void)hudWithView:(UIView *)view label:(NSString *)msg +{ + if (view == nil) + { + view = [UIApplication sharedApplication].delegate.window; + } + + + MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:view]; + + HUD.dimBackground = NO; + HUD.animationType = MBProgressHUDAnimationFade; + [view addSubview:HUD]; + HUD.mode = MBProgressHUDModeIndeterminate; + HUD.labelText = msg; + [HUD show:YES]; + activityHUD = HUD; +} + ++ (void)errorHudWithView:(UIView *)view label:(NSString *)msg hidesAfter:(NSTimeInterval)delay +{ + if (view == nil) + { + view = [UIApplication sharedApplication].delegate.window; + } + [MBProgressHUD hideAllHUDsForView:view animated:YES]; + MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:view]; + HUD.dimBackground = NO; + HUD.animationType = MBProgressHUDAnimationFade; + [view addSubview:HUD]; + HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_dock_forum"]]; + HUD.mode = MBProgressHUDModeCustomView; + HUD.labelText = msg; + [HUD show:YES]; + [HUD hide:YES afterDelay:delay]; +} + ++ (void)checkHudWithView:(UIView *)view label:(NSString *)msg hidesAfter:(NSTimeInterval)delay +{ + if (view == nil) + { + view = [UIApplication sharedApplication].delegate.window; + } + + [MBProgressHUD hideAllHUDsForView:view animated:YES]; + MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:view]; + + HUD.dimBackground = NO; + HUD.animationType = MBProgressHUDAnimationFade; + [view addSubview:HUD]; + HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_success"]]; + HUD.mode = MBProgressHUDModeCustomView; + HUD.labelText = msg; + [HUD show:YES]; + [HUD hide:YES afterDelay:delay]; +} +@end \ No newline at end of file diff --git a/Ash_AWord/ViewControllers/Main/MainViewController.h b/Ash_AWord/ViewControllers/Main/MainViewController.h new file mode 100644 index 0000000..84c829b --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MainViewController.h @@ -0,0 +1,13 @@ +// +// MainViewController.h +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "ViewController.h" + +@interface MainViewController : ViewController + +@end diff --git a/Ash_AWord/ViewControllers/Main/MainViewController.m b/Ash_AWord/ViewControllers/Main/MainViewController.m new file mode 100644 index 0000000..02b1b49 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MainViewController.m @@ -0,0 +1,38 @@ +// +// MainViewController.m +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "MainViewController.h" + +@interface MainViewController () + +@end + +@implementation MainViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Ash_AWord/ViewControllers/Main/MainViewController.xib b/Ash_AWord/ViewControllers/Main/MainViewController.xib new file mode 100644 index 0000000..9a685fd --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MainViewController.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Ash_AWord/ViewControllers/Main/MessageViewController.h b/Ash_AWord/ViewControllers/Main/MessageViewController.h new file mode 100644 index 0000000..b883311 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MessageViewController.h @@ -0,0 +1,13 @@ +// +// MessageViewController.h +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "ViewController.h" + +@interface MessageViewController : ViewController + +@end diff --git a/Ash_AWord/ViewControllers/Main/MessageViewController.m b/Ash_AWord/ViewControllers/Main/MessageViewController.m new file mode 100644 index 0000000..0841e30 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MessageViewController.m @@ -0,0 +1,38 @@ +// +// MessageViewController.m +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "MessageViewController.h" + +@interface MessageViewController () + +@end + +@implementation MessageViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Ash_AWord/ViewControllers/Main/MessageViewController.xib b/Ash_AWord/ViewControllers/Main/MessageViewController.xib new file mode 100644 index 0000000..e93b557 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MessageViewController.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Ash_AWord/ViewControllers/Main/MyViewController.h b/Ash_AWord/ViewControllers/Main/MyViewController.h new file mode 100644 index 0000000..85b3e19 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MyViewController.h @@ -0,0 +1,13 @@ +// +// MyViewController.h +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "ViewController.h" + +@interface MyViewController : ViewController + +@end diff --git a/Ash_AWord/ViewControllers/Main/MyViewController.m b/Ash_AWord/ViewControllers/Main/MyViewController.m new file mode 100644 index 0000000..69c3bcb --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MyViewController.m @@ -0,0 +1,38 @@ +// +// MyViewController.m +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "MyViewController.h" + +@interface MyViewController () + +@end + +@implementation MyViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Ash_AWord/ViewControllers/Main/MyViewController.xib b/Ash_AWord/ViewControllers/Main/MyViewController.xib new file mode 100644 index 0000000..c181fe3 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/MyViewController.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Ash_AWord/ViewControllers/Main/NoteViewController.h b/Ash_AWord/ViewControllers/Main/NoteViewController.h new file mode 100644 index 0000000..0189eaa --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/NoteViewController.h @@ -0,0 +1,13 @@ +// +// NoteViewController.h +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "ViewController.h" + +@interface NoteViewController : ViewController + +@end diff --git a/Ash_AWord/ViewControllers/Main/NoteViewController.m b/Ash_AWord/ViewControllers/Main/NoteViewController.m new file mode 100644 index 0000000..79d513d --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/NoteViewController.m @@ -0,0 +1,38 @@ +// +// NoteViewController.m +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "NoteViewController.h" + +@interface NoteViewController () + +@end + +@implementation NoteViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Ash_AWord/ViewControllers/Main/NoteViewController.xib b/Ash_AWord/ViewControllers/Main/NoteViewController.xib new file mode 100644 index 0000000..632d090 --- /dev/null +++ b/Ash_AWord/ViewControllers/Main/NoteViewController.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Ash_AWord/ViewModels/LoginViewModel.h b/Ash_AWord/ViewModels/LoginViewModel.h new file mode 100644 index 0000000..c415805 --- /dev/null +++ b/Ash_AWord/ViewModels/LoginViewModel.h @@ -0,0 +1,13 @@ +// +// LoginViewModel.h +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "BaseViewModel.h" + +@interface LoginViewModel : BaseViewModel + +@end diff --git a/Ash_AWord/ViewModels/LoginViewModel.m b/Ash_AWord/ViewModels/LoginViewModel.m new file mode 100644 index 0000000..d525806 --- /dev/null +++ b/Ash_AWord/ViewModels/LoginViewModel.m @@ -0,0 +1,13 @@ +// +// LoginViewModel.m +// Ash_AWord +// +// Created by xmfish on 15/3/30. +// Copyright (c) 2015年 ash. All rights reserved. +// + +#import "LoginViewModel.h" + +@implementation LoginViewModel + +@end diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..71c8dc7 --- /dev/null +++ b/Podfile @@ -0,0 +1,13 @@ +platform :ios, '6.0' +pod 'Reachability' +pod 'SBJson' +pod 'SDWebImage' +pod 'DTCoreText' +pod 'STableViewController' +pod 'MBProgressHUD' +pod 'AFNetworking' +pod 'Mantle' +pod 'Godzippa' +pod 'EGOCache' +pod 'SecureUDID' +pod 'MJRefresh' \ No newline at end of file diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h new file mode 100644 index 0000000..c1e37f6 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h @@ -0,0 +1,67 @@ +// AFHTTPRequestOperation.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import "AFURLConnectionOperation.h" + +/** + `AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request. + */ +@interface AFHTTPRequestOperation : AFURLConnectionOperation + +///------------------------------------------------ +/// @name Getting HTTP URL Connection Information +///------------------------------------------------ + +/** + The last HTTP response received by the operation's connection. + */ +@property (readonly, nonatomic, strong) NSHTTPURLResponse *response; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. + + @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +/** + An object constructed by the `responseSerializer` from the response and response data. Returns `nil` unless the operation `isFinished`, has a `response`, and has `responseData` with non-zero content length. If an error occurs during serialization, `nil` will be returned, and the `error` property will be populated with the serialization error. + */ +@property (readonly, nonatomic, strong) id responseObject; + +///----------------------------------------------------------- +/// @name Setting Completion Block Success / Failure Callbacks +///----------------------------------------------------------- + +/** + Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed. + + This method should be overridden in subclasses in order to specify the response object passed into the success block. + + @param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request. + @param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request. + */ +- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m new file mode 100644 index 0000000..1f93549 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m @@ -0,0 +1,207 @@ +// AFHTTPRequestOperation.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFHTTPRequestOperation.h" + +static dispatch_queue_t http_request_operation_processing_queue() { + static dispatch_queue_t af_http_request_operation_processing_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT); + }); + + return af_http_request_operation_processing_queue; +} + +static dispatch_group_t http_request_operation_completion_group() { + static dispatch_group_t af_http_request_operation_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_http_request_operation_completion_group = dispatch_group_create(); + }); + + return af_http_request_operation_completion_group; +} + +#pragma mark - + +@interface AFURLConnectionOperation () +@property (readwrite, nonatomic, strong) NSURLRequest *request; +@property (readwrite, nonatomic, strong) NSURLResponse *response; +@end + +@interface AFHTTPRequestOperation () +@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; +@property (readwrite, nonatomic, strong) id responseObject; +@property (readwrite, nonatomic, strong) NSError *responseSerializationError; +@property (readwrite, nonatomic, strong) NSRecursiveLock *lock; +@end + +@implementation AFHTTPRequestOperation +@dynamic response; +@dynamic lock; + +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest { + self = [super initWithRequest:urlRequest]; + if (!self) { + return nil; + } + + self.responseSerializer = [AFHTTPResponseSerializer serializer]; + + return self; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + [self.lock lock]; + _responseSerializer = responseSerializer; + self.responseObject = nil; + self.responseSerializationError = nil; + [self.lock unlock]; +} + +- (id)responseObject { + [self.lock lock]; + if (!_responseObject && [self isFinished] && !self.error) { + NSError *error = nil; + self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error]; + if (error) { + self.responseSerializationError = error; + } + } + [self.lock unlock]; + + return _responseObject; +} + +- (NSError *)error { + if (_responseSerializationError) { + return _responseSerializationError; + } else { + return [super error]; + } +} + +#pragma mark - AFHTTPRequestOperation + +- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" +#pragma clang diagnostic ignored "-Wgnu" + self.completionBlock = ^{ + if (self.completionGroup) { + dispatch_group_enter(self.completionGroup); + } + + dispatch_async(http_request_operation_processing_queue(), ^{ + if (self.error) { + if (failure) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(self, self.error); + }); + } + } else { + id responseObject = self.responseObject; + if (self.error) { + if (failure) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(self, self.error); + }); + } + } else { + if (success) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + success(self, responseObject); + }); + } + } + } + + if (self.completionGroup) { + dispatch_group_leave(self.completionGroup); + } + }); + }; +#pragma clang diagnostic pop +} + +#pragma mark - AFURLRequestOperation + +- (void)pause { + [super pause]; + + u_int64_t offset = 0; + if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) { + offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; + } else { + offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length]; + } + + NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy]; + if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) { + [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"]; + } + [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"]; + self.request = mutableURLRequest; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestOperation *operation = [super copyWithZone:zone]; + + operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h new file mode 100644 index 0000000..214937e --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h @@ -0,0 +1,324 @@ +// AFHTTPRequestOperationManager.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import +#import + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +#import +#else +#import +#endif + +#import "AFHTTPRequestOperation.h" +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" +#import "AFSecurityPolicy.h" +#import "AFNetworkReachabilityManager.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFHTTPRequestOperationManager` encapsulates the common patterns of communicating with a web application over HTTP, including request creation, response serialization, network reachability monitoring, and security, as well as request operation management. + + ## Subclassing Notes + + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. + + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. + + ## Methods to Override + + To change the behavior of all request operation construction for an `AFHTTPRequestOperationManager` subclass, override `HTTPRequestOperationWithRequest:success:failure`. + + ## Serialization + + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` + + ## URL Construction Using Relative Paths + + For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. + + Below are a few examples of how `baseURL` and relative paths interact: + + NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; + [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz + [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo + [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ + [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ + + Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. + + ## Network Reachability Monitoring + + Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. + + ## NSSecureCoding & NSCopying Caveats + + `AFHTTPRequestOperationManager` conforms to the `NSSecureCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however: + + - Archives and copies of HTTP clients will be initialized with an empty operation queue. + - NSSecureCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set. + */ +@interface AFHTTPRequestOperationManager : NSObject + +/** + The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. + */ +@property (readonly, nonatomic, strong) NSURL *baseURL; + +/** + Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. + + @warning `requestSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to a JSON serializer, which serializes data from responses with a `application/json` MIME type, and falls back to the raw data object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +/** + The operation queue on which request operations are scheduled and run. + */ +@property (nonatomic, strong) NSOperationQueue *operationQueue; + +///------------------------------- +/// @name Managing URL Credentials +///------------------------------- + +/** + Whether request operations should consult the credential storage for authenticating the connection. `YES` by default. + + @see AFURLConnectionOperation -shouldUseCredentialStorage + */ +@property (nonatomic, assign) BOOL shouldUseCredentialStorage; + +/** + The credential used by request operations for authentication challenges. + + @see AFURLConnectionOperation -credential + */ +@property (nonatomic, strong) NSURLCredential *credential; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created request operations to evaluate server trust for secure connections. `AFHTTPRequestOperationManager` uses the `defaultPolicy` unless otherwise specified. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +///------------------------------------ +/// @name Managing Network Reachability +///------------------------------------ + +/** + The network reachability manager. `AFHTTPRequestOperationManager` uses the `sharedManager` by default. + */ +@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; + +///------------------------------- +/// @name Managing Callback Queues +///------------------------------- + +/** + The dispatch queue for the `completionBlock` of request operations. If `NULL` (default), the main queue is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign) dispatch_queue_t completionQueue; +#endif + +/** + The dispatch group for the `completionBlock` of request operations. If `NULL` (default), a private dispatch group is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign) dispatch_group_t completionGroup; +#endif + +///--------------------------------------------- +/// @name Creating and Initializing HTTP Clients +///--------------------------------------------- + +/** + Creates and returns an `AFHTTPRequestOperationManager` object. + */ ++ (instancetype)manager; + +/** + Initializes an `AFHTTPRequestOperationManager` object with the specified base URL. + + This is the designated initializer. + + @param url The base URL for the HTTP client. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(NSURL *)url NS_DESIGNATED_INITIALIZER; + +///--------------------------------------- +/// @name Managing HTTP Request Operations +///--------------------------------------- + +/** + Creates an `AFHTTPRequestOperation`, and sets the response serializers to that of the HTTP client. + + @param request The request object to be loaded asynchronously during execution of the operation. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred. + */ +- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +///--------------------------- +/// @name Making HTTP Requests +///--------------------------- + +/** + Creates and runs an `AFHTTPRequestOperation` with a `GET` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)GET:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `HEAD` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single arguments: the request operation. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)HEAD:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a multipart `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `PUT` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)PUT:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `PATCH` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)PATCH:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `DELETE` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (AFHTTPRequestOperation *)DELETE:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +@end + diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m new file mode 100644 index 0000000..665111f --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m @@ -0,0 +1,285 @@ +// AFHTTPRequestOperationManager.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import "AFHTTPRequestOperationManager.h" +#import "AFHTTPRequestOperation.h" + +#import +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#endif + +@interface AFHTTPRequestOperationManager () +@property (readwrite, nonatomic, strong) NSURL *baseURL; +@end + +@implementation AFHTTPRequestOperationManager + ++ (instancetype)manager { + return [[self alloc] initWithBaseURL:nil]; +} + +- (instancetype)init { + return [self initWithBaseURL:nil]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url { + self = [super init]; + if (!self) { + return nil; + } + + // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected + if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { + url = [url URLByAppendingPathComponent:@""]; + } + + self.baseURL = url; + + self.requestSerializer = [AFHTTPRequestSerializer serializer]; + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + + self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; + + self.operationQueue = [[NSOperationQueue alloc] init]; + + self.shouldUseCredentialStorage = YES; + + return self; +} + +#pragma mark - + +#ifdef _SYSTEMCONFIGURATION_H +#endif + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + NSParameterAssert(requestSerializer); + + _requestSerializer = requestSerializer; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + _responseSerializer = responseSerializer; +} + +#pragma mark - + +- (AFHTTPRequestOperation *)HTTPRequestOperationWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + return [self HTTPRequestOperationWithRequest:request success:success failure:failure]; +} + +- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + operation.responseSerializer = self.responseSerializer; + operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage; + operation.credential = self.credential; + operation.securityPolicy = self.securityPolicy; + + [operation setCompletionBlockWithSuccess:success failure:failure]; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +#pragma mark - + +- (AFHTTPRequestOperation *)GET:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)HEAD:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(AFHTTPRequestOperation *requestOperation, __unused id responseObject) { + if (success) { + success(requestOperation); + } + } failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)PUT:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)PATCH:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)DELETE:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))]; + + self = [self initWithBaseURL:baseURL]; + if (!self) { + return nil; + } + + self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; + [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestOperationManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL]; + + HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; + HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; + + return HTTPClient; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h new file mode 100644 index 0000000..e77da03 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1,248 @@ +// AFHTTPSessionManager.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import +#import + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +#import +#else +#import +#endif + +#import "AFURLSessionManager.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. + + ## Subclassing Notes + + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. + + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. + + ## Methods to Override + + To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`. + + ## Serialization + + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` + + ## URL Construction Using Relative Paths + + For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. + + Below are a few examples of how `baseURL` and relative paths interact: + + NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; + [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz + [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo + [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ + [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ + + Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. + */ + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) + +@interface AFHTTPSessionManager : AFURLSessionManager + +/** + The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. + */ +@property (readonly, nonatomic, strong) NSURL *baseURL; + +/** + Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. + + @warning `requestSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns an `AFHTTPSessionManager` object. + */ ++ (instancetype)manager; + +/** + Initializes an `AFHTTPSessionManager` object with the specified base URL. + + @param url The base URL for the HTTP client. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(NSURL *)url; + +/** + Initializes an `AFHTTPSessionManager` object with the specified base URL. + + This is the designated initializer. + + @param url The base URL for the HTTP client. + @param configuration The configuration used to create the managed session. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(NSURL *)url + sessionConfiguration:(NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +///--------------------------- +/// @name Making HTTP Requests +///--------------------------- + +/** + Creates and runs an `NSURLSessionDataTask` with a `GET` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `HEAD` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)HEAD:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `PUT` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)PUT:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `PATCH` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)PATCH:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `DELETE` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (NSURLSessionDataTask *)DELETE:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +@end + +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m new file mode 100644 index 0000000..5a0fd6f --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m @@ -0,0 +1,322 @@ +// AFHTTPSessionManager.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFHTTPSessionManager.h" + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) + +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" + +#import +#import + +#ifdef _SYSTEMCONFIGURATION_H +#import +#import +#import +#import +#import +#endif + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#endif + +@interface AFHTTPSessionManager () +@property (readwrite, nonatomic, strong) NSURL *baseURL; +@end + +@implementation AFHTTPSessionManager +@dynamic responseSerializer; + ++ (instancetype)manager { + return [[[self class] alloc] initWithBaseURL:nil]; +} + +- (instancetype)init { + return [self initWithBaseURL:nil]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url { + return [self initWithBaseURL:url sessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + return [self initWithBaseURL:nil sessionConfiguration:configuration]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url + sessionConfiguration:(NSURLSessionConfiguration *)configuration +{ + self = [super initWithSessionConfiguration:configuration]; + if (!self) { + return nil; + } + + // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected + if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { + url = [url URLByAppendingPathComponent:@""]; + } + + self.baseURL = url; + + self.requestSerializer = [AFHTTPRequestSerializer serializer]; + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + return self; +} + +#pragma mark - + +#ifdef _SYSTEMCONFIGURATION_H +#endif + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + NSParameterAssert(requestSerializer); + + _requestSerializer = requestSerializer; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + [super setResponseSerializer:responseSerializer]; +} + +#pragma mark - + +- (NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)HEAD:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(NSURLSessionDataTask *task, __unused id responseObject) { + if (success) { + success(task); + } + } failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + if (error) { + if (failure) { + failure(task, error); + } + } else { + if (success) { + success(task, responseObject); + } + } + }]; + + [task resume]; + + return task; +} + +- (NSURLSessionDataTask *)PUT:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)PATCH:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)DELETE:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *, id))success + failure:(void (^)(NSURLSessionDataTask *, NSError *))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + __block NSURLSessionDataTask *dataTask = nil; + dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + if (error) { + if (failure) { + failure(dataTask, error); + } + } else { + if (success) { + success(dataTask, responseObject); + } + } + }]; + + return dataTask; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))]; + NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; + if (!configuration) { + NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; + if (configurationIdentifier) { +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) + configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; +#else + configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; +#endif + } + } + + self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; + if (!self) { + return nil; + } + + self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; + if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { + [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; + } else { + [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; + } + [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; + + HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; + HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; + + return HTTPClient; +} + +@end + +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h new file mode 100644 index 0000000..a458d10 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1,201 @@ +// AFNetworkReachabilityManager.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { + AFNetworkReachabilityStatusUnknown = -1, + AFNetworkReachabilityStatusNotReachable = 0, + AFNetworkReachabilityStatusReachableViaWWAN = 1, + AFNetworkReachabilityStatusReachableViaWiFi = 2, +}; + +/** + `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. + + Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. + + See Apple's Reachability Sample Code (https://developer.apple.com/library/ios/samplecode/reachability/) + + @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. + */ +@interface AFNetworkReachabilityManager : NSObject + +/** + The current network reachability status. + */ +@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; + +/** + Whether or not the network is currently reachable. + */ +@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; + +/** + Whether or not the network is currently reachable via WWAN. + */ +@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; + +/** + Whether or not the network is currently reachable via WiFi. + */ +@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Returns the shared network reachability manager. + */ ++ (instancetype)sharedManager; + +/** + Creates and returns a network reachability manager for the specified domain. + + @param domain The domain used to evaluate network reachability. + + @return An initialized network reachability manager, actively monitoring the specified domain. + */ ++ (instancetype)managerForDomain:(NSString *)domain; + +/** + Creates and returns a network reachability manager for the socket address. + + @param address The socket address (`sockaddr_in`) used to evaluate network reachability. + + @return An initialized network reachability manager, actively monitoring the specified socket address. + */ ++ (instancetype)managerForAddress:(const void *)address; + +/** + Initializes an instance of a network reachability manager from the specified reachability object. + + @param reachability The reachability object to monitor. + + @return An initialized network reachability manager, actively monitoring the specified reachability. + */ +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; + +///-------------------------------------------------- +/// @name Starting & Stopping Reachability Monitoring +///-------------------------------------------------- + +/** + Starts monitoring for changes in network reachability status. + */ +- (void)startMonitoring; + +/** + Stops monitoring for changes in network reachability status. + */ +- (void)stopMonitoring; + +///------------------------------------------------- +/// @name Getting Localized Reachability Description +///------------------------------------------------- + +/** + Returns a localized string representation of the current network reachability status. + */ +- (NSString *)localizedNetworkReachabilityStatusString; + +///--------------------------------------------------- +/// @name Setting Network Reachability Change Callback +///--------------------------------------------------- + +/** + Sets a callback to be executed when the network availability of the `baseURL` host changes. + + @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. + */ +- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## Network Reachability + + The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. + + enum { + AFNetworkReachabilityStatusUnknown, + AFNetworkReachabilityStatusNotReachable, + AFNetworkReachabilityStatusReachableViaWWAN, + AFNetworkReachabilityStatusReachableViaWiFi, + } + + `AFNetworkReachabilityStatusUnknown` + The `baseURL` host reachability is not known. + + `AFNetworkReachabilityStatusNotReachable` + The `baseURL` host cannot be reached. + + `AFNetworkReachabilityStatusReachableViaWWAN` + The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. + + `AFNetworkReachabilityStatusReachableViaWiFi` + The `baseURL` host can be reached via a Wi-Fi connection. + + ### Keys for Notification UserInfo Dictionary + + Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. + + `AFNetworkingReachabilityNotificationStatusItem` + A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. + The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. + */ + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when network reachability changes. + This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. + + @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). + */ +extern NSString * const AFNetworkingReachabilityDidChangeNotification; +extern NSString * const AFNetworkingReachabilityNotificationStatusItem; + +///-------------------- +/// @name Functions +///-------------------- + +/** + Returns a localized string representation of an `AFNetworkReachabilityStatus` value. + */ +extern NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m new file mode 100644 index 0000000..8806804 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m @@ -0,0 +1,241 @@ +// AFNetworkReachabilityManager.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFNetworkReachabilityManager.h" + +#import +#import +#import +#import +#import + +NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; +NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; + +typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); + +NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { + switch (status) { + case AFNetworkReachabilityStatusNotReachable: + return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); + case AFNetworkReachabilityStatusReachableViaWWAN: + return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); + case AFNetworkReachabilityStatusReachableViaWiFi: + return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); + case AFNetworkReachabilityStatusUnknown: + default: + return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); + } +} + +static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { + BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); + BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); + BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); + BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); + BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); + + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; + if (isNetworkReachable == NO) { + status = AFNetworkReachabilityStatusNotReachable; + } +#if TARGET_OS_IPHONE + else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { + status = AFNetworkReachabilityStatusReachableViaWWAN; + } +#endif + else { + status = AFNetworkReachabilityStatusReachableViaWiFi; + } + + return status; +} + +static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + AFNetworkReachabilityStatusBlock block = (__bridge AFNetworkReachabilityStatusBlock)info; + if (block) { + block(status); + } + + + dispatch_async(dispatch_get_main_queue(), ^{ + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; + }); + +} + +static const void * AFNetworkReachabilityRetainCallback(const void *info) { + return Block_copy(info); +} + +static void AFNetworkReachabilityReleaseCallback(const void *info) { + if (info) { + Block_release(info); + } +} + +@interface AFNetworkReachabilityManager () +@property (readwrite, nonatomic, assign) SCNetworkReachabilityRef networkReachability; +@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; +@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; +@end + +@implementation AFNetworkReachabilityManager + ++ (instancetype)sharedManager { + static AFNetworkReachabilityManager *_sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + struct sockaddr_in address; + bzero(&address, sizeof(address)); + address.sin_len = sizeof(address); + address.sin_family = AF_INET; + + _sharedManager = [self managerForAddress:&address]; + }); + + return _sharedManager; +} + ++ (instancetype)managerForDomain:(NSString *)domain { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); + + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + + return manager; +} + ++ (instancetype)managerForAddress:(const void *)address { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); + + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + + return manager; +} + +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { + self = [super init]; + if (!self) { + return nil; + } + + self.networkReachability = reachability; + self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + + return self; +} + +- (void)dealloc { + [self stopMonitoring]; + + if (_networkReachability) { + CFRelease(_networkReachability); + _networkReachability = NULL; + } +} + +#pragma mark - + +- (BOOL)isReachable { + return [self isReachableViaWWAN] || [self isReachableViaWiFi]; +} + +- (BOOL)isReachableViaWWAN { + return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; +} + +- (BOOL)isReachableViaWiFi { + return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; +} + +#pragma mark - + +- (void)startMonitoring { + [self stopMonitoring]; + + if (!self.networkReachability) { + return; + } + + __weak __typeof(self)weakSelf = self; + AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + + strongSelf.networkReachabilityStatus = status; + if (strongSelf.networkReachabilityStatusBlock) { + strongSelf.networkReachabilityStatusBlock(status); + } + + }; + + SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; + SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ + SCNetworkReachabilityFlags flags; + SCNetworkReachabilityGetFlags(self.networkReachability, &flags); + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + dispatch_async(dispatch_get_main_queue(), ^{ + callback(status); + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; + }); + }); +} + +- (void)stopMonitoring { + if (!self.networkReachability) { + return; + } + + SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); +} + +#pragma mark - + +- (NSString *)localizedNetworkReachabilityStatusString { + return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); +} + +#pragma mark - + +- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { + self.networkReachabilityStatusBlock = block; +} + +#pragma mark - NSKeyValueObserving + ++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { + if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { + return [NSSet setWithObject:@"networkReachabilityStatus"]; + } + + return [super keyPathsForValuesAffectingValueForKey:key]; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFNetworking.h b/Pods/AFNetworking/AFNetworking/AFNetworking.h new file mode 100644 index 0000000..68273da --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworking.h @@ -0,0 +1,44 @@ +// AFNetworking.h +// +// Copyright (c) 2013 AFNetworking (http://afnetworking.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +#ifndef _AFNETWORKING_ + #define _AFNETWORKING_ + + #import "AFURLRequestSerialization.h" + #import "AFURLResponseSerialization.h" + #import "AFSecurityPolicy.h" + #import "AFNetworkReachabilityManager.h" + + #import "AFURLConnectionOperation.h" + #import "AFHTTPRequestOperation.h" + #import "AFHTTPRequestOperationManager.h" + +#if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \ + ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) ) + #import "AFURLSessionManager.h" + #import "AFHTTPSessionManager.h" +#endif + +#endif /* _AFNETWORKING_ */ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h new file mode 100644 index 0000000..4906f3b --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1,143 @@ +// AFSecurityPolicy.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { + AFSSLPinningModeNone, + AFSSLPinningModePublicKey, + AFSSLPinningModeCertificate, +}; + +/** + `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. + + Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. + */ +@interface AFSecurityPolicy : NSObject + +/** + The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. + */ +@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; + +/** + Whether to evaluate an entire SSL certificate chain, or just the leaf certificate. Defaults to `YES`. + */ +@property (nonatomic, assign) BOOL validatesCertificateChain; + +/** + The certificates used to evaluate server trust according to the SSL pinning mode. By default, this property is set to any (`.cer`) certificates included in the app bundle. + */ +@property (nonatomic, strong) NSArray *pinnedCertificates; + +/** + Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. + */ +@property (nonatomic, assign) BOOL allowInvalidCertificates; + +/** + Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES` for `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate`, otherwise `NO`. + */ +@property (nonatomic, assign) BOOL validatesDomainName; + +///----------------------------------------- +/// @name Getting Specific Security Policies +///----------------------------------------- + +/** + Returns the shared default security policy, which does not allow invalid certificates, does not validate domain name, and does not validate against pinned certificates or public keys. + + @return The default security policy. + */ ++ (instancetype)defaultPolicy; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns a security policy with the specified pinning mode. + + @param pinningMode The SSL pinning mode. + + @return A new security policy. + */ ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; + +///------------------------------ +/// @name Evaluating Server Trust +///------------------------------ + +/** + Whether or not the specified server trust should be accepted, based on the security policy. + + This method should be used when responding to an authentication challenge from a server. + + @param serverTrust The X.509 certificate trust of the server. + + @return Whether or not to trust the server. + + @warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`. + */ +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE; + +/** + Whether or not the specified server trust should be accepted, based on the security policy. + + This method should be used when responding to an authentication challenge from a server. + + @param serverTrust The X.509 certificate trust of the server. + @param domain The domain of serverTrust. If `nil`, the domain will not be validated. + + @return Whether or not to trust the server. + */ +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust + forDomain:(NSString *)domain; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## SSL Pinning Modes + + The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. + + enum { + AFSSLPinningModeNone, + AFSSLPinningModePublicKey, + AFSSLPinningModeCertificate, + } + + `AFSSLPinningModeNone` + Do not used pinned certificates to validate servers. + + `AFSSLPinningModePublicKey` + Validate host certificates against public keys of pinned certificates. + + `AFSSLPinningModeCertificate` + Validate host certificates against pinned certificates. +*/ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m new file mode 100644 index 0000000..b7ca853 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m @@ -0,0 +1,324 @@ +// AFSecurityPolicy.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFSecurityPolicy.h" + +#import + +#if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +static NSData * AFSecKeyGetData(SecKeyRef key) { + CFDataRef data = NULL; + + __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); + + return (__bridge_transfer NSData *)data; + +_out: + if (data) { + CFRelease(data); + } + + return nil; +} +#endif + +static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + return [(__bridge id)key1 isEqual:(__bridge id)key2]; +#else + return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; +#endif +} + +static id AFPublicKeyForCertificate(NSData *certificate) { + id allowedPublicKey = nil; + SecCertificateRef allowedCertificate; + SecCertificateRef allowedCertificates[1]; + CFArrayRef tempCertificates = nil; + SecPolicyRef policy = nil; + SecTrustRef allowedTrust = nil; + SecTrustResultType result; + + allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); + __Require_Quiet(allowedCertificate != NULL, _out); + + allowedCertificates[0] = allowedCertificate; + tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); + + policy = SecPolicyCreateBasicX509(); + __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); + __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); + + allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); + +_out: + if (allowedTrust) { + CFRelease(allowedTrust); + } + + if (policy) { + CFRelease(policy); + } + + if (tempCertificates) { + CFRelease(tempCertificates); + } + + if (allowedCertificate) { + CFRelease(allowedCertificate); + } + + return allowedPublicKey; +} + +static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { + BOOL isValid = NO; + SecTrustResultType result; + __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); + + isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); + +_out: + return isValid; +} + +static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; + } + + return [NSArray arrayWithArray:trustChain]; +} + +static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { + SecPolicyRef policy = SecPolicyCreateBasicX509(); + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + + SecCertificateRef someCertificates[] = {certificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); + + SecTrustRef trust; + __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); + + SecTrustResultType result; + __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); + + [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; + + _out: + if (trust) { + CFRelease(trust); + } + + if (certificates) { + CFRelease(certificates); + } + + continue; + } + CFRelease(policy); + + return [NSArray arrayWithArray:trustChain]; +} + +#pragma mark - + +@interface AFSecurityPolicy() +@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; +@property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys; +@end + +@implementation AFSecurityPolicy + ++ (NSArray *)defaultPinnedCertificates { + static NSArray *_defaultPinnedCertificates = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; + + NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]]; + for (NSString *path in paths) { + NSData *certificateData = [NSData dataWithContentsOfFile:path]; + [certificates addObject:certificateData]; + } + + _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates]; + }); + + return _defaultPinnedCertificates; +} + ++ (instancetype)defaultPolicy { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = AFSSLPinningModeNone; + + return securityPolicy; +} + ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = pinningMode; + + [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]]; + + return securityPolicy; +} + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + self.validatesCertificateChain = YES; + + return self; +} + +#pragma mark - + +- (void)setSSLPinningMode:(AFSSLPinningMode)SSLPinningMode { + _SSLPinningMode = SSLPinningMode; + + switch (self.SSLPinningMode) { + case AFSSLPinningModePublicKey: + case AFSSLPinningModeCertificate: + self.validatesDomainName = YES; + break; + default: + self.validatesDomainName = NO; + break; + } +} + +- (void)setPinnedCertificates:(NSArray *)pinnedCertificates { + _pinnedCertificates = pinnedCertificates; + + if (self.pinnedCertificates) { + NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]]; + for (NSData *certificate in self.pinnedCertificates) { + id publicKey = AFPublicKeyForCertificate(certificate); + if (!publicKey) { + continue; + } + [mutablePinnedPublicKeys addObject:publicKey]; + } + self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys]; + } else { + self.pinnedPublicKeys = nil; + } +} + +#pragma mark - + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { + return [self evaluateServerTrust:serverTrust forDomain:nil]; +} + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust + forDomain:(NSString *)domain +{ + NSMutableArray *policies = [NSMutableArray array]; + if (self.validatesDomainName) { + [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; + } else { + [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; + } + + SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); + + if(self.SSLPinningMode == AFSSLPinningModeNone) { + if(self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust)){ + return YES; + } else { + return NO; + } + } else if(!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { + return NO; + } + + NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); + switch (self.SSLPinningMode) { + case AFSSLPinningModeNone: + default: + return NO; + case AFSSLPinningModeCertificate: { + NSMutableArray *pinnedCertificates = [NSMutableArray array]; + for (NSData *certificateData in self.pinnedCertificates) { + [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; + } + SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); + + if (!AFServerTrustIsValid(serverTrust)) { + return NO; + } + + if (!self.validatesCertificateChain) { + return YES; + } + + NSUInteger trustedCertificateCount = 0; + for (NSData *trustChainCertificate in serverCertificates) { + if ([self.pinnedCertificates containsObject:trustChainCertificate]) { + trustedCertificateCount++; + } + } + + return trustedCertificateCount == [serverCertificates count]; + } + case AFSSLPinningModePublicKey: { + NSUInteger trustedPublicKeyCount = 0; + NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); + if (!self.validatesCertificateChain && [publicKeys count] > 0) { + publicKeys = @[[publicKeys firstObject]]; + } + + for (id trustChainPublicKey in publicKeys) { + for (id pinnedPublicKey in self.pinnedPublicKeys) { + if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { + trustedPublicKeyCount += 1; + } + } + } + + return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1)); + } + } + + return NO; +} + +#pragma mark - NSKeyValueObserving + ++ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { + return [NSSet setWithObject:@"pinnedCertificates"]; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h new file mode 100644 index 0000000..797c7ef --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h @@ -0,0 +1,344 @@ +// AFURLConnectionOperation.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" +#import "AFSecurityPolicy.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods. + + ## Subclassing Notes + + This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors. + + If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes. + + ## NSURLConnection Delegate Methods + + `AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods: + + - `connection:didReceiveResponse:` + - `connection:didReceiveData:` + - `connectionDidFinishLoading:` + - `connection:didFailWithError:` + - `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:` + - `connection:willCacheResponse:` + - `connectionShouldUseCredentialStorage:` + - `connection:needNewBodyStream:` + - `connection:willSendRequestForAuthenticationChallenge:` + + If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. + + ## Callbacks and Completion Blocks + + The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this. + + Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/). + + ## SSL Pinning + + Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle. + + SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice. + + Connections will be validated on all matching certificates with a `.cer` extension in the bundle root. + + ## App Extensions + + When using AFNetworking in an App Extension, `#define AF_APP_EXTENSIONS` to avoid using unavailable APIs. + + ## NSCoding & NSCopying Conformance + + `AFURLConnectionOperation` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. However, because of the intrinsic limitations of capturing the exact state of an operation at a particular moment, there are some important caveats to keep in mind: + + ### NSCoding Caveats + + - Encoded operations do not include any block or stream properties. Be sure to set `completionBlock`, `outputStream`, and any callback blocks as necessary when using `-initWithCoder:` or `NSKeyedUnarchiver`. + - Operations are paused on `encodeWithCoder:`. If the operation was encoded while paused or still executing, its archived state will return `YES` for `isReady`. Otherwise, the state of an operation when encoding will remain unchanged. + + ### NSCopying Caveats + + - `-copy` and `-copyWithZone:` return a new operation with the `NSURLRequest` of the original. So rather than an exact copy of the operation at that particular instant, the copying mechanism returns a completely new instance, which can be useful for retrying operations. + - A copy of an operation will not include the `outputStream` of the original. + - Operation copies do not include `completionBlock`, as it often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied. + */ + +@interface AFURLConnectionOperation : NSOperation + +///------------------------------- +/// @name Accessing Run Loop Modes +///------------------------------- + +/** + The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`. + */ +@property (nonatomic, strong) NSSet *runLoopModes; + +///----------------------------------------- +/// @name Getting URL Connection Information +///----------------------------------------- + +/** + The request used by the operation's connection. + */ +@property (readonly, nonatomic, strong) NSURLRequest *request; + +/** + The last response received by the operation's connection. + */ +@property (readonly, nonatomic, strong) NSURLResponse *response; + +/** + The error, if any, that occurred in the lifecycle of the request. + */ +@property (readonly, nonatomic, strong) NSError *error; + +///---------------------------- +/// @name Getting Response Data +///---------------------------- + +/** + The data received during the request. + */ +@property (readonly, nonatomic, strong) NSData *responseData; + +/** + The string representation of the response data. + */ +@property (readonly, nonatomic, copy) NSString *responseString; + +/** + The string encoding of the response. + + If the response does not specify a valid string encoding, `responseStringEncoding` will return `NSUTF8StringEncoding`. + */ +@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding; + +///------------------------------- +/// @name Managing URL Credentials +///------------------------------- + +/** + Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. + + This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. + */ +@property (nonatomic, assign) BOOL shouldUseCredentialStorage; + +/** + The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. + + This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. + */ +@property (nonatomic, strong) NSURLCredential *credential; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used to evaluate server trust for secure connections. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +///------------------------ +/// @name Accessing Streams +///------------------------ + +/** + The input stream used to read data to be sent during the request. + + This property acts as a proxy to the `HTTPBodyStream` property of `request`. + */ +@property (nonatomic, strong) NSInputStream *inputStream; + +/** + The output stream that is used to write data received until the request is finished. + + By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request, with the intermediary `outputStream` property set to `nil`. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`. The output stream will be scheduled in the network thread runloop upon being set. + */ +@property (nonatomic, strong) NSOutputStream *outputStream; + +///--------------------------------- +/// @name Managing Callback Queues +///--------------------------------- + +/** + The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign) dispatch_queue_t completionQueue; +#endif + +/** + The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign) dispatch_group_t completionGroup; +#endif + +///--------------------------------------------- +/// @name Managing Request Operation Information +///--------------------------------------------- + +/** + The user info dictionary for the receiver. + */ +@property (nonatomic, strong) NSDictionary *userInfo; + +///------------------------------------------------------ +/// @name Initializing an AFURLConnectionOperation Object +///------------------------------------------------------ + +/** + Initializes and returns a newly allocated operation object with a url connection configured with the specified url request. + + This is the designated initializer. + + @param urlRequest The request object to be used by the operation connection. + */ +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest NS_DESIGNATED_INITIALIZER; + +///---------------------------------- +/// @name Pausing / Resuming Requests +///---------------------------------- + +/** + Pauses the execution of the request operation. + + A paused operation returns `NO` for `-isReady`, `-isExecuting`, and `-isFinished`. As such, it will remain in an `NSOperationQueue` until it is either cancelled or resumed. Pausing a finished, cancelled, or paused operation has no effect. + */ +- (void)pause; + +/** + Whether the request operation is currently paused. + + @return `YES` if the operation is currently paused, otherwise `NO`. + */ +- (BOOL)isPaused; + +/** + Resumes the execution of the paused request operation. + + Pause/Resume behavior varies depending on the underlying implementation for the operation class. In its base implementation, resuming a paused requests restarts the original request. However, since HTTP defines a specification for how to request a specific content range, `AFHTTPRequestOperation` will resume downloading the request from where it left off, instead of restarting the original request. + */ +- (void)resume; + +///---------------------------------------------- +/// @name Configuring Backgrounding Task Behavior +///---------------------------------------------- + +/** + Specifies that the operation should continue execution after the app has entered the background, and the expiration handler for that background task. + + @param handler A handler to be called shortly before the application’s remaining background time reaches 0. The handler is wrapped in a block that cancels the operation, and cleans up and marks the end of execution, unlike the `handler` parameter in `UIApplication -beginBackgroundTaskWithExpirationHandler:`, which expects this to be done in the handler itself. The handler is called synchronously on the main thread, thus blocking the application’s suspension momentarily while the application is notified. + */ +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) +- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler; +#endif + +///--------------------------------- +/// @name Setting Progress Callbacks +///--------------------------------- + +/** + Sets a callback to be called when an undetermined number of bytes have been uploaded to the server. + + @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block; + +/** + Sets a callback to be called when an undetermined number of bytes have been downloaded from the server. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block; + +///------------------------------------------------- +/// @name Setting NSURLConnection Delegate Callbacks +///------------------------------------------------- + +/** + Sets a block to be executed when the connection will authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequestForAuthenticationChallenge:`. + + @param block A block object to be executed when the connection will authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated. This block must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender protocol). + + If `allowsInvalidSSLCertificate` is set to YES, `connection:willSendRequestForAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates. + */ +- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block; + +/** + Sets a block to be executed when the server redirects the request from one URL to another URL, or when the request URL changed by the `NSURLProtocol` subclass handling the request in order to standardize its format, as handled by the `NSURLConnectionDataDelegate` method `connection:willSendRequest:redirectResponse:`. + + @param block A block object to be executed when the request URL was changed. The block returns an `NSURLRequest` object, the URL request to redirect, and takes three arguments: the URL connection object, the the proposed redirected request, and the URL response that caused the redirect. + */ +- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block; + + +/** + Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`. + + @param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request. + */ +- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block; + +/// + +/** + + */ ++ (NSArray *)batchOfRequestOperations:(NSArray *)operations + progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock; + +@end + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when an operation begins executing. + */ +extern NSString * const AFNetworkingOperationDidStartNotification; + +/** + Posted when an operation finishes. + */ +extern NSString * const AFNetworkingOperationDidFinishNotification; diff --git a/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m new file mode 100644 index 0000000..becfb83 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m @@ -0,0 +1,789 @@ +// AFURLConnectionOperation.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLConnectionOperation.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#endif + +#if !__has_feature(objc_arc) +#error AFNetworking must be built with ARC. +// You can turn on ARC for only AFNetworking files by adding -fobjc-arc to the build phase for each of its files. +#endif + +typedef NS_ENUM(NSInteger, AFOperationState) { + AFOperationPausedState = -1, + AFOperationReadyState = 1, + AFOperationExecutingState = 2, + AFOperationFinishedState = 3, +}; + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) +typedef UIBackgroundTaskIdentifier AFBackgroundTaskIdentifier; +#else +typedef id AFBackgroundTaskIdentifier; +#endif + +static dispatch_group_t url_request_operation_completion_group() { + static dispatch_group_t af_url_request_operation_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_request_operation_completion_group = dispatch_group_create(); + }); + + return af_url_request_operation_completion_group; +} + +static dispatch_queue_t url_request_operation_completion_queue() { + static dispatch_queue_t af_url_request_operation_completion_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT ); + }); + + return af_url_request_operation_completion_queue; +} + +static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock"; + +NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start"; +NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish"; + +typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); +typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge); +typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse); +typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse); + +static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { + switch (state) { + case AFOperationReadyState: + return @"isReady"; + case AFOperationExecutingState: + return @"isExecuting"; + case AFOperationFinishedState: + return @"isFinished"; + case AFOperationPausedState: + return @"isPaused"; + default: { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" + return @"state"; +#pragma clang diagnostic pop + } + } +} + +static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) { + switch (fromState) { + case AFOperationReadyState: + switch (toState) { + case AFOperationPausedState: + case AFOperationExecutingState: + return YES; + case AFOperationFinishedState: + return isCancelled; + default: + return NO; + } + case AFOperationExecutingState: + switch (toState) { + case AFOperationPausedState: + case AFOperationFinishedState: + return YES; + default: + return NO; + } + case AFOperationFinishedState: + return NO; + case AFOperationPausedState: + return toState == AFOperationReadyState; + default: { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" + switch (toState) { + case AFOperationPausedState: + case AFOperationReadyState: + case AFOperationExecutingState: + case AFOperationFinishedState: + return YES; + default: + return NO; + } + } +#pragma clang diagnostic pop + } +} + +@interface AFURLConnectionOperation () +@property (readwrite, nonatomic, assign) AFOperationState state; +@property (readwrite, nonatomic, strong) NSRecursiveLock *lock; +@property (readwrite, nonatomic, strong) NSURLConnection *connection; +@property (readwrite, nonatomic, strong) NSURLRequest *request; +@property (readwrite, nonatomic, strong) NSURLResponse *response; +@property (readwrite, nonatomic, strong) NSError *error; +@property (readwrite, nonatomic, strong) NSData *responseData; +@property (readwrite, nonatomic, copy) NSString *responseString; +@property (readwrite, nonatomic, assign) NSStringEncoding responseStringEncoding; +@property (readwrite, nonatomic, assign) long long totalBytesRead; +@property (readwrite, nonatomic, assign) AFBackgroundTaskIdentifier backgroundTaskIdentifier; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse; + +- (void)operationDidStart; +- (void)finish; +- (void)cancelConnection; +@end + +@implementation AFURLConnectionOperation +@synthesize outputStream = _outputStream; + ++ (void)networkRequestThreadEntryPoint:(id)__unused object { + @autoreleasepool { + [[NSThread currentThread] setName:@"AFNetworking"]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; + [runLoop run]; + } +} + ++ (NSThread *)networkRequestThread { + static NSThread *_networkRequestThread = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; + [_networkRequestThread start]; + }); + + return _networkRequestThread; +} + +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest { + NSParameterAssert(urlRequest); + + self = [super init]; + if (!self) { + return nil; + } + + _state = AFOperationReadyState; + + self.lock = [[NSRecursiveLock alloc] init]; + self.lock.name = kAFNetworkingLockName; + + self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; + + self.request = urlRequest; + + self.shouldUseCredentialStorage = YES; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + + return self; +} + +- (void)dealloc { + if (_outputStream) { + [_outputStream close]; + _outputStream = nil; + } + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) + if (_backgroundTaskIdentifier) { + [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; + _backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } +#endif +} + +#pragma mark - + +- (void)setResponseData:(NSData *)responseData { + [self.lock lock]; + if (!responseData) { + _responseData = nil; + } else { + _responseData = [NSData dataWithBytes:responseData.bytes length:responseData.length]; + } + [self.lock unlock]; +} + +- (NSString *)responseString { + [self.lock lock]; + if (!_responseString && self.response && self.responseData) { + self.responseString = [[NSString alloc] initWithData:self.responseData encoding:self.responseStringEncoding]; + } + [self.lock unlock]; + + return _responseString; +} + +- (NSStringEncoding)responseStringEncoding { + [self.lock lock]; + if (!_responseStringEncoding && self.response) { + NSStringEncoding stringEncoding = NSUTF8StringEncoding; + if (self.response.textEncodingName) { + CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName); + if (IANAEncoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(IANAEncoding); + } + } + + self.responseStringEncoding = stringEncoding; + } + [self.lock unlock]; + + return _responseStringEncoding; +} + +- (NSInputStream *)inputStream { + return self.request.HTTPBodyStream; +} + +- (void)setInputStream:(NSInputStream *)inputStream { + NSMutableURLRequest *mutableRequest = [self.request mutableCopy]; + mutableRequest.HTTPBodyStream = inputStream; + self.request = mutableRequest; +} + +- (NSOutputStream *)outputStream { + if (!_outputStream) { + self.outputStream = [NSOutputStream outputStreamToMemory]; + } + + return _outputStream; +} + +- (void)setOutputStream:(NSOutputStream *)outputStream { + [self.lock lock]; + if (outputStream != _outputStream) { + if (_outputStream) { + [_outputStream close]; + } + + _outputStream = outputStream; + } + [self.lock unlock]; +} + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) +- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler { + [self.lock lock]; + if (!self.backgroundTaskIdentifier) { + UIApplication *application = [UIApplication sharedApplication]; + __weak __typeof(self)weakSelf = self; + self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + + if (handler) { + handler(); + } + + if (strongSelf) { + [strongSelf cancel]; + + [application endBackgroundTask:strongSelf.backgroundTaskIdentifier]; + strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } + }]; + } + [self.lock unlock]; +} +#endif + +#pragma mark - + +- (void)setState:(AFOperationState)state { + if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) { + return; + } + + [self.lock lock]; + NSString *oldStateKey = AFKeyPathFromOperationState(self.state); + NSString *newStateKey = AFKeyPathFromOperationState(state); + + [self willChangeValueForKey:newStateKey]; + [self willChangeValueForKey:oldStateKey]; + _state = state; + [self didChangeValueForKey:oldStateKey]; + [self didChangeValueForKey:newStateKey]; + [self.lock unlock]; +} + +- (void)pause { + if ([self isPaused] || [self isFinished] || [self isCancelled]) { + return; + } + + [self.lock lock]; + if ([self isExecuting]) { + [self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; + }); + } + + self.state = AFOperationPausedState; + [self.lock unlock]; +} + +- (void)operationDidPause { + [self.lock lock]; + [self.connection cancel]; + [self.lock unlock]; +} + +- (BOOL)isPaused { + return self.state == AFOperationPausedState; +} + +- (void)resume { + if (![self isPaused]) { + return; + } + + [self.lock lock]; + self.state = AFOperationReadyState; + + [self start]; + [self.lock unlock]; +} + +#pragma mark - + +- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block { + self.uploadProgress = block; +} + +- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block { + self.downloadProgress = block; +} + +- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block { + self.authenticationChallenge = block; +} + +- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block { + self.cacheResponse = block; +} + +- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block { + self.redirectResponse = block; +} + +#pragma mark - NSOperation + +- (void)setCompletionBlock:(void (^)(void))block { + [self.lock lock]; + if (!block) { + [super setCompletionBlock:nil]; + } else { + __weak __typeof(self)weakSelf = self; + [super setCompletionBlock:^ { + __strong __typeof(weakSelf)strongSelf = weakSelf; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group(); + dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue(); +#pragma clang diagnostic pop + + dispatch_group_async(group, queue, ^{ + block(); + }); + + dispatch_group_notify(group, url_request_operation_completion_queue(), ^{ + [strongSelf setCompletionBlock:nil]; + }); + }]; + } + [self.lock unlock]; +} + +- (BOOL)isReady { + return self.state == AFOperationReadyState && [super isReady]; +} + +- (BOOL)isExecuting { + return self.state == AFOperationExecutingState; +} + +- (BOOL)isFinished { + return self.state == AFOperationFinishedState; +} + +- (BOOL)isConcurrent { + return YES; +} + +- (void)start { + [self.lock lock]; + if ([self isCancelled]) { + [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + } else if ([self isReady]) { + self.state = AFOperationExecutingState; + + [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + } + [self.lock unlock]; +} + +- (void)operationDidStart { + [self.lock lock]; + if (![self isCancelled]) { + self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + for (NSString *runLoopMode in self.runLoopModes) { + [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; + [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; + } + + [self.outputStream open]; + [self.connection start]; + } + [self.lock unlock]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; + }); +} + +- (void)finish { + [self.lock lock]; + self.state = AFOperationFinishedState; + [self.lock unlock]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; + }); +} + +- (void)cancel { + [self.lock lock]; + if (![self isFinished] && ![self isCancelled]) { + [super cancel]; + + if ([self isExecuting]) { + [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + } + } + [self.lock unlock]; +} + +- (void)cancelConnection { + NSDictionary *userInfo = nil; + if ([self.request URL]) { + userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey]; + } + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]; + + if (![self isFinished]) { + if (self.connection) { + [self.connection cancel]; + [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error]; + } else { + // Accomodate race condition where `self.connection` has not yet been set before cancellation + self.error = error; + [self finish]; + } + } +} + +#pragma mark - + ++ (NSArray *)batchOfRequestOperations:(NSArray *)operations + progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock +{ + if (!operations || [operations count] == 0) { + return @[[NSBlockOperation blockOperationWithBlock:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(@[]); + } + }); + }]]; + } + + __block dispatch_group_t group = dispatch_group_create(); + NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(operations); + } + }); + }]; + + for (AFURLConnectionOperation *operation in operations) { + operation.completionGroup = group; + void (^originalCompletionBlock)(void) = [operation.completionBlock copy]; + __weak __typeof(operation)weakOperation = operation; + operation.completionBlock = ^{ + __strong __typeof(weakOperation)strongOperation = weakOperation; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue(); +#pragma clang diagnostic pop + dispatch_group_async(group, queue, ^{ + if (originalCompletionBlock) { + originalCompletionBlock(); + } + + NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx, BOOL __unused *stop) { + return [op isFinished]; + }] count]; + + if (progressBlock) { + progressBlock(numberOfFinishedOperations, [operations count]); + } + + dispatch_group_leave(group); + }); + }; + + dispatch_group_enter(group); + [batchedOperation addDependency:operation]; + } + + return [operations arrayByAddingObject:batchedOperation]; +} + +#pragma mark - NSObject + +- (NSString *)description { + [self.lock lock]; + NSString *description = [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response]; + [self.lock unlock]; + return description; +} + +#pragma mark - NSURLConnectionDelegate + +- (void)connection:(NSURLConnection *)connection +willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (self.authenticationChallenge) { + self.authenticationChallenge(connection, challenge); + return; + } + + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] cancelAuthenticationChallenge:challenge]; + } + } else { + if ([challenge previousFailureCount] == 0) { + if (self.credential) { + [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { + return self.shouldUseCredentialStorage; +} + +- (NSURLRequest *)connection:(NSURLConnection *)connection + willSendRequest:(NSURLRequest *)request + redirectResponse:(NSURLResponse *)redirectResponse +{ + if (self.redirectResponse) { + return self.redirectResponse(connection, request, redirectResponse); + } else { + return request; + } +} + +- (void)connection:(NSURLConnection __unused *)connection + didSendBodyData:(NSInteger)bytesWritten + totalBytesWritten:(NSInteger)totalBytesWritten +totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.uploadProgress) { + self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } + }); +} + +- (void)connection:(NSURLConnection __unused *)connection +didReceiveResponse:(NSURLResponse *)response +{ + self.response = response; +} + +- (void)connection:(NSURLConnection __unused *)connection + didReceiveData:(NSData *)data +{ + NSUInteger length = [data length]; + while (YES) { + NSInteger totalNumberOfBytesWritten = 0; + if ([self.outputStream hasSpaceAvailable]) { + const uint8_t *dataBuffer = (uint8_t *)[data bytes]; + + NSInteger numberOfBytesWritten = 0; + while (totalNumberOfBytesWritten < (NSInteger)length) { + numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)]; + if (numberOfBytesWritten == -1) { + break; + } + + totalNumberOfBytesWritten += numberOfBytesWritten; + } + + break; + } + + if (self.outputStream.streamError) { + [self.connection cancel]; + [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError]; + return; + } + } + + dispatch_async(dispatch_get_main_queue(), ^{ + self.totalBytesRead += (long long)length; + + if (self.downloadProgress) { + self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength); + } + }); +} + +- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection { + self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; + + [self.outputStream close]; + if (self.responseData) { + self.outputStream = nil; + } + + self.connection = nil; + + [self finish]; +} + +- (void)connection:(NSURLConnection __unused *)connection + didFailWithError:(NSError *)error +{ + self.error = error; + + [self.outputStream close]; + if (self.responseData) { + self.outputStream = nil; + } + + self.connection = nil; + + [self finish]; +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection + willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + if (self.cacheResponse) { + return self.cacheResponse(connection, cachedResponse); + } else { + if ([self isCancelled]) { + return nil; + } + + return cachedResponse; + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURLRequest *request = [decoder decodeObjectOfClass:[NSURLRequest class] forKey:NSStringFromSelector(@selector(request))]; + + self = [self initWithRequest:request]; + if (!self) { + return nil; + } + + self.state = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(state))] integerValue]; + self.response = [decoder decodeObjectOfClass:[NSHTTPURLResponse class] forKey:NSStringFromSelector(@selector(response))]; + self.error = [decoder decodeObjectOfClass:[NSError class] forKey:NSStringFromSelector(@selector(error))]; + self.responseData = [decoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(responseData))]; + self.totalBytesRead = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(totalBytesRead))] longLongValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [self pause]; + + [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))]; + + switch (self.state) { + case AFOperationExecutingState: + case AFOperationPausedState: + [coder encodeInteger:AFOperationReadyState forKey:NSStringFromSelector(@selector(state))]; + break; + default: + [coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))]; + break; + } + + [coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))]; + [coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))]; + [coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))]; + [coder encodeInt64:self.totalBytesRead forKey:NSStringFromSelector(@selector(totalBytesRead))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request]; + + operation.uploadProgress = self.uploadProgress; + operation.downloadProgress = self.downloadProgress; + operation.authenticationChallenge = self.authenticationChallenge; + operation.cacheResponse = self.cacheResponse; + operation.redirectResponse = self.redirectResponse; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h new file mode 100644 index 0000000..e60a267 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1,468 @@ +// AFURLRequestSerialization.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#endif + +/** + The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. + + For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`. + */ +@protocol AFURLRequestSerialization + +/** + Returns a request with the specified parameters encoded into a copy of the original request. + + @param request The original request. + @param parameters The parameters to be encoded. + @param error The error that occurred while attempting to encode the request parameters. + + @return A serialized request. + */ +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError * __autoreleasing *)error; + +@end + +#pragma mark - + +/** + + */ +typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { + AFHTTPRequestQueryStringDefaultStyle = 0, +}; + +@protocol AFMultipartFormData; + +/** + `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + + Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior. + */ +@interface AFHTTPRequestSerializer : NSObject + +/** + The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default. + */ +@property (nonatomic, assign) NSStringEncoding stringEncoding; + +/** + Whether created requests can use the device’s cellular radio (if present). `YES` by default. + + @see NSMutableURLRequest -setAllowsCellularAccess: + */ +@property (nonatomic, assign) BOOL allowsCellularAccess; + +/** + The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. + + @see NSMutableURLRequest -setCachePolicy: + */ +@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; + +/** + Whether created requests should use the default cookie handling. `YES` by default. + + @see NSMutableURLRequest -setHTTPShouldHandleCookies: + */ +@property (nonatomic, assign) BOOL HTTPShouldHandleCookies; + +/** + Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default + + @see NSMutableURLRequest -setHTTPShouldUsePipelining: + */ +@property (nonatomic, assign) BOOL HTTPShouldUsePipelining; + +/** + The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. + + @see NSMutableURLRequest -setNetworkServiceType: + */ +@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; + +/** + The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. + + @see NSMutableURLRequest -setTimeoutInterval: + */ +@property (nonatomic, assign) NSTimeInterval timeoutInterval; + +///--------------------------------------- +/// @name Configuring HTTP Request Headers +///--------------------------------------- + +/** + Default HTTP header field values to be applied to serialized requests. By default, these include the following: + + - `Accept-Language` with the contents of `NSLocale +preferredLanguages` + - `User-Agent` with the contents of various bundle identifiers and OS designations + + @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`. + */ +@property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders; + +/** + Creates and returns a serializer with default configuration. + */ ++ (instancetype)serializer; + +/** + Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header. + + @param field The HTTP header to set a default value for + @param value The value set as default for the specified header, or `nil` + */ +- (void)setValue:(NSString *)value +forHTTPHeaderField:(NSString *)field; + +/** + Returns the value for the HTTP headers set in the request serializer. + + @param field The HTTP header to retrieve the default value for + + @return The value set as default for the specified header, or `nil` + */ +- (NSString *)valueForHTTPHeaderField:(NSString *)field; + +/** + Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header. + + @param username The HTTP basic auth username + @param password The HTTP basic auth password + */ +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password; + +/** + @deprecated This method has been deprecated. Use -setValue:forHTTPHeaderField: instead. + */ +- (void)setAuthorizationHeaderFieldWithToken:(NSString *)token DEPRECATED_ATTRIBUTE; + + +/** + Clears any existing value for the "Authorization" HTTP header. + */ +- (void)clearAuthorizationHeader; + +///------------------------------------------------------- +/// @name Configuring Query String Parameter Serialization +///------------------------------------------------------- + +/** + HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default. + */ +@property (nonatomic, strong) NSSet *HTTPMethodsEncodingParametersInURI; + +/** + Set the method of query string serialization according to one of the pre-defined styles. + + @param style The serialization style. + + @see AFHTTPRequestQueryStringSerializationStyle + */ +- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; + +/** + Set the a custom method of query string serialization according to the specified block. + + @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request. + */ +- (void)setQueryStringSerializationWithBlock:(NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block; + +///------------------------------- +/// @name Creating Request Objects +///------------------------------- + +/** + @deprecated This method has been deprecated. Use -requestWithMethod:URLString:parameters:error: instead. + */ +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters DEPRECATED_ATTRIBUTE; + +/** + Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string. + + If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body. + + @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body. + @param error The error that occured while constructing the request. + + @return An `NSMutableURLRequest` object. + */ +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + error:(NSError * __autoreleasing *)error; + +/** + @deprecated This method has been deprecated. Use -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error: instead. + */ +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block DEPRECATED_ATTRIBUTE; + +/** + Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2 + + Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream. + + @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded and set in the request HTTP body. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param error The error that occured while constructing the request. + + @return An `NSMutableURLRequest` object + */ +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block + error:(NSError * __autoreleasing *)error; + +/** + Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished. + + @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`. + @param fileURL The file URL to write multipart form contents to. + @param handler A handler block to execute. + + @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. + + @see https://github.com/AFNetworking/AFNetworking/issues/1398 + */ +- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request + writingStreamContentsToFile:(NSURL *)fileURL + completionHandler:(void (^)(NSError *error))handler; + +@end + +#pragma mark - + +/** + The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. + */ +@protocol AFMultipartFormData + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary. + + The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively. + + @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + + @return `YES` if the file data was successfully appended, otherwise `NO`. + */ +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + error:(NSError * __autoreleasing *)error; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. + + @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`. + @param mimeType The declared MIME type of the file data. This parameter must not be `nil`. + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + + @return `YES` if the file data was successfully appended otherwise `NO`. + */ +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + error:(NSError * __autoreleasing *)error; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary. + + @param inputStream The input stream to be appended to the form data + @param name The name to be associated with the specified input stream. This parameter must not be `nil`. + @param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`. + @param length The length of the specified input stream in bytes. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + */ +- (void)appendPartWithInputStream:(NSInputStream *)inputStream + name:(NSString *)name + fileName:(NSString *)fileName + length:(int64_t)length + mimeType:(NSString *)mimeType; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. + + @param data The data to be encoded and appended to the form data. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param fileName The filename to be associated with the specified data. This parameter must not be `nil`. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + */ +- (void)appendPartWithFileData:(NSData *)data + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType; + +/** + Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. + + @param data The data to be encoded and appended to the form data. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + */ + +- (void)appendPartWithFormData:(NSData *)data + name:(NSString *)name; + + +/** + Appends HTTP headers, followed by the encoded data and the multipart form boundary. + + @param headers The HTTP headers to be appended to the form data. + @param body The data to be encoded and appended to the form data. This parameter must not be `nil`. + */ +- (void)appendPartWithHeaders:(NSDictionary *)headers + body:(NSData *)body; + +/** + Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. + + When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. + + @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. + @param delay Duration of delay each time a packet is read. By default, no delay is set. + */ +- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes + delay:(NSTimeInterval)delay; + +@end + +#pragma mark - + +/** + `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. + */ +@interface AFJSONRequestSerializer : AFHTTPRequestSerializer + +/** + Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default. + */ +@property (nonatomic, assign) NSJSONWritingOptions writingOptions; + +/** + Creates and returns a JSON serializer with specified reading and writing options. + + @param writingOptions The specified JSON writing options. + */ ++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions; + +@end + +#pragma mark - + +/** + `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. + */ +@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer + +/** + The property list format. Possible values are described in "NSPropertyListFormat". + */ +@property (nonatomic, assign) NSPropertyListFormat format; + +/** + @warning The `writeOptions` property is currently unused. + */ +@property (nonatomic, assign) NSPropertyListWriteOptions writeOptions; + +/** + Creates and returns a property list serializer with a specified format, read options, and write options. + + @param format The property list format. + @param writeOptions The property list write options. + + @warning The `writeOptions` property is currently unused. + */ ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + writeOptions:(NSPropertyListWriteOptions)writeOptions; + +@end + +#pragma mark - + +///---------------- +/// @name Constants +///---------------- + +/** + ## Error Domains + + The following error domain is predefined. + + - `NSString * const AFURLRequestSerializationErrorDomain` + + ### Constants + + `AFURLRequestSerializationErrorDomain` + AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + */ +extern NSString * const AFURLRequestSerializationErrorDomain; + +/** + ## User info dictionary keys + + These keys may exist in the user info dictionary, in addition to those defined for NSError. + + - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` + + ### Constants + + `AFNetworkingOperationFailingURLRequestErrorKey` + The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`. + */ +extern NSString * const AFNetworkingOperationFailingURLRequestErrorKey; + +/** + ## Throttling Bandwidth for HTTP Request Input Streams + + @see -throttleBandwidthWithPacketSize:delay: + + ### Constants + + `kAFUploadStream3GSuggestedPacketSize` + Maximum packet size, in number of bytes. Equal to 16kb. + + `kAFUploadStream3GSuggestedDelay` + Duration of delay each time a packet is read. Equal to 0.2 seconds. + */ +extern NSUInteger const kAFUploadStream3GSuggestedPacketSize; +extern NSTimeInterval const kAFUploadStream3GSuggestedDelay; diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m new file mode 100644 index 0000000..8f602bc --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m @@ -0,0 +1,1385 @@ +// AFURLRequestSerialization.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLRequestSerialization.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +#import +#else +#import +#endif + +NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; +NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; + +typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); + +static NSString * AFBase64EncodedStringFromString(NSString *string) { + NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; + NSUInteger length = [data length]; + NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; + + uint8_t *input = (uint8_t *)[data bytes]; + uint8_t *output = (uint8_t *)[mutableData mutableBytes]; + + for (NSUInteger i = 0; i < length; i += 3) { + NSUInteger value = 0; + for (NSUInteger j = i; j < (i + 3); j++) { + value <<= 8; + if (j < length) { + value |= (0xFF & input[j]); + } + } + + static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + NSUInteger idx = (i / 3) * 4; + output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; + output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; + } + + return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding]; +} + +static NSString * const kAFCharactersToBeEscapedInQueryString = @":/?&=;+!@#$()',*"; + +static NSString * AFPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { + static NSString * const kAFCharactersToLeaveUnescapedInQueryStringPairKey = @"[]."; + + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding)); +} + +static NSString * AFPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding)); +} + +#pragma mark - + +@interface AFQueryStringPair : NSObject +@property (readwrite, nonatomic, strong) id field; +@property (readwrite, nonatomic, strong) id value; + +- (id)initWithField:(id)field value:(id)value; + +- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding; +@end + +@implementation AFQueryStringPair + +- (id)initWithField:(id)field value:(id)value { + self = [super init]; + if (!self) { + return nil; + } + + self.field = field; + self.value = value; + + return self; +} + +- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding { + if (!self.value || [self.value isEqual:[NSNull null]]) { + return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding); + } else { + return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], stringEncoding)]; + } +} + +@end + +#pragma mark - + +extern NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary); +extern NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value); + +static NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) { + NSMutableArray *mutablePairs = [NSMutableArray array]; + for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { + [mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]]; + } + + return [mutablePairs componentsJoinedByString:@"&"]; +} + +NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) { + return AFQueryStringPairsFromKeyAndValue(nil, dictionary); +} + +NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) { + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; + + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionary = value; + // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries + for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + id nestedValue = [dictionary objectForKey:nestedKey]; + if (nestedValue) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; + } + } + } else if ([value isKindOfClass:[NSArray class]]) { + NSArray *array = value; + for (id nestedValue in array) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; + } + } else if ([value isKindOfClass:[NSSet class]]) { + NSSet *set = value; + for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)]; + } + } else { + [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; + } + + return mutableQueryStringComponents; +} + +#pragma mark - + +@interface AFStreamingMultipartFormData : NSObject +- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest + stringEncoding:(NSStringEncoding)encoding; + +- (NSMutableURLRequest *)requestByFinalizingMultipartFormData; +@end + +#pragma mark - + +static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { + static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; + }); + + return _AFHTTPRequestSerializerObservedKeyPaths; +} + +static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext; + +@interface AFHTTPRequestSerializer () +@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; +@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; +@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; +@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; +@end + +@implementation AFHTTPRequestSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = NSUTF8StringEncoding; + + self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; + + // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; + [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + float q = 1.0f - (idx * 0.1f); + [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]]; + *stop = q <= 0.5f; + }]; + [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; + + NSString *userAgent = nil; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) + userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; +#endif +#pragma clang diagnostic pop + if (userAgent) { + if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { + NSMutableString *mutableUserAgent = [userAgent mutableCopy]; + if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) { + userAgent = mutableUserAgent; + } + } + [self setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + + // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; + + self.mutableObservedChangedKeyPaths = [NSMutableSet set]; + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; + } + } + + return self; +} + +- (void)dealloc { + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; + } + } +} + +#pragma mark - + +// Workarounds for crashing behavior using Key-Value Observing with XCTest +// See https://github.com/AFNetworking/AFNetworking/issues/2523 + +- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess { + [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; + _allowsCellularAccess = allowsCellularAccess; + [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; +} + +- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy { + [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; + _cachePolicy = cachePolicy; + [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; +} + +- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; + _HTTPShouldHandleCookies = HTTPShouldHandleCookies; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; +} + +- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; + _HTTPShouldUsePipelining = HTTPShouldUsePipelining; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; +} + +- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType { + [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; + _networkServiceType = networkServiceType; + [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; +} + +- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; + _timeoutInterval = timeoutInterval; + [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; +} + +#pragma mark - + +- (NSDictionary *)HTTPRequestHeaders { + return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; +} + +- (void)setValue:(NSString *)value +forHTTPHeaderField:(NSString *)field +{ + [self.mutableHTTPRequestHeaders setValue:value forKey:field]; +} + +- (NSString *)valueForHTTPHeaderField:(NSString *)field { + return [self.mutableHTTPRequestHeaders valueForKey:field]; +} + +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password +{ + NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", username, password]; + [self setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"]; +} + +- (void)setAuthorizationHeaderFieldWithToken:(NSString *)token { + [self setValue:[NSString stringWithFormat:@"Token token=\"%@\"", token] forHTTPHeaderField:@"Authorization"]; +} + +- (void)clearAuthorizationHeader { + [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; +} + +#pragma mark - + +- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style { + self.queryStringSerializationStyle = style; + self.queryStringSerialization = nil; +} + +- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block { + self.queryStringSerialization = block; +} + +#pragma mark - + +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters +{ + return [self requestWithMethod:method URLString:URLString parameters:parameters error:nil]; +} + +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(method); + NSParameterAssert(URLString); + + NSURL *url = [NSURL URLWithString:URLString]; + + NSParameterAssert(url); + + NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; + mutableRequest.HTTPMethod = method; + + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { + [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; + } + } + + mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; + + return mutableRequest; +} + +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block +{ + return [self multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:nil]; +} + +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(method); + NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]); + + NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error]; + + __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding]; + + if (parameters) { + for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { + NSData *data = nil; + if ([pair.value isKindOfClass:[NSData class]]) { + data = pair.value; + } else if ([pair.value isEqual:[NSNull null]]) { + data = [NSData data]; + } else { + data = [[pair.value description] dataUsingEncoding:self.stringEncoding]; + } + + if (data) { + [formData appendPartWithFormData:data name:[pair.field description]]; + } + } + } + + if (block) { + block(formData); + } + + return [formData requestByFinalizingMultipartFormData]; +} + +- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request + writingStreamContentsToFile:(NSURL *)fileURL + completionHandler:(void (^)(NSError *error))handler +{ + NSParameterAssert(request.HTTPBodyStream); + NSParameterAssert([fileURL isFileURL]); + + NSInputStream *inputStream = request.HTTPBodyStream; + NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO]; + __block NSError *error = nil; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + [inputStream open]; + [outputStream open]; + + while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) { + uint8_t buffer[1024]; + + NSInteger bytesRead = [inputStream read:buffer maxLength:1024]; + if (inputStream.streamError || bytesRead < 0) { + error = inputStream.streamError; + break; + } + + NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead]; + if (outputStream.streamError || bytesWritten < 0) { + error = outputStream.streamError; + break; + } + + if (bytesRead == 0 && bytesWritten == 0) { + break; + } + } + + [outputStream close]; + [inputStream close]; + + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(error); + }); + } + }); + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + mutableRequest.HTTPBodyStream = nil; + + return mutableRequest; +} + +#pragma mark - AFURLRequestSerialization + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + NSString *query = nil; + if (self.queryStringSerialization) { + NSError *serializationError; + query = self.queryStringSerialization(request, parameters, &serializationError); + + if (serializationError) { + if (error) { + *error = serializationError; + } + + return nil; + } + } else { + switch (self.queryStringSerializationStyle) { + case AFHTTPRequestQueryStringDefaultStyle: + query = AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding); + break; + } + } + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; + } else { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + } + [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; + } + } + + return mutableRequest; +} + +#pragma mark - NSKeyValueObserving + ++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { + if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { + return NO; + } + + return [super automaticallyNotifiesObserversForKey:key]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(__unused id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == AFHTTPRequestSerializerObserverContext) { + if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { + [self.mutableObservedChangedKeyPaths removeObject:keyPath]; + } else { + [self.mutableObservedChangedKeyPaths addObject:keyPath]; + } + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; + self.queryStringSerializationStyle = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; + [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; + serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; + serializer.queryStringSerialization = self.queryStringSerialization; + + return serializer; +} + +@end + +#pragma mark - + +static NSString * AFCreateMultipartFormBoundary() { + return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; +} + +static NSString * const kAFMultipartFormCRLF = @"\r\n"; + +static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFContentTypeForPathExtension(NSString *extension) { +#ifdef __UTTYPE__ + NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); + NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); + if (!contentType) { + return @"application/octet-stream"; + } else { + return contentType; + } +#else +#pragma unused (extension) + return @"application/octet-stream"; +#endif +} + +NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16; +NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2; + +@interface AFHTTPBodyPart : NSObject +@property (nonatomic, assign) NSStringEncoding stringEncoding; +@property (nonatomic, strong) NSDictionary *headers; +@property (nonatomic, copy) NSString *boundary; +@property (nonatomic, strong) id body; +@property (nonatomic, assign) unsigned long long bodyContentLength; +@property (nonatomic, strong) NSInputStream *inputStream; + +@property (nonatomic, assign) BOOL hasInitialBoundary; +@property (nonatomic, assign) BOOL hasFinalBoundary; + +@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable; +@property (readonly, nonatomic, assign) unsigned long long contentLength; + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length; +@end + +@interface AFMultipartBodyStream : NSInputStream +@property (nonatomic, assign) NSUInteger numberOfBytesInPacket; +@property (nonatomic, assign) NSTimeInterval delay; +@property (nonatomic, strong) NSInputStream *inputStream; +@property (readonly, nonatomic, assign) unsigned long long contentLength; +@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty; + +- (id)initWithStringEncoding:(NSStringEncoding)encoding; +- (void)setInitialAndFinalBoundaries; +- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart; +@end + +#pragma mark - + +@interface AFStreamingMultipartFormData () +@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; +@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; +@property (readwrite, nonatomic, copy) NSString *boundary; +@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; +@end + +@implementation AFStreamingMultipartFormData + +- (id)initWithURLRequest:(NSMutableURLRequest *)urlRequest + stringEncoding:(NSStringEncoding)encoding +{ + self = [super init]; + if (!self) { + return nil; + } + + self.request = urlRequest; + self.stringEncoding = encoding; + self.boundary = AFCreateMultipartFormBoundary(); + self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; + + return self; +} + +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(fileURL); + NSParameterAssert(name); + + NSString *fileName = [fileURL lastPathComponent]; + NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]); + + return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; +} + +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(fileURL); + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + if (![fileURL isFileURL]) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)}; + if (error) { + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; + } + + return NO; + } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)}; + if (error) { + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; + } + + return NO; + } + + NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error]; + if (!fileAttributes) { + return NO; + } + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = mutableHeaders; + bodyPart.boundary = self.boundary; + bodyPart.body = fileURL; + bodyPart.bodyContentLength = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue]; + [self.bodyStream appendHTTPBodyPart:bodyPart]; + + return YES; +} + +- (void)appendPartWithInputStream:(NSInputStream *)inputStream + name:(NSString *)name + fileName:(NSString *)fileName + length:(int64_t)length + mimeType:(NSString *)mimeType +{ + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = mutableHeaders; + bodyPart.boundary = self.boundary; + bodyPart.body = inputStream; + + bodyPart.bodyContentLength = (unsigned long long)length; + + [self.bodyStream appendHTTPBodyPart:bodyPart]; +} + +- (void)appendPartWithFileData:(NSData *)data + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType +{ + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + [self appendPartWithHeaders:mutableHeaders body:data]; +} + +- (void)appendPartWithFormData:(NSData *)data + name:(NSString *)name +{ + NSParameterAssert(name); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; + + [self appendPartWithHeaders:mutableHeaders body:data]; +} + +- (void)appendPartWithHeaders:(NSDictionary *)headers + body:(NSData *)body +{ + NSParameterAssert(body); + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = headers; + bodyPart.boundary = self.boundary; + bodyPart.bodyContentLength = [body length]; + bodyPart.body = body; + + [self.bodyStream appendHTTPBodyPart:bodyPart]; +} + +- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes + delay:(NSTimeInterval)delay +{ + self.bodyStream.numberOfBytesInPacket = numberOfBytes; + self.bodyStream.delay = delay; +} + +- (NSMutableURLRequest *)requestByFinalizingMultipartFormData { + if ([self.bodyStream isEmpty]) { + return self.request; + } + + // Reset the initial and final boundaries to ensure correct Content-Length + [self.bodyStream setInitialAndFinalBoundaries]; + [self.request setHTTPBodyStream:self.bodyStream]; + + [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"]; + [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; + + return self.request; +} + +@end + +#pragma mark - + +@interface NSStream () +@property (readwrite) NSStreamStatus streamStatus; +@property (readwrite, copy) NSError *streamError; +@end + +@interface AFMultipartBodyStream () +@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; +@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts; +@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; +@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; +@property (readwrite, nonatomic, strong) NSOutputStream *outputStream; +@property (readwrite, nonatomic, strong) NSMutableData *buffer; +@end + +@implementation AFMultipartBodyStream +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-atomic-properties" +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) +@synthesize delegate; +#endif +@synthesize streamStatus; +@synthesize streamError; +#pragma clang diagnostic pop + +- (id)initWithStringEncoding:(NSStringEncoding)encoding { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = encoding; + self.HTTPBodyParts = [NSMutableArray array]; + self.numberOfBytesInPacket = NSIntegerMax; + + return self; +} + +- (void)setInitialAndFinalBoundaries { + if ([self.HTTPBodyParts count] > 0) { + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + bodyPart.hasInitialBoundary = NO; + bodyPart.hasFinalBoundary = NO; + } + + [[self.HTTPBodyParts objectAtIndex:0] setHasInitialBoundary:YES]; + [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES]; + } +} + +- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart { + [self.HTTPBodyParts addObject:bodyPart]; +} + +- (BOOL)isEmpty { + return [self.HTTPBodyParts count] == 0; +} + +#pragma mark - NSInputStream + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + if ([self streamStatus] == NSStreamStatusClosed) { + return 0; + } + + NSInteger totalNumberOfBytesRead = 0; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) { + if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { + if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { + break; + } + } else { + NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead; + NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength]; + if (numberOfBytesRead == -1) { + self.streamError = self.currentHTTPBodyPart.inputStream.streamError; + break; + } else { + totalNumberOfBytesRead += numberOfBytesRead; + + if (self.delay > 0.0f) { + [NSThread sleepForTimeInterval:self.delay]; + } + } + } + } +#pragma clang diagnostic pop + + return totalNumberOfBytesRead; +} + +- (BOOL)getBuffer:(__unused uint8_t **)buffer + length:(__unused NSUInteger *)len +{ + return NO; +} + +- (BOOL)hasBytesAvailable { + return [self streamStatus] == NSStreamStatusOpen; +} + +#pragma mark - NSStream + +- (void)open { + if (self.streamStatus == NSStreamStatusOpen) { + return; + } + + self.streamStatus = NSStreamStatusOpen; + + [self setInitialAndFinalBoundaries]; + self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator]; +} + +- (void)close { + self.streamStatus = NSStreamStatusClosed; +} + +- (id)propertyForKey:(__unused NSString *)key { + return nil; +} + +- (BOOL)setProperty:(__unused id)property + forKey:(__unused NSString *)key +{ + return NO; +} + +- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop + forMode:(__unused NSString *)mode +{} + +- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop + forMode:(__unused NSString *)mode +{} + +- (unsigned long long)contentLength { + unsigned long long length = 0; + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + length += [bodyPart contentLength]; + } + + return length; +} + +#pragma mark - Undocumented CFReadStream Bridged Methods + +- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop + forMode:(__unused CFStringRef)aMode +{} + +- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop + forMode:(__unused CFStringRef)aMode +{} + +- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags + callback:(__unused CFReadStreamClientCallBack)inCallback + context:(__unused CFStreamClientContext *)inContext { + return NO; +} + +#pragma mark - NSCopying + +-(id)copyWithZone:(NSZone *)zone { + AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; + + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]]; + } + + [bodyStreamCopy setInitialAndFinalBoundaries]; + + return bodyStreamCopy; +} + +@end + +#pragma mark - + +typedef enum { + AFEncapsulationBoundaryPhase = 1, + AFHeaderPhase = 2, + AFBodyPhase = 3, + AFFinalBoundaryPhase = 4, +} AFHTTPBodyPartReadPhase; + +@interface AFHTTPBodyPart () { + AFHTTPBodyPartReadPhase _phase; + NSInputStream *_inputStream; + unsigned long long _phaseReadOffset; +} + +- (BOOL)transitionToNextPhase; +- (NSInteger)readData:(NSData *)data + intoBuffer:(uint8_t *)buffer + maxLength:(NSUInteger)length; +@end + +@implementation AFHTTPBodyPart + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + [self transitionToNextPhase]; + + return self; +} + +- (void)dealloc { + if (_inputStream) { + [_inputStream close]; + _inputStream = nil; + } +} + +- (NSInputStream *)inputStream { + if (!_inputStream) { + if ([self.body isKindOfClass:[NSData class]]) { + _inputStream = [NSInputStream inputStreamWithData:self.body]; + } else if ([self.body isKindOfClass:[NSURL class]]) { + _inputStream = [NSInputStream inputStreamWithURL:self.body]; + } else if ([self.body isKindOfClass:[NSInputStream class]]) { + _inputStream = self.body; + } else { + _inputStream = [NSInputStream inputStreamWithData:[NSData data]]; + } + } + + return _inputStream; +} + +- (NSString *)stringForHeaders { + NSMutableString *headerString = [NSMutableString string]; + for (NSString *field in [self.headers allKeys]) { + [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]]; + } + [headerString appendString:kAFMultipartFormCRLF]; + + return [NSString stringWithString:headerString]; +} + +- (unsigned long long)contentLength { + unsigned long long length = 0; + + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + length += [encapsulationBoundaryData length]; + + NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; + length += [headersData length]; + + length += _bodyContentLength; + + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + length += [closingBoundaryData length]; + + return length; +} + +- (BOOL)hasBytesAvailable { + // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer + if (_phase == AFFinalBoundaryPhase) { + return YES; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" + switch (self.inputStream.streamStatus) { + case NSStreamStatusNotOpen: + case NSStreamStatusOpening: + case NSStreamStatusOpen: + case NSStreamStatusReading: + case NSStreamStatusWriting: + return YES; + case NSStreamStatusAtEnd: + case NSStreamStatusClosed: + case NSStreamStatusError: + default: + return NO; + } +#pragma clang diagnostic pop +} + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + NSInteger totalNumberOfBytesRead = 0; + + if (_phase == AFEncapsulationBoundaryPhase) { + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + if (_phase == AFHeaderPhase) { + NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; + totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + if (_phase == AFBodyPhase) { + NSInteger numberOfBytesRead = 0; + + numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + if (numberOfBytesRead == -1) { + return -1; + } else { + totalNumberOfBytesRead += numberOfBytesRead; + + if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) { + [self transitionToNextPhase]; + } + } + } + + if (_phase == AFFinalBoundaryPhase) { + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + return totalNumberOfBytesRead; +} + +- (NSInteger)readData:(NSData *)data + intoBuffer:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length)); + [data getBytes:buffer range:range]; +#pragma clang diagnostic pop + + _phaseReadOffset += range.length; + + if (((NSUInteger)_phaseReadOffset) >= [data length]) { + [self transitionToNextPhase]; + } + + return (NSInteger)range.length; +} + +- (BOOL)transitionToNextPhase { + if (![[NSThread currentThread] isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self transitionToNextPhase]; + }); + return YES; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" + switch (_phase) { + case AFEncapsulationBoundaryPhase: + _phase = AFHeaderPhase; + break; + case AFHeaderPhase: + [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [self.inputStream open]; + _phase = AFBodyPhase; + break; + case AFBodyPhase: + [self.inputStream close]; + _phase = AFFinalBoundaryPhase; + break; + case AFFinalBoundaryPhase: + default: + _phase = AFEncapsulationBoundaryPhase; + break; + } + _phaseReadOffset = 0; +#pragma clang diagnostic pop + + return YES; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; + + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = self.headers; + bodyPart.bodyContentLength = self.bodyContentLength; + bodyPart.body = self.body; + bodyPart.boundary = self.boundary; + + return bodyPart; +} + +@end + +#pragma mark - + +@implementation AFJSONRequestSerializer + ++ (instancetype)serializer { + return [self serializerWithWritingOptions:(NSJSONWritingOptions)0]; +} + ++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions +{ + AFJSONRequestSerializer *serializer = [[self alloc] init]; + serializer.writingOptions = writingOptions; + + return serializer; +} + +#pragma mark - AFURLRequestSerialization + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + return [super requestBySerializingRequest:request withParameters:parameters error:error]; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + } + + [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]]; + } + + return mutableRequest; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFJSONRequestSerializer *serializer = [super copyWithZone:zone]; + serializer.writingOptions = self.writingOptions; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFPropertyListRequestSerializer + ++ (instancetype)serializer { + return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0]; +} + ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + writeOptions:(NSPropertyListWriteOptions)writeOptions +{ + AFPropertyListRequestSerializer *serializer = [[self alloc] init]; + serializer.format = format; + serializer.writeOptions = writeOptions; + + return serializer; +} + +#pragma mark - AFURLRequestSerializer + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + return [super requestBySerializingRequest:request withParameters:parameters error:error]; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; + } + + [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]]; + } + + return mutableRequest; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; + self.writeOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writeOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))]; + [coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone]; + serializer.format = self.format; + serializer.writeOptions = self.writeOptions; + + return serializer; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h new file mode 100644 index 0000000..5eff8e6 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1,310 @@ +// AFURLResponseSerialization.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +/** + The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. + + For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. + */ +@protocol AFURLResponseSerialization + +/** + The response object decoded from the data associated with a specified response. + + @param response The response to be processed. + @param data The response data to be decoded. + @param error The error that occurred while attempting to decode the response data. + + @return The object decoded from the specified response data. + */ +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error; + +@end + +#pragma mark - + +/** + `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + + Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. + */ +@interface AFHTTPResponseSerializer : NSObject + +- (instancetype) init; + +/** + The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. + */ +@property (nonatomic, assign) NSStringEncoding stringEncoding; + +/** + Creates and returns a serializer with default configuration. + */ ++ (instancetype)serializer; + +///----------------------------------------- +/// @name Configuring Response Serialization +///----------------------------------------- + +/** + The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. + + See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + */ +@property (nonatomic, copy) NSIndexSet *acceptableStatusCodes; + +/** + The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. + */ +@property (nonatomic, copy) NSSet *acceptableContentTypes; + +/** + Validates the specified response and data. + + In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. + + @param response The response to be validated. + @param data The data associated with the response. + @param error The error that occurred while attempting to validate the response. + + @return `YES` if the response is valid, otherwise `NO`. + */ +- (BOOL)validateResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error; + +@end + +#pragma mark - + + +/** + `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. + + By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: + + - `application/json` + - `text/json` + - `text/javascript` + */ +@interface AFJSONResponseSerializer : AFHTTPResponseSerializer + +- (instancetype) init; + +/** + Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. + */ +@property (nonatomic, assign) NSJSONReadingOptions readingOptions; + +/** + Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`. + */ +@property (nonatomic, assign) BOOL removesKeysWithNullValues; + +/** + Creates and returns a JSON serializer with specified reading and writing options. + + @param readingOptions The specified JSON reading options. + */ ++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; + +@end + +#pragma mark - + +/** + `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. + + By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + + - `application/xml` + - `text/xml` + */ +@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer + +@end + +#pragma mark - + +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED + +/** + `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + + By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + + - `application/xml` + - `text/xml` + */ +@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer + +- (instancetype) init; + +/** + Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. + */ +@property (nonatomic, assign) NSUInteger options; + +/** + Creates and returns an XML document serializer with the specified options. + + @param mask The XML document options. + */ ++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; + +@end + +#endif + +#pragma mark - + +/** + `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + + By default, `AFPropertyListResponseSerializer` accepts the following MIME types: + + - `application/x-plist` + */ +@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer + +- (instancetype) init; + +/** + The property list format. Possible values are described in "NSPropertyListFormat". + */ +@property (nonatomic, assign) NSPropertyListFormat format; + +/** + The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." + */ +@property (nonatomic, assign) NSPropertyListReadOptions readOptions; + +/** + Creates and returns a property list serializer with a specified format, read options, and write options. + + @param format The property list format. + @param readOptions The property list reading options. + */ ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + readOptions:(NSPropertyListReadOptions)readOptions; + +@end + +#pragma mark - + +/** + `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. + + By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: + + - `image/tiff` + - `image/jpeg` + - `image/gif` + - `image/png` + - `image/ico` + - `image/x-icon` + - `image/bmp` + - `image/x-bmp` + - `image/x-xbitmap` + - `image/x-win-bitmap` + */ +@interface AFImageResponseSerializer : AFHTTPResponseSerializer + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +/** + The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. + */ +@property (nonatomic, assign) CGFloat imageScale; + +/** + Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. + */ +@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; +#endif + +@end + +#pragma mark - + +/** + `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. + */ +@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer + +/** + The component response serializers. + */ +@property (readonly, nonatomic, copy) NSArray *responseSerializers; + +/** + Creates and returns a compound serializer comprised of the specified response serializers. + + @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. + */ ++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## Error Domains + + The following error domain is predefined. + + - `NSString * const AFURLResponseSerializationErrorDomain` + + ### Constants + + `AFURLResponseSerializationErrorDomain` + AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + */ +extern NSString * const AFURLResponseSerializationErrorDomain; + +/** + ## User info dictionary keys + + These keys may exist in the user info dictionary, in addition to those defined for NSError. + + - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` + - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` + + ### Constants + + `AFNetworkingOperationFailingURLResponseErrorKey` + The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + + `AFNetworkingOperationFailingURLResponseDataErrorKey` + The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + */ +extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey; + +extern NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey; + + diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m new file mode 100644 index 0000000..b8e4d1c --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m @@ -0,0 +1,798 @@ +// AFURLResponseSerialization.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLResponseSerialization.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#import +#endif + +NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; +NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; +NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; + +static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { + if (!error) { + return underlyingError; + } + + if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) { + return error; + } + + NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy]; + mutableUserInfo[NSUnderlyingErrorKey] = underlyingError; + + return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; +} + +static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { + if ([error.domain isEqualToString:domain] && error.code == code) { + return YES; + } else if (error.userInfo[NSUnderlyingErrorKey]) { + return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); + } + + return NO; +} + +static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { + if ([JSONObject isKindOfClass:[NSArray class]]) { + NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; + for (id value in (NSArray *)JSONObject) { + [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; + } + + return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray]; + } else if ([JSONObject isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject]; + for (id key in [(NSDictionary *)JSONObject allKeys]) { + id value = [(NSDictionary *)JSONObject objectForKey:key]; + if (!value || [value isEqual:[NSNull null]]) { + [mutableDictionary removeObjectForKey:key]; + } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) { + [mutableDictionary setObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions) forKey:key]; + } + } + + return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary]; + } + + return JSONObject; +} + +@implementation AFHTTPResponseSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = NSUTF8StringEncoding; + + self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; + self.acceptableContentTypes = nil; + + return self; +} + +#pragma mark - + +- (BOOL)validateResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError * __autoreleasing *)error +{ + BOOL responseIsValid = YES; + NSError *validationError = nil; + + if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { + if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) { + if ([data length] > 0 && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } + + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); + } + + responseIsValid = NO; + } + + if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } + + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); + + responseIsValid = NO; + } + } + + if (error && !responseIsValid) { + *error = validationError; + } + + return responseIsValid; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; + + return data; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; + self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; + [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone]; + serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone]; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFJSONResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; +} + ++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { + AFJSONResponseSerializer *serializer = [[self alloc] init]; + serializer.readingOptions = readingOptions; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. + // See https://github.com/rails/rails/issues/1742 + NSStringEncoding stringEncoding = self.stringEncoding; + if (response.textEncodingName) { + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (encoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); + } + } + + id responseObject = nil; + NSError *serializationError = nil; + @autoreleasepool { + NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding]; + if (responseString && ![responseString isEqualToString:@" "]) { + // Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character + // See http://stackoverflow.com/a/12843465/157142 + data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + + if (data) { + if ([data length] > 0) { + responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; + } else { + return nil; + } + } else { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", @"AFNetworking", nil), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", @"AFNetworking", nil), responseString] + }; + + serializationError = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; + } + } + } + + if (self.removesKeysWithNullValues && responseObject) { + responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); + } + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue]; + self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))]; + [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.readingOptions = self.readingOptions; + serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFXMLParserResponseSerializer + ++ (instancetype)serializer { + AFXMLParserResponseSerializer *serializer = [[self alloc] init]; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + return [[NSXMLParser alloc] initWithData:data]; +} + +@end + +#pragma mark - + +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED + +@implementation AFXMLDocumentResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithXMLDocumentOptions:0]; +} + ++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask { + AFXMLDocumentResponseSerializer *serializer = [[self alloc] init]; + serializer.options = mask; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + NSError *serializationError = nil; + NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return document; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.options = self.options; + + return serializer; +} + +@end + +#endif + +#pragma mark - + +@implementation AFPropertyListResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0]; +} + ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + readOptions:(NSPropertyListReadOptions)readOptions +{ + AFPropertyListResponseSerializer *serializer = [[self alloc] init]; + serializer.format = format; + serializer.readOptions = readOptions; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + id responseObject; + NSError *serializationError = nil; + + if (data) { + responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; + } + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; + self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))]; + [coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.format = self.format; + serializer.readOptions = self.readOptions; + + return serializer; +} + +@end + +#pragma mark - + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import + +static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { + UIImage *image = [[UIImage alloc] initWithData:data]; + if (image.images) { + return image; + } + + return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; +} + +static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { + if (!data || [data length] == 0) { + return nil; + } + + CGImageRef imageRef = NULL; + CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + if ([response.MIMEType isEqualToString:@"image/png"]) { + imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); + } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { + imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); + + if (imageRef) { + CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); + CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); + + // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale + if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { + CGImageRelease(imageRef); + imageRef = NULL; + } + } + } + + CGDataProviderRelease(dataProvider); + + UIImage *image = AFImageWithDataAtScale(data, scale); + if (!imageRef) { + if (image.images || !image) { + return image; + } + + imageRef = CGImageCreateCopy([image CGImage]); + if (!imageRef) { + return nil; + } + } + + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); + size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); + + if (width * height > 1024 * 1024 || bitsPerComponent > 8) { + CGImageRelease(imageRef); + + return image; + } + + // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate + size_t bytesPerRow = 0; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); + + if (colorSpaceModel == kCGColorSpaceModelRGB) { + uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wassign-enum" + if (alpha == kCGImageAlphaNone) { + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaNoneSkipFirst; + } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaPremultipliedFirst; + } +#pragma clang diagnostic pop + } + + CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); + + CGColorSpaceRelease(colorSpace); + + if (!context) { + CGImageRelease(imageRef); + + return image; + } + + CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); + CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); + + CGContextRelease(context); + + UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; + + CGImageRelease(inflatedImageRef); + CGImageRelease(imageRef); + + return inflatedImage; +} +#endif + + +@implementation AFImageResponseSerializer + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + self.imageScale = [[UIScreen mainScreen] scale]; + self.automaticallyInflatesResponseImage = YES; +#endif + + return self; +} + +#pragma mark - AFURLResponseSerializer + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + if (self.automaticallyInflatesResponseImage) { + return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); + } else { + return AFImageWithDataAtScale(data, self.imageScale); + } +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) + // Ensure that the image is set to it's correct pixel width and height + NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data]; + NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])]; + [image addRepresentation:bitimage]; + + return image; +#endif + + return nil; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))]; +#if CGFLOAT_IS_DOUBLE + self.imageScale = [imageScale doubleValue]; +#else + self.imageScale = [imageScale floatValue]; +#endif + + self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; + [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + serializer.imageScale = self.imageScale; + serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage; +#endif + + return serializer; +} + +@end + +#pragma mark - + +@interface AFCompoundResponseSerializer () +@property (readwrite, nonatomic, copy) NSArray *responseSerializers; +@end + +@implementation AFCompoundResponseSerializer + ++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers { + AFCompoundResponseSerializer *serializer = [[self alloc] init]; + serializer.responseSerializers = responseSerializers; + + return serializer; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + for (id serializer in self.responseSerializers) { + if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) { + continue; + } + + NSError *serializerError = nil; + id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; + if (responseObject) { + if (error) { + *error = AFErrorWithUnderlyingError(serializerError, *error); + } + + return responseObject; + } + } + + return [super responseObjectForResponse:response data:data error:error]; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.responseSerializers = self.responseSerializers; + + return serializer; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h new file mode 100644 index 0000000..584d964 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h @@ -0,0 +1,547 @@ +// AFURLSessionManager.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" +#import "AFSecurityPolicy.h" +#import "AFNetworkReachabilityManager.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. + + ## Subclassing Notes + + This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. + + ## NSURLSession & NSURLSessionTask Delegate Methods + + `AFURLSessionManager` implements the following delegate methods: + + ### `NSURLSessionDelegate` + + - `URLSession:didBecomeInvalidWithError:` + - `URLSession:didReceiveChallenge:completionHandler:` + - `URLSessionDidFinishEventsForBackgroundURLSession:` + + ### `NSURLSessionTaskDelegate` + + - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:` + - `URLSession:task:didReceiveChallenge:completionHandler:` + - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` + - `URLSession:task:didCompleteWithError:` + + ### `NSURLSessionDataDelegate` + + - `URLSession:dataTask:didReceiveResponse:completionHandler:` + - `URLSession:dataTask:didBecomeDownloadTask:` + - `URLSession:dataTask:didReceiveData:` + - `URLSession:dataTask:willCacheResponse:completionHandler:` + + ### `NSURLSessionDownloadDelegate` + + - `URLSession:downloadTask:didFinishDownloadingToURL:` + - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:` + - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:` + + If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. + + ## Network Reachability Monitoring + + Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. + + ## NSCoding Caveats + + - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`. + + ## NSCopying Caveats + + - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original. + - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. + */ + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) + +@interface AFURLSessionManager : NSObject + +/** + The managed session. + */ +@property (readonly, nonatomic, strong) NSURLSession *session; + +/** + The operation queue on which delegate callbacks are run. + */ +@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) id responseSerializer; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created request operations to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +///-------------------------------------- +/// @name Monitoring Network Reachability +///-------------------------------------- + +/** + The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default. + */ +@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; + +///---------------------------- +/// @name Getting Session Tasks +///---------------------------- + +/** + The data, upload, and download tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *tasks; + +/** + The data tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *dataTasks; + +/** + The upload tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *uploadTasks; + +/** + The download tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *downloadTasks; + +///------------------------------- +/// @name Managing Callback Queues +///------------------------------- + +/** + The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign) dispatch_queue_t completionQueue; +#endif + +/** + The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign) dispatch_group_t completionGroup; +#endif + +///--------------------------------- +/// @name Working Around System Bugs +///--------------------------------- + +/** + Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default. + + @bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again. + + @see https://github.com/AFNetworking/AFNetworking/issues/1675 + */ +@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns a manager for a session created with the specified configuration. This is the designated initializer. + + @param configuration The configuration used to create the managed session. + + @return A manager for a newly-created session. + */ +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +/** + Invalidates the managed session, optionally canceling pending tasks. + + @param cancelPendingTasks Whether or not to cancel pending tasks. + */ +- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; + +///------------------------- +/// @name Running Data Tasks +///------------------------- + +/** + Creates an `NSURLSessionDataTask` with the specified request. + + @param request The HTTP request for the request. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; + +///--------------------------- +/// @name Running Upload Tasks +///--------------------------- + +/** + Creates an `NSURLSessionUploadTask` with the specified request for a local file. + + @param request The HTTP request for the request. + @param fileURL A URL to the local file to be uploaded. + @param progress A progress object monitoring the current upload progress. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + + @see `attemptsToRecreateUploadTasksForBackgroundSessions` + */ +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; + +/** + Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body. + + @param request The HTTP request for the request. + @param bodyData A data object containing the HTTP body to be uploaded. + @param progress A progress object monitoring the current upload progress. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(NSData *)bodyData + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; + +/** + Creates an `NSURLSessionUploadTask` with the specified streaming request. + + @param request The HTTP request for the request. + @param progress A progress object monitoring the current upload progress. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler; + +///----------------------------- +/// @name Running Download Tasks +///----------------------------- + +/** + Creates an `NSURLSessionDownloadTask` with the specified request. + + @param request The HTTP request for the request. + @param progress A progress object monitoring the current download progress. + @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. + @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. + + @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method. + */ +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler; + +/** + Creates an `NSURLSessionDownloadTask` with the specified resume data. + + @param resumeData The data used to resume downloading. + @param progress A progress object monitoring the current download progress. + @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. + @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. + */ +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler; + +///--------------------------------- +/// @name Getting Progress for Tasks +///--------------------------------- + +/** + Returns the upload progress of the specified task. + + @param uploadTask The session upload task. Must not be `nil`. + + @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable. + */ +- (NSProgress *)uploadProgressForTask:(NSURLSessionUploadTask *)uploadTask; + +/** + Returns the download progress of the specified task. + + @param downloadTask The session download task. Must not be `nil`. + + @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable. + */ +- (NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask; + +///----------------------------------------- +/// @name Setting Session Delegate Callbacks +///----------------------------------------- + +/** + Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`. + + @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation. + */ +- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block; + +/** + Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`. + + @param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. + */ +- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block; + +///-------------------------------------- +/// @name Setting Task Delegate Callbacks +///-------------------------------------- + +/** + Sets a block to be executed when a task requires a new request body stream to send to the remote server, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:needNewBodyStream:`. + + @param block A block object to be executed when a task requires a new request body stream. + */ +- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block; + +/** + Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`. + + @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. + */ +- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; + +/** + Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. + + @param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. + */ +- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block; + +/** + Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. + + @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; + +/** + Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`. + + @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task. + */ +- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block; + +///------------------------------------------- +/// @name Setting Data Task Delegate Callbacks +///------------------------------------------- + +/** + Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`. + + @param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response. + */ +- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block; + +/** + Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`. + + @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become. + */ +- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; + +/** + Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue. + */ +- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; + +/** + Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`. + + @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response. + */ +- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; + +/** + Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`. + + @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. + */ +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block; + +///----------------------------------------------- +/// @name Setting Download Task Delegate Callbacks +///----------------------------------------------- + +/** + Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. + + @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. + */ +- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; + +/** + Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue. + */ +- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; + +/** + Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. + + @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded. + */ +- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block; + +@end + +#endif + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when a task begins executing. + + @deprecated Use `AFNetworkingTaskDidResumeNotification` instead. + */ +extern NSString * const AFNetworkingTaskDidStartNotification DEPRECATED_ATTRIBUTE; + +/** + Posted when a task resumes. + */ +extern NSString * const AFNetworkingTaskDidResumeNotification; + +/** + Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. + + @deprecated Use `AFNetworkingTaskDidCompleteNotification` instead. + */ +extern NSString * const AFNetworkingTaskDidFinishNotification DEPRECATED_ATTRIBUTE; + +/** + Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. + */ +extern NSString * const AFNetworkingTaskDidCompleteNotification; + +/** + Posted when a task suspends its execution. + */ +extern NSString * const AFNetworkingTaskDidSuspendNotification; + +/** + Posted when a session is invalidated. + */ +extern NSString * const AFURLSessionDidInvalidateNotification; + +/** + Posted when a session download task encountered an error when moving the temporary download file to a specified destination. + */ +extern NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; + +/** + The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. + + @deprecated Use `AFNetworkingTaskDidCompleteResponseDataKey` instead. + */ +extern NSString * const AFNetworkingTaskDidFinishResponseDataKey DEPRECATED_ATTRIBUTE; + +/** + The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. + */ +extern NSString * const AFNetworkingTaskDidCompleteResponseDataKey; + +/** + The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. + + @deprecated Use `AFNetworkingTaskDidCompleteSerializedResponseKey` instead. + */ +extern NSString * const AFNetworkingTaskDidFinishSerializedResponseKey DEPRECATED_ATTRIBUTE; + +/** + The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. + */ +extern NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey; + +/** + The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. + + @deprecated Use `AFNetworkingTaskDidCompleteResponseSerializerKey` instead. + */ +extern NSString * const AFNetworkingTaskDidFinishResponseSerializerKey DEPRECATED_ATTRIBUTE; + +/** + The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. + */ +extern NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey; + +/** + The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. + + @deprecated Use `AFNetworkingTaskDidCompleteAssetPathKey` instead. + */ +extern NSString * const AFNetworkingTaskDidFinishAssetPathKey DEPRECATED_ATTRIBUTE; + +/** + The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. + */ +extern NSString * const AFNetworkingTaskDidCompleteAssetPathKey; + +/** + Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. + + @deprecated Use `AFNetworkingTaskDidCompleteErrorKey` instead. + */ +extern NSString * const AFNetworkingTaskDidFinishErrorKey DEPRECATED_ATTRIBUTE; + +/** + Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. + */ +extern NSString * const AFNetworkingTaskDidCompleteErrorKey; diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m new file mode 100644 index 0000000..b06e1ad --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m @@ -0,0 +1,1102 @@ +// AFURLSessionManager.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLSessionManager.h" +#import + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) + +static dispatch_queue_t url_session_manager_creation_queue() { + static dispatch_queue_t af_url_session_manager_creation_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); + }); + + return af_url_session_manager_creation_queue; +} + +static dispatch_queue_t url_session_manager_processing_queue() { + static dispatch_queue_t af_url_session_manager_processing_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); + }); + + return af_url_session_manager_processing_queue; +} + +static dispatch_group_t url_session_manager_completion_group() { + static dispatch_group_t af_url_session_manager_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_completion_group = dispatch_group_create(); + }); + + return af_url_session_manager_completion_group; +} + +NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; +NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; +NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; +NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; +NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; + +NSString * const AFNetworkingTaskDidStartNotification = @"com.alamofire.networking.task.resume"; // Deprecated +NSString * const AFNetworkingTaskDidFinishNotification = @"com.alamofire.networking.task.complete"; // Deprecated + +NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; +NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; +NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; +NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; +NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; + +NSString * const AFNetworkingTaskDidFinishSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; // Deprecated +NSString * const AFNetworkingTaskDidFinishResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; // Deprecated +NSString * const AFNetworkingTaskDidFinishResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; // Deprecated +NSString * const AFNetworkingTaskDidFinishErrorKey = @"com.alamofire.networking.task.complete.error"; // Deprecated +NSString * const AFNetworkingTaskDidFinishAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; // Deprecated + +static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; + +static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; + +static void * AFTaskStateChangedContext = &AFTaskStateChangedContext; + +typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); +typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); + +typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); +typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); +typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); + +typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); +typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); +typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); + +typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); +typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); +typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); +typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); + +typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); +typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); +typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); + +typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); + +#pragma mark - + +@interface AFURLSessionManagerTaskDelegate : NSObject +@property (nonatomic, weak) AFURLSessionManager *manager; +@property (nonatomic, strong) NSMutableData *mutableData; +@property (nonatomic, strong) NSProgress *progress; +@property (nonatomic, copy) NSURL *downloadFileURL; +@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; +@end + +@implementation AFURLSessionManagerTaskDelegate + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.mutableData = [NSMutableData data]; + + self.progress = [NSProgress progressWithTotalUnitCount:0]; + + return self; +} + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(__unused NSURLSession *)session + task:(__unused NSURLSessionTask *)task + didSendBodyData:(__unused int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +{ + self.progress.totalUnitCount = totalBytesExpectedToSend; + self.progress.completedUnitCount = totalBytesSent; +} + +- (void)URLSession:(__unused NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + __strong AFURLSessionManager *manager = self.manager; + + __block id responseObject = nil; + + __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; + + if (self.downloadFileURL) { + userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; + } else if (self.mutableData) { + userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = [NSData dataWithData:self.mutableData]; + } + + if (error) { + userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; + + dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ + if (self.completionHandler) { + self.completionHandler(task.response, responseObject, error); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; + }); + }); + } else { + dispatch_async(url_session_manager_processing_queue(), ^{ + NSError *serializationError = nil; + responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:[NSData dataWithData:self.mutableData] error:&serializationError]; + + if (self.downloadFileURL) { + responseObject = self.downloadFileURL; + } + + if (responseObject) { + userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; + } + + if (serializationError) { + userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; + } + + dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ + if (self.completionHandler) { + self.completionHandler(task.response, responseObject, serializationError); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; + }); + }); + }); + } +#pragma clang diagnostic pop +} + +#pragma mark - NSURLSessionDataTaskDelegate + +- (void)URLSession:(__unused NSURLSession *)session + dataTask:(__unused NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data +{ + [self.mutableData appendData:data]; +} + +#pragma mark - NSURLSessionDownloadTaskDelegate + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location +{ + NSError *fileManagerError = nil; + self.downloadFileURL = nil; + + if (self.downloadTaskDidFinishDownloading) { + self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); + if (self.downloadFileURL) { + [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; + + if (fileManagerError) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; + } + } + } +} + +- (void)URLSession:(__unused NSURLSession *)session + downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask + didWriteData:(__unused int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite +{ + self.progress.totalUnitCount = totalBytesExpectedToWrite; + self.progress.completedUnitCount = totalBytesWritten; +} + +- (void)URLSession:(__unused NSURLSession *)session + downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes { + self.progress.totalUnitCount = expectedTotalBytes; + self.progress.completedUnitCount = fileOffset; +} + +@end + +#pragma mark - + +/* + A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. + + See https://github.com/AFNetworking/AFNetworking/issues/1477 + */ + +static inline void af_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) { + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) { + class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } +} + +static inline void af_addMethod(Class class, SEL selector, Method method) { + class_addMethod(class, selector, method_getImplementation(method), method_getTypeEncoding(method)); +} + +static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; +static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; + +@interface NSURLSessionDataTask (_AFStateObserving) +@end + +@implementation NSURLSessionDataTask (_AFStateObserving) + ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSURLSessionDataTask *dataTask = [[NSURLSession sessionWithConfiguration:nil] dataTaskWithURL:nil]; + Class taskClass = [dataTask superclass]; + af_addMethod(taskClass, @selector(af_resume), class_getInstanceMethod(self, @selector(af_resume))); + af_addMethod(taskClass, @selector(af_suspend), class_getInstanceMethod(self, @selector(af_suspend))); + af_swizzleSelector(taskClass, @selector(resume), @selector(af_resume)); + af_swizzleSelector(taskClass, @selector(suspend), @selector(af_suspend)); + }); +} + +#pragma mark - + +- (void)af_resume { + NSURLSessionTaskState state = self.state; + [self af_resume]; + + if (state != NSURLSessionTaskStateRunning) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; + } +} + +- (void)af_suspend { + NSURLSessionTaskState state = self.state; + [self af_suspend]; + + if (state != NSURLSessionTaskStateSuspended) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; + } +} + +@end + +#pragma mark - + +@interface AFURLSessionManager () +@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; +@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; +@property (readwrite, nonatomic, strong) NSURLSession *session; +@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; +@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; +@property (readwrite, nonatomic, strong) NSLock *lock; +@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; +@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; +@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; +@end + +@implementation AFURLSessionManager + +- (instancetype)init { + return [self initWithSessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + self = [super init]; + if (!self) { + return nil; + } + + if (!configuration) { + configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + } + + self.sessionConfiguration = configuration; + + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.maxConcurrentOperationCount = 1; + + self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; + + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + + self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; + + self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; + + self.lock = [[NSLock alloc] init]; + self.lock.name = AFURLSessionManagerLockName; + + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { + for (NSURLSessionDataTask *task in dataTasks) { + [self addDelegateForDataTask:task completionHandler:nil]; + } + + for (NSURLSessionUploadTask *uploadTask in uploadTasks) { + [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; + } + + for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { + [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; + } + }]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:nil]; + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - + +- (NSString *)taskDescriptionForSessionTasks { + return [NSString stringWithFormat:@"%p", self]; +} + +- (void)taskDidResume:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; + }); + } + } +} + +- (void)taskDidSuspend:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; + }); + } + } +} + +#pragma mark - + +- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + AFURLSessionManagerTaskDelegate *delegate = nil; + [self.lock lock]; + delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; + [self.lock unlock]; + + return delegate; +} + +- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate + forTask:(NSURLSessionTask *)task +{ + NSParameterAssert(task); + NSParameterAssert(delegate); + + [self.lock lock]; + self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; + [self.lock unlock]; +} + +- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + dataTask.taskDescription = self.taskDescriptionForSessionTasks; + [self setDelegate:delegate forTask:dataTask]; +} + +- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + int64_t totalUnitCount = uploadTask.countOfBytesExpectedToSend; + if(totalUnitCount == NSURLSessionTransferSizeUnknown) { + NSString *contentLength = [uploadTask.originalRequest valueForHTTPHeaderField:@"Content-Length"]; + if(contentLength) { + totalUnitCount = (int64_t)[contentLength longLongValue]; + } + } + + if (delegate.progress) { + delegate.progress.totalUnitCount = totalUnitCount; + } else { + delegate.progress = [NSProgress progressWithTotalUnitCount:totalUnitCount]; + } + + delegate.progress.pausingHandler = ^{ + [uploadTask suspend]; + }; + delegate.progress.cancellationHandler = ^{ + [uploadTask cancel]; + }; + + if (progress) { + *progress = delegate.progress; + } + + uploadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:uploadTask]; +} + +- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + if (destination) { + delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { + return destination(location, task.response); + }; + } + + if (progress) { + *progress = delegate.progress; + } + + downloadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:downloadTask]; +} + +- (void)removeDelegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + [self.lock lock]; + [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; + [self.lock unlock]; +} + +- (void)removeAllDelegates { + [self.lock lock]; + [self.mutableTaskDelegatesKeyedByTaskIdentifier removeAllObjects]; + [self.lock unlock]; +} + +#pragma mark - + +- (NSArray *)tasksForKeyPath:(NSString *)keyPath { + __block NSArray *tasks = nil; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { + tasks = dataTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { + tasks = uploadTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { + tasks = downloadTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { + tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; + } + + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + return tasks; +} + +- (NSArray *)tasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)dataTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)uploadTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)downloadTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +#pragma mark - + +- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelPendingTasks) { + [self.session invalidateAndCancel]; + } else { + [self.session finishTasksAndInvalidate]; + } + }); +} + +#pragma mark - + +- (void)setResponseSerializer:(id )responseSerializer { + NSParameterAssert(responseSerializer); + + _responseSerializer = responseSerializer; +} + +#pragma mark - + +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionDataTask *dataTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + dataTask = [self.session dataTaskWithRequest:request]; + }); + + [self addDelegateForDataTask:dataTask completionHandler:completionHandler]; + + return dataTask; +} + +#pragma mark - + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + }); + + if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { + for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { + uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + } + } + + [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(NSData *)bodyData + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; + }); + + [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithStreamedRequest:request]; + }); + + [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; + + return uploadTask; +} + +#pragma mark - + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + __block NSURLSessionDownloadTask *downloadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + downloadTask = [self.session downloadTaskWithRequest:request]; + }); + + [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + __block NSURLSessionDownloadTask *downloadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + downloadTask = [self.session downloadTaskWithResumeData:resumeData]; + }); + + [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +#pragma mark - + +- (NSProgress *)uploadProgressForTask:(NSURLSessionUploadTask *)uploadTask { + return [[self delegateForTask:uploadTask] progress]; +} + +- (NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask { + return [[self delegateForTask:downloadTask] progress]; +} + +#pragma mark - + +- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { + self.sessionDidBecomeInvalid = block; +} + +- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { + self.sessionDidReceiveAuthenticationChallenge = block; +} + +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { + self.didFinishEventsForBackgroundURLSession = block; +} + +#pragma mark - + +- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { + self.taskNeedNewBodyStream = block; +} + +- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block { + self.taskWillPerformHTTPRedirection = block; +} + +- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { + self.taskDidReceiveAuthenticationChallenge = block; +} + +- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block { + self.taskDidSendBodyData = block; +} + +- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block { + self.taskDidComplete = block; +} + +#pragma mark - + +- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block { + self.dataTaskDidReceiveResponse = block; +} + +- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block { + self.dataTaskDidBecomeDownloadTask = block; +} + +- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block { + self.dataTaskDidReceiveData = block; +} + +- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block { + self.dataTaskWillCacheResponse = block; +} + +#pragma mark - + +- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { + self.downloadTaskDidFinishDownloading = block; +} + +- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block { + self.downloadTaskDidWriteData = block; +} + +- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block { + self.downloadTaskDidResume = block; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; +} + +- (BOOL)respondsToSelector:(SEL)selector { + if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) { + return self.taskWillPerformHTTPRedirection != nil; + } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { + return self.dataTaskDidReceiveResponse != nil; + } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) { + return self.dataTaskWillCacheResponse != nil; + } else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { + return self.didFinishEventsForBackgroundURLSession != nil; + } + + return [[self class] instancesRespondToSelector:selector]; +} + +#pragma mark - NSURLSessionDelegate + +- (void)URLSession:(NSURLSession *)session +didBecomeInvalidWithError:(NSError *)error +{ + if (self.sessionDidBecomeInvalid) { + self.sessionDidBecomeInvalid(session, error); + } + + [self removeAllDelegates]; + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; +} + +- (void)URLSession:(NSURLSession *)session +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; + __block NSURLCredential *credential = nil; + + if (self.sessionDidReceiveAuthenticationChallenge) { + disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); + } else { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + if (credential) { + disposition = NSURLSessionAuthChallengeUseCredential; + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } else { + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler +{ + NSURLRequest *redirectRequest = request; + + if (self.taskWillPerformHTTPRedirection) { + redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); + } + + if (completionHandler) { + completionHandler(redirectRequest); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; + __block NSURLCredential *credential = nil; + + if (self.taskDidReceiveAuthenticationChallenge) { + disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); + } else { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + disposition = NSURLSessionAuthChallengeUseCredential; + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + } else { + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler +{ + NSInputStream *inputStream = nil; + + if (self.taskNeedNewBodyStream) { + inputStream = self.taskNeedNewBodyStream(session, task); + } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { + inputStream = [task.originalRequest.HTTPBodyStream copy]; + } + + if (completionHandler) { + completionHandler(inputStream); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +{ + + int64_t totalUnitCount = totalBytesExpectedToSend; + if(totalUnitCount == NSURLSessionTransferSizeUnknown) { + NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; + if(contentLength) { + totalUnitCount = (int64_t) [contentLength longLongValue]; + } + } + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalUnitCount]; + + if (self.taskDidSendBodyData) { + self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + + // delegate may be nil when completing a task in the background + if (delegate) { + [delegate URLSession:session task:task didCompleteWithError:error]; + + [self removeDelegateForTask:task]; + } + + if (self.taskDidComplete) { + self.taskDidComplete(session, task, error); + } + +} + +#pragma mark - NSURLSessionDataDelegate + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler +{ + NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; + + if (self.dataTaskDidReceiveResponse) { + disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); + } + + if (completionHandler) { + completionHandler(disposition); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; + if (delegate) { + [self removeDelegateForTask:dataTask]; + [self setDelegate:delegate forTask:downloadTask]; + } + + if (self.dataTaskDidBecomeDownloadTask) { + self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; + [delegate URLSession:session dataTask:dataTask didReceiveData:data]; + + if (self.dataTaskDidReceiveData) { + self.dataTaskDidReceiveData(session, dataTask, data); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + willCacheResponse:(NSCachedURLResponse *)proposedResponse + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler +{ + NSCachedURLResponse *cachedResponse = proposedResponse; + + if (self.dataTaskWillCacheResponse) { + cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); + } + + if (completionHandler) { + completionHandler(cachedResponse); + } +} + +- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { + if (self.didFinishEventsForBackgroundURLSession) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.didFinishEventsForBackgroundURLSession(session); + }); + } +} + +#pragma mark - NSURLSessionDownloadDelegate + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + if (self.downloadTaskDidFinishDownloading) { + NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); + if (fileURL) { + delegate.downloadFileURL = fileURL; + NSError *error = nil; + [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; + if (error) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; + } + + return; + } + } + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; + } +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + + if (self.downloadTaskDidWriteData) { + self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; + + if (self.downloadTaskDidResume) { + self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; + + self = [self initWithSessionConfiguration:configuration]; + if (!self) { + return nil; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 100644 index 0000000..afebad3 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1,76 @@ +// AFNetworkActivityIndicatorManager.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +/** + `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a network request operation has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. + + You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code: + + [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES]; + + By setting `enabled` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. + + See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information: + http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44 + */ +@interface AFNetworkActivityIndicatorManager : NSObject + +/** + A Boolean value indicating whether the manager is enabled. + + If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO. + */ +@property (nonatomic, assign, getter = isEnabled) BOOL enabled; + +/** + A Boolean value indicating whether the network activity indicator is currently displayed in the status bar. + */ +@property (readonly, nonatomic, assign) BOOL isNetworkActivityIndicatorVisible; + +/** + Returns the shared network activity indicator manager object for the system. + + @return The systemwide network activity indicator manager. + */ ++ (instancetype)sharedManager; + +/** + Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator. + */ +- (void)incrementActivityCount; + +/** + Decrements the number of active network requests. If this number becomes zero after decrementing, this will stop animating the status bar network activity indicator. + */ +- (void)decrementActivityCount; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m new file mode 100644 index 0000000..03afaf0 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m @@ -0,0 +1,173 @@ +// AFNetworkActivityIndicatorManager.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFNetworkActivityIndicatorManager.h" + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) + +#import "AFHTTPRequestOperation.h" + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +#import "AFURLSessionManager.h" +#endif + +static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17; + +static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) { + if ([[notification object] isKindOfClass:[AFURLConnectionOperation class]]) { + return [(AFURLConnectionOperation *)[notification object] request]; + } + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + if ([[notification object] respondsToSelector:@selector(originalRequest)]) { + return [(NSURLSessionTask *)[notification object] originalRequest]; + } +#endif + + return nil; +} + +@interface AFNetworkActivityIndicatorManager () +@property (readwrite, nonatomic, assign) NSInteger activityCount; +@property (readwrite, nonatomic, strong) NSTimer *activityIndicatorVisibilityTimer; +@property (readonly, nonatomic, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +- (void)updateNetworkActivityIndicatorVisibility; +- (void)updateNetworkActivityIndicatorVisibilityDelayed; +@end + +@implementation AFNetworkActivityIndicatorManager +@dynamic networkActivityIndicatorVisible; + ++ (instancetype)sharedManager { + static AFNetworkActivityIndicatorManager *_sharedManager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _sharedManager = [[self alloc] init]; + }); + + return _sharedManager; +} + ++ (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible { + return [NSSet setWithObject:@"activityCount"]; +} + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingOperationDidStartNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil]; +#endif + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_activityIndicatorVisibilityTimer invalidate]; +} + +- (void)updateNetworkActivityIndicatorVisibilityDelayed { + if (self.enabled) { + // Delay hiding of activity indicator for a short interval, to avoid flickering + if (![self isNetworkActivityIndicatorVisible]) { + [self.activityIndicatorVisibilityTimer invalidate]; + self.activityIndicatorVisibilityTimer = [NSTimer timerWithTimeInterval:kAFNetworkActivityIndicatorInvisibilityDelay target:self selector:@selector(updateNetworkActivityIndicatorVisibility) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.activityIndicatorVisibilityTimer forMode:NSRunLoopCommonModes]; + } else { + [self performSelectorOnMainThread:@selector(updateNetworkActivityIndicatorVisibility) withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]]; + } + } +} + +- (BOOL)isNetworkActivityIndicatorVisible { + return self.activityCount > 0; +} + +- (void)updateNetworkActivityIndicatorVisibility { +#if !defined(AF_APP_EXTENSIONS) + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]]; +#endif +} + +- (void)setActivityCount:(NSInteger)activityCount { + @synchronized(self) { + _activityCount = activityCount; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNetworkActivityIndicatorVisibilityDelayed]; + }); +} + +- (void)incrementActivityCount { + [self willChangeValueForKey:@"activityCount"]; + @synchronized(self) { + _activityCount++; + } + [self didChangeValueForKey:@"activityCount"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNetworkActivityIndicatorVisibilityDelayed]; + }); +} + +- (void)decrementActivityCount { + [self willChangeValueForKey:@"activityCount"]; + @synchronized(self) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + _activityCount = MAX(_activityCount - 1, 0); +#pragma clang diagnostic pop + } + [self didChangeValueForKey:@"activityCount"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNetworkActivityIndicatorVisibilityDelayed]; + }); +} + +- (void)networkRequestDidStart:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self incrementActivityCount]; + } +} + +- (void)networkRequestDidFinish:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self decrementActivityCount]; + } +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 100644 index 0000000..cbefccd --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1,64 @@ +// UIActivityIndicatorView+AFNetworking.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIActivityIndicatorView` class. The methods in this category provide support for automatically starting and stopping animation depending on the loading state of a request operation or session task. + */ +@interface UIActivityIndicatorView (AFNetworking) + +///---------------------------------- +/// @name Animating for Session Tasks +///---------------------------------- + +/** + Binds the animating state to the state of the specified task. + + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task; +#endif + +///--------------------------------------- +/// @name Animating for Request Operations +///--------------------------------------- + +/** + Binds the animating state to the execution state of the specified operation. + + @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +- (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m new file mode 100644 index 0000000..c981314 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m @@ -0,0 +1,97 @@ +// UIActivityIndicatorView+AFNetworking.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIActivityIndicatorView+AFNetworking.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +@implementation UIActivityIndicatorView (AFNetworking) + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + + if (task) { + if (task.state != NSURLSessionTaskStateCompleted) { + if (task.state == NSURLSessionTaskStateRunning) { + [self startAnimating]; + } else { + [self stopAnimating]; + } + + [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task]; + } + } +} +#endif + +#pragma mark - + +- (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; + + if (operation) { + if (![operation isFinished]) { + if ([operation isExecuting]) { + [self startAnimating]; + } else { + [self stopAnimating]; + } + + [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingOperationDidStartNotification object:operation]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingOperationDidFinishNotification object:operation]; + } + } +} + +#pragma mark - + +- (void)af_startAnimating { + dispatch_async(dispatch_get_main_queue(), ^{ + [self startAnimating]; + }); +} + +- (void)af_stopAnimating { + dispatch_async(dispatch_get_main_queue(), ^{ + [self stopAnimating]; + }); +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h new file mode 100644 index 0000000..a6f3fc0 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h @@ -0,0 +1,96 @@ +// UIAlertView+AFNetworking.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) + +#import + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIAlertView` class. The methods in this category provide support for automatically showing an alert if a session task or request operation finishes with an error. Alert title and message are filled from the corresponding `localizedDescription` & `localizedRecoverySuggestion` or `localizedFailureReason` of the error. + */ +@interface UIAlertView (AFNetworking) + +///------------------------------------- +/// @name Showing Alert for Session Task +///------------------------------------- + +/** + Shows an alert view with the error of the specified session task, if any. + + @param task The session task. + @param delegate The alert view delegate. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(id)delegate; +#endif + +/** + Shows an alert view with the error of the specified session task, if any, with a custom cancel button title and other button titles. + + @param task The session task. + @param delegate The alert view delegate. + @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. + @param otherButtonTitles The title of another button. Using this argument is equivalent to invoking addButtonWithTitle: with this title to add more buttons. Too many buttons can cause the alert view to scroll. For guidelines on the best ways to use an alert in an app, see "Temporary Views". Titles of additional buttons to add to the receiver, terminated with `nil`. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(id)delegate + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION; +#endif + +///------------------------------------------ +/// @name Showing Alert for Request Operation +///------------------------------------------ + +/** + Shows an alert view with the error of the specified request operation, if any. + + @param operation The request operation. + @param delegate The alert view delegate. + */ ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(id)delegate; + +/** + Shows an alert view with the error of the specified request operation, if any, with a custom cancel button title and other button titles. + + @param operation The request operation. + @param delegate The alert view delegate. + @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. + @param otherButtonTitles The title of another button. Using this argument is equivalent to invoking addButtonWithTitle: with this title to add more buttons. Too many buttons can cause the alert view to scroll. For guidelines on the best ways to use an alert in an app, see "Temporary Views". Titles of additional buttons to add to the receiver, terminated with `nil`. + */ ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(id)delegate + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m new file mode 100644 index 0000000..aa4d5a4 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m @@ -0,0 +1,129 @@ +// UIAlertView+AFNetworking.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIAlertView+AFNetworking.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) + +#import "AFURLConnectionOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +static void AFGetAlertViewTitleAndMessageFromError(NSError *error, NSString * __autoreleasing *title, NSString * __autoreleasing *message) { + if (error.localizedDescription && (error.localizedRecoverySuggestion || error.localizedFailureReason)) { + *title = error.localizedDescription; + + if (error.localizedRecoverySuggestion) { + *message = error.localizedRecoverySuggestion; + } else { + *message = error.localizedFailureReason; + } + } else if (error.localizedDescription) { + *title = NSLocalizedStringFromTable(@"Error", @"AFNetworking", @"Fallback Error Description"); + *message = error.localizedDescription; + } else { + *title = NSLocalizedStringFromTable(@"Error", @"AFNetworking", @"Fallback Error Description"); + *message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"%@ Error: %ld", @"AFNetworking", @"Fallback Error Failure Reason Format"), error.domain, (long)error.code]; + } +} + +@implementation UIAlertView (AFNetworking) + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(id)delegate +{ + [self showAlertViewForTaskWithErrorOnCompletion:task delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlertView Cancel Button Title") otherButtonTitles:nil, nil]; +} + ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(id)delegate + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION +{ + va_list otherTitleList; + va_start(otherTitleList, otherButtonTitles); + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil]; + for(NSString *otherTitle = otherButtonTitles; otherTitle != nil; otherTitle = va_arg(otherTitleList, NSString *)){ + [alertView addButtonWithTitle:otherTitle]; + } + va_end(otherTitleList); + __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingTaskDidCompleteNotification object:task queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + + NSError *error = notification.userInfo[AFNetworkingTaskDidCompleteErrorKey]; + if (error) { + NSString *title, *message; + AFGetAlertViewTitleAndMessageFromError(error, &title, &message); + + [alertView setTitle:title]; + [alertView setMessage:message]; + [alertView show]; + } + + [[NSNotificationCenter defaultCenter] removeObserver:observer name:AFNetworkingTaskDidCompleteNotification object:notification.object]; + }]; +} +#endif + +#pragma mark - + ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(id)delegate +{ + [self showAlertViewForRequestOperationWithErrorOnCompletion:operation delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlertView Cancel Button Title") otherButtonTitles:nil, nil]; +} + ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(id)delegate + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION +{ + va_list otherTitleList; + va_start(otherTitleList, otherButtonTitles); + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil]; + for(NSString *otherTitle = otherButtonTitles; otherTitle != nil; otherTitle = va_arg(otherTitleList, NSString *)){ + [alertView addButtonWithTitle:otherTitle]; + } + va_end(otherTitleList); + __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingOperationDidFinishNotification object:operation queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + + if (notification.object && [notification.object isKindOfClass:[AFURLConnectionOperation class]]) { + NSError *error = [(AFURLConnectionOperation *)notification.object error]; + if (error) { + NSString *title, *message; + AFGetAlertViewTitleAndMessageFromError(error, &title, &message); + + [alertView setTitle:title]; + [alertView setMessage:message]; + [alertView show]; + } + } + + [[NSNotificationCenter defaultCenter] removeObserver:observer name:AFNetworkingOperationDidFinishNotification object:notification.object]; + }]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h new file mode 100644 index 0000000..af8ff82 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1,181 @@ +// UIButton+AFNetworking.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@protocol AFURLResponseSerialization, AFImageCache; + +/** + This category adds methods to the UIKit framework's `UIButton` class. The methods in this category provide support for loading remote images and background images asynchronously from a URL. + + @warning Compound values for control `state` (such as `UIControlStateHighlighted | UIControlStateDisabled`) are unsupported. + */ +@interface UIButton (AFNetworking) + +///---------------------------- +/// @name Accessing Image Cache +///---------------------------- + +/** + The image cache used to improve image loadiing performance on scroll views. By default, `UIButton` will use the `sharedImageCache` of `UIImageView`. + */ ++ (id )sharedImageCache; + +/** + Set the cache used for image loading. + + @param imageCache The image cache. + */ ++ (void)setSharedImageCache:(id )imageCache; + +///------------------------------------ +/// @name Accessing Response Serializer +///------------------------------------ + +/** + The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. + + @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer + */ +@property (nonatomic, strong) id imageResponseSerializer; + +///-------------------- +/// @name Setting Image +///-------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the image request. + */ +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. + */ +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setImage:forState:` is applied. + + @param state The control state. + @param urlRequest The URL request used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. + @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes two arguments: the server response and the image. If the image was returned from cache, the request and response parameters will be `nil`. + @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)setImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success + failure:(void (^)(NSError *error))failure; + + +///------------------------------- +/// @name Setting Background Image +///------------------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous background image request for the receiver will be cancelled. + + If the background image is cached locally, the background image is set immediately, otherwise the specified placeholder background image will be set immediately, and then the remote background image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the background image request. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the background image request. + @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setBackgroundImage:forState:` is applied. + + @param state The control state. + @param urlRequest The URL request used for the image request. + @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success + failure:(void (^)(NSError *error))failure; + + +///------------------------------ +/// @name Canceling Image Loading +///------------------------------ + +/** + Cancels any executing image operation for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelImageRequestOperationForState:(UIControlState)state; + +/** + Cancels any executing background image operation for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelBackgroundImageRequestOperationForState:(UIControlState)state; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m new file mode 100644 index 0000000..d7a0a1b --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m @@ -0,0 +1,294 @@ +// UIButton+AFNetworking.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIButton+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFURLResponseSerialization.h" +#import "AFHTTPRequestOperation.h" + +#import "UIImageView+AFNetworking.h" + +@interface UIButton (_AFNetworking) +@end + +@implementation UIButton (_AFNetworking) + ++ (NSOperationQueue *)af_sharedImageRequestOperationQueue { + static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; + _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; + }); + + return _af_sharedImageRequestOperationQueue; +} + +#pragma mark - + +static char AFImageRequestOperationNormal; +static char AFImageRequestOperationHighlighted; +static char AFImageRequestOperationSelected; +static char AFImageRequestOperationDisabled; + +static const char * af_imageRequestOperationKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFImageRequestOperationHighlighted; + case UIControlStateSelected: + return &AFImageRequestOperationSelected; + case UIControlStateDisabled: + return &AFImageRequestOperationDisabled; + case UIControlStateNormal: + default: + return &AFImageRequestOperationNormal; + } +} + +- (AFHTTPRequestOperation *)af_imageRequestOperationForState:(UIControlState)state { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, af_imageRequestOperationKeyForState(state)); +} + +- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_imageRequestOperationKeyForState(state), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +static char AFBackgroundImageRequestOperationNormal; +static char AFBackgroundImageRequestOperationHighlighted; +static char AFBackgroundImageRequestOperationSelected; +static char AFBackgroundImageRequestOperationDisabled; + +static const char * af_backgroundImageRequestOperationKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFBackgroundImageRequestOperationHighlighted; + case UIControlStateSelected: + return &AFBackgroundImageRequestOperationSelected; + case UIControlStateDisabled: + return &AFBackgroundImageRequestOperationDisabled; + case UIControlStateNormal: + default: + return &AFBackgroundImageRequestOperationNormal; + } +} + +- (AFHTTPRequestOperation *)af_backgroundImageRequestOperationForState:(UIControlState)state { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, af_backgroundImageRequestOperationKeyForState(state)); +} + +- (void)af_setBackgroundImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_backgroundImageRequestOperationKeyForState(state), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIButton (AFNetworking) + ++ (id )sharedImageCache { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: [UIImageView sharedImageCache]; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageCache:(id )imageCache { + objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (id )imageResponseSerializer { + static id _af_defaultImageResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setImageResponseSerializer:(id )serializer { + objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success + failure:(void (^)(NSError *error))failure +{ + [self cancelImageRequestOperationForState:state]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(nil, nil, cachedImage); + } else { + [self setImage:cachedImage forState:state]; + } + + [self af_setImageRequestOperation:nil forState:state]; + } else { + if (placeholderImage) { + [self setImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + AFHTTPRequestOperation *imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + imageRequestOperation.responseSerializer = self.imageResponseSerializer; + [imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (success) { + success(operation.request, operation.response, responseObject); + } else if (responseObject) { + [strongSelf setImage:responseObject forState:state]; + } + } + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (failure) { + failure(error); + } + } + }]; + + [self af_setImageRequestOperation:imageRequestOperation forState:state]; + [[[self class] af_sharedImageRequestOperationQueue] addOperation:imageRequestOperation]; + } +} + +#pragma mark - + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setBackgroundImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success + failure:(void (^)(NSError *error))failure +{ + [self cancelBackgroundImageRequestOperationForState:state]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(nil, nil, cachedImage); + } else { + [self setBackgroundImage:cachedImage forState:state]; + } + + [self af_setBackgroundImageRequestOperation:nil forState:state]; + } else { + if (placeholderImage) { + [self setBackgroundImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + AFHTTPRequestOperation *backgroundImageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + backgroundImageRequestOperation.responseSerializer = self.imageResponseSerializer; + [backgroundImageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (success) { + success(operation.request, operation.response, responseObject); + } else if (responseObject) { + [strongSelf setBackgroundImage:responseObject forState:state]; + } + } + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (failure) { + failure(error); + } + } + }]; + + [self af_setBackgroundImageRequestOperation:backgroundImageRequestOperation forState:state]; + [[[self class] af_sharedImageRequestOperationQueue] addOperation:backgroundImageRequestOperation]; + } +} + +#pragma mark - + +- (void)cancelImageRequestOperationForState:(UIControlState)state { + [[self af_imageRequestOperationForState:state] cancel]; + [self af_setImageRequestOperation:nil forState:state]; +} + +- (void)cancelBackgroundImageRequestOperationForState:(UIControlState)state { + [[self af_backgroundImageRequestOperationForState:state] cancel]; + [self af_setBackgroundImageRequestOperation:nil forState:state]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h new file mode 100644 index 0000000..5838b56 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1,143 @@ +// UIImageView+AFNetworking.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@protocol AFURLResponseSerialization, AFImageCache; + +/** + This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL. + */ +@interface UIImageView (AFNetworking) + +///---------------------------- +/// @name Accessing Image Cache +///---------------------------- + +/** + The image cache used to improve image loadiing performance on scroll views. By default, this is an `NSCache` subclass conforming to the `AFImageCache` protocol, which listens for notification warnings and evicts objects accordingly. +*/ ++ (id )sharedImageCache; + +/** + Set the cache used for image loading. + + @param imageCache The image cache. + */ ++ (void)setSharedImageCache:(id )imageCache; + +///------------------------------------ +/// @name Accessing Response Serializer +///------------------------------------ + +/** + The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. + + @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer + */ +@property (nonatomic, strong) id imageResponseSerializer; + +///-------------------- +/// @name Setting Image +///-------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` + + @param url The URL used for the image request. + */ +- (void)setImageWithURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` + + @param url The URL used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. + */ +- (void)setImageWithURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied. + + @param urlRequest The URL request used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. + @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the request and response parameters will be `nil`. + @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + */ +- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success + failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure; + +/** + Cancels any executing image operation for the receiver, if one exists. + */ +- (void)cancelImageRequestOperation; + +@end + +#pragma mark - + +/** + The `AFImageCache` protocol is adopted by an object used to cache images loaded by the AFNetworking category on `UIImageView`. + */ +@protocol AFImageCache + +/** + Returns a cached image for the specififed request, if available. + + @param request The image request. + + @return The cached image. + */ +- (UIImage *)cachedImageForRequest:(NSURLRequest *)request; + +/** + Caches a particular image for the specified request. + + @param image The image to cache. + @param request The request to be used as a cache key. + */ +- (void)cacheImage:(UIImage *)image + forRequest:(NSURLRequest *)request; +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m new file mode 100644 index 0000000..04b6b0c --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m @@ -0,0 +1,216 @@ +// UIImageView+AFNetworking.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIImageView+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" + +@interface AFImageCache : NSCache +@end + +#pragma mark - + +@interface UIImageView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation; +@end + +@implementation UIImageView (_AFNetworking) + ++ (NSOperationQueue *)af_sharedImageRequestOperationQueue { + static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; + _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; + }); + + return _af_sharedImageRequestOperationQueue; +} + +- (AFHTTPRequestOperation *)af_imageRequestOperation { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation)); +} + +- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { + objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIImageView (AFNetworking) +@dynamic imageResponseSerializer; + ++ (id )sharedImageCache { + static AFImageCache *_af_defaultImageCache = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _af_defaultImageCache = [[AFImageCache alloc] init]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) { + [_af_defaultImageCache removeAllObjects]; + }]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: _af_defaultImageCache; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageCache:(id )imageCache { + objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (id )imageResponseSerializer { + static id _af_defaultImageResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setImageResponseSerializer:(id )serializer { + objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setImageWithURL:(NSURL *)url { + [self setImageWithURL:url placeholderImage:nil]; +} + +- (void)setImageWithURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success + failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure +{ + [self cancelImageRequestOperation]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(nil, nil, cachedImage); + } else { + self.image = cachedImage; + } + + self.af_imageRequestOperation = nil; + } else { + if (placeholderImage) { + self.image = placeholderImage; + } + + __weak __typeof(self)weakSelf = self; + self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer; + [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) { + if (success) { + success(urlRequest, operation.response, responseObject); + } else if (responseObject) { + strongSelf.image = responseObject; + } + + if (operation == strongSelf.af_imageRequestOperation){ + strongSelf.af_imageRequestOperation = nil; + } + } + + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) { + if (failure) { + failure(urlRequest, operation.response, error); + } + + if (operation == strongSelf.af_imageRequestOperation){ + strongSelf.af_imageRequestOperation = nil; + } + } + }]; + + [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation]; + } +} + +- (void)cancelImageRequestOperation { + [self.af_imageRequestOperation cancel]; + self.af_imageRequestOperation = nil; +} + +@end + +#pragma mark - + +static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) { + return [[request URL] absoluteString]; +} + +@implementation AFImageCache + +- (UIImage *)cachedImageForRequest:(NSURLRequest *)request { + switch ([request cachePolicy]) { + case NSURLRequestReloadIgnoringCacheData: + case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: + return nil; + default: + break; + } + + return [self objectForKey:AFImageCacheKeyFromURLRequest(request)]; +} + +- (void)cacheImage:(UIImage *)image + forRequest:(NSURLRequest *)request +{ + if (image && request) { + [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)]; + } +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h new file mode 100644 index 0000000..94082f6 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1,38 @@ +// UIKit+AFNetworking.h +// +// Copyright (c) 2013 AFNetworking (http://afnetworking.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#ifndef _UIKIT_AFNETWORKING_ + #define _UIKIT_AFNETWORKING_ + + #import "AFNetworkActivityIndicatorManager.h" + + #import "UIActivityIndicatorView+AFNetworking.h" + #import "UIAlertView+AFNetworking.h" + #import "UIButton+AFNetworking.h" + #import "UIImageView+AFNetworking.h" + #import "UIKit+AFNetworking.h" + #import "UIProgressView+AFNetworking.h" + #import "UIRefreshControl+AFNetworking.h" + #import "UIWebView+AFNetworking.h" +#endif /* _UIKIT_AFNETWORKING_ */ diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h new file mode 100644 index 0000000..06f3808 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1,88 @@ +// UIProgressView+AFNetworking.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIProgressView` class. The methods in this category provide support for binding the progress to the upload and download progress of a session task or request operation. + */ +@interface UIProgressView (AFNetworking) + +///------------------------------------ +/// @name Setting Session Task Progress +///------------------------------------ + +/** + Binds the progress to the upload progress of the specified session task. + + @param task The session task. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated; +#endif + +/** + Binds the progress to the download progress of the specified session task. + + @param task The session task. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + animated:(BOOL)animated; +#endif + +///------------------------------------ +/// @name Setting Session Task Progress +///------------------------------------ + +/** + Binds the progress to the upload progress of the specified request operation. + + @param operation The request operation. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +- (void)setProgressWithUploadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated; + +/** + Binds the progress to the download progress of the specified request operation. + + @param operation The request operation. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +- (void)setProgressWithDownloadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m new file mode 100644 index 0000000..dfe4151 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m @@ -0,0 +1,183 @@ +// UIProgressView+AFNetworking.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIProgressView+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFURLConnectionOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext; +static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext; + +@interface AFURLConnectionOperation (_UIProgressView) +@property (readwrite, nonatomic, copy) void (^uploadProgress)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); +@property (readwrite, nonatomic, assign, setter = af_setUploadProgressAnimated:) BOOL af_uploadProgressAnimated; + +@property (readwrite, nonatomic, copy) void (^downloadProgress)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); +@property (readwrite, nonatomic, assign, setter = af_setDownloadProgressAnimated:) BOOL af_downloadProgressAnimated; +@end + +@implementation AFURLConnectionOperation (_UIProgressView) +@dynamic uploadProgress; // Implemented in AFURLConnectionOperation +@dynamic af_uploadProgressAnimated; + +@dynamic downloadProgress; // Implemented in AFURLConnectionOperation +@dynamic af_downloadProgressAnimated; +@end + +#pragma mark - + +@implementation UIProgressView (AFNetworking) + +- (BOOL)af_uploadProgressAnimated { + return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue]; +} + +- (void)af_setUploadProgressAnimated:(BOOL)animated { + objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)af_downloadProgressAnimated { + return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue]; +} + +- (void)af_setDownloadProgressAnimated:(BOOL)animated { + objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated +{ + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + [task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + + [self af_setUploadProgressAnimated:animated]; +} + +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + animated:(BOOL)animated +{ + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + [task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + + [self af_setDownloadProgressAnimated:animated]; +} +#endif + +#pragma mark - + +- (void)setProgressWithUploadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated +{ + __weak __typeof(self)weakSelf = self; + void (^original)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) = [operation.uploadProgress copy]; + [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { + if (original) { + original(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (totalBytesExpectedToWrite > 0) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf setProgress:(totalBytesWritten / (totalBytesExpectedToWrite * 1.0f)) animated:animated]; + } + }); + }]; +} + +- (void)setProgressWithDownloadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated +{ + __weak __typeof(self)weakSelf = self; + void (^original)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) = [operation.downloadProgress copy]; + [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { + if (original) { + original(bytesRead, totalBytesRead, totalBytesExpectedToRead); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (totalBytesExpectedToRead > 0) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf setProgress:(totalBytesRead / (totalBytesExpectedToRead * 1.0f)) animated:animated]; + } + }); + }]; +} + +#pragma mark - NSKeyValueObserving + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(__unused NSDictionary *)change + context:(void *)context +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { + if ([object countOfBytesExpectedToSend] > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated]; + }); + } + } + + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { + if ([object countOfBytesExpectedToReceive] > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated]; + }); + } + } + + if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) { + if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) { + @try { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))]; + + if (context == AFTaskCountOfBytesSentContext) { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; + } + + if (context == AFTaskCountOfBytesReceivedContext) { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; + } + } + @catch (NSException * __unused exception) {} + } + } + } +#endif +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h new file mode 100644 index 0000000..18c12ea --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1,64 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2014 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIRefreshControl` class. The methods in this category provide support for automatically begining and ending refreshing depending on the loading state of a request operation or session task. + */ +@interface UIRefreshControl (AFNetworking) + +///----------------------------------- +/// @name Refreshing for Session Tasks +///----------------------------------- + +/** + Binds the refreshing state to the state of the specified task. + + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; +#endif + +///---------------------------------------- +/// @name Refreshing for Request Operations +///---------------------------------------- + +/** + Binds the refreshing state to the execution state of the specified operation. + + @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +- (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m new file mode 100644 index 0000000..e266451 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m @@ -0,0 +1,93 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2014 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIRefreshControl+AFNetworking.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +@implementation UIRefreshControl (AFNetworking) + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + + if (task) { + if (task.state == NSURLSessionTaskStateRunning) { + [self beginRefreshing]; + + [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task]; + } else { + [self endRefreshing]; + } + } +} +#endif + +- (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; + + if (operation) { + if (![operation isFinished]) { + if ([operation isExecuting]) { + [self beginRefreshing]; + } else { + [self endRefreshing]; + } + + [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingOperationDidStartNotification object:operation]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingOperationDidFinishNotification object:operation]; + } + } +} + +#pragma mark - + +- (void)af_beginRefreshing { + dispatch_async(dispatch_get_main_queue(), ^{ + [self beginRefreshing]; + }); +} + +- (void)af_endRefreshing { + dispatch_async(dispatch_get_main_queue(), ^{ + [self endRefreshing]; + }); +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h new file mode 100644 index 0000000..159a51a --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h @@ -0,0 +1,83 @@ +// UIWebView+AFNetworking.h +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@class AFHTTPRequestSerializer, AFHTTPResponseSerializer; +@protocol AFURLRequestSerialization, AFURLResponseSerialization; + +/** + This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling. + + @discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly. + */ +@interface UIWebView (AFNetworking) + +/** + The request serializer used to serialize requests made with the `-loadRequest:...` category methods. By default, this is an instance of `AFHTTPRequestSerializer`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + The response serializer used to serialize responses made with the `-loadRequest:...` category methods. By default, this is an instance of `AFHTTPResponseSerializer`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +/** + Asynchronously loads the specified request. + + @param request A URL request identifying the location of the content to load. This must not be `nil`. + @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. + @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success + failure:(void (^)(NSError *error))failure; + +/** + Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding. + + @param request A URL request identifying the location of the content to load. This must not be `nil`. + @param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified. + @param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified. + @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. + @param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + MIMEType:(NSString *)MIMEType + textEncodingName:(NSString *)textEncodingName + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(void (^)(NSError *error))failure; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m new file mode 100644 index 0000000..7cc7f37 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m @@ -0,0 +1,160 @@ +// UIWebView+AFNetworking.m +// +// Copyright (c) 2013-2015 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIWebView+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" + +@interface UIWebView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setHTTPRequestOperation:) AFHTTPRequestOperation *af_HTTPRequestOperation; +@end + +@implementation UIWebView (_AFNetworking) + +- (AFHTTPRequestOperation *)af_HTTPRequestOperation { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_HTTPRequestOperation)); +} + +- (void)af_setHTTPRequestOperation:(AFHTTPRequestOperation *)operation { + objc_setAssociatedObject(self, @selector(af_HTTPRequestOperation), operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIWebView (AFNetworking) + +- (AFHTTPRequestSerializer *)requestSerializer { + static AFHTTPRequestSerializer *_af_defaultRequestSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultRequestSerializer = [AFHTTPRequestSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(requestSerializer)) ?: _af_defaultRequestSerializer; +#pragma clang diagnostic pop +} + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + objc_setAssociatedObject(self, @selector(requestSerializer), requestSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (AFHTTPResponseSerializer *)responseSerializer { + static AFHTTPResponseSerializer *_af_defaultResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)loadRequest:(NSURLRequest *)request + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success + failure:(void (^)(NSError *error))failure +{ + [self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) { + NSStringEncoding stringEncoding = NSUTF8StringEncoding; + if (response.textEncodingName) { + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (encoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); + } + } + + NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding]; + if (success) { + string = success(response, string); + } + + return [string dataUsingEncoding:stringEncoding]; + } failure:failure]; +} + +- (void)loadRequest:(NSURLRequest *)request + MIMEType:(NSString *)MIMEType + textEncodingName:(NSString *)textEncodingName + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(void (^)(NSError *error))failure +{ + NSParameterAssert(request); + + if (self.af_HTTPRequestOperation) { + [self.af_HTTPRequestOperation cancel]; + } + + request = [self.requestSerializer requestBySerializingRequest:request withParameters:nil error:nil]; + + self.af_HTTPRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + self.af_HTTPRequestOperation.responseSerializer = self.responseSerializer; + + __weak __typeof(self)weakSelf = self; + [self.af_HTTPRequestOperation setDownloadProgressBlock:progress]; + [self.af_HTTPRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id __unused responseObject) { + NSData *data = success ? success(operation.response, operation.responseData) : operation.responseData; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + __strong __typeof(weakSelf) strongSelf = weakSelf; + [strongSelf loadData:data MIMEType:(MIMEType ?: [operation.response MIMEType]) textEncodingName:(textEncodingName ?: [operation.response textEncodingName]) baseURL:[operation.response URL]]; + + if ([strongSelf.delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { + [strongSelf.delegate webViewDidFinishLoad:strongSelf]; + } + +#pragma clang diagnostic pop + } failure:^(AFHTTPRequestOperation * __unused operation, NSError *error) { + if (failure) { + failure(error); + } + }]; + + [self.af_HTTPRequestOperation start]; + + if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { + [self.delegate webViewDidStartLoad:self]; + } +} + +@end + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTAccessibilityElement.h b/Pods/DTCoreText/Core/Source/DTAccessibilityElement.h new file mode 100644 index 0000000..a841c12 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAccessibilityElement.h @@ -0,0 +1,32 @@ +// +// DTAccessibilityElement.h +// DTCoreText +// +// Created by Austen Green on 3/13/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import + +/** + A UIAccessibilityElement subclass that automatically converts its local accessibilityFrame to screen coordinates. + */ +@interface DTAccessibilityElement : UIAccessibilityElement +/** + The frame for the accessibility element in terms of the receiver's superview. + */ +@property (nonatomic, assign) CGRect localCoordinateAccessibilityFrame; + +/** + The point for activating accessibility events in terms of the receiver's superview. + */ +@property (nonatomic, assign) CGPoint localCoordinateAccessibilityActivationPoint; + +/** + The designated initializer. This class should be initialized with a UIView as its accessibility container. + @param parentView The logical superview for the onscreen element the receiver represents. + @returns Returns an initialized DTAccessibilityElement */ + +- (id)initWithParentView:(UIView *)parentView; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAccessibilityElement.m b/Pods/DTCoreText/Core/Source/DTAccessibilityElement.m new file mode 100644 index 0000000..458c350 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAccessibilityElement.m @@ -0,0 +1,50 @@ +// +// DTAccessibilityElement.m +// DTCoreText +// +// Created by Austen Green on 3/13/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTAccessibilityElement.h" + +static const CGPoint DTAccessibilityElementNullActivationPoint = {CGFLOAT_MAX, CGFLOAT_MAX}; + +@interface DTAccessibilityElement() +@property (nonatomic, unsafe_unretained) UIView *parentView; +@end + +@implementation DTAccessibilityElement + +- (id)initWithParentView:(UIView *)parentView +{ + self = [super initWithAccessibilityContainer:parentView]; + if (self) + { + _parentView = parentView; + _localCoordinateAccessibilityActivationPoint = DTAccessibilityElementNullActivationPoint; + } + return self; +} + +- (CGRect)accessibilityFrame +{ + CGRect frame = self.localCoordinateAccessibilityFrame; + frame = [self.parentView.window convertRect:frame fromView:self.parentView]; + return frame; +} + +- (CGPoint)accessibilityActivationPoint +{ + CGPoint point = self.localCoordinateAccessibilityActivationPoint; + if (CGPointEqualToPoint(point, DTAccessibilityElementNullActivationPoint)) + { + point = [super accessibilityActivationPoint]; + } + + point = [self.parentView.window convertPoint:point fromView:self.parentView]; + + return point; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAccessibilityViewProxy.h b/Pods/DTCoreText/Core/Source/DTAccessibilityViewProxy.h new file mode 100644 index 0000000..9ea1e30 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAccessibilityViewProxy.h @@ -0,0 +1,54 @@ +// +// DTAccessibilityViewProxy.h +// DTCoreText +// +// Created by Austen Green on 5/6/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import +#import "DTTextAttachment.h" + +@protocol DTAccessibilityViewProxyDelegate; + +/** + UIView proxy for DTAttributedTextContentView custom subviews for text attachments. + */ + +@interface DTAccessibilityViewProxy : NSProxy +/** + The delegate for the proxy + */ +@property (nonatomic, unsafe_unretained, readonly) id delegate; + +/** + The text attachment represented by the proxy + */ +@property (nonatomic, strong, readonly) DTTextAttachment *textAttachment; + +/** + Creates a text attachment proxy for use with the VoiceOver system. + @param textAttachment The that will be represented by a view. + @param delegate An object conforming to that will provide a view when needed by the proxy. + @returns A new proxy object + */ + +- (id)initWithTextAttachment:(DTTextAttachment *)textAttachment delegate:(id)delegate; + +@end + +/** + Protocol to provide custom views for accessibility elements representing a DTTextAttachment. + */ +@protocol DTAccessibilityViewProxyDelegate +@required +/** + Provides a view for an attachment, e.g. an imageView for images + + @param attachment The that the requested view should represent + @param proxy The frame that the view should use to fit on top of the space reserved for the attachment. + @returns The sender requesting the view. + */ + +- (UIView *)viewForTextAttachment:(DTTextAttachment *)attachment proxy:(DTAccessibilityViewProxy *)proxy; +@end \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/DTAccessibilityViewProxy.m b/Pods/DTCoreText/Core/Source/DTAccessibilityViewProxy.m new file mode 100644 index 0000000..33e5beb --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAccessibilityViewProxy.m @@ -0,0 +1,58 @@ +// +// DTAccessibilityViewProxy.m +// DTCoreText +// +// Created by Austen Green on 5/6/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTAccessibilityViewProxy.h" + +@implementation DTAccessibilityViewProxy + +- (id)initWithTextAttachment:(DTTextAttachment *)textAttachment delegate:(id)delegate +{ + _textAttachment = textAttachment; + _delegate = delegate; + return self; +} + +- (UIView *)proxiedView +{ + return [self.delegate viewForTextAttachment:self.textAttachment proxy:self]; +} + +- (Class)class +{ + Class aClass = [[self proxiedView] class]; + + if (!aClass) + aClass = [DTAccessibilityViewProxy class]; + + return aClass; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel +{ + NSMethodSignature *signature = [UIView instanceMethodSignatureForSelector:sel]; + + return signature; +} + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + UIView *view = [self proxiedView]; + [invocation invokeWithTarget:view]; +} + +- (BOOL)isEqual:(id)object +{ + return [[self proxiedView] isEqual:object]; +} + +- (NSUInteger)hash +{ + return [[self proxiedView] hash]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAnchorHTMLElement.h b/Pods/DTCoreText/Core/Source/DTAnchorHTMLElement.h new file mode 100644 index 0000000..dfa0671 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAnchorHTMLElement.h @@ -0,0 +1,21 @@ +// +// DTHTMLElementA.h +// DTCoreText +// +// Created by Oliver Drobnik on 21.03.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +/** + Specialized subclass of that represents a hyperlink. + */ +@interface DTAnchorHTMLElement : DTHTMLElement + +/** + Foreground text color of the receiver when highlighted + */ +@property (nonatomic, strong) DTColor *highlightedTextColor; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAnchorHTMLElement.m b/Pods/DTCoreText/Core/Source/DTAnchorHTMLElement.m new file mode 100644 index 0000000..f89cdb4 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAnchorHTMLElement.m @@ -0,0 +1,53 @@ +// +// DTHTMLElementA.m +// DTCoreText +// +// Created by Oliver Drobnik on 21.03.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTAnchorHTMLElement.h" + +@implementation DTAnchorHTMLElement +{ + DTColor *_highlightedTextColor; +} + +- (void)applyStyleDictionary:(NSDictionary *)styles +{ + [super applyStyleDictionary:styles]; + + // get highlight color from a:active pseudo-selector + NSString *activeColor = [styles objectForKey:@"active:color"]; + + if (activeColor) + { + self.highlightedTextColor = DTColorCreateWithHTMLName(activeColor); + } +} + +- (NSAttributedString *)attributedString +{ + // super returns a mutable attributed string + NSMutableAttributedString *mutableAttributedString = (NSMutableAttributedString *)[super attributedString]; + + if (_highlightedTextColor) + { + NSRange range = NSMakeRange(0, [mutableAttributedString length]); + + // this additional attribute keeps the highlight color + [mutableAttributedString addAttribute:DTLinkHighlightColorAttribute value:(id)_highlightedTextColor range:range]; + + // we need to set the text color via the graphics context + [mutableAttributedString addAttribute:(id)kCTForegroundColorFromContextAttributeName value:[NSNumber numberWithBool:YES] range:range]; + } + + return mutableAttributedString; +} + +#pragma mark - Properties + +@synthesize highlightedTextColor = _highlightedTextColor; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedLabel.h b/Pods/DTCoreText/Core/Source/DTAttributedLabel.h new file mode 100644 index 0000000..48ab46d --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedLabel.h @@ -0,0 +1,38 @@ +// +// DTAttributedLabel.h +// DTCoreText +// +// Created by Brian Kenny on 1/17/13. +// Copyright (c) 2013 Cocoanetics.com. All rights reserved. +// + +#import "DTAttributedTextContentView.h" + +/** + A Rich Text replacement for `UILabel`. It inherits from and as such you can also set the delegate to provide custom subviews i.e. for images or hyperlinks. + + Contrary to DTAttributedTextContentView the intrinsicContentSize is only as wide as the text content. To shrink the DTAttributedLabel to that call -sizeToFit. + */ + +@interface DTAttributedLabel : DTAttributedTextContentView + +/** + @name Setting Attributes + */ + +/** + The number of lines to display in the receiver + */ +@property(nonatomic, assign) NSInteger numberOfLines; + +/** + The line break mode of the receiver + */ +@property(nonatomic, assign) NSLineBreakMode lineBreakMode; + +/** + The string to append to the visible string in case a trunction occurs + */ +@property(nonatomic, strong) NSAttributedString *truncationString; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedLabel.m b/Pods/DTCoreText/Core/Source/DTAttributedLabel.m new file mode 100644 index 0000000..1ce2ab1 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedLabel.m @@ -0,0 +1,107 @@ +// +// DTAttributedLabel.m +// DTCoreText +// +// Created by Brian Kenny on 1/17/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTAttributedLabel.h" +#import "DTCoreTextLayoutFrame.h" +#import + +@implementation DTAttributedLabel + ++ (Class)layerClass +{ + // most likely the label will be less than a screen size and so we don't want any tiling behavior + return [CALayer class]; +} + +- (void) setupAttributedLabel +{ + // we want to relayout the text if height or width change + self.relayoutMask = DTAttributedTextContentViewRelayoutOnHeightChanged | DTAttributedTextContentViewRelayoutOnWidthChanged; + + self.layoutFrameHeightIsConstrainedByBounds = YES; // height is not flexible +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) + { + [self setupAttributedLabel]; + } + + return self; +} + +- (void) awakeFromNib +{ + [super awakeFromNib]; + [self setupAttributedLabel]; +} + +#pragma mark - Sizing + +- (CGSize)intrinsicContentSize +{ + if (!self.layoutFrame) // creates new layout frame if possible + { + return CGSizeMake(-1, -1); // UIViewNoIntrinsicMetric as of iOS 6 + } + + // we have a layout frame and from this we get the needed size + CGSize intrisicContentSize = [_layoutFrame intrinsicContentFrame].size; + return CGSizeMake(intrisicContentSize.width + _edgeInsets.left + _edgeInsets.right, + intrisicContentSize.height + _edgeInsets.top + _edgeInsets.bottom); +} + +#pragma mark - Properties + +- (NSInteger)numberOfLines +{ + return _numberOfLines; +} + +- (void)setNumberOfLines:(NSInteger)numberOfLines +{ + if (numberOfLines != _numberOfLines) + { + _numberOfLines = numberOfLines; + [self relayoutText]; + } +} + +- (NSLineBreakMode)lineBreakMode +{ + return _lineBreakMode; +} + +- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode +{ + if (lineBreakMode != _lineBreakMode) + { + _lineBreakMode = lineBreakMode; + [self relayoutText]; + } +} + +- (NSAttributedString*)truncationString +{ + return _truncationString; +} + +- (void)setTruncationString:(NSAttributedString *)truncationString +{ + if (![truncationString isEqualToAttributedString:_truncationString]) + { + _truncationString = truncationString; + [self relayoutText]; + } +} + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextCell.h b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.h new file mode 100644 index 0000000..23a4c5f --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.h @@ -0,0 +1,85 @@ +// +// DTAttributedTextCell.h +// DTCoreText +// +// Created by Oliver Drobnik on 8/4/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTAttributedTextContentView.h" +#import "DTWeakSupport.h" + +/** + This class represents a tableview cell that contains an attributed text as its content. + */ +@interface DTAttributedTextCell : UITableViewCell + +/** + @name Creating Cells + */ + +/** + Creates a tableview cell with a given reuse identifier. + @param reuseIdentifier The reuse identifier to use for the cell + @returns A prepared cell + */ +- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier; + +/** + @name Setting Attributed Content + */ + +/** + The attributed string content of the receiver + */ +@property (nonatomic, strong) NSAttributedString *attributedString; + +/** + A delegate implementing DTAttributedTextContentViewDelegate to provide custom subviews for images and links. + */ +@property (nonatomic, DT_WEAK_PROPERTY) IBOutlet id textDelegate; + +/** + This method allows to set HTML text directly as content of the receiver. + + This will be converted to an attributed string. + @param html The HTML string to set as the receiver's text content + */ +- (void)setHTMLString:(NSString *)html; + +/** + This method allows to set HTML text directly as content of the receiver. + + This will be converted to an attributed string. + @param html The HTML string to set as the receiver's text content + @param options The options used for rendering the HTML + */ +- (void) setHTMLString:(NSString *)html options:(NSDictionary*) options; + + +/** + @name Getting Information + */ + +/** + Determines the row height that is needed in a specific table view to show the entire text content. + + The table view is necessary because from this the method can know the style. Also the accessory type needs to be set before calling this method because this reduces the available space. + @note This value is only useful for table views with variable row height. + @param tableView The table view to determine the height for. + */ +- (CGFloat)requiredRowHeightInTableView:(UITableView *)tableView; + +/** + Determines whether the cells built-in contentView is allowed to dictate the size available for text. If active then attributedTextContextView's height always matches the cell height. + + Set this to `YES` for use in fixed row height table views, leave it `NO` for flexible row height table views. + */ +@property (nonatomic, assign) BOOL hasFixedRowHeight; + +/** + The attributed text content view that the receiver uses to display the attributed text content. + */ +@property (nonatomic, readonly) DTAttributedTextContentView *attributedTextContextView; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextCell.m b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.m new file mode 100644 index 0000000..8d34d99 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.m @@ -0,0 +1,268 @@ +// +// DTAttributedTextCell.m +// DTCoreText +// +// Created by Oliver Drobnik on 8/4/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTAttributedTextCell.h" +#import "DTCSSStylesheet.h" +#import "DTLog.h" + +@implementation DTAttributedTextCell +{ + DTAttributedTextContentView *_attributedTextContextView; + + DT_WEAK_VARIABLE id _textDelegate; + + NSUInteger _htmlHash; // preserved hash to avoid relayouting for same HTML + + BOOL _hasFixedRowHeight; + DT_WEAK_VARIABLE UITableView *_containingTableView; +} + +- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; + + if (self) + { + // content view created lazily + } + + return self; +} + +- (void)dealloc +{ + _textDelegate = nil; + _containingTableView = nil; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (!self.superview) + { + return; + } + + if (_hasFixedRowHeight) + { + self.attributedTextContextView.frame = self.contentView.bounds; + } + else + { + CGFloat neededContentHeight = [self requiredRowHeightInTableView:_containingTableView]; + + // after the first call here the content view size is correct + CGRect frame = CGRectMake(0, 0, self.contentView.bounds.size.width, neededContentHeight); + self.attributedTextContextView.frame = frame; + } +} + +- (UITableView *)_findContainingTableView +{ + UIView *tableView = self.superview; + + while (tableView) + { + if ([tableView isKindOfClass:[UITableView class]]) + { + return (UITableView *)tableView; + } + + tableView = tableView.superview; + } + + return nil; +} + +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; + + _containingTableView = [self _findContainingTableView]; + + // on < iOS 7 we need to make the background translucent to avoid artefacts at rounded edges + if (_containingTableView.style == UITableViewStyleGrouped) + { + if (NSFoundationVersionNumber < DTNSFoundationVersionNumber_iOS_7_0) + { + _attributedTextContextView.backgroundColor = [UIColor clearColor]; + } + } +} + +// http://stackoverflow.com/questions/4708085/how-to-determine-margin-of-a-grouped-uitableview-or-better-how-to-set-it/4872199#4872199 +- (CGFloat)_groupedCellMarginWithTableWidth:(CGFloat)tableViewWidth +{ + CGFloat marginWidth; + if(tableViewWidth > 20) + { + if(tableViewWidth < 400 || [UIDevice currentDevice].userInterfaceIdiom==UIUserInterfaceIdiomPhone) + { + marginWidth = 10; + } + else + { + marginWidth = MAX(31.f, MIN(45.f, tableViewWidth*0.06f)); + } + } + else + { + marginWidth = tableViewWidth - 10; + } + return marginWidth; +} + +- (CGFloat)requiredRowHeightInTableView:(UITableView *)tableView +{ + if (_hasFixedRowHeight) + { + DTLogWarning(@"You are calling %s even though the cell is configured with fixed row height", (const char *)__PRETTY_FUNCTION__); + } + + BOOL ios6Style = (NSFoundationVersionNumber < DTNSFoundationVersionNumber_iOS_7_0); + CGFloat contentWidth = tableView.frame.size.width; + + // reduce width for grouped table views + if (ios6Style && tableView.style == UITableViewStyleGrouped) + { + contentWidth -= [self _groupedCellMarginWithTableWidth:contentWidth] * 2; + } + + // reduce width for accessories + + switch (self.accessoryType) + { + case UITableViewCellAccessoryDisclosureIndicator: + { + contentWidth -= ios6Style ? 20.0f : 10.0f + 8.0f + 15.0f; + break; + } + + case UITableViewCellAccessoryCheckmark: + { + contentWidth -= ios6Style ? 20.0f : 10.0f + 14.0f + 15.0f; + break; + } + + case UITableViewCellAccessoryDetailDisclosureButton: + { + contentWidth -= ios6Style ? 33.0f : 10.0f + 42.0f + 15.0f; + break; + } + +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1 + case UITableViewCellAccessoryDetailButton: + { + contentWidth -= 10.0f + 22.0f + 15.0f; + break; + } +#endif + + case UITableViewCellAccessoryNone: + { + break; + } + + default: + { + DTLogWarning(@"AccessoryType %d not implemented on %@", self.accessoryType, NSStringFromClass([self class])); + break; + } + } + + CGSize neededSize = [self.attributedTextContextView suggestedFrameSizeToFitEntireStringConstraintedToWidth:contentWidth]; + + // note: non-integer row heights caused trouble < iOS 5.0 + return neededSize.height; +} + +#pragma mark Properties + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + +- (void)setHTMLString:(NSString *)html +{ + [self setHTMLString:html options:nil]; +} + +- (void) setHTMLString:(NSString *)html options:(NSDictionary*) options { + + NSUInteger newHash = [html hash]; + + if (newHash == _htmlHash) + { + return; + } + + _htmlHash = newHash; + + NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding]; + NSAttributedString *string = [[NSAttributedString alloc] initWithHTMLData:data options:options documentAttributes:NULL]; + self.attributedString = string; + + [self setNeedsLayout]; + +} + +- (void)setAttributedString:(NSAttributedString *)attributedString +{ + // passthrough + self.attributedTextContextView.attributedString = attributedString; +} + +- (NSAttributedString *)attributedString +{ + // passthrough + return _attributedTextContextView.attributedString; +} + +- (DTAttributedTextContentView *)attributedTextContextView +{ + if (!_attributedTextContextView) + { + // don't know size jetzt because there's no string in it + _attributedTextContextView = [[DTAttributedTextContentView alloc] initWithFrame:self.contentView.bounds]; + + _attributedTextContextView.edgeInsets = UIEdgeInsetsMake(5, 5, 5, 5); + _attributedTextContextView.layoutFrameHeightIsConstrainedByBounds = _hasFixedRowHeight; + _attributedTextContextView.delegate = _textDelegate; + + [self.contentView addSubview:_attributedTextContextView]; + } + + return _attributedTextContextView; +} + +- (void)setHasFixedRowHeight:(BOOL)hasFixedRowHeight +{ + if (_hasFixedRowHeight != hasFixedRowHeight) + { + _hasFixedRowHeight = hasFixedRowHeight; + + [self setNeedsLayout]; + } +} + +- (void)setTextDelegate:(id)textDelegate +{ + _textDelegate = textDelegate; + _attributedTextContextView.delegate = _textDelegate; +} + +@synthesize attributedTextContextView = _attributedTextContextView; +@synthesize hasFixedRowHeight = _hasFixedRowHeight; +@synthesize textDelegate = _textDelegate; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.h b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.h new file mode 100644 index 0000000..e017b9e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.h @@ -0,0 +1,364 @@ +// +// DTAttributedTextContentView.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayoutFrame.h" +#import "DTWeakSupport.h" + +@class DTAttributedTextContentView; +@class DTCoreTextLayoutFrame; +@class DTTextBlock; +@class DTCoreTextLayouter; +@class DTTextAttachment; + +/** + notification that gets sent as soon as the receiver has done a layout pass + */ +extern NSString * const DTAttributedTextContentViewDidFinishLayoutNotification; + +/** + Protocol to provide custom views for elements in an DTAttributedTextContentView. Also the delegate gets notified once the text view has been drawn. + */ +@protocol DTAttributedTextContentViewDelegate + +@optional + +/** + @name Notifications + */ + +/** + Called before a layout frame or a part of it is drawn. The text delegate can draw contents that goes under the text in this method. + + @param attributedTextContentView The content view that will be drawing a layout frame + @param layoutFrame The layout frame that will be drawn for + @param context The graphics context that will drawn into + */ +- (void)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView willDrawLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame inContext:(CGContextRef)context; + + +/** + Called after a layout frame or a part of it is drawn. The text delegate can draw contents that goes over the text in this method. + + @param attributedTextContentView The content view that drew a layout frame + @param layoutFrame The layout frame that was drawn for + @param context The graphics context that was drawn into + */ +- (void)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView didDrawLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame inContext:(CGContextRef)context; + + +/** + Called before the text belonging to a text block is drawn. + + This gives the developer an opportunity to draw a custom background below a text block. + + @param attributedTextContentView The content view that drew a layout frame + @param textBlock The text block + @param frame The frame within the content view's coordinate system that will be drawn into + @param context The graphics context that will be drawn into + @param layoutFrame The layout frame that will be drawn for + @returns `YES` is the standard fill of the text block should be drawn, `NO` if it should not + */ +- (BOOL)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView shouldDrawBackgroundForTextBlock:(DTTextBlock *)textBlock frame:(CGRect)frame context:(CGContextRef)context forLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame; + +/** + @name Providing Custom Views for Content + */ + + +/** + Provide custom view for an attachment, e.g. an imageView for images + + @param attributedTextContentView The content view asking for a custom view + @param attachment The that this view should represent + @param frame The frame that the view should use to fit on top of the space reserved for the attachment + @returns The view that should represent the given attachment + */ +- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttachment:(DTTextAttachment *)attachment frame:(CGRect)frame; + + +/** + Provide button to be placed over links, the identifier is used to link multiple parts of the same A tag + + @param attributedTextContentView The content view asking for a custom view + @param url The `NSURL` of the hyperlink + @param identifier An identifier that uniquely identifies the hyperlink within the document + @param frame The frame that the view should use to fit on top of the space reserved for the attachment + @returns The view that should represent the given hyperlink + */ +- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForLink:(NSURL *)url identifier:(NSString *)identifier frame:(CGRect)frame; + + +/** + Provide generic views for all attachments. + + This is only called if the more specific delegate methods are not implemented. + + @param attributedTextContentView The content view asking for a custom view + @param string The attributed sub-string containing this element + @param frame The frame that the view should use to fit on top of the space reserved for the attachment + @returns The view that should represent the given hyperlink or text attachment + @see attributedTextContentView:viewForAttachment:frame: and attributedTextContentView:viewForAttachment:frame: + */ +- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttributedString:(NSAttributedString *)string frame:(CGRect)frame; + +@end + + +enum { + DTAttributedTextContentViewRelayoutNever = 0, + DTAttributedTextContentViewRelayoutOnWidthChanged = 1 << 0, + DTAttributedTextContentViewRelayoutOnHeightChanged = 1 << 1, +}; +typedef NSUInteger DTAttributedTextContentViewRelayoutMask; + + +/** + Attributed Text Content Views display attributed strings generated by DTHTMLAttributedStringBuilder. They can display images and hyperlinks inline or optionally place custom subviews (which get provided via the in the appropriate places. By itself content views do not scroll, for that there is the `UIScrollView` subclass . + + Generally you have two options to providing content: + + - set the attributed string + - set a layout frame + + The first you would normally use, the second you would use if you are layouting a larger text and then simply want to display individual parts (e.g. pages from an e-book) in a content view. + + DTAttributedTextContentView is designed to be used as the content view inside a DTAttributedTextView and thus sizes its intrinsicContentSize always to be the same as the width of the set frame. Use DTAttributedLabel if you don't require scrolling behavior. + */ + +@interface DTAttributedTextContentView : UIView +{ + NSAttributedString *_attributedString; + DTCoreTextLayoutFrame *_layoutFrame; + + UIEdgeInsets _edgeInsets; + + NSMutableDictionary *customViewsForAttachmentsIndex; + + BOOL _flexibleHeight; + + // for layoutFrame + NSInteger _numberOfLines; + NSLineBreakMode _lineBreakMode; + NSAttributedString *_truncationString; +} + + +/** + @name Sizing + */ + +/** + Calculates the suggested frame size that would fit the entire with a maximum width. + + This does a full layout pass that is cached in . If you specify a frame that fits the result from this method then the resulting layoutFrame is reused. + + Since this obeys the you have to add these to the final frame size. + + @param width The maximum width to layout for + @returns The suggested frame size + */ +- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width; // obeys the edge insets + +/** + The size of contents of the receiver. This is possibly used by auto-layout, but also for example if you want to get the size of the receiver necessary for a scroll view + + This method is defined as of iOS 6, but to support earlier OS versions + */ +- (CGSize)intrinsicContentSize; + +/** + Whether the receiver calculates layout limited to the view bounds. + + If set to `YES` then the layout process calculates the layoutFrame with open ended height. If set to ´NO` then the current bounds of the receiver determine the height. + */ +@property (nonatomic, assign) BOOL layoutFrameHeightIsConstrainedByBounds; + + +/** + @name Layouting + */ + +/** + Discards the current and creates a new one based on the . + */ +- (void)relayoutText; + + +/** + The layouter to use for the receiver. Created by default. + + By default this is generated automatically for the current . You can also supply your own if you require special layouting behavior. + */ +@property (atomic, strong) DTCoreTextLayouter *layouter; + + +/** + The layout frame to use for the receiver. Created by default. + + A layout frame is basically one rectangle, inset by the . By default this is automatically generated for the current . You can also create a seperately and set this property to display the layout frame. This is usedful for example if you layout entire e-book and then set the for displaying individual pages. + */ +@property (atomic, strong) DTCoreTextLayoutFrame *layoutFrame; + + +/** + @name Working with Custom Subviews + */ + +/** + Removes all custom subviews (excluding views representing links) from the receiver. + */ +- (void)removeAllCustomViews; + + +/** + Removes all custom subviews representing links from the receiver + */ +- (void)removeAllCustomViewsForLinks; + + +/** + Removes invisible custom subviews and lays out subviews visible in the given rectangle + @param rect The bounds of the visible area to layout custom subviews in. + */ +- (void)layoutSubviewsInRect:(CGRect)rect; + + +/** + @name Providing Content + */ + +/** + The attributed string to display in the receiver + */ +@property (nonatomic, copy) NSAttributedString *attributedString; + + +/** + The delegate that is in charge of supplying custom behavior for the receiver. It must conform to and provide custom subviews, link buttons, etc. + */ + +@property (nonatomic, DT_WEAK_PROPERTY) IBOutlet id delegate; + +/** + @name Customizing Content Display + */ + +/** + The insets to apply around the text content + */ +@property (nonatomic) UIEdgeInsets edgeInsets; + +/** + Specifies if the receiver should draw image text attachments. + + Set to `NO` if you use the delegate methods to provide custom subviews to display images. + */ +@property (nonatomic) BOOL shouldDrawImages; + + +/** + Specified if the receiver should draw hyperlinks. + + If set to `NO` then your custom subview/button for hyperlinks is responsible for displaying hyperlinks. You can use to have links show a differently for normal and highlighted style + */ +@property (nonatomic) BOOL shouldDrawLinks; + + +/** + Specifies if the receiver should layout custom subviews in layoutSubviews. + + If set to `YES` then all custom subviews will always be layouted. Set to `NO` to only layout visible subviews, e.g. in a scroll view. Defaults to `YES` if used stand-alone, `NO` inside a . + */ +@property (nonatomic) BOOL shouldLayoutCustomSubviews; + + +/** + The amount by which all contents of the receiver will offset of display and subview layouting + */ +@property (nonatomic) CGPoint layoutOffset; + + +/** + The offset to apply for drawing the background. + + If you set a pattern color as background color you can have the pattern phase be offset by this value. + */ +@property (nonatomic) CGSize backgroundOffset; + + +/** + An integer bit mask that determines how the receiver relayouts its contents when its bounds change. + + When the view’s bounds change, that view automatically re-layouts its text according to the relayout mask. You specify the value of this mask by combining the constants described in DTAttributedTextContentViewRelayoutMask using the C bitwise OR operator. Combining these constants lets you specify which dimensions will cause a re-layout if modified. The default value of this property is DTAttributedTextContentViewRelayoutOnWidthChanged, which indicates that the text will be re-layouted if the width changes, but not if the height changes. + */ +@property (nonatomic) DTAttributedTextContentViewRelayoutMask relayoutMask; + +@end + + +/** + You can globally customize the layer class to be used for new instances of . By itself it makes most sense to go with the default `CALayer`. For larger bodies of text, i.e. if there is scrolling then you should use a `CATiledLayer` subclass instead. + */ +@interface DTAttributedTextContentView (Tiling) + +/** + Sets the layer class globally to use in new instances of content views. Defaults to `CALayer`. + + While being fine for most use cases you should use a `CATiledLayer` subclass for anything larger than a screen full, e.g. in scroll views. + @param layerClass The class to use, should be a `CALayer` subclass + */ ++ (void)setLayerClass:(Class)layerClass; + +/** + The current layer class that is used for new instances + @returns The `CALayer` subclass that new instances are using + */ ++ (Class)layerClass; + +@end + + +/** + Methods for drawing the content view + */ +@interface DTAttributedTextContentView (Drawing) + +/** + Creates an image from a part of the receiver's content view + @param bounds The bounds of the content to draw + @param options The drawing options to apply when drawing + @see [DTCoreTextLayoutFrame drawInContext:options:] for a list of available drawing options + @returns A `UIImage` with the specified content + */ +- (UIImage *)contentImageWithBounds:(CGRect)bounds options:(DTCoreTextLayoutFrameDrawingOptions)options; + +@end + + +/** + Methods for getting cursor position and frame. Those are convenience methods that call through to the layoutFrame property which has the same coordinate system as the receiver. + */ +@interface DTAttributedTextContentView (Cursor) + +/** + Determines the closest string index to a point in the receiver's frame. + + This can be used to find the cursor position to position an input caret at. + @param point The point + @returns The resulting string index + */ +- (NSInteger)closestCursorIndexToPoint:(CGPoint)point; + +/** + The rectangle to draw a caret for a given index + @param index The string index for which to determine a cursor frame + @returns The cursor rectangle + */ +- (CGRect)cursorRectAtIndex:(NSInteger)index; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.m b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.m new file mode 100644 index 0000000..03b8df5 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.m @@ -0,0 +1,1096 @@ +// +// DTAttributedTextContentView.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import + +#import "DTCoreText.h" +#import "DTAttributedTextContentView.h" +#import "DTDictationPlaceholderTextAttachment.h" +#import "DTAccessibilityViewProxy.h" +#import "DTAccessibilityElement.h" +#import "DTCoreTextLayoutFrameAccessibilityElementGenerator.h" +#import "DTBlockFunctions.h" + +#if !__has_feature(objc_arc) +#error THIS CODE MUST BE COMPILED WITH ARC ENABLED! +#endif + +NSString * const DTAttributedTextContentViewDidFinishLayoutNotification = @"DTAttributedTextContentViewDidFinishLayoutNotification"; + +@interface DTAttributedTextContentView () +{ + BOOL _shouldAddFirstLineLeading; + BOOL _shouldDrawImages; + BOOL _shouldDrawLinks; + BOOL _shouldLayoutCustomSubviews; + DTAttributedTextContentViewRelayoutMask _relayoutMask; + + NSMutableSet *customViews; + NSMutableDictionary *customViewsForLinksIndex; + + BOOL _isTiling; + BOOL _layoutFrameHeightIsConstrainedByBounds; + + DTCoreTextLayouter *_layouter; + + CGPoint _layoutOffset; + CGSize _backgroundOffset; + + // lookup bitmask what delegate methods are implemented + struct + { + unsigned int delegateSupportsCustomViewsForAttachments:1; + unsigned int delegateSupportsCustomViewsForLinks:1; + unsigned int delegateSupportsGenericCustomViews:1; + unsigned int delegateSupportsNotificationBeforeDrawing:1; + unsigned int delegateSupportsNotificationAfterDrawing:1; + unsigned int delegateSupportsNotificationBeforeTextBoxDrawing:1; + } _delegateFlags; + + DT_WEAK_VARIABLE id _delegate; +} + +@property (nonatomic, strong) NSMutableDictionary *customViewsForLinksIndex; +@property (nonatomic, strong) NSMutableDictionary *customViewsForAttachmentsIndex; +@property (nonatomic, strong) NSMutableSet *customViews; + +@property (nonatomic, strong) NSArray *accessibilityElements; + +- (void)removeAllCustomViews; +- (void)removeAllCustomViewsForLinks; +- (void)removeSubviewsOutsideRect:(CGRect)rect; + +@end + +static Class _layerClassToUseForDTAttributedTextContentView = nil; + +@implementation DTAttributedTextContentView (Tiling) + ++ (void)setLayerClass:(Class)layerClass +{ + _layerClassToUseForDTAttributedTextContentView = layerClass; +} + ++ (Class)layerClass +{ + if (_layerClassToUseForDTAttributedTextContentView) + { + return _layerClassToUseForDTAttributedTextContentView; + } + + return [CALayer class]; +} + +@end + + +@implementation DTAttributedTextContentView (Cursor) + +- (NSInteger)closestCursorIndexToPoint:(CGPoint)point +{ + return [self.layoutFrame closestCursorIndexToPoint:point]; +} + +- (CGRect)cursorRectAtIndex:(NSInteger)index +{ + return [self.layoutFrame cursorRectAtIndex:index]; +} + +@end + + +@implementation DTAttributedTextContentView + +- (void)setup +{ + self.contentMode = UIViewContentModeTopLeft; // to avoid bitmap scaling effect on resize + _shouldLayoutCustomSubviews = YES; + + // no extra leading is added by default + _shouldAddFirstLineLeading = NO; + + // by default we draw images, if custom views are supported (by setting delegate) this is disabled + // if you still want images to be drawn together with text then set it back to YES after setting delegate + _shouldDrawImages = YES; + + // by default we draw links. If you don't want that because you want to highlight the text in + // DTLinkButton set this property to NO and create a highlighted version of the attributed string + _shouldDrawLinks = YES; + + _layoutFrameHeightIsConstrainedByBounds = NO; // we calculate the necessary height unemcumbered by bounds + _relayoutMask = DTAttributedTextContentViewRelayoutOnWidthChanged; + + // possibly already set in NIB + if (!self.backgroundColor) + { + self.backgroundColor = [DTColor whiteColor]; + } + + // set tile size if applicable + CATiledLayer *layer = (id)self.layer; + if ([layer isKindOfClass:[CATiledLayer class]]) + { + // get larger dimension and multiply by scale + UIScreen *mainScreen = [UIScreen mainScreen]; + CGFloat largerDimension = MAX(mainScreen.applicationFrame.size.width, mainScreen.applicationFrame.size.height); + CGFloat scale = mainScreen.scale; + + // this way tiles cover entire screen regardless of orientation or scale + CGSize tileSize = CGSizeMake(largerDimension * scale, largerDimension * scale); + layer.tileSize = tileSize; + + _isTiling = YES; + } +} + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + [self setup]; + } + return self; +} + +- (void)awakeFromNib +{ + [self setup]; +} + +- (void)dealloc +{ + [self removeAllCustomViews]; +} + +- (NSString *)debugDescription +{ + NSString *extract = [[[_layoutFrame attributedStringFragment] string] substringFromIndex:[self.layoutFrame visibleStringRange].location]; + + if ([extract length]>10) + { + extract = [extract substringToIndex:10]; + } + + return [NSString stringWithFormat:@"<%@ %@ range:%@ '%@...'>", [self class], NSStringFromCGRect(self.frame),NSStringFromRange([self.layoutFrame visibleStringRange]), extract]; +} + +- (void)layoutSubviewsInRect:(CGRect)rect +{ + // if we are called for partial (non-infinate) we remove unneeded custom subviews first + if (!CGRectIsInfinite(rect)) + { + [self removeSubviewsOutsideRect:rect]; + } + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + + DTCoreTextLayoutFrame *theLayoutFrame = self.layoutFrame; + + NSAttributedString *layoutString = [theLayoutFrame attributedStringFragment]; + NSArray *lines; + if (CGRectIsInfinite(rect)) + { + lines = [theLayoutFrame lines]; + } + else + { + lines = [theLayoutFrame linesVisibleInRect:rect]; + } + + // hide all customViews + for (UIView *view in self.customViews) + { + view.hidden = YES; + } + + for (DTCoreTextLayoutLine *oneLine in lines) + { + NSUInteger skipRunsBeforeLocation = 0; + + for (DTCoreTextGlyphRun *oneRun in oneLine.glyphRuns) + { + // add custom views if necessary + NSRange runRange = [oneRun stringRange]; + CGRect frameForSubview = CGRectZero; + + if (runRange.location>=skipRunsBeforeLocation) + { + // see if it's a link + NSRange effectiveRangeOfLink; + + // make sure that a link is only as long as the area to the next attachment or the current attachment itself + DTTextAttachment *attachment = oneRun.attributes[NSAttachmentAttributeName]; + + // if there is no attachment then the effectiveRangeOfAttachment contains the range until the next attachment + NSURL *linkURL = oneRun.attributes[DTLinkAttribute]; + + if (linkURL) + { + effectiveRangeOfLink = runRange; + } + + // avoid chaining together glyph runs for an attachment + if (linkURL && !attachment) + { + NSInteger glyphIndex = [oneLine.glyphRuns indexOfObject:oneRun]; + + // chain together this glyph run with following runs for combined link buttons + for (NSInteger i=glyphIndex+1; i<[oneLine.glyphRuns count]; i++) + { + DTCoreTextGlyphRun *followingRun = oneLine.glyphRuns[i]; + NSURL *followingURL = followingRun.attributes[DTLinkAttribute]; + + if (![[followingURL absoluteString] isEqualToString:[linkURL absoluteString]]) + { + // following glyph run has different or no link URL + break; + } + + if (followingRun.attachment) + { + // do not join a following attachment to the combined link + break; + } + + // extend effective link range + effectiveRangeOfLink = NSUnionRange(effectiveRangeOfLink, followingRun.stringRange); + } + + // frame for link view includes for all joined glyphruns with same link in this line + frameForSubview = [oneLine frameOfGlyphsWithRange:effectiveRangeOfLink]; + + // this following glyph run link attribute is joined to link range + skipRunsBeforeLocation = NSMaxRange(effectiveRangeOfLink); + } + else + { + // individual glyph run + + if (attachment) + { + // frame might be different due to image vertical alignment + CGFloat ascender = [attachment ascentForLayout]; + CGFloat descender = [attachment descentForLayout]; + + frameForSubview = CGRectMake(oneRun.frame.origin.x, oneLine.baselineOrigin.y - ascender, oneRun.frame.size.width, ascender+descender); + } + else + { + frameForSubview = oneRun.frame; + } + } + + // if there is an attachment then we continue even with empty frame, might be a lazily loaded image + if ((frameForSubview.size.width<=0 || frameForSubview.size.height<=0) && !attachment) + { + continue; + } + + NSNumber *indexKey = [NSNumber numberWithInteger:runRange.location]; + + // offset layout if necessary + if (!CGPointEqualToPoint(_layoutOffset, CGPointZero)) + { + frameForSubview.origin.x += _layoutOffset.x; + frameForSubview.origin.y += _layoutOffset.y; + } + + // round frame + frameForSubview.origin.x = floor(frameForSubview.origin.x); + frameForSubview.origin.y = ceil(frameForSubview.origin.y); + frameForSubview.size.width = round(frameForSubview.size.width); + frameForSubview.size.height = round(frameForSubview.size.height); + + if (CGRectGetMinY(frameForSubview)> CGRectGetMaxY(rect) || CGRectGetMaxY(frameForSubview) < CGRectGetMinY(rect)) + { + // is still outside even though the bounds of the line already intersect visible area + continue; + } + + if (attachment) + { + indexKey = [NSNumber numberWithInteger:[attachment hash]]; + UIView *existingAttachmentView = [self.customViewsForAttachmentsIndex objectForKey:indexKey]; + + if (existingAttachmentView) + { + existingAttachmentView.hidden = NO; + existingAttachmentView.frame = frameForSubview; + + existingAttachmentView.alpha = 1; + + [existingAttachmentView setNeedsLayout]; + [existingAttachmentView setNeedsDisplay]; + + linkURL = nil; // prevent adding link button on top of image view + } + else + { + UIView *newCustomAttachmentView = nil; + + if ([attachment isKindOfClass:[DTDictationPlaceholderTextAttachment class]]) + { + newCustomAttachmentView = [DTDictationPlaceholderView placeholderView]; + newCustomAttachmentView.frame = frameForSubview; // set fixed frame + } + else if (_delegateFlags.delegateSupportsCustomViewsForAttachments) + { + newCustomAttachmentView = [_delegate attributedTextContentView:self viewForAttachment:attachment frame:frameForSubview]; + } + else if (_delegateFlags.delegateSupportsGenericCustomViews) + { + NSAttributedString *string = [layoutString attributedSubstringFromRange:runRange]; + newCustomAttachmentView = [_delegate attributedTextContentView:self viewForAttributedString:string frame:frameForSubview]; + } + + if (newCustomAttachmentView) + { + // delegate responsible to set frame + if (newCustomAttachmentView) + { + newCustomAttachmentView.tag = [indexKey integerValue]; + [self addSubview:newCustomAttachmentView]; + + [self.customViews addObject:newCustomAttachmentView]; + [self.customViewsForAttachmentsIndex setObject:newCustomAttachmentView forKey:indexKey]; + + linkURL = nil; // prevent adding link button on top of image view + } + } + } + } + + if (linkURL && (_delegateFlags.delegateSupportsCustomViewsForLinks || _delegateFlags.delegateSupportsGenericCustomViews)) + { + UIView *existingLinkView = [self.customViewsForLinksIndex objectForKey:indexKey]; + + // make sure that the frame height is no less than the line height for hyperlinks + if (frameForSubview.size.height < oneLine.frame.size.height) + { + frameForSubview.origin.y = trunc(oneLine.frame.origin.y); + frameForSubview.size.height = ceil(oneLine.frame.size.height); + } + + if (existingLinkView) + { + existingLinkView.frame = frameForSubview; + existingLinkView.hidden = NO; + } + else + { + UIView *newCustomLinkView = nil; + + // make sure that the frame height is no less than the line height for hyperlinks + if (frameForSubview.size.height < oneLine.frame.size.height) + { + frameForSubview.origin.y = trunc(oneLine.frame.origin.y); + frameForSubview.size.height = ceil(oneLine.frame.size.height); + } + + if (_delegateFlags.delegateSupportsCustomViewsForLinks) + { + NSDictionary *attributes = [layoutString attributesAtIndex:runRange.location effectiveRange:NULL]; + + NSString *guid = [attributes objectForKey:DTGUIDAttribute]; + newCustomLinkView = [_delegate attributedTextContentView:self viewForLink:linkURL identifier:guid frame:frameForSubview]; + } + else if (_delegateFlags.delegateSupportsGenericCustomViews) + { + NSAttributedString *string = [layoutString attributedSubstringFromRange:runRange]; + newCustomLinkView = [_delegate attributedTextContentView:self viewForAttributedString:string frame:frameForSubview]; + } + + // delegate responsible to set frame + if (newCustomLinkView) + { + newCustomLinkView.tag = runRange.location; + [self addSubview:newCustomLinkView]; + + [self.customViews addObject:newCustomLinkView]; + [self.customViewsForLinksIndex setObject:newCustomLinkView forKey:indexKey]; + } + } + } + } + } + } + + [CATransaction commit]; +} + +- (void)layoutSubviews +{ + if (!_isTiling && (self.bounds.size.width>1024.0 || self.bounds.size.height>1024.0)) + { + if (![self.layer isKindOfClass:[CATiledLayer class]]) + { + NSLog(@"Warning: A %@ with size %@ is using a non-tiled layer. Set the layer class to a CATiledLayer subclass with [DTAttributedTextContentView setLayerClass:[DTTiledLayerWithoutFade class]].", NSStringFromClass([self class]), NSStringFromCGSize(self.bounds.size)); + } + } + + if (_shouldLayoutCustomSubviews) + { + [self layoutSubviewsInRect:CGRectInfinite]; + } + + [super layoutSubviews]; +} + +- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx +{ + // needs clearing of background + CGRect rect = CGContextGetClipBoundingBox(ctx); + + if (_backgroundOffset.height || _backgroundOffset.width) + { + CGContextSetPatternPhase(ctx, _backgroundOffset); + } + + CGContextSetFillColorWithColor(ctx, [self.backgroundColor CGColor]); + CGContextFillRect(ctx, rect); + + // offset layout if necessary + if (!CGPointEqualToPoint(_layoutOffset, CGPointZero)) + { + CGAffineTransform transform = CGAffineTransformMakeTranslation(_layoutOffset.x, _layoutOffset.y); + CGContextConcatCTM(ctx, transform); + } + + DTCoreTextLayoutFrame *theLayoutFrame = self.layoutFrame; // this is synchronized + + // construct drawing options + DTCoreTextLayoutFrameDrawingOptions options = DTCoreTextLayoutFrameDrawingDefault; + + if (!_shouldDrawImages) + { + options |= DTCoreTextLayoutFrameDrawingOmitAttachments; + } + + if (!_shouldDrawLinks) + { + options |= DTCoreTextLayoutFrameDrawingOmitLinks; + } + + if (_delegateFlags.delegateSupportsNotificationBeforeDrawing) + { + [_delegate attributedTextContentView:self willDrawLayoutFrame:theLayoutFrame inContext:ctx]; + } + + // need to prevent updating of string and drawing at the same time + [theLayoutFrame drawInContext:ctx options:options]; + + if (_delegateFlags.delegateSupportsNotificationAfterDrawing) + { + [_delegate attributedTextContentView:self didDrawLayoutFrame:theLayoutFrame inContext:ctx]; + } +} + +- (void)drawRect:(CGRect)rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + [self.layoutFrame drawInContext:context options:DTCoreTextLayoutFrameDrawingDefault]; +} + +- (void)relayoutText +{ + DTBlockPerformSyncIfOnMainThreadElseAsync(^{ + + // Make sure we actually have a superview and a previous layout before attempting to relayout the text. + if (_layoutFrame && self.superview) + { + // need new layout frame, layouter can remain because the attributed string is probably the same + self.layoutFrame = nil; + + // remove all links because they might have merged or split + [self removeAllCustomViewsForLinks]; + + if (_attributedString) + { + // triggers new layout + CGSize neededSize = [self intrinsicContentSize]; + + CGRect optimalFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, neededSize.width, neededSize.height); + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSValue valueWithCGRect:optimalFrame] forKey:@"OptimalFrame"]; + + [[NSNotificationCenter defaultCenter] postNotificationName:DTAttributedTextContentViewDidFinishLayoutNotification object:self userInfo:userInfo]; + } + + [self setNeedsLayout]; + [self setNeedsDisplayInRect:self.bounds]; + + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) + { + [self invalidateIntrinsicContentSize]; + } + } + }); +} + +- (void)removeAllCustomViewsForLinks +{ + NSArray *linkViews = [customViewsForLinksIndex allValues]; + + for (UIView *customView in linkViews) + { + [customView removeFromSuperview]; + [customViews removeObject:customView]; + } + + [customViewsForLinksIndex removeAllObjects]; +} + +- (void)removeAllCustomViews +{ + NSSet *allCustomViews = [NSSet setWithSet:customViews]; + for (UIView *customView in allCustomViews) + { + [customView removeFromSuperview]; + [customViews removeObject:customView]; + } + + [customViewsForAttachmentsIndex removeAllObjects]; + [customViewsForLinksIndex removeAllObjects]; +} + +- (void)removeSubviewsOutsideRect:(CGRect)rect +{ + NSSet *allCustomViews = [NSSet setWithSet:customViews]; + for (UIView *customView in allCustomViews) + { + if (CGRectGetMinY(customView.frame)> CGRectGetMaxY(rect) || CGRectGetMaxY(customView.frame) < CGRectGetMinY(rect)) + { + NSNumber *indexKey = [NSNumber numberWithInteger:customView.tag]; + + [customView removeFromSuperview]; + [customViews removeObject:customView]; + + [customViewsForAttachmentsIndex removeObjectForKey:indexKey]; + [customViewsForLinksIndex removeObjectForKey:indexKey]; + } + } +} + +#pragma mark - Sizing + +- (CGSize)intrinsicContentSize +{ + if (!self.layoutFrame) // creates new layout frame if possible + { + return CGSizeMake(-1, -1); // UIViewNoIntrinsicMetric as of iOS 6 + } + + // we have a layout frame and from this we get the needed size + return CGSizeMake(_layoutFrame.frame.size.width + _edgeInsets.left + _edgeInsets.right, CGRectGetMaxY(_layoutFrame.frame) + _edgeInsets.bottom); +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + CGSize neededSize = [self intrinsicContentSize]; // creates layout frame if necessary + + if (neededSize.width>=0 && neededSize.height>=0) + { + return neededSize; + } + + // return empty size plus padding + return CGSizeMake(_edgeInsets.left + _edgeInsets.right, _edgeInsets.bottom + _edgeInsets.top); +} + +- (CGRect)_frameForLayoutFrameConstraintedToWidth:(CGFloat)constrainWidth +{ + if (!isnormal(constrainWidth)) + { + constrainWidth = self.bounds.size.width; + } + + CGRect bounds = self.bounds; + bounds.size.width = constrainWidth; + CGRect rect = UIEdgeInsetsInsetRect(bounds, _edgeInsets); + + if (rect.size.width<=0) + { + // cannot create layout frame with negative or zero width + return CGRectZero; + } + + if (_layoutFrameHeightIsConstrainedByBounds) + { + if (rect.size.height<=0) + { + // cannot create layout frame with negative or zero height if flexible height is disabled + return CGRectZero; + } + + // already set height to bounds height + } + else + { + rect.size.height = CGFLOAT_HEIGHT_UNKNOWN; // necessary height set as soon as we know it. + } + + return rect; +} + +- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width +{ + // create a temporary frame, will be cached by the layouter for the given rect + CGRect rect = [self _frameForLayoutFrameConstraintedToWidth:width]; + DTCoreTextLayoutFrame *tmpLayoutFrame = [self.layouter layoutFrameWithRect:rect range:NSMakeRange(0, 0)]; + + // assign current layout frame properties to tmpLayoutFrame + tmpLayoutFrame.numberOfLines = _numberOfLines; + tmpLayoutFrame.lineBreakMode = _lineBreakMode; + tmpLayoutFrame.truncationString = _truncationString; + + // we have a layout frame and from this we get the needed size + return CGSizeMake(tmpLayoutFrame.frame.size.width + _edgeInsets.left + _edgeInsets.right, CGRectGetMaxY(tmpLayoutFrame.frame) + _edgeInsets.bottom); +} + +#pragma mark Properties +- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets +{ + if (!UIEdgeInsetsEqualToEdgeInsets(edgeInsets, _edgeInsets)) + { + _edgeInsets = edgeInsets; + + [self relayoutText]; + + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) + { + [self invalidateIntrinsicContentSize]; + } + } +} + +- (void)setAttributedString:(NSAttributedString *)string +{ + if (_attributedString != string) + { + // keep the layouter, update string + self.layouter.attributedString = string; + + _attributedString = [string copy]; + + // only do relayout if there is a previous layout frame and visible + if (_layoutFrame) + { + // new layout invalidates all positions for custom views + [self removeAllCustomViews]; + + // relayout only occurs if the view is visible + [self relayoutText]; + } + else + { + // this is needed or else no lazy layout will be triggered if there is no layout frame yet (before this is added to a superview) + [self setNeedsLayout]; + [self setNeedsDisplayInRect:self.bounds]; + } + + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) + { + [self invalidateIntrinsicContentSize]; + } + } +} + +- (void)setFrame:(CGRect)frame +{ + CGRect oldFrame = self.frame; + + [super setFrame:frame]; + + if (!_layoutFrame) + { + return; + } + + // having a layouter means we are responsible for layouting yourselves + + // relayout based on relayoutMask + + BOOL shouldRelayout = NO; + + if (_relayoutMask & DTAttributedTextContentViewRelayoutOnHeightChanged) + { + if (oldFrame.size.height != frame.size.height) + { + shouldRelayout = YES; + } + } + + if (_relayoutMask & DTAttributedTextContentViewRelayoutOnWidthChanged) + { + if (oldFrame.size.width != frame.size.width) + { + shouldRelayout = YES; + } + } + + if (shouldRelayout) + { + [self relayoutText]; + } + + if (oldFrame.size.height < frame.size.height) + { + // need to draw the newly visible area + [self setNeedsDisplayInRect:CGRectMake(0, oldFrame.size.height, self.bounds.size.width, frame.size.height - oldFrame.size.height)]; + } +} + +- (void)setShouldAddFirstLineLeading:(BOOL)shouldAddLeading +{ + if (_shouldAddFirstLineLeading != shouldAddLeading) + { + _shouldAddFirstLineLeading = shouldAddLeading; + + [self setNeedsDisplay]; + + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) + { + [self invalidateIntrinsicContentSize]; + } + } +} + +- (void)setShouldDrawImages:(BOOL)shouldDrawImages +{ + if (_shouldDrawImages != shouldDrawImages) + { + _shouldDrawImages = shouldDrawImages; + + [self setNeedsDisplay]; + + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) + { + [self invalidateIntrinsicContentSize]; + } + } +} + +- (void)setShouldDrawLinks:(BOOL)shouldDrawLinks +{ + if (_shouldDrawLinks != shouldDrawLinks) + { + _shouldDrawLinks = shouldDrawLinks; + + [self setNeedsDisplay]; + + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) + { + [self invalidateIntrinsicContentSize]; + } + } +} + +- (void)setBackgroundColor:(DTColor *)newColor +{ + super.backgroundColor = newColor; + + if ([newColor alphaComponent]<1.0) + { + self.opaque = NO; + } + else + { + self.opaque = YES; + } +} + + +- (DTCoreTextLayouter *)layouter +{ + @synchronized(self) + { + if (!_layouter) + { + if (_attributedString) + { + _layouter = [[DTCoreTextLayouter alloc] initWithAttributedString:_attributedString]; + + // allow frame caching if somebody uses the suggestedSize + _layouter.shouldCacheLayoutFrames = YES; + } + } + + return _layouter; + } +} + +- (void)setLayouter:(DTCoreTextLayouter *)layouter +{ + @synchronized(self) + { + if (_layouter != layouter) + { + _layouter = layouter; + } + } +} + +- (DTCoreTextLayoutFrame *)layoutFrame +{ + @synchronized(self) + { + DTCoreTextLayouter *theLayouter = self.layouter; + + if (!_layoutFrame) + { + // we can only layout if we have our own layouter + if (theLayouter) + { + CGRect rect = UIEdgeInsetsInsetRect(self.bounds, _edgeInsets); + + if (rect.size.width<=0) + { + // cannot create layout frame with negative or zero width + return nil; + } + + if (_layoutFrameHeightIsConstrainedByBounds) + { + if (rect.size.height<=0) + { + // cannot create layout frame with negative or zero height if flexible height is disabled + return nil; + } + + // height already set + } + else + { + rect.size.height = CGFLOAT_HEIGHT_UNKNOWN; // necessary height set as soon as we know it. + } + + _layoutFrame = [theLayouter layoutFrameWithRect:rect range:NSMakeRange(0, 0)]; + _layoutFrame.numberOfLines = _numberOfLines; + _layoutFrame.lineBreakMode = _lineBreakMode; + _layoutFrame.truncationString = _truncationString; + + // this must have been the initial layout pass + CGSize neededSize = CGSizeMake(_layoutFrame.frame.size.width + _edgeInsets.left + _edgeInsets.right, CGRectGetMaxY(_layoutFrame.frame) + _edgeInsets.bottom); + + CGRect optimalFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, neededSize.width, neededSize.height); + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSValue valueWithCGRect:optimalFrame] forKey:@"OptimalFrame"]; + + [[NSNotificationCenter defaultCenter] postNotificationName:DTAttributedTextContentViewDidFinishLayoutNotification object:self userInfo:userInfo]; + + if (_delegateFlags.delegateSupportsNotificationBeforeTextBoxDrawing) + { + DT_WEAK_VARIABLE DTAttributedTextContentView *weakself = self; + + [_layoutFrame setTextBlockHandler:^(DTTextBlock *textBlock, CGRect frame, CGContextRef context, BOOL *shouldDrawDefaultBackground) { + + DTAttributedTextContentView *strongself = weakself; + + BOOL result = [strongself->_delegate attributedTextContentView:strongself shouldDrawBackgroundForTextBlock:textBlock frame:frame context:context forLayoutFrame:strongself->_layoutFrame]; + + if (shouldDrawDefaultBackground) + { + *shouldDrawDefaultBackground = result; + } + + }]; + } + + [self invalidateAccessibilityElements]; + } + } + + return _layoutFrame; + } +} + +- (void)setLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame +{ + @synchronized(self) + { + if (_layoutFrame != layoutFrame) + { + [self removeAllCustomViewsForLinks]; + + if (layoutFrame) + { + [self setNeedsLayout]; + [self setNeedsDisplayInRect:self.bounds]; + } + _layoutFrame = layoutFrame; + + [self invalidateAccessibilityElements]; + } + }; +} + +- (NSMutableSet *)customViews +{ + if (!customViews) + { + customViews = [[NSMutableSet alloc] init]; + } + + return customViews; +} + +- (NSMutableDictionary *)customViewsForLinksIndex +{ + if (!customViewsForLinksIndex) + { + customViewsForLinksIndex = [[NSMutableDictionary alloc] init]; + } + + return customViewsForLinksIndex; +} + +- (NSMutableDictionary *)customViewsForAttachmentsIndex +{ + if (!customViewsForAttachmentsIndex) + { + customViewsForAttachmentsIndex = [[NSMutableDictionary alloc] init]; + } + + return customViewsForAttachmentsIndex; +} + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; + + _delegateFlags.delegateSupportsCustomViewsForAttachments = [_delegate respondsToSelector:@selector(attributedTextContentView:viewForAttachment:frame:)]; + _delegateFlags.delegateSupportsCustomViewsForLinks = [_delegate respondsToSelector:@selector(attributedTextContentView:viewForLink:identifier:frame:)]; + _delegateFlags.delegateSupportsGenericCustomViews = [_delegate respondsToSelector:@selector(attributedTextContentView:viewForAttributedString:frame:)]; + _delegateFlags.delegateSupportsNotificationBeforeDrawing = [_delegate respondsToSelector:@selector(attributedTextContentView:willDrawLayoutFrame:inContext:)]; + _delegateFlags.delegateSupportsNotificationAfterDrawing = [_delegate respondsToSelector:@selector(attributedTextContentView:didDrawLayoutFrame:inContext:)]; + _delegateFlags.delegateSupportsNotificationBeforeTextBoxDrawing = [_delegate respondsToSelector:@selector(attributedTextContentView:shouldDrawBackgroundForTextBlock:frame:context:forLayoutFrame:)]; + + if (!_delegateFlags.delegateSupportsCustomViewsForLinks && !_delegateFlags.delegateSupportsGenericCustomViews) + { + [self removeAllCustomViewsForLinks]; + } + + // we don't draw the images if imageViews are provided by the delegate method + // if you want images to be drawn even though you use custom views, set it back to YES after setting delegate + if (_delegateFlags.delegateSupportsGenericCustomViews || _delegateFlags.delegateSupportsCustomViewsForAttachments) + { + _shouldDrawImages = NO; + } + else + { + _shouldDrawImages = YES; + } +} + +#pragma mark - Accessibility + +- (void)invalidateAccessibilityElements +{ + _accessibilityElements = nil; +} + +- (BOOL)isAccessibilityElement +{ + return NO; +} + +- (NSInteger)accessibilityElementCount +{ + return [[self accessibilityElements] count]; +} + +- (id)accessibilityElementAtIndex:(NSInteger)index +{ + NSUInteger count = [self accessibilityElementCount]; + + if (count > 0 && index < count) + { + return [[self accessibilityElements] objectAtIndex:index]; + } + + return nil; +} + +- (NSInteger)indexOfAccessibilityElement:(id)element +{ + // It seems like indexOfObject: is failing for the proxy views, even though isEqual: and hash are both implemented + // on the proxy. Perhaps UIView doesn't like isEqual: with our proxy view. Regardless, this implementation seems to work. + NSInteger index = [[self accessibilityElements] indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + BOOL equal = [obj isEqual:element]; + *stop = equal; + return equal; + }]; + + return index; +} + +- (NSArray *)accessibilityElements +{ + if (!_accessibilityElements) + { + DTCoreTextLayoutFrameAccessibilityElementGenerator *generator = [[DTCoreTextLayoutFrameAccessibilityElementGenerator alloc] init]; + _accessibilityElements = [generator accessibilityElementsForLayoutFrame:self.layoutFrame view:self attachmentViewProvider:^id(DTTextAttachment *attachment) { + // Since we actually take the views out of the view hierarchy when they're off screen, create a proxy object that stands + // in for the view until it's needed by VoiceOver. By the time VoiceOver asks for the view, it should already be onscreen. + return [[DTAccessibilityViewProxy alloc] initWithTextAttachment:attachment delegate:self]; + }]; + } + return _accessibilityElements; +} + +#pragma mark - DTAccessibilityViewProxyDelegate + +- (UIView *)viewForTextAttachment:(DTTextAttachment *)textAttachment proxy:(DTAccessibilityViewProxy *)proxy +{ + NSNumber *indexKey = [NSNumber numberWithInteger:[textAttachment hash]]; + UIView *existingAttachmentView = [self.customViewsForAttachmentsIndex objectForKey:indexKey]; + return existingAttachmentView; +} + +@synthesize layouter = _layouter; +@synthesize layoutFrame = _layoutFrame; +@synthesize attributedString = _attributedString; +@synthesize delegate = _delegate; +@synthesize edgeInsets = _edgeInsets; +@synthesize shouldDrawImages = _shouldDrawImages; +@synthesize shouldDrawLinks = _shouldDrawLinks; +@synthesize shouldLayoutCustomSubviews = _shouldLayoutCustomSubviews; +@synthesize layoutOffset = _layoutOffset; +@synthesize backgroundOffset = _backgroundOffset; + +@synthesize customViews; +@synthesize customViewsForLinksIndex; +@synthesize customViewsForAttachmentsIndex; +@synthesize relayoutMask = _relayoutMask; + +@synthesize accessibilityElements = _accessibilityElements; + +@end + +@implementation DTAttributedTextContentView (Drawing) + +- (UIImage *)contentImageWithBounds:(CGRect)bounds options:(DTCoreTextLayoutFrameDrawingOptions)options +{ + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); + + CGContextRef context = UIGraphicsGetCurrentContext(); + + if(!context) { + return nil; + } + + CGContextTranslateCTM(context, -bounds.origin.x, -bounds.origin.y); + + [self.layoutFrame drawInContext:context options:options]; + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextView.h b/Pods/DTCoreText/Core/Source/DTAttributedTextView.h new file mode 100644 index 0000000..3c22ea2 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextView.h @@ -0,0 +1,128 @@ +// +// DTAttributedTextView.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/12/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTAttributedTextContentView.h" + +@class DTAttributedTextView; + +/** + This view is designed to be a replacement for `UITextView`. It is a `UIScrollView` subclass and creates a as content view for displaying the text. + + The content view of type is created lazily. You should not set values on it directly if you use it in conjunction with this class for scrolling. + */ + +@interface DTAttributedTextView : UIScrollView +{ + // ivars needed by subclasses + DTAttributedTextContentView *_attributedTextContentView; +} + +/** + @name Providing Content + */ + +/** + The attributed text to be displayed in the text content view of the receiver. + */ +@property (nonatomic, strong) NSAttributedString *attributedString; + + +/** + A delegate implementing DTAttributedTextContentViewDelegate to provide custom subviews for images and links. + */ +@property (nonatomic, DT_WEAK_PROPERTY) IBOutlet id textDelegate; + + +/** + Performs a new layout pass on the receiver. This destroys the frame setter, calls relayoutText on the content view and marks the receiver as needing layout so that custom subviews get appropriately sized. + */ +- (void)relayoutText; + +/** + @name Accessing Subviews + */ + +/** + References to the DTAttributedTextContentView that display the text. This is not named contentView because this class inherits from `UIScrollView` which has an internal property of this name + */ +@property (nonatomic, strong, readonly) DTAttributedTextContentView *attributedTextContentView; + +/** + A view to be displayed behind the text content view + */ +@property (nonatomic, strong) IBOutlet UIView *backgroundView; + + +/** + @name Customizing Display + */ + +/** + If the content view of the receiver should draw links. Set to `NO` if displaying links as custom views via textDelegate; + + Defaults to `YES` if you supply your own link drawing then set this property to NO and supply your custom view (e.g. ) via the . + */ +@property (nonatomic, assign) BOOL shouldDrawLinks; + +/** + If the content view of the receiver should draw images. Set to `NO` if displaying images as custom views via textDelegate; + + Defaults to `YES` if you supply your own image drawing then set this property to NO and supply your custom image view (e.g. ) via the . + */ +@property (nonatomic, assign) BOOL shouldDrawImages; + + +/** + @name Customizing Content View + */ + +/** + You can override this method to provide a different class to use for the content view. If you replace the content view class then it should inherit from which is also the default. + @returns The class to use for the content view. + */ +- (Class)classForContentView; + +/** + @name User Interaction + */ + +/** + Scrolls the receiver to the anchor with the given name to the top. + @param anchorName The name of the href anchor. + @param animated `YES` if the movement should be animated. + */ +- (void)scrollToAnchorNamed:(NSString *)anchorName animated:(BOOL)animated; + +/** + Scrolls the receiver until the text in the specified range is visible. + @param range The range of text to scroll into view. + @param animated `YES` if the movement should be animated. + */ +- (void)scrollRangeToVisible:(NSRange)range animated:(BOOL)animated; + +/** + @name Working with a Cursor + */ + +/** + Determines the closest string index to a point in the receiver's frame. + + This can be used to find the cursor position to position an input caret at. + @param point The point + @returns The resulting string index + */ +- (NSInteger)closestCursorIndexToPoint:(CGPoint)point; + +/** + The rectangle to draw a caret for a given index + @param index The string index for which to determine a cursor frame + @returns The cursor rectangle + */ +- (CGRect)cursorRectAtIndex:(NSInteger)index; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextView.m b/Pods/DTCoreText/Core/Source/DTAttributedTextView.m new file mode 100644 index 0000000..ae8c2ba --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextView.m @@ -0,0 +1,413 @@ +// +// DTAttributedTextView.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/12/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import + +#import "DTAttributedTextView.h" +#import "DTCoreText.h" +#import "DTTiledLayerWithoutFade.h" +#import "DTBlockFunctions.h" + + +@interface DTAttributedTextView () + +- (void)_setup; + +@end + + + +@implementation DTAttributedTextView +{ + UIView *_backgroundView; + + // these are pass-through, i.e. store until the content view is created + DT_WEAK_VARIABLE id textDelegate; + NSAttributedString *_attributedString; + + BOOL _shouldDrawLinks; + BOOL _shouldDrawImages; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) + { + [self _setup]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)layoutSubviews +{ + [self attributedTextContentView]; + + // layout custom subviews for visible area + [_attributedTextContentView layoutSubviewsInRect:self.bounds]; + + [super layoutSubviews]; +} + +- (void)awakeFromNib +{ + [self _setup]; +} + +// default +- (void)_setup +{ + if (self.backgroundColor) + { + CGFloat alpha = [self.backgroundColor alphaComponent]; + + if (alpha < 1.0) + { + self.opaque = NO; + } + else + { + self.opaque = YES; + } + } + else + { + self.backgroundColor = [DTColor whiteColor]; + self.opaque = YES; + } + + self.autoresizesSubviews = NO; + self.clipsToBounds = YES; + + // defaults + _shouldDrawLinks = YES; + _shouldDrawImages = YES; +} + +// override class e.g. for mutable content view +- (Class)classForContentView +{ + return [DTAttributedTextContentView class]; +} + +#pragma mark External Methods +- (void)scrollToAnchorNamed:(NSString *)anchorName animated:(BOOL)animated +{ + NSRange range = [self.attributedTextContentView.attributedString rangeOfAnchorNamed:anchorName]; + + if (range.location != NSNotFound) + { + [self scrollRangeToVisible:range animated:animated]; + } +} + +- (void)scrollRangeToVisible:(NSRange)range animated:(BOOL)animated +{ + // get the line of the first index of the anchor range + DTCoreTextLayoutLine *line = [self.attributedTextContentView.layoutFrame lineContainingIndex:range.location]; + + // make sure we don't scroll too far + CGFloat maxScrollPos = self.contentSize.height - self.bounds.size.height + self.contentInset.bottom + self.contentInset.top; + CGFloat scrollPos = MIN(line.frame.origin.y, maxScrollPos); + + // scroll + [self setContentOffset:CGPointMake(0, scrollPos) animated:animated]; +} + +- (void)relayoutText +{ + DTBlockPerformSyncIfOnMainThreadElseAsync(^{ + + // need to reset the layouter because otherwise we get the old framesetter or cached layout frames + _attributedTextContentView.layouter=nil; + + // here we're layouting the entire string, might be more efficient to only relayout the paragraphs that contain these attachments + [_attributedTextContentView relayoutText]; + + // layout custom subviews for visible area + [self setNeedsLayout]; + }); +} + +#pragma mark - Working with a Cursor + +- (NSInteger)closestCursorIndexToPoint:(CGPoint)point +{ + // the point is in the coordinate system of the receiver, need to convert into those of the content view first + CGPoint pointInContentView = [self.attributedTextContentView convertPoint:point fromView:self]; + + return [self.attributedTextContentView closestCursorIndexToPoint:pointInContentView]; +} + +- (CGRect)cursorRectAtIndex:(NSInteger)index +{ + CGRect rectInContentView = [self.attributedTextContentView cursorRectAtIndex:index]; + + // the point is in the coordinate system of the content view, need to convert into those of the receiver first + CGRect rect = [self.attributedTextContentView convertRect:rectInContentView toView:self]; + + return rect; +} + +#pragma mark Notifications +- (void)contentViewDidLayout:(NSNotification *)notification +{ + DTBlockPerformSyncIfOnMainThreadElseAsync(^{ + + NSDictionary *userInfo = [notification userInfo]; + CGRect optimalFrame = [[userInfo objectForKey:@"OptimalFrame"] CGRectValue]; + + CGRect frame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset); + + // ignore possibly delayed layout notification for a different width + if (optimalFrame.size.width == frame.size.width) + { + _attributedTextContentView.frame = optimalFrame; + self.contentSize = [_attributedTextContentView intrinsicContentSize]; + } + }); +} + +#pragma mark Properties +- (DTAttributedTextContentView *)attributedTextContentView +{ + if (!_attributedTextContentView) + { + // subclasses can specify a DTAttributedTextContentView subclass instead + Class classToUse = [self classForContentView]; + + CGRect frame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset); + + if (frame.size.width<=0 || frame.size.height<=0) + { + frame = CGRectZero; + } + + // make sure we always have a tiled layer + Class previousLayerClass = nil; + + // for DTAttributedTextContentView subclasses we force a tiled layer + if ([classToUse isSubclassOfClass:[DTAttributedTextContentView class]]) + { + Class layerClass = [DTAttributedTextContentView layerClass]; + + if (![layerClass isSubclassOfClass:[CATiledLayer class]]) + { + [DTAttributedTextContentView setLayerClass:[DTTiledLayerWithoutFade class]]; + previousLayerClass = layerClass; + } + } + + _attributedTextContentView = [[classToUse alloc] initWithFrame:frame]; + + // restore previous layer class if we changed the layer class for the content view + if (previousLayerClass) + { + [DTAttributedTextContentView setLayerClass:previousLayerClass]; + } + + _attributedTextContentView.userInteractionEnabled = YES; + _attributedTextContentView.backgroundColor = self.backgroundColor; + _attributedTextContentView.shouldLayoutCustomSubviews = NO; // we call layout when scrolling + + // adjust opaqueness based on background color alpha + CGFloat alpha = [self.backgroundColor alphaComponent]; + + if (alpha < 1.0) + { + _attributedTextContentView.opaque = NO; + } + else + { + _attributedTextContentView.opaque = YES; + } + + // set text delegate if it was set before instantiation of content view + _attributedTextContentView.delegate = textDelegate; + + // pass on setting + _attributedTextContentView.shouldDrawLinks = _shouldDrawLinks; + + // notification that tells us about the actual size of the content view + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentViewDidLayout:) name:DTAttributedTextContentViewDidFinishLayoutNotification object:_attributedTextContentView]; + + // temporary frame to specify the width + _attributedTextContentView.frame = frame; + + // set text we previously got, this also triggers a relayout + _attributedTextContentView.attributedString = _attributedString; + + // this causes a relayout and the resulting notification will allow us to set the final frame + + [self addSubview:_attributedTextContentView]; + } + + return _attributedTextContentView; +} + +- (void)setBackgroundColor:(DTColor *)newColor +{ + if ([newColor alphaComponent]<1.0) + { + super.backgroundColor = newColor; + _attributedTextContentView.backgroundColor = [DTColor clearColor]; + self.opaque = NO; + } + else + { + super.backgroundColor = newColor; + + if (_attributedTextContentView.opaque) + { + _attributedTextContentView.backgroundColor = newColor; + } + } +} + +- (void)setContentInset:(UIEdgeInsets)contentInset +{ + if (!UIEdgeInsetsEqualToEdgeInsets(self.contentInset, contentInset)) + { + [super setContentInset:contentInset]; + + // height does not matter, that will be determined anyhow + CGRect contentFrame = CGRectMake(0, 0, self.frame.size.width - self.contentInset.left - self.contentInset.right, _attributedTextContentView.frame.size.height); + + _attributedTextContentView.frame = contentFrame; + } +} + +- (UIView *)backgroundView +{ + if (!_backgroundView) + { + _backgroundView = [[UIView alloc] initWithFrame:self.bounds]; + _backgroundView.backgroundColor = [DTColor whiteColor]; + + // default is no interaction because background should have no interaction + _backgroundView.userInteractionEnabled = NO; + + [self insertSubview:_backgroundView belowSubview:self.attributedTextContentView]; + + // make content transparent so that we see the background + _attributedTextContentView.backgroundColor = [DTColor clearColor]; + _attributedTextContentView.opaque = NO; + } + + return _backgroundView; +} + +- (void)setBackgroundView:(UIView *)backgroundView +{ + if (_backgroundView != backgroundView) + { + [_backgroundView removeFromSuperview]; + _backgroundView = backgroundView; + + if (_attributedTextContentView) + { + [self insertSubview:_backgroundView belowSubview:_attributedTextContentView]; + } + else + { + [self addSubview:_backgroundView]; + } + + if (_backgroundView) + { + // make content transparent so that we see the background + _attributedTextContentView.backgroundColor = [DTColor clearColor]; + _attributedTextContentView.opaque = NO; + } + else + { + _attributedTextContentView.backgroundColor = [DTColor whiteColor]; + _attributedTextContentView.opaque = YES; + } + } +} + +- (void)setAttributedString:(NSAttributedString *)string +{ + _attributedString = string; + + // might need layout for visible custom views + [self setNeedsLayout]; + + if (_attributedTextContentView) + { + // pass it along if contentView already exists + _attributedTextContentView.attributedString = string; + + // this causes a relayout and the resulting notification will allow us to set the frame and contentSize + } +} + +- (NSAttributedString *)attributedString +{ + return _attributedString; +} + +- (void)setFrame:(CGRect)frame +{ + CGRect oldFrame = self.frame; + + if (!CGRectEqualToRect(oldFrame, frame)) + { + [super setFrame:frame]; // need to set own frame first because layout completion needs this updated frame + + if (oldFrame.size.width != frame.size.width) + { + // height does not matter, that will be determined anyhow + CGRect contentFrame = CGRectMake(0, 0, frame.size.width - self.contentInset.left - self.contentInset.right, _attributedTextContentView.frame.size.height); + + _attributedTextContentView.frame = contentFrame; + } + } +} + +- (void)setTextDelegate:(id)aTextDelegate +{ + // store unsafe pointer to delegate because we might not have a contentView yet + textDelegate = aTextDelegate; + + // set it if possible, otherwise it will be set in contentView lazy property + _attributedTextContentView.delegate = aTextDelegate; +} + +- (id)textDelegate +{ + return _attributedTextContentView.delegate; +} + +- (void)setShouldDrawLinks:(BOOL)shouldDrawLinks +{ + _shouldDrawLinks = shouldDrawLinks; + _attributedTextContentView.shouldDrawLinks = _shouldDrawLinks; +} + +- (void)setShouldDrawImages:(BOOL)shouldDrawImages +{ + _shouldDrawImages = shouldDrawImages; + _attributedTextContentView.shouldDrawImages = _shouldDrawImages; +} + +@synthesize attributedTextContentView = _attributedTextContentView; +@synthesize attributedString = _attributedString; +@synthesize textDelegate = _textDelegate; + +@synthesize shouldDrawLinks = _shouldDrawLinks; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTBreakHTMLElement.h b/Pods/DTCoreText/Core/Source/DTBreakHTMLElement.h new file mode 100644 index 0000000..ba83df1 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTBreakHTMLElement.h @@ -0,0 +1,17 @@ +// +// DTHTMLElementBR.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +/** + Specialized subclass of that represents a line break. + */ + +@interface DTBreakHTMLElement : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTBreakHTMLElement.m b/Pods/DTCoreText/Core/Source/DTBreakHTMLElement.m new file mode 100644 index 0000000..f903223 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTBreakHTMLElement.m @@ -0,0 +1,22 @@ +// +// DTHTMLElementBR.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTBreakHTMLElement.h" + +@implementation DTBreakHTMLElement + +- (NSAttributedString *)attributedString +{ + @synchronized(self) + { + NSDictionary *attributes = [self attributesForAttributedStringRepresentation]; + return [[NSAttributedString alloc] initWithString:UNICODE_LINE_FEED attributes:attributes]; + } +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCSSListStyle.h b/Pods/DTCoreText/Core/Source/DTCSSListStyle.h new file mode 100644 index 0000000..79cb735 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSListStyle.h @@ -0,0 +1,253 @@ +// +// DTCSSListStyle.h +// DTCoreText +// +// Created by Oliver Drobnik on 8/11/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +/** + List Styles + */ +typedef NS_ENUM(NSUInteger, DTCSSListStyleType) +{ + /** + The list style should be inherited from the parent + */ + DTCSSListStyleTypeInherit = 0, + + /** + No list style + */ + DTCSSListStyleTypeNone, + + /** + Circle bullet list style + */ + DTCSSListStyleTypeCircle, + + /** + Decimal number list style + */ + DTCSSListStyleTypeDecimal, + + /** + Decimal number list style with a leading zero + */ + DTCSSListStyleTypeDecimalLeadingZero, + + /** + Disc bullet list style + */ + DTCSSListStyleTypeDisc, + + /** + Square bullet list style + */ + DTCSSListStyleTypeSquare, + + /** + Numbered list style with uppercase letters + */ + DTCSSListStyleTypeUpperAlpha, + + /** + Numbered list style with uppercase letters + */ + DTCSSListStyleTypeUpperLatin, + + /** + Numbered list style with lowercase letters + */ + DTCSSListStyleTypeLowerAlpha, + + /** + Numbered list style with lowercase letters + */ + DTCSSListStyleTypeLowerLatin, + + /** + Plus bullet list style + */ + DTCSSListStyleTypePlus, + + /** + Underscore bullet list style + */ + DTCSSListStyleTypeUnderscore, + + + /** + Image bullet list style + */ + DTCSSListStyleTypeImage, + + /** + Value used to represent an invalid list style + */ + DTCSSListStyleTypeInvalid = NSIntegerMax +}; + +/** + List Marker Positions + */ +typedef NS_ENUM(NSUInteger, DTCSSListStylePosition) +{ + /** + List position should be inherited + */ + DTCSSListStylePositionInherit = 0, + + /** + List prefix position inside + */ + DTCSSListStylePositionInside, + + /** + List prefix position outside + */ + DTCSSListStylePositionOutside, + + /** + Value used to represent an invalid list style position + */ + DTCSSListStylePositionInvalid = NSIntegerMax +}; + + +/** + This class is the equivalent of `NSTextList` on Mac with the added handling of the marker position. + */ +@interface DTCSSListStyle : NSObject + + +/** + @name Getting Types from Strings + */ + +/** + Convert a string into a list style type. + + @param string The string to convert + */ ++ (DTCSSListStyleType)listStyleTypeFromString:(NSString *)string; + + +/** + Convert a string into a marker position. + + @param string The string to convert + */ ++ (DTCSSListStylePosition)listStylePositionFromString:(NSString *)string; + +/** + @name Creating List Styles + */ + +/** + Creates a list style from the passed CSS style dictionary + + @param styles A CSS style dictionary from which the construct a suitable list style + */ +- (id)initWithStyles:(NSDictionary *)styles; + +/** + @name Working with CSS Styles + */ + +/** + Update the receiver from the CSS styles dictionary passed + + @param styles A dictionary of CSS styles. + */ +- (void)updateFromStyleDictionary:(NSDictionary *)styles; + + +/** + @name Working with Prefixes + */ + + +/** + Returns the prefix for lists of the receiver's settings. + + @param counter The counter value to use for ordered lists. + @returns The prefix string to prepend to list items. + */ +- (NSString *)prefixWithCounter:(NSInteger)counter; + + +/** + @name Managing Item Numbering + */ + + +/** + Sets the starting item number for the text list. + + The default value is `1`. This value will be used only for ordered lists, and ignored in other cases. + + @param itemNum The item number. + */ +- (void)setStartingItemNumber:(NSInteger)itemNum; + + +/** + Returns the starting item number for the text list. + + The default value is `1`. This value will be used only for ordered lists, and ignored in other cases. + @returns The item number. + */ +- (NSInteger)startingItemNumber; + + +/** + @name Comparing Lists + */ + +/** + Determine if another list style has equivalent settings. Note that this does not mean that they are identical, only that they look the same. + @param otherListStyle The other list style to compare the receiver with + @returns `YES` if the other list style has the same values + */ +- (BOOL)isEqualToListStyle:(DTCSSListStyle *)otherListStyle; + + +/** + @name Getting Information about Lists + */ + +/** + Returns if the receiver is an ordered or unordered list + + @returns `YES` if the receiver is ordered, `NO` if it is unordered + */ +- (BOOL)isOrdered; + +/** + If the list style is inherited. + + @warn This is not implemented. + */ +@property (nonatomic, assign) BOOL inherit; + + +/** + The type of the text list. See DTCSSListStyleType for available types + */ +@property (nonatomic, assign) DTCSSListStyleType type; + + +/** + The position of the marker in the prefix. See DTCSSListStylePosition for available positions. + */ +@property (nonatomic, assign) DTCSSListStylePosition position; + + +/** + The image name to use for the marker + */ +@property (nonatomic, copy) NSString *imageName; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCSSListStyle.m b/Pods/DTCoreText/Core/Source/DTCSSListStyle.m new file mode 100644 index 0000000..08c18d9 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSListStyle.m @@ -0,0 +1,466 @@ +// +// DTCSSListStyle.m +// DTCoreText +// +// Created by Oliver Drobnik on 8/11/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCSSListStyle.h" + +#import "DTCoreTextConstants.h" + +#import "NSScanner+HTML.h" +//#import "NSString+HTML.h" + + + + +@interface DTCSSListStyle () + +- (void)updateFromStyleDictionary:(NSDictionary *)styles; + +@property (nonatomic, assign) NSInteger startingItemNumber; + +@end + + + +@implementation DTCSSListStyle +{ + BOOL _inherit; + + DTCSSListStyleType _type; + DTCSSListStylePosition _position; + + NSString *_imageName; + NSInteger _startingItemNumber; +} + +- (id)initWithStyles:(NSDictionary *)styles +{ + self = [super init]; + + if (self) + { + // default + _position = DTCSSListStylePositionOutside; + _startingItemNumber = 1; + + [self updateFromStyleDictionary:styles]; + } + + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self) { + _inherit = [aDecoder decodeBoolForKey:@"inherit"]; + _type = [aDecoder decodeIntegerForKey:@"type"]; + _position = [aDecoder decodeIntegerForKey:@"position"]; + _imageName = [aDecoder decodeObjectForKey:@"imageName"]; + _startingItemNumber = [aDecoder decodeIntegerForKey:@"startingItemNumber"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeBool:_inherit forKey:@"inherit"]; + [aCoder encodeInteger:_type forKey:@"type"]; + [aCoder encodeInteger:_position forKey:@"position"]; + [aCoder encodeObject:_imageName forKey:@"imageName"]; + [aCoder encodeInteger:_startingItemNumber forKey:@"startingItemNumber"]; +} + +// convert string to listStyleType ++ (DTCSSListStyleType)listStyleTypeFromString:(NSString *)string +{ + if (!string) + { + return DTCSSListStyleTypeInvalid; + } + + // always compare lower case + string = [string lowercaseString]; + + if ([string isEqualToString:@"inherit"]) + { + return DTCSSListStyleTypeInherit; + } + else if ([string isEqualToString:@"none"]) + { + return DTCSSListStyleTypeNone; + } + else if ([string isEqualToString:@"circle"]) + { + return DTCSSListStyleTypeCircle; + } + else if ([string isEqualToString:@"square"]) + { + return DTCSSListStyleTypeSquare; + } + else if ([string isEqualToString:@"decimal"]) + { + return DTCSSListStyleTypeDecimal; + } + else if ([string isEqualToString:@"decimal-leading-zero"]) + { + return DTCSSListStyleTypeDecimalLeadingZero; + } + else if ([string isEqualToString:@"disc"]) + { + return DTCSSListStyleTypeDisc; + } + else if ([string isEqualToString:@"upper-alpha"]||[string isEqualToString:@"upper-latin"]) + { + return DTCSSListStyleTypeUpperAlpha; + } + else if ([string isEqualToString:@"lower-alpha"]||[string isEqualToString:@"lower-latin"]) + { + return DTCSSListStyleTypeLowerAlpha; + } + else if ([string isEqualToString:@"plus"]) + { + return DTCSSListStyleTypePlus; + } + else if ([string isEqualToString:@"underscore"]) + { + return DTCSSListStyleTypeUnderscore; + } + else + { + return DTCSSListStyleTypeNone; + } +} + ++ (DTCSSListStylePosition)listStylePositionFromString:(NSString *)string +{ + if (!string) + { + return DTCSSListStylePositionInvalid; + } + + // always compare lower case + string = [string lowercaseString]; + + if ([string isEqualToString:@"inherit"]) + { + return DTCSSListStylePositionInherit; + } + else if ([string isEqualToString:@"inside"]) + { + return DTCSSListStylePositionInside; + } + else if ([string isEqualToString:@"outside"]) + { + return DTCSSListStylePositionOutside; + } + else + { + return DTCSSListStylePositionInherit; + } +} + +// returns NO if not a valid type +- (BOOL)setTypeWithString:(NSString *)string +{ + DTCSSListStyleType type = [DTCSSListStyle listStyleTypeFromString:string]; + if (type == DTCSSListStyleTypeInvalid) + { + return NO; + } + + _type = type; + + return YES; +} + +// returns NO if not a valid type +- (BOOL)setPositionWithString:(NSString *)string +{ + DTCSSListStylePosition position = [DTCSSListStyle listStylePositionFromString:string]; + + if (position == DTCSSListStylePositionInvalid) + { + return NO; + } + + _position = position; + return YES; +} + +- (void)updateFromStyleDictionary:(NSDictionary *)styles +{ + NSString *shortHand = [[styles objectForKey:@"list-style"] lowercaseString]; + + if (shortHand) + { + if ([shortHand isEqualToString:@"inherit"]) + { + _inherit = YES; + return; + } + + NSArray *components = [shortHand componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + BOOL typeWasSet = NO; + BOOL positionWasSet = NO; + + + for (NSString *oneComponent in components) + { + if ([oneComponent hasPrefix:@"url"]) + { + // list-style-image + NSString *urlString; + NSScanner *scanner = [NSScanner scannerWithString:oneComponent]; + + if ([scanner scanCSSURL:&urlString]) + { + self.imageName = urlString; + continue; + } + } + + if (!typeWasSet && [self setTypeWithString:oneComponent]) + { + typeWasSet = YES; + continue; + } + + if (!positionWasSet && [self setPositionWithString:oneComponent]) + { + positionWasSet = YES; + continue; + } + } + + return; + } + + // not a short hand, set from individual types + + [self setTypeWithString:[styles objectForKey:@"list-style-type"]]; + [self setPositionWithString:[styles objectForKey:@"list-style-position"]]; + + NSString *tmpStr = [styles objectForKey:@"list-style-image"]; + + if (tmpStr) + { + // extract just the name + + NSString *urlString; + NSScanner *scanner = [NSScanner scannerWithString:tmpStr]; + + if ([scanner scanCSSURL:&urlString]) + { + self.imageName = urlString; + } + } +} + +#ifndef COVERAGE +// exclude methods from coverage testing + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %p type=%d position=%d>", NSStringFromClass([self class]), self, (int)_type, (int)_position]; +} + +- (NSUInteger)hash +{ + NSUInteger calcHash = 7; + + calcHash = calcHash*31 + [_imageName hash]; + calcHash = calcHash*31 + (NSUInteger)_type; + calcHash = calcHash*31 + (NSUInteger)_position; + calcHash = calcHash*31 + (NSUInteger)_startingItemNumber; + calcHash = calcHash*31 + (NSUInteger)_inherit; + + return calcHash; +} + +#endif + +/* + Note: this is not isEqual: because on iOS 7 -[NSMutableAttributedString initWithString:attributes:] calls this via -[NSArray isEqualToArray:]. There isEqual: needs to be returning NO, because otherwise there is some weird internal caching side effect where it reuses previous list arrays + */ +- (BOOL)isEqualToListStyle:(DTCSSListStyle *)otherListStyle +{ + if (!otherListStyle) + { + return NO; + } + + if (otherListStyle == self) + { + return YES; + } + + if (![otherListStyle isKindOfClass:[DTCSSListStyle class]]) + { + return NO; + } + + if (_inherit != otherListStyle->_inherit) + { + return NO; + } + + if (_type != otherListStyle->_type) + { + return NO; + } + + if (_position != otherListStyle->_position) + { + return NO; + } + + if (_startingItemNumber != otherListStyle->_startingItemNumber) + { + return NO; + } + + if (_imageName == otherListStyle->_imageName) + { + return YES; + } + + return ([_imageName isEqualToString:otherListStyle->_imageName]); +} + +#pragma mark Copying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCSSListStyle *newStyle = [[DTCSSListStyle allocWithZone:zone] init]; + newStyle.type = self.type; + newStyle.position = self.position; + newStyle.imageName = self.imageName; + newStyle.startingItemNumber = self.startingItemNumber; + + return newStyle; +} + +#pragma mark Utilities + +- (NSString *)prefixWithCounter:(NSInteger)counter +{ + NSString *token = nil; + + DTCSSListStyleType listStyleType = _type; + + if (self.imageName) + { + listStyleType = DTCSSListStyleTypeImage; + } + + + switch (listStyleType) + { + case DTCSSListStyleTypeNone: + case DTCSSListStyleTypeInherit: // should never be called with inherit + case DTCSSListStyleTypeInvalid: + { + return nil; + } + case DTCSSListStyleTypeImage: + { + token = UNICODE_OBJECT_PLACEHOLDER; + break; + } + case DTCSSListStyleTypeCircle: + { + token = @"\u25e6"; + break; + } + case DTCSSListStyleTypeSquare: + { + token = @"\u25aa"; + break; + } + case DTCSSListStyleTypeDecimal: + { + token = [NSString stringWithFormat:@"%d.", (int)counter]; + break; + } + case DTCSSListStyleTypeDecimalLeadingZero: + { + token = [NSString stringWithFormat:@"%02d.", (int)counter]; + break; + } + case DTCSSListStyleTypeDisc: + { + token = @"\u2022"; + break; + } + case DTCSSListStyleTypeUpperAlpha: + case DTCSSListStyleTypeUpperLatin: + { + char letter = 'A' + (char)(counter - 1); + token = [NSString stringWithFormat:@"%c.", letter]; + break; + } + case DTCSSListStyleTypeLowerAlpha: + case DTCSSListStyleTypeLowerLatin: + { + char letter = 'a' + (char)(counter - 1); + token = [NSString stringWithFormat:@"%c.", letter]; + break; + } + case DTCSSListStyleTypePlus: + { + token = @"+"; + break; + } + case DTCSSListStyleTypeUnderscore: + { + token = @"_"; + } + } + + if (_position == DTCSSListStylePositionInside) + { + // iOS needs second tab, Mac ignores position outside +#if TARGET_OS_IPHONE + return [NSString stringWithFormat:@"\x09\x09%@", token]; +#else + return [NSString stringWithFormat:@"\x09%@\x09", token]; +#endif + } + else + { + return [NSString stringWithFormat:@"\x09%@\x09", token]; + } +} + +- (BOOL)isOrdered +{ + switch (_type) + { + case DTCSSListStyleTypeDecimal: + case DTCSSListStyleTypeDecimalLeadingZero: + case DTCSSListStyleTypeUpperAlpha: + case DTCSSListStyleTypeUpperLatin: + case DTCSSListStyleTypeLowerAlpha: + case DTCSSListStyleTypeLowerLatin: + return YES; + + default: + return NO; + } +} + +#pragma mark Properties + +@synthesize inherit = _inherit; +@synthesize type = _type; +@synthesize position = _position; +@synthesize imageName = _imageName; +@synthesize startingItemNumber = _startingItemNumber; + +@end + +// TO DO: Implement image diff --git a/Pods/DTCoreText/Core/Source/DTCSSStylesheet.h b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.h new file mode 100644 index 0000000..6cdc6d7 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.h @@ -0,0 +1,86 @@ +// +// DTCSSStylesheet.h +// DTCoreText +// +// Created by Oliver Drobnik on 9/5/11. +// Copyright (c) 2011 Drobnik.com. All rights reserved. +// + +#import + +@class DTHTMLElement; + +/** + This class represents a CSS style sheet used for specifying formatting for certain CSS selectors. + + It supports matching styles by class, by id or by tag name. Hierarchy matching is not supported yet. + */ +@interface DTCSSStylesheet : NSObject + + +/** + @name Creating Stylesheets + */ + +/** + Creates the default stylesheet. + + This stylesheet is based on the standard styles that Webkit provides for these tags. This stylesheet is loaded from an embedded copy of default.css. + */ ++ (DTCSSStylesheet *)defaultStyleSheet; + + +/** + Creates a stylesheet with a given style block + + @param css The CSS string for the style block + */ +- (id)initWithStyleBlock:(NSString *)css; + + +/** + @name Working with CSS Style Blocks + */ + + +/** + Parses a style block string and adds the found style rules to the receiver. + + @param css The CSS string for the style block +*/ +- (void)parseStyleBlock:(NSString *)css; + + +/** + Merges styles from given stylesheet into the receiver + + @param stylesheet the stylesheet to merge + */ +- (void)mergeStylesheet:(DTCSSStylesheet *)stylesheet; + + +/** + @name Accessing Style Information + */ + +/** + Returns a dictionary that contains the merged style for a given element and the applicable style rules from the receiver. + + @param element The HTML element. + @param matchedSelectors The CSS selectors that caused a match + @param ignoreInlineStyle If `YES` then the inline styles of the element will be ignored and only the receiver's styles used + @returns The merged style dictionary containing only styles which selector matches the element + */ +- (NSDictionary *)mergedStyleDictionaryForElement:(DTHTMLElement *)element matchedSelectors:(NSSet * __autoreleasing*)matchedSelectors ignoreInlineStyle:(BOOL)ignoreInlineStyle; + +/** + Returns a dictionary of the styles of the receiver + */ +- (NSDictionary *)styles; + +/** + Returns an ordered (by declaration) set of the selectors for all of the styles. + */ +- (NSArray *)orderedSelectors; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCSSStylesheet.m b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.m new file mode 100644 index 0000000..2080d50 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.m @@ -0,0 +1,953 @@ +// +// DTCSSStylesheet.m +// DTCoreText +// +// Created by Oliver Drobnik on 9/5/11. +// Copyright (c) 2011 Drobnik.com. All rights reserved. +// + +#import "DTCSSStylesheet.h" +#import "DTCSSListStyle.h" + +#import "DTHTMLElement.h" +#import "NSScanner+HTML.h" +#import "NSString+CSS.h" +#import "NSString+HTML.h" + + +// external symbols generated via custom build rule and xxd +extern unsigned char default_css[]; +extern unsigned int default_css_len; + + +@implementation DTCSSStylesheet +{ + NSMutableDictionary *_styles; + NSMutableDictionary *_orderedSelectorWeights; + NSMutableArray *_orderedSelectors; +} + +#pragma mark Creating Stylesheets + ++ (DTCSSStylesheet *)defaultStyleSheet +{ + static DTCSSStylesheet *defaultDTCSSStylesheet = nil; + if (defaultDTCSSStylesheet) + { + return defaultDTCSSStylesheet; + } + + @synchronized(self) + { + if (!defaultDTCSSStylesheet) + { + // get the data from the external symbol + NSData *data = [NSData dataWithBytes:default_css length:default_css_len]; + NSString *cssString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + defaultDTCSSStylesheet = [[DTCSSStylesheet alloc] initWithStyleBlock:cssString]; + } + } + return defaultDTCSSStylesheet; +} + +- (id)initWithStyleBlock:(NSString *)css +{ + self = [super init]; + + if (self) + { + _styles = [[NSMutableDictionary alloc] init]; + _orderedSelectorWeights = [[NSMutableDictionary alloc] init]; + _orderedSelectors = [[NSMutableArray alloc] init]; + + [self parseStyleBlock:css]; + } + + return self; +} + +- (id)initWithStylesheet:(DTCSSStylesheet *)stylesheet +{ + self = [super init]; + + if (self) + { + _styles = [[NSMutableDictionary alloc] init]; + _orderedSelectorWeights = [[NSMutableDictionary alloc] init]; + _orderedSelectors = [[NSMutableArray alloc] init]; + + [self mergeStylesheet:stylesheet]; + } + + return self; +} + +#ifndef COVERAGE + +- (NSString *)description +{ + return [_styles description]; + +} +#endif + +#pragma mark Working with Style Blocks + +- (void)_uncompressShorthands:(NSMutableDictionary *)styles +{ + // list-style shorthand + NSString *shortHand = [[styles objectForKey:@"list-style"] lowercaseString]; + + if (shortHand) + { + [styles removeObjectForKey:@"list-style"]; + + if ([shortHand isEqualToString:@"inherit"]) + { + [styles setObject:@"inherit" forKey:@"list-style-type"]; + [styles setObject:@"inherit" forKey:@"list-style-position"]; + return; + } + + NSArray *components = [shortHand componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + BOOL typeWasSet = NO; + BOOL positionWasSet = NO; + + DTCSSListStyleType listStyleType = DTCSSListStyleTypeNone; + DTCSSListStylePosition listStylePosition = DTCSSListStylePositionInherit; + + for (NSString *oneComponent in components) + { + if ([oneComponent hasPrefix:@"url"]) + { + // list-style-image + NSScanner *scanner = [NSScanner scannerWithString:oneComponent]; + + if ([scanner scanCSSURL:NULL]) + { + [styles setObject:oneComponent forKey:@"list-style-image"]; + + continue; + } + } + + if (!typeWasSet) + { + // check if valid type + listStyleType = [DTCSSListStyle listStyleTypeFromString:oneComponent]; + + if (listStyleType != DTCSSListStyleTypeInvalid) + { + [styles setObject:oneComponent forKey:@"list-style-type"]; + + typeWasSet = YES; + continue; + } + } + + if (!positionWasSet) + { + // check if valid position + listStylePosition = [DTCSSListStyle listStylePositionFromString:oneComponent]; + + if (listStylePosition != DTCSSListStylePositionInvalid) + { + [styles setObject:oneComponent forKey:@"list-style-position"]; + + positionWasSet = YES; + continue; + } + } + } + } + + // font shorthand, see http://www.w3.org/TR/CSS21/fonts.html#font-shorthand + shortHand = [styles objectForKey:@"font"]; + + if (shortHand) + { + NSString *fontStyle = @"normal"; + NSArray *validFontStyles = [NSArray arrayWithObjects:@"italic", @"oblique", nil]; + + NSString *fontVariant = @"normal"; + NSArray *validFontVariants = [NSArray arrayWithObjects:@"small-caps", nil]; + BOOL fontVariantSet = NO; + + NSString *fontWeight = @"normal"; + NSArray *validFontWeights = [NSArray arrayWithObjects:@"bold", @"bolder", @"lighter", @"100", @"200", @"300", @"400", @"500", @"600", @"700", @"800", @"900", nil]; + BOOL fontWeightSet = NO; + + NSString *fontSize = @"normal"; + NSArray *validFontSizes = [NSArray arrayWithObjects:@"xx-small", @"x-small", @"small", @"medium", @"large", @"x-large", @"xx-large", @"larger", @"smaller", nil]; + BOOL fontSizeSet = NO; + + NSArray *suffixesToIgnore = [NSArray arrayWithObjects:@"caption", @"icon", @"menu", @"message-box", @"small-caption", @"status-bar", @"inherit", nil]; + + NSString *lineHeight = @"normal"; + + NSMutableString *fontFamily = [NSMutableString string]; + + NSArray *components = [shortHand componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + for (NSString *oneComponent in components) + { + // try font size keywords + if ([validFontSizes containsObject:oneComponent]) + { + fontSize = oneComponent; + fontSizeSet = YES; + + continue; + } + + NSInteger slashIndex = [oneComponent rangeOfString:@"/"].location; + + if (slashIndex != NSNotFound) + { + // font-size / line-height + + fontSize = [oneComponent substringToIndex:slashIndex]; + fontSizeSet = YES; + + lineHeight = [oneComponent substringFromIndex:slashIndex+1]; + + continue; + } + else + { + // length + if ([oneComponent hasSuffix:@"%"] || [oneComponent hasSuffix:@"em"] || [oneComponent hasSuffix:@"px"] || [oneComponent hasSuffix:@"pt"]) + { + fontSize = oneComponent; + fontSizeSet = YES; + + continue; + } + } + + if (fontSizeSet) + { + if ([suffixesToIgnore containsObject:oneComponent]) + { + break; + } + + // assume that this is part of font family + if ([fontFamily length]) + { + [fontFamily appendString:@" "]; + } + + [fontFamily appendString:oneComponent]; + } + else + { + if (!fontWeightSet && [validFontStyles containsObject:oneComponent]) + { + fontStyle = oneComponent; + } + else if (!fontVariantSet && [validFontVariants containsObject:oneComponent]) + { + fontVariant = oneComponent; + fontVariantSet = YES; + } + else if (!fontWeightSet && [validFontWeights containsObject:oneComponent]) + { + fontWeight = oneComponent; + fontWeightSet = YES; + } + } + } + + [styles removeObjectForKey:@"font"]; + + // size and family are mandatory, without them this is invalid + if ([fontSize length] && [fontFamily length]) + { + [styles setObject:fontStyle forKey:@"font-style"]; + [styles setObject:fontWeight forKey:@"font-weight"]; + [styles setObject:fontVariant forKey:@"font-variant"]; + [styles setObject:fontSize forKey:@"font-size"]; + [styles setObject:lineHeight forKey:@"line-height"]; + [styles setObject:fontFamily forKey:@"font-family"]; + } + } + + shortHand = [styles objectForKey:@"margin"]; + + if (shortHand) + { + NSArray *parts = [shortHand componentsSeparatedByString:@" "]; + + NSString *topMargin; + NSString *rightMargin; + NSString *bottomMargin; + NSString *leftMargin; + + if ([parts count] == 4) + { + topMargin = [parts objectAtIndex:0]; + rightMargin = [parts objectAtIndex:1]; + bottomMargin = [parts objectAtIndex:2]; + leftMargin = [parts objectAtIndex:3]; + } + else if ([parts count] == 3) + { + topMargin = [parts objectAtIndex:0]; + rightMargin = [parts objectAtIndex:1]; + bottomMargin = [parts objectAtIndex:2]; + leftMargin = [parts objectAtIndex:1]; + } + else if ([parts count] == 2) + { + topMargin = [parts objectAtIndex:0]; + rightMargin = [parts objectAtIndex:1]; + bottomMargin = [parts objectAtIndex:0]; + leftMargin = [parts objectAtIndex:1]; + } + else + { + NSString *onlyValue = [parts objectAtIndex:0]; + + topMargin = onlyValue; + rightMargin = onlyValue; + bottomMargin = onlyValue; + leftMargin = onlyValue; + } + + // only apply the ones where there is no previous direct setting + + if (![styles objectForKey:@"margin-top"]) + { + [styles setObject:topMargin forKey:@"margin-top"]; + } + + if (![styles objectForKey:@"margin-right"]) + { + [styles setObject:rightMargin forKey:@"margin-right"]; + } + + if (![styles objectForKey:@"margin-bottom"]) + { + [styles setObject:bottomMargin forKey:@"margin-bottom"]; + } + + if (![styles objectForKey:@"margin-left"]) + { + [styles setObject:leftMargin forKey:@"margin-left"]; + } + + // remove the shorthand + [styles removeObjectForKey:@"margin"]; + } + + shortHand = [styles objectForKey:@"padding"]; + + if (shortHand) + { + NSArray *parts = [shortHand componentsSeparatedByString:@" "]; + + NSString *topPadding; + NSString *rightPadding; + NSString *bottomPadding; + NSString *leftPadding; + + if ([parts count] == 4) + { + topPadding = [parts objectAtIndex:0]; + rightPadding = [parts objectAtIndex:1]; + bottomPadding = [parts objectAtIndex:2]; + leftPadding = [parts objectAtIndex:3]; + } + else if ([parts count] == 3) + { + topPadding = [parts objectAtIndex:0]; + rightPadding = [parts objectAtIndex:1]; + bottomPadding = [parts objectAtIndex:2]; + leftPadding = [parts objectAtIndex:1]; + } + else if ([parts count] == 2) + { + topPadding = [parts objectAtIndex:0]; + rightPadding = [parts objectAtIndex:1]; + bottomPadding = [parts objectAtIndex:0]; + leftPadding = [parts objectAtIndex:1]; + } + else + { + NSString *onlyValue = [parts objectAtIndex:0]; + + topPadding = onlyValue; + rightPadding = onlyValue; + bottomPadding = onlyValue; + leftPadding = onlyValue; + } + + // only apply the ones where there is no previous direct setting + + if (![styles objectForKey:@"padding-top"]) + { + [styles setObject:topPadding forKey:@"padding-top"]; + } + + if (![styles objectForKey:@"padding-right"]) + { + [styles setObject:rightPadding forKey:@"padding-right"]; + } + + if (![styles objectForKey:@"padding-bottom"]) + { + [styles setObject:bottomPadding forKey:@"padding-bottom"]; + } + + if (![styles objectForKey:@"padding-left"]) + { + [styles setObject:leftPadding forKey:@"padding-left"]; + } + + // remove the shorthand + [styles removeObjectForKey:@"padding"]; + } + + shortHand = [styles objectForKey:@"background"]; + + if (shortHand) + { + // ignore most tokens except background-color + + [styles removeObjectForKey:@"background"]; + + NSCharacterSet *tokenDelimiters = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + NSString *trimmedString = [shortHand stringByTrimmingCharactersInSet:tokenDelimiters]; + NSScanner *scanner = [NSScanner scannerWithString:trimmedString]; + + while (![scanner isAtEnd]) + { + NSString *colorName; + if ([scanner scanHTMLColor:NULL HTMLName:&colorName]) + { + [styles setObject:colorName forKey:@"background-color"]; + break; + } + [scanner scanUpToCharactersFromSet:tokenDelimiters intoString:NULL]; + } + } +} + +- (void)_addStyleRule:(NSString *)rule withSelector:(NSString*)selectors +{ + NSArray *split = [selectors componentsSeparatedByString:@","]; + + for (NSString *selector in split) + { + NSString *cleanSelector = [selector stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + NSMutableDictionary *ruleDictionary = [[rule dictionaryOfCSSStyles] mutableCopy]; + + // remove !important, we're ignoring these + for (NSString *oneKey in [ruleDictionary allKeys]) + { + id value = [ruleDictionary objectForKey:oneKey]; + if ([value isKindOfClass:[NSString class]]) + { + NSRange rangeOfImportant = [value rangeOfString:@"!important" options:NSCaseInsensitiveSearch]; + + if (rangeOfImportant.location != NSNotFound) + { + value = [value stringByReplacingCharactersInRange:rangeOfImportant withString:@""]; + value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + [ruleDictionary setObject:value forKey:oneKey]; + } + + } else if ([value isKindOfClass:[NSArray class]]) + { + NSMutableArray *newVal; + + for (NSUInteger i = 0; i < [(NSArray*)value count]; ++i) + { + NSString *s = [value objectAtIndex:i]; + + NSRange rangeOfImportant = [s rangeOfString:@"!important" options:NSCaseInsensitiveSearch]; + + if (rangeOfImportant.location != NSNotFound) + { + s = [s stringByReplacingCharactersInRange:rangeOfImportant withString:@""]; + s = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + if (!newVal) + { + if ([value isKindOfClass:[NSMutableArray class]]) + { + newVal = value; + } else + { + newVal = [value mutableCopy]; + } + } + + // replace the value that had !important with a version without it + [newVal replaceObjectAtIndex:i withObject:s]; + } + } + + if (newVal) + { + [ruleDictionary setObject:newVal forKey:oneKey]; + } + } + } + + // need to uncompress because otherwise we might get shorthands and non-shorthands together + [self _uncompressShorthands:ruleDictionary]; + + // check if there is a pseudo selector + NSRange colonRange = [cleanSelector rangeOfString:@":"]; + NSString *pseudoSelector = nil; + + if (colonRange.length==1) + { + pseudoSelector = [cleanSelector substringFromIndex:colonRange.location+1]; + cleanSelector = [cleanSelector substringToIndex:colonRange.location]; + + // prefix all rules with the pseudo-selector + for (NSString *oneRuleKey in [ruleDictionary allKeys]) + { + id value = [ruleDictionary objectForKey:oneRuleKey]; + + // prefix key with the pseudo selector + NSString *prefixedKey = [NSString stringWithFormat:@"%@:%@", pseudoSelector, oneRuleKey]; + [ruleDictionary setObject:value forKey:prefixedKey]; + [ruleDictionary removeObjectForKey:oneRuleKey]; + } + } + + NSDictionary *existingRulesForSelector = [_styles objectForKey:cleanSelector]; + + if (existingRulesForSelector) + { + // substitute new rules over old ones + NSMutableDictionary *tmpDict = [existingRulesForSelector mutableCopy]; + + // append new rules + [tmpDict addEntriesFromDictionary:ruleDictionary]; + + // save it + [self _addStyles:tmpDict withSelector:cleanSelector]; + } + else + { + [self _addStyles:ruleDictionary withSelector:cleanSelector]; + } + } +} + + +- (void)parseStyleBlock:(NSString*)css +{ + NSUInteger braceLevel = 0, braceMarker = 0; + + NSString* selector; + + NSUInteger length = [css length]; + + for (NSUInteger i = 0; i < length; i++) + { + unichar c = [css characterAtIndex:i]; + + if (c == '/') + { + i++; + + if (i < length) + { + c = [css characterAtIndex:i]; + + if (c == '*') + { + // skip comment until closing / + + for (; i < length; i++) + { + if ([css characterAtIndex:i] == '/') + { + break; + } + } + + if (i < length) + { + braceMarker = i+1; + continue; + } + else + { + // end of string + return; + } + } + else + { + // not a comment + i--; + } + } + } + + // An opening brace! It could be the start of a new rule, or it could be a nested brace. + if (c == '{') + { + // If we start a new rule... + + if (braceLevel == 0) + { + // Grab the selector and clean up extraneous spaces (we'll process it in a moment) + selector = [css substringWithRange:NSMakeRange(braceMarker, i-braceMarker)]; + NSArray *selectorParts = [selector componentsSeparatedByString:@" "]; + NSMutableArray *cleanSelectorParts = [NSMutableArray array]; + for (NSString *partialSelector in selectorParts) + { + if (partialSelector.length) + { + [cleanSelectorParts addObject:partialSelector]; + } + } + selector = [cleanSelectorParts componentsJoinedByString:@" "]; + + // And mark our position so we can grab the rule's CSS when it is closed + braceMarker = i + 1; + } + + // Increase the brace level. + braceLevel += 1; + } + + // A closing brace! + else if (c == '}') + { + // If we finished a rule... + if (braceLevel == 1) + { + NSString *rule = [css substringWithRange:NSMakeRange(braceMarker, i-braceMarker)]; + + [self _addStyleRule:rule withSelector: selector]; + + braceMarker = i + 1; + } + + braceLevel = MAX(braceLevel-1, 0ul); + } + } +} + + +- (void)mergeStylesheet:(DTCSSStylesheet *)stylesheet +{ + NSArray *otherStylesheetStyleKeys = stylesheet.orderedSelectors; + + for (NSString *oneKey in otherStylesheetStyleKeys) + { + NSDictionary *existingStyles = [_styles objectForKey:oneKey]; + NSDictionary *stylesToMerge = [[stylesheet styles] objectForKey:oneKey]; + if (existingStyles) + { + NSMutableDictionary *mutableStyles = [existingStyles mutableCopy]; + + for (NSString *oneStyleKey in stylesToMerge) + { + NSString *mergingStyleString = [stylesToMerge objectForKey:oneStyleKey]; + + [mutableStyles setObject:mergingStyleString forKey:oneStyleKey]; + } + + [self _addStyles:mutableStyles withSelector:oneKey]; + } + else + { + // nothing to worry + [self _addStyles:stylesToMerge withSelector:oneKey]; + } + } +} + +- (void)_addStyles:(NSDictionary *)styles withSelector:(NSString *)selector { + [_styles setObject:styles forKey:selector]; + + if (![_orderedSelectors containsObject:selector]) + { + [_orderedSelectors addObject:selector]; + [_orderedSelectorWeights setObject:@([self _weightForSelector:selector]) forKey:selector]; + } +} + +#pragma mark Accessing Style Information + +- (NSDictionary *)mergedStyleDictionaryForElement:(DTHTMLElement *)element matchedSelectors:(NSSet * __autoreleasing*)matchedSelectors ignoreInlineStyle:(BOOL)ignoreInlineStyle +{ + // We are going to combine all the relevant styles for this tag. + // (Note that when styles are applied, the later styles take precedence, + // so the order in which we grab them matters!) + + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + // Get based on element + NSDictionary *byTagName = [self.styles objectForKey:element.name]; + + if (byTagName) + { + [tmpDict addEntriesFromDictionary:byTagName]; + } + + // Get based on class(es) + NSString *classString = [element.attributes objectForKey:@"class"]; + NSArray *classes = [classString componentsSeparatedByString:@" "]; + + // Cascaded selectors with more than one part are sorted by specificity + NSMutableArray *matchingCascadingSelectors = [self matchingComplexCascadingSelectorsForElement:element]; + [matchingCascadingSelectors sortUsingComparator:^NSComparisonResult(NSString *selector1, NSString *selector2) + { + NSInteger weightForSelector1 = [[_orderedSelectorWeights objectForKey:selector1] integerValue]; + NSInteger weightForSelector2 = [[_orderedSelectorWeights objectForKey:selector2] integerValue]; + + if (weightForSelector1 == weightForSelector2) + { + weightForSelector1 += [_orderedSelectors indexOfObject:selector1]; + weightForSelector2 += [_orderedSelectors indexOfObject:selector2]; + } + + if (weightForSelector1 > weightForSelector2) + { + return (NSComparisonResult)NSOrderedDescending; + } + + if (weightForSelector1 < weightForSelector2) + { + return (NSComparisonResult)NSOrderedAscending; + } + + return (NSComparisonResult)NSOrderedSame; + }]; + + NSMutableSet *tmpMatchedSelectors; + + if (matchedSelectors) + { + tmpMatchedSelectors = [NSMutableSet set]; + } + + // Apply complex cascading selectors first, then apply most specific selectors + for (NSString *cascadingSelector in matchingCascadingSelectors) + { + NSDictionary *byCascadingSelector = [_styles objectForKey:cascadingSelector]; + [tmpDict addEntriesFromDictionary:byCascadingSelector]; + [tmpMatchedSelectors addObject:cascadingSelector]; + } + + // Applied the parameter element's classes last + for (NSString *class in classes) + { + NSString *classRule = [NSString stringWithFormat:@".%@", class]; + NSDictionary *byClass = [_styles objectForKey: classRule]; + + if (byClass) + { + [tmpDict addEntriesFromDictionary:byClass]; + [tmpMatchedSelectors addObject:class]; + } + + NSString *classAndTagRule = [NSString stringWithFormat:@"%@.%@", element.name, class]; + NSDictionary *byClassAndName = [_styles objectForKey:classAndTagRule]; + + if (byClassAndName) + { + [tmpDict addEntriesFromDictionary:byClassAndName]; + [tmpMatchedSelectors addObject:classAndTagRule]; + } + } + + // Get based on id + NSString *idRule = [NSString stringWithFormat:@"#%@", [element.attributes objectForKey:@"id"]]; + NSDictionary *byID = [_styles objectForKey:idRule]; + + if (byID) + { + [tmpDict addEntriesFromDictionary:byID]; + [tmpMatchedSelectors addObject:idRule]; + } + + if (!ignoreInlineStyle) + { + // Get tag's local style attribute + NSString *styleString = [element.attributes objectForKey:@"style"]; + + if ([styleString length]) + { + NSMutableDictionary *localStyles = [[styleString dictionaryOfCSSStyles] mutableCopy]; + + // need to uncompress because otherwise we might get shorthands and non-shorthands together + [self _uncompressShorthands:localStyles]; + + [tmpDict addEntriesFromDictionary:localStyles]; + } + } + + if ([tmpDict count]) + { + if (matchedSelectors && [tmpMatchedSelectors count]) + { + *matchedSelectors = [tmpMatchedSelectors copy]; + } + + return tmpDict; + } + else + { + return nil; + } +} + +- (NSDictionary *)styles +{ + return _styles; +} + +- (NSArray *)orderedSelectors +{ + return _orderedSelectors; +} + +// This looks for cascaded selectors with more than one part to them +- (NSMutableArray *)matchingComplexCascadingSelectorsForElement:(DTHTMLElement *)element +{ + __block NSMutableArray *matchedSelectors = [NSMutableArray array]; + + for (NSString *selector in _orderedSelectors) + { + NSArray *selectorParts = [selector componentsSeparatedByString:@" "]; + + // We only process the selector if our selector has more than 1 part to it (e.g. ".foo" would be skipped and ".foo .bar" would not) + if (selectorParts.count < 2) + { + continue; + } + + DTHTMLElement *nextElement = element; + + // Walking up the hierarchy so start at the right side of the selector and work to the left + // Aside: Manual for loop here is faster than for in with reverseObjectEnumerator + for (NSUInteger j = selectorParts.count; j-- > 0;) + { + NSString *selectorPart = [selectorParts objectAtIndex:j]; + BOOL matched = NO; + + if (selectorPart.length) + { + while (nextElement != nil) + { + DTHTMLElement *currentElement = nextElement; + + //This must be set to advance here, above all of the breaks, so the loop properly advances. + nextElement = currentElement.parentElement; + + if ([selectorPart characterAtIndex:0] == '#') + { + // If we're at an id and it doesn't match the current element then the style doesn't apply + NSString *currentElementId = [currentElement.attributes objectForKey:@"id"]; + if (currentElementId && [[selectorPart substringFromIndex:1] isEqualToString:currentElementId]) + { + matched = YES; + break; + } + } else if ([selectorPart characterAtIndex:0] == '.') + { + NSString *currentElementClassesString = [currentElement.attributes objectForKey:@"class"]; + NSArray *currentElementClasses = [currentElementClassesString componentsSeparatedByString:@" "]; + for (NSString *currentElementClass in currentElementClasses) + { + if ([currentElementClass isEqualToString:[selectorPart substringFromIndex:1]]) + { + matched = YES; + break; + } + } + + if (matched) + { + break; + } + } else if ([selectorPart isEqualToString:currentElement.name] && (selectorParts.count > 1)) + { + // This condition depends on the "if (selectorParts.count < 2)" conditional above. If that's removed, we must make sure selectorParts + // contains > 1 item for this to be matched (we want the element name alone to be matched last). + matched = YES; + break; + } + + // break if the right most portion of the selector doesn't match the target element + if (!matched && ([currentElement isEqual:element])) { + break; + } + } + } + + if (!matched) + { + break; + } + + //Only match if we really are on the last part of the selector and all other parts have matched so far + if (j == 0) + { + if (matched && ![matchedSelectors containsObject:selector]) + { + [matchedSelectors addObject:selector]; + } + } + } + } + + return matchedSelectors; +} + +// This computes the specificity for a given selector +- (NSUInteger)_weightForSelector:(NSString *)selector { + if ((selector == nil) || (selector.length == 0)) + { + return 0; + } + + NSUInteger weight = 0; + + NSArray *selectorParts = [selector componentsSeparatedByString:@" "]; + for (NSString *selectorPart in selectorParts) + { + if (selectorPart.length == 0) { + continue; + } + + if ([selectorPart characterAtIndex:0] == '#') + { + weight += 100; + } else if ([selectorPart characterAtIndex:0] == '.') + { + weight += 10; + } else { + weight += 1; + } + } + + return weight; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCSSStylesheet *newStylesheet = [[DTCSSStylesheet allocWithZone:zone] initWithStylesheet:self]; + + return newStylesheet; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTColor+Compatibility.h b/Pods/DTCoreText/Core/Source/DTColor+Compatibility.h new file mode 100755 index 0000000..f792318 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTColor+Compatibility.h @@ -0,0 +1,58 @@ +// +// DTColor+Compatibility.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import + +#if TARGET_OS_IPHONE + +/** + Implementations of methods on NSColor/UIColor which are missing on the other platform. + */ +@interface UIColor (HTML) + + +/** + A quick method to return the alpha component of this UIColor by using the CGColorGetAlpha method. + @returns The floating point alpha value of this UIColor. + */ +- (CGFloat)alphaComponent; + +@end + +#else + +/** + Methods used to work with HTML representations of colors. + */ +@interface NSColor (HTML) + + +/** + Return a string hexadecimal representation of this NSColor. Splits the color into components with CGColor methods, re-maps them from percentages in the range 0-255, and returns the RGB color (alpha is stripped) in a six character string. + @returns A CSS hexadecimal NSString specifying this NSColor. + */ +//- (NSString *)htmlHexString; + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 +/** + Converts a CGColorRef into an NSColor by placing each component into an NSColor and pending on the component count to return a grayscale or rgb color. If there are not 2 (grayscale) or 4 (rgba) components the color is from an unsupported color space and nil is returned. + @param cgColor The CGColorRef to convert + @returns An NSColor of this CGColorRef + */ ++ (NSColor *)colorWithCGColor:(CGColorRef)cgColor; + +/** + Converts an NSColor into a CGColorRef. + @returns A CGColorRef of this NSColor +*/ +- (CGColorRef)CGColor DT_RETURNS_INNER_POINTER; +#endif + +@end + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTColor+Compatibility.m b/Pods/DTCoreText/Core/Source/DTColor+Compatibility.m new file mode 100644 index 0000000..692503c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTColor+Compatibility.m @@ -0,0 +1,121 @@ +// +// DTColor+Compatibility.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTColor+Compatibility.h" +#import "DTColorFunctions.h" + +#if TARGET_OS_IPHONE + +@implementation UIColor (HTML) + +- (CGFloat)alphaComponent +{ + return CGColorGetAlpha(self.CGColor); +} + +@end + +#else + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 || MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7 +#import "DTCoreTextMacros.h" +#import + +static void* DTCoreTextCGColorKey = &DTCoreTextCGColorKey; +#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 || MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7 + + +#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7 +@interface NSColor (DTCoreText) ++ (NSColor *)DTCoreText_colorWithCGColor:(CGColorRef)cgColor; +- (CGColorRef)DTCoreText_CGColor DT_RETURNS_INNER_POINTER; +@end + +static void DTCoreTextAddMissingSelector(Class aClass, SEL aSelector, SEL implementationSelector) +{ + Method method = class_getInstanceMethod(aClass, aSelector); + if (method == NULL) { + method = class_getInstanceMethod(aClass, implementationSelector); + NSCAssert(method != NULL, @"missing implementation method"); + + IMP methodImplementation = method_getImplementation(method); + const char *methodTypeEncoding = method_getTypeEncoding(method); + +#if !defined(NS_BLOCK_ASSERTIONS) + BOOL rc = class_addMethod(aClass, aSelector, methodImplementation, methodTypeEncoding); + NSCAssert(rc, @"failed to add missing method"); +#else + (void) class_addMethod(aClass, aSelector, methodImplementation, methodTypeEncoding); +#endif + } +} + +__attribute__((constructor)) +static void DTCoreTextNSColorInitialization(void) +{ + Class NSColorClass = objc_getClass("NSColor"); + Class NSColorMetaClass = object_getClass(NSColorClass); + DTCoreTextAddMissingSelector(NSColorMetaClass, @selector(colorWithCGColor:), @selector(DTCoreText_colorWithCGColor:)); + DTCoreTextAddMissingSelector(NSColorClass, @selector(CGColor), @selector(DTCoreText_CGColor)); +} + +#define colorWithCGColor DTCoreText_colorWithCGColor +#define CGColor DTCoreText_CGColor +#define HTML DTCoreText +#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7 + +@implementation NSColor (HTML) + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 || MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7 ++ (NSColor *)colorWithCGColor:(CGColorRef)cgColor +{ + size_t count = CGColorGetNumberOfComponents(cgColor); + const CGFloat *components = CGColorGetComponents(cgColor); + + // Grayscale + if (count == 2) + { + return [NSColor colorWithDeviceWhite:components[0] alpha:components[1]]; + } + + // RGB + else if (count == 4) + { + return [NSColor colorWithDeviceRed:components[0] green:components[1] blue:components[2] alpha:components[3]]; + } + + // neigher grayscale nor rgba + return nil; +} + +// From https://gist.github.com/1593255 +- (CGColorRef)CGColor +{ + CGColorRef color = (__bridge CGColorRef)objc_getAssociatedObject(self, DTCoreTextCGColorKey); + if (color == NULL) + { + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + NSColor *selfCopy = [self colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + + CGFloat colorValues[4]; + [selfCopy getRed:&colorValues[0] green:&colorValues[1] blue:&colorValues[2] alpha:&colorValues[3]]; + + color = CGColorCreate(colorSpace, colorValues); + CGColorSpaceRelease(colorSpace); + + objc_setAssociatedObject(self, DTCoreTextCGColorKey, CFBridgingRelease(color), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + return color; +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 || MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7 + +@end + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTColorFunctions.h b/Pods/DTCoreText/Core/Source/DTColorFunctions.h new file mode 100644 index 0000000..2f507d5 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTColorFunctions.h @@ -0,0 +1,34 @@ +// +// DTColorFunctions.h +// DTCoreText +// +// Created by Oliver Drobnik on 9/9/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +@class DTColor; + +/** + Takes a CSS color string ('333', 'F9FFF9'), determines the RGB values used, and returns a UIColor object of that color. + For each part of the RGB color those numbers for that color are converted to a number using a category on NSString. Then that number is divided by the maximum value, 15 for 3 character strings and 255 for 6 character strings, making the color a percentage and within the range 0.0 and 1.0 that UIColor uses. + @param hexString A CSS hexadecimal color string of length 6 or 3. + @returns A UIColor object generated from the hexadecimal color string with alpha 1.0. + */ +DTColor *DTColorCreateWithHexString(NSString *hexString); + + +/** + Takes an English string representing a color and maps it to a numeric RGB value as declared by the HTML and CSS specifications (see http://www.w3schools.com/html/html_colornames.asp). Also accepts CSS `#` hexadecimal colors, `rgba`, and `rgb` and does the right thing returning a corresponding UIColor. + If a color begins with a `#` we know that it is a hexadecimal color and send it to colorWithHexString:. If the string is an `rgba()` color declaration the comma delimited r, g, b, and a values are made into percentages and then made into a UIColor which is returned. If the string is an `rgb()` color declaration the same process happens except with an alpha of 1.0. + The last case is that the color string is not a numeric declaration `#`, nor a `rgba` or `rgb` declaration so the CSS color value matching the English string is found in a lookup dictionary and then passed to colorWithHexString: which will make a UIColor out of the hexadecimal string. + @param name The CSS color string that we want to map from a name into an RGB color. + @returns A UIColor object representing the name parameter as numeric values declared by the HTML and CSS specifications, a `rgba()` color, or a `rgb()` color. + */ +DTColor *DTColorCreateWithHTMLName(NSString *name); + + +/** + Return a string hexadecimal representation of this UIColor. Splits the color into components with CGColor methods, re-maps them from percentages to the range 0-255, and depending on the number of components returns a grayscale (repeating string of two characters) or color RGB (alpha is stripped) six character string. In the event of a non-2 or non-4 component color nil is returned as it is from an unsupported color space. + @returns A CSS hexadecimal NSString specifying this UIColor. + */ +NSString *DTHexStringFromDTColor(DTColor *color); diff --git a/Pods/DTCoreText/Core/Source/DTColorFunctions.m b/Pods/DTCoreText/Core/Source/DTColorFunctions.m new file mode 100644 index 0000000..c5eb951 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTColorFunctions.m @@ -0,0 +1,305 @@ +// +// DTColorFunctions.m +// DTCoreText +// +// Created by Oliver Drobnik on 9/9/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCompatibility.h" +#import "DTColorFunctions.h" + + +static NSDictionary *colorLookup = nil; + +#pragma mark - Private Functions + +NSUInteger _integerValueFromHexString(NSString *hexString); + +#pragma mark - Implementations + +NSUInteger _integerValueFromHexString(NSString *hexString) +{ + int result = 0; + sscanf([hexString UTF8String], "%x", &result); + return result; +} + +//- (BOOL)isNumeric +//{ +// const char *s = [self UTF8String]; +// +// for (size_t i=0;i'9') && (s[i] != '.')) +// { +// return NO; +// } +// } +// +// return YES; +//} + + +DTColor *DTColorCreateWithHexString(NSString *hexString) +{ + if ([hexString length]!=6 && [hexString length]!=3) + { + return nil; + } + + NSUInteger digits = [hexString length]/3; + CGFloat maxValue = (digits==1)?15.0:255.0; + + NSUInteger redValue = _integerValueFromHexString([hexString substringWithRange:NSMakeRange(0, digits)]); + NSUInteger greenValue = _integerValueFromHexString([hexString substringWithRange:NSMakeRange(digits, digits)]); + NSUInteger blueValue = _integerValueFromHexString([hexString substringWithRange:NSMakeRange(2*digits, digits)]); + + CGFloat red = redValue/maxValue; + CGFloat green = greenValue/maxValue; + CGFloat blue = blueValue/maxValue; + +#if TARGET_OS_IPHONE + return [DTColor colorWithRed:red green:green blue:blue alpha:1.0]; +#else + return (DTColor *)[NSColor colorWithDeviceRed:red green:green blue:blue alpha:1.0]; +#endif +} + +DTColor *DTColorCreateWithHTMLName(NSString *name) +{ + if ([name hasPrefix:@"#"]) + { + return DTColorCreateWithHexString([name substringFromIndex:1]); + } + + if ([name hasPrefix:@"rgba"]) + { + NSString *rgbaName = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"rgba() "]]; + NSArray *rgba = [rgbaName componentsSeparatedByString:@","]; + + if ([rgba count] != 4) + { + // Incorrect syntax + return nil; + } + + CGFloat red = (CGFloat)[[rgba objectAtIndex:0] floatValue] / 255; + CGFloat green = [[rgba objectAtIndex:1] floatValue] / 255; + CGFloat blue = [[rgba objectAtIndex:2] floatValue] / 255; + CGFloat alpha = [[rgba objectAtIndex:3] floatValue]; + +#if TARGET_OS_IPHONE + return [DTColor colorWithRed:red green:green blue:blue alpha:alpha]; +#else + return (DTColor *)[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha]; +#endif + } + + if([name hasPrefix:@"rgb"]) + { + NSString * rgbName = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"rbg() "]]; + NSArray* rgb = [rgbName componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]]; + + if ([rgb count] != 3) + { + // Incorrect syntax + return nil; + } + + CGFloat red = [[rgb objectAtIndex:0] floatValue] / 255; + CGFloat green = [[rgb objectAtIndex:1] floatValue] / 255; + CGFloat blue = [[rgb objectAtIndex:2] floatValue] / 255; + CGFloat alpha = 1.0; + +#if TARGET_OS_IPHONE + return [DTColor colorWithRed:red green:green blue:blue alpha:alpha]; +#else + return (DTColor *)[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha]; +#endif + } + + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + colorLookup = [[NSDictionary alloc] initWithObjectsAndKeys: + @"F0F8FF", @"aliceblue", + @"FAEBD7", @"antiquewhite", + @"00FFFF", @"aqua", + @"7FFFD4", @"aquamarine", + @"F0FFFF", @"azure", + @"F5F5DC", @"beige", + @"FFE4C4", @"bisque", + @"000000", @"black", + @"FFEBCD", @"blanchedalmond", + @"0000FF", @"blue", + @"8A2BE2", @"blueviolet", + @"A52A2A", @"brown", + @"DEB887", @"burlywood", + @"5F9EA0", @"cadetblue", + @"7FFF00", @"chartreuse", + @"D2691E", @"chocolate", + @"FF7F50", @"coral", + @"6495ED", @"cornflowerblue", + @"FFF8DC", @"cornsilk", + @"DC143C", @"crimson", + @"00FFFF", @"cyan", + @"00008B", @"darkblue", + @"008B8B", @"darkcyan", + @"B8860B", @"darkgoldenrod", + @"A9A9A9", @"darkgray", + @"A9A9A9", @"darkgrey", + @"006400", @"darkgreen", + @"BDB76B", @"darkkhaki", + @"8B008B", @"darkmagenta", + @"556B2F", @"darkolivegreen", + @"FF8C00", @"darkorange", + @"9932CC", @"darkorchid", + @"8B0000", @"darkred", + @"E9967A", @"darksalmon", + @"8FBC8F", @"darkseagreen", + @"483D8B", @"darkslateblue", + @"2F4F4F", @"darkslategray", + @"2F4F4F", @"darkslategrey", + @"00CED1", @"darkturquoise", + @"9400D3", @"darkviolet", + @"FF1493", @"deeppink", + @"00BFFF", @"deepskyblue", + @"696969", @"dimgray", + @"696969", @"dimgrey", + @"1E90FF", @"dodgerblue", + @"B22222", @"firebrick", + @"FFFAF0", @"floralwhite", + @"228B22", @"forestgreen", + @"FF00FF", @"fuchsia", + @"DCDCDC", @"gainsboro", + @"F8F8FF", @"ghostwhite", + @"FFD700", @"gold", + @"DAA520", @"goldenrod", + @"808080", @"gray", + @"808080", @"grey", + @"008000", @"green", + @"ADFF2F", @"greenyellow", + @"F0FFF0", @"honeydew", + @"FF69B4", @"hotpink", + @"CD5C5C", @"indianred", + @"4B0082", @"indigo", + @"FFFFF0", @"ivory", + @"F0E68C", @"khaki", + @"E6E6FA", @"lavender", + @"FFF0F5", @"lavenderblush", + @"7CFC00", @"lawngreen", + @"FFFACD", @"lemonchiffon", + @"ADD8E6", @"lightblue", + @"F08080", @"lightcoral", + @"E0FFFF", @"lightcyan", + @"FAFAD2", @"lightgoldenrodyellow", + @"D3D3D3", @"lightgray", + @"D3D3D3", @"lightgrey", + @"90EE90", @"lightgreen", + @"FFB6C1", @"lightpink", + @"FFA07A", @"lightsalmon", + @"20B2AA", @"lightseagreen", + @"87CEFA", @"lightskyblue", + @"778899", @"lightslategray", + @"778899", @"lightslategrey", + @"B0C4DE", @"lightsteelblue", + @"FFFFE0", @"lightyellow", + @"00FF00", @"lime", + @"32CD32", @"limegreen", + @"FAF0E6", @"linen", + @"FF00FF", @"magenta", + @"800000", @"maroon", + @"66CDAA", @"mediumaquamarine", + @"0000CD", @"mediumblue", + @"BA55D3", @"mediumorchid", + @"9370D8", @"mediumpurple", + @"3CB371", @"mediumseagreen", + @"7B68EE", @"mediumslateblue", + @"00FA9A", @"mediumspringgreen", + @"48D1CC", @"mediumturquoise", + @"C71585", @"mediumvioletred", + @"191970", @"midnightblue", + @"F5FFFA", @"mintcream", + @"FFE4E1", @"mistyrose", + @"FFE4B5", @"moccasin", + @"FFDEAD", @"navajowhite", + @"000080", @"navy", + @"FDF5E6", @"oldlace", + @"808000", @"olive", + @"6B8E23", @"olivedrab", + @"FFA500", @"orange", + @"FF4500", @"orangered", + @"DA70D6", @"orchid", + @"EEE8AA", @"palegoldenrod", + @"98FB98", @"palegreen", + @"AFEEEE", @"paleturquoise", + @"D87093", @"palevioletred", + @"FFEFD5", @"papayawhip", + @"FFDAB9", @"peachpuff", + @"CD853F", @"peru", + @"FFC0CB", @"pink", + @"DDA0DD", @"plum", + @"B0E0E6", @"powderblue", + @"800080", @"purple", + @"FF0000", @"red", + @"BC8F8F", @"rosybrown", + @"4169E1", @"royalblue", + @"8B4513", @"saddlebrown", + @"FA8072", @"salmon", + @"F4A460", @"sandybrown", + @"2E8B57", @"seagreen", + @"FFF5EE", @"seashell", + @"A0522D", @"sienna", + @"C0C0C0", @"silver", + @"87CEEB", @"skyblue", + @"6A5ACD", @"slateblue", + @"708090", @"slategray", + @"708090", @"slategrey", + @"FFFAFA", @"snow", + @"00FF7F", @"springgreen", + @"4682B4", @"steelblue", + @"D2B48C", @"tan", + @"008080", @"teal", + @"D8BFD8", @"thistle", + @"FF6347", @"tomato", + @"40E0D0", @"turquoise", + @"EE82EE", @"violet", + @"F5DEB3", @"wheat", + @"FFFFFF", @"white", + @"F5F5F5", @"whitesmoke", + @"FFFF00", @"yellow", + @"9ACD32", @"yellowgreen", + nil]; + }); + + NSString *hexString = [colorLookup objectForKey:[name lowercaseString]]; + + return DTColorCreateWithHexString(hexString); +} + +NSString *DTHexStringFromDTColor(DTColor *color) +{ + CGColorRef cgColor = color.CGColor; + size_t count = CGColorGetNumberOfComponents(cgColor); + const CGFloat *components = CGColorGetComponents(cgColor); + + static NSString *stringFormat = @"%02x%02x%02x"; + + // Grayscale + if (count == 2) + { + NSUInteger white = (NSUInteger)(components[0] * (CGFloat)255); + return [NSString stringWithFormat:stringFormat, white, white, white]; + } + + // RGB + else if (count == 4) + { + return [NSString stringWithFormat:stringFormat, (NSUInteger)(components[0] * (CGFloat)255), + (NSUInteger)(components[1] * (CGFloat)255), (NSUInteger)(components[2] * (CGFloat)255)]; + } + + // Unsupported color space + return nil; +} + diff --git a/Pods/DTCoreText/Core/Source/DTCompatibility.h b/Pods/DTCoreText/Core/Source/DTCompatibility.h new file mode 100644 index 0000000..f86ebdc --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCompatibility.h @@ -0,0 +1,139 @@ +// +// DTCompatibility.h +// DTCoreText +// +// Created by Oliver Letterer on 09.04.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#pragma mark - iOS + +#if TARGET_OS_IPHONE + + // Compatibility Aliases + @compatibility_alias DTColor UIColor; + @compatibility_alias DTImage UIImage; + @compatibility_alias DTFont UIFont; + + // Edge Insets + #define DTEdgeInsets UIEdgeInsets + #define DTEdgeInsetsMake(top, left, bottom, right) UIEdgeInsetsMake(top, left, bottom, right) + + // NS-style text attributes are possible with iOS SDK 6.0 or higher + #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + #define DTCORETEXT_SUPPORT_NS_ATTRIBUTES 1 + #endif + + // NSParagraphStyle supports tabs as of iOS SDK 7.0 or higher + #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1 + #define DTCORETEXT_SUPPORT_NSPARAGRAPHSTYLE_TABS 1 + #endif + + // iOS before 5.0 has leak in CoreText replacing attributes + #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 + #define DTCORETEXT_NEEDS_ATTRIBUTE_REPLACEMENT_LEAK_FIX 1 + #endif + + // iOS 7 bug (rdar://14684188) workaround, can be removed once this bug is fixed + #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1 + #define DTCORETEXT_FIX_14684188 1 + #endif + + // constant for checking for iOS 6 + #define DTNSFoundationVersionNumber_iOS_6_0 992.00 + + // constant for checking for iOS 7 + #define DTNSFoundationVersionNumber_iOS_7_0 1047.00 + + + // runtime-check if NS-style attributes are allowed + static inline BOOL DTCoreTextModernAttributesPossible() + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (floor(NSFoundationVersionNumber) >= DTNSFoundationVersionNumber_iOS_6_0) + { + return YES; + } +#endif + return NO; + } + + // runtime-check if CoreText draws underlines + static inline BOOL DTCoreTextDrawsUnderlinesWithGlyphs() + { + if (floor(NSFoundationVersionNumber) >= DTNSFoundationVersionNumber_iOS_7_0) + { + return YES; + } + + return NO; + } + +#if TARGET_CPU_ARM64 || TARGET_CPU_X86_64 + #define DTNSNumberFromCGFloat(x) [NSNumber numberWithDouble:x] +#else + #define DTNSNumberFromCGFloat(x) [NSNumber numberWithFloat:x] +#endif + +#endif + + +#pragma mark - Mac + + +#if !TARGET_OS_IPHONE + + // Compatibility Aliases + @compatibility_alias DTColor NSColor; + @compatibility_alias DTImage NSImage; + @compatibility_alias DTFont NSFont; + + // Edge Insets + #define DTEdgeInsets NSEdgeInsets + #define DTEdgeInsetsMake(top, left, bottom, right) NSEdgeInsetsMake(top, left, bottom, right) + + // Mac supports NS-Style Text Attributes since 10.0 + #define DTCORETEXT_SUPPORT_NS_ATTRIBUTES 1 + #define DTCORETEXT_SUPPORT_NSPARAGRAPHSTYLE_TABS 1 + + // theoretically MacOS before 10.8 might have a leak in CoreText replacing attributes + #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7 + #define DTCORETEXT_NEEDS_ATTRIBUTE_REPLACEMENT_LEAK_FIX 1 + #endif + + // NSValue has sizeValue on Mac, CGSizeValue on iOS + #define CGSizeValue sizeValue + + // String functions named differently on Mac + static inline NSString *NSStringFromCGRect(const CGRect rect) + { + return NSStringFromRect(NSRectFromCGRect(rect)); + } + + static inline NSString *NSStringFromCGSize(const CGSize size) + { + return NSStringFromSize(NSSizeFromCGSize(size)); + } + + static inline NSString *NSStringFromCGPoint(const CGPoint point) + { + return NSStringFromPoint(NSPointFromCGPoint(point)); + } + + // runtime-check if NS-style attributes are allowed + static inline BOOL DTCoreTextModernAttributesPossible() + { + return YES; + } + + // runtime-check if CoreText draws underlines + static inline BOOL DTCoreTextDrawsUnderlinesWithGlyphs() + { + return NO; + } + + #define DTNSNumberFromCGFloat(x) [NSNumber numberWithDouble:x] +#endif + +// this enables generic ceil, floor, abs, round functions that work for 64 and 32 bit +#include diff --git a/Pods/DTCoreText/Core/Source/DTCoreText.h b/Pods/DTCoreText/Core/Source/DTCoreText.h new file mode 100644 index 0000000..34176cd --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreText.h @@ -0,0 +1,79 @@ +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +// global constants +#import "DTCoreTextMacros.h" +#import "DTCoreTextConstants.h" +#import "DTCompatibility.h" + +#import "DTColor+Compatibility.h" +#import "DTImage+HTML.h" + +// common utilities +#if TARGET_OS_IPHONE +#import "DTCoreTextFunctions.h" +#endif + +#import "DTColorFunctions.h" + +// common classes +#import "DTCSSListStyle.h" +#import "DTTextBlock.h" +#import "DTCSSStylesheet.h" +#import "DTCoreTextFontDescriptor.h" +#import "DTCoreTextParagraphStyle.h" +#import "DTHTMLAttributedStringBuilder.h" +#import "DTHTMLElement.h" +#import "DTHTMLWriter.h" +#import "NSCharacterSet+HTML.h" +#import "NSDictionary+DTCoreText.h" +#import "NSAttributedString+HTML.h" +#import "NSAttributedString+SmallCaps.h" +#import "NSAttributedString+DTCoreText.h" +#import "NSMutableAttributedString+HTML.h" +#import "NSMutableString+HTML.h" +#import "NSScanner+HTML.h" +#import "NSString+CSS.h" +#import "NSString+HTML.h" +#import "NSString+Paragraphs.h" + +// parsing classes +#import "DTHTMLParserNode.h" +#import "DTHTMLParserTextNode.h" + +// text attachment cluster +#import "DTTextAttachment.h" +#import "DTDictationPlaceholderTextAttachment.h" +#import "DTIframeTextAttachment.h" +#import "DTImageTextAttachment.h" +#import "DTObjectTextAttachment.h" +#import "DTVideoTextAttachment.h" + +// These classes only work with UIKit on iOS +#if TARGET_OS_IPHONE + +#import "DTLazyImageView.h" +#import "DTLinkButton.h" +#import "DTWebVideoView.h" +#import "NSAttributedStringRunDelegates.h" + +#import "DTAttributedLabel.h" +#import "DTAttributedTextCell.h" +#import "DTAttributedTextContentView.h" +#import "DTAttributedTextView.h" +#import "DTCoreTextFontCollection.h" +#import "DTCoreTextGlyphRun.h" +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayoutFrame+Cursor.h" +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextLayouter.h" + +#import "DTDictationPlaceholderView.h" + +#import "UIFont+DTCoreText.h" + +#endif + diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextConstants.h b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.h new file mode 100644 index 0000000..aca0365 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.h @@ -0,0 +1,195 @@ +// unicode characters + +#define UNICODE_OBJECT_PLACEHOLDER @"\ufffc" +#define UNICODE_LINE_FEED @"\u2028" + +// unicode spaces (see http://www.cs.tut.fi/~jkorpela/chars/spaces.html) + +#define UNICODE_SPACE @"\u0020" +#define UNICODE_NON_BREAKING_SPACE @"\u00a0" +#define UNICODE_OGHAM_SPACE_MARK @"\u1680" +#define UNICODE_MONGOLIAN_VOWEL_SEPARATOR @"\u180e" +#define UNICODE_EN_QUAD @"\u2000" +#define UNICODE_EM_QUAD @"\u2001" +#define UNICODE_EN_SPACE @"\u2002" +#define UNICODE_EM_SPACE @"\u2003" +#define UNICODE_THREE_PER_EM_SPACE @"\u2004" +#define UNICODE_FOUR_PER_EM_SPACE @"\u2005" +#define UNICODE_SIX_PER_EM_SPACE @"\u2006" +#define UNICODE_FIGURE_SPACE @"\u2007" +#define UNICODE_PUNCTUATION_SPACE @"\u2008" +#define UNICODE_THIN_SPACE @"\u2009" +#define UNICODE_HAIR_SPACE @"\u200a" +#define UNICODE_ZERO_WIDTH_SPACE @"\u200b" +#define UNICODE_NARROW_NO_BREAK_SPACE @"\u202f" +#define UNICODE_MEDIUM_MATHEMATICAL_SPACE @"\u205f" +#define UNICODE_IDEOGRAPHIC_SPACE @"\u3000" +#define UNICODE_ZERO_WIDTH_NO_BREAK_SPACE @"\ufeff" + +// standard options + +#if TARGET_OS_IPHONE +extern NSString * const NSBaseURLDocumentOption; +extern NSString * const NSTextEncodingNameDocumentOption; +extern NSString * const NSTextSizeMultiplierDocumentOption; +extern NSString * const NSAttachmentAttributeName; +#endif + +// custom options + +extern NSString * const DTMaxImageSize; +extern NSString * const DTDefaultFontFamily; +extern NSString * const DTDefaultFontName; +extern NSString * const DTDefaultFontSize; +extern NSString * const DTDefaultTextColor; +extern NSString * const DTDefaultLinkColor; +extern NSString * const DTDefaultLinkDecoration; +extern NSString * const DTDefaultLinkHighlightColor; +extern NSString * const DTDefaultTextAlignment; +extern NSString * const DTDefaultLineHeightMultiplier; +extern NSString * const DTDefaultLineHeightMultiplier; +extern NSString * const DTDefaultFirstLineHeadIndent; +extern NSString * const DTDefaultHeadIndent; +extern NSString * const DTDefaultStyleSheet; +extern NSString * const DTUseiOS6Attributes; +extern NSString * const DTWillFlushBlockCallBack; +extern NSString * const DTProcessCustomHTMLAttributes; +extern NSString * const DTIgnoreInlineStylesOption; + + +// attributed string attribute constants + +extern NSString * const DTTextListsAttribute; +extern NSString * const DTAttachmentParagraphSpacingAttribute; +extern NSString * const DTLinkAttribute; +extern NSString * const DTLinkHighlightColorAttribute; +extern NSString * const DTAnchorAttribute; +extern NSString * const DTGUIDAttribute; +extern NSString * const DTHeaderLevelAttribute; +extern NSString * const DTStrikeOutAttribute; +extern NSString * const DTBackgroundColorAttribute; +extern NSString * const DTShadowsAttribute; +extern NSString * const DTHorizontalRuleStyleAttribute; +extern NSString * const DTTextBlocksAttribute; +extern NSString * const DTFieldAttribute; +extern NSString * const DTCustomAttributesAttribute; +extern NSString * const DTAscentMultiplierAttribute; +extern NSString * const DTBackgroundStrokeColorAttribute; +extern NSString * const DTBackgroundStrokeWidthAttribute; +extern NSString * const DTBackgroundCornerRadiusAttribute; + +// field constants + +extern NSString * const DTListPrefixField; + +// iOS 6 compatibility +extern BOOL ___useiOS6Attributes; + +// exceptions +extern NSString * const DTCoreTextFontDescriptorException; + +// macros + +#define IS_WHITESPACE(_c) (_c == ' ' || _c == '\t' || _c == 0xA || _c == 0xB || _c == 0xC || _c == 0xD || _c == 0x85) + +// types + +/** + DTHTMLElement display style + */ +typedef NS_ENUM(NSUInteger, DTHTMLElementDisplayStyle) +{ + /** + The element is inline text + */ + DTHTMLElementDisplayStyleInline = 0, // default + + /** + The element is not displayed + */ + DTHTMLElementDisplayStyleNone, + + /** + The element is a block + */ + DTHTMLElementDisplayStyleBlock, + + /** + The element is an item in a list + */ + DTHTMLElementDisplayStyleListItem, + + /** + The element is a table + */ + DTHTMLElementDisplayStyleTable, +}; + +/** + DTHTMLElement floating style + */ +typedef NS_ENUM(NSUInteger, DTHTMLElementFloatStyle) +{ + /** + The element does not float + */ + DTHTMLElementFloatStyleNone = 0, + + + /** + The element should float left-aligned + */ + DTHTMLElementFloatStyleLeft, + + + /** + The element should float right-aligned + */ + DTHTMLElementFloatStyleRight +}; + +/** + DTHTMLElement font variants + */ +typedef NS_ENUM(NSUInteger, DTHTMLElementFontVariant) +{ + /** + The element inherts the font variant + */ + DTHTMLElementFontVariantInherit = 0, + + /** + The element uses the normal font variant + */ + DTHTMLElementFontVariantNormal, + + /** + The element should display in small caps + */ + DTHTMLElementFontVariantSmallCaps +}; + +/** + The algorithm that DTCoreTextLayoutFrame uses for positioning lines + */ +typedef NS_ENUM(NSUInteger, DTCoreTextLayoutFrameLinePositioningOptions) +{ + /** + The line positioning algorithm is similar to how Safari positions lines + */ + DTCoreTextLayoutFrameLinePositioningOptionAlgorithmWebKit = 1, + + /** + The line positioning algorithm is how it was before the implementation of DTCoreTextLayoutFrameLinePositioningOptionAlgorithmWebKit + */ + DTCoreTextLayoutFrameLinePositioningOptionAlgorithmLegacy = 2 +}; + +// layouting + +// the value to use if the width is unknown +#define CGFLOAT_WIDTH_UNKNOWN 16777215.0f + +// the value to use if the height is unknown +#define CGFLOAT_HEIGHT_UNKNOWN 16777215.0f + diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextConstants.m b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.m new file mode 100644 index 0000000..9236e65 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.m @@ -0,0 +1,64 @@ +#import "DTCoreTextConstants.h" + +// standard options + +#if TARGET_OS_IPHONE +NSString * const NSBaseURLDocumentOption = @"NSBaseURLDocumentOption"; +NSString * const NSTextEncodingNameDocumentOption = @"NSTextEncodingNameDocumentOption"; +NSString * const NSTextSizeMultiplierDocumentOption = @"NSTextSizeMultiplierDocumentOption"; +NSString * const NSAttachmentAttributeName = @"NSAttachmentAttributeName"; +#endif + +// custom options + +NSString * const DTMaxImageSize = @"DTMaxImageSize"; +NSString * const DTDefaultFontFamily = @"DTDefaultFontFamily"; +NSString * const DTDefaultFontName = @"DTDefaultFontName"; +NSString * const DTDefaultFontSize = @"DTDefaultFontSize"; +NSString * const DTDefaultTextColor = @"DTDefaultTextColor"; +NSString * const DTDefaultLinkColor = @"DTDefaultLinkColor"; +NSString * const DTDefaultLinkHighlightColor = @"DTDefaultLinkHighlightColor"; +NSString * const DTDefaultLinkDecoration = @"DTDefaultLinkDecoration"; +NSString * const DTDefaultTextAlignment = @"DTDefaultTextAlignment"; +NSString * const DTDefaultLineHeightMultiplier = @"DTDefaultLineHeightMultiplier"; +NSString * const DTDefaultFirstLineHeadIndent = @"DTDefaultFirstLineHeadIndent"; +NSString * const DTDefaultHeadIndent = @"DTDefaultHeadIndent"; +NSString * const DTDefaultStyleSheet = @"DTDefaultStyleSheet"; +NSString * const DTUseiOS6Attributes = @"DTUseiOS6Attributes"; +NSString * const DTWillFlushBlockCallBack = @"DTWillFlushBlockCallBack"; +NSString * const DTProcessCustomHTMLAttributes = @"DTProcessCustomHTMLAttributes"; +NSString * const DTIgnoreInlineStylesOption = @"DTIgnoreInlineStyles"; + +// attributed string attribute constants + +NSString * const DTTextListsAttribute = @"DTTextLists"; +NSString * const DTAttachmentParagraphSpacingAttribute = @"DTAttachmentParagraphSpacing"; +NSString * const DTLinkAttribute = @"NSLink"; +NSString * const DTLinkHighlightColorAttribute = @"DTLinkHighlightColor"; +NSString * const DTAnchorAttribute = @"DTAnchor"; +NSString * const DTGUIDAttribute = @"DTGUID"; +NSString * const DTHeaderLevelAttribute = @"DTHeaderLevel"; +NSString * const DTStrikeOutAttribute = @"DTStrikethrough"; +NSString * const DTBackgroundColorAttribute = @"DTBackgroundColor"; +NSString * const DTShadowsAttribute = @"DTShadows"; +NSString * const DTHorizontalRuleStyleAttribute = @"DTHorizontalRuleStyle"; +NSString * const DTTextBlocksAttribute = @"DTTextBlocks"; +NSString * const DTFieldAttribute = @"DTField"; +NSString * const DTCustomAttributesAttribute = @"DTCustomAttributes"; +NSString * const DTAscentMultiplierAttribute = @"DTAscentMultiplierAttribute"; +NSString * const DTBackgroundStrokeColorAttribute = @"DTBackgroundStrokeColor"; +NSString * const DTBackgroundStrokeWidthAttribute = @"DTBackgroundStrokeWidth"; +NSString * const DTBackgroundCornerRadiusAttribute = @"DTBackgroundCornerRadius"; + +// field constants +NSString * const DTListPrefixField = @"{listprefix}"; + +// iOS 6 compatibility + +BOOL ___useiOS6Attributes = NO; // this gets set globally by DTHTMLAttributedStringBuilder + + +// exceptions + +NSString * const DTCoreTextFontDescriptorException = @"DTCoreTextFontDescriptorException"; + diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.h b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.h new file mode 100644 index 0000000..1797212 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.h @@ -0,0 +1,53 @@ +// +// DTCoreTextFontCollection.h +// DTCoreText +// +// Created by Oliver Drobnik on 5/23/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + + +@class DTCoreTextFontDescriptor; + +/** + Class representing a collection of fonts + */ + +@interface DTCoreTextFontCollection : NSObject + +/** + @name Creating Font Collections + */ + +/** + Creates a font collection with all available fonts on the system + */ ++ (DTCoreTextFontCollection *)availableFontsCollection; + +/** + @name Getting Information about Font Collections + */ + +/** + The font family names that occur in the receiver's list of fonts + */ +- (NSArray *)fontFamilyNames; + +/** + The font descriptors describing all fonts in the receiver's font collection + */ +- (NSArray *)fontDescriptors; + +/** + @name Searching for Fonts + */ + +/** + The font descriptor describing a font in the receiver's collection that matches a given descriptor + @param descriptor The font descriptor to search for + @returns The first found font descriptor in the font collection + */ +- (DTCoreTextFontDescriptor *)matchingFontDescriptorForFontDescriptor:(DTCoreTextFontDescriptor *)descriptor; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.m b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.m new file mode 100644 index 0000000..65484c5 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.m @@ -0,0 +1,161 @@ +// +// DTCoreTextFontCollection.m +// DTCoreText +// +// Created by Oliver Drobnik on 5/23/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextFontCollection.h" +#import "DTCoreTextFontDescriptor.h" + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@interface DTCoreTextFontCollection () + +@property (nonatomic, strong) NSArray *fontDescriptors; +@property (nonatomic, strong) NSCache *fontMatchCache; + +- (id)initWithAvailableFonts; + +@end + +static DTCoreTextFontCollection *_availableFontsCollection = nil; + + +@implementation DTCoreTextFontCollection +{ + NSArray *_fontDescriptors; + NSCache *_fontMatchCache; +} + ++ (DTCoreTextFontCollection *)availableFontsCollection +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _availableFontsCollection = [[DTCoreTextFontCollection alloc] initWithAvailableFonts]; + }); + + return _availableFontsCollection; +} + +- (id)initWithAvailableFonts +{ + self = [super init]; + + if (self) + { + + } + + return self; +} + + +- (DTCoreTextFontDescriptor *)matchingFontDescriptorForFontDescriptor:(DTCoreTextFontDescriptor *)descriptor +{ + DTCoreTextFontDescriptor *firstMatch = nil; + NSString *cacheKey = [NSString stringWithFormat:@"fontFamily BEGINSWITH[cd] %@ and boldTrait == %d and italicTrait == %d", descriptor.fontFamily, descriptor.boldTrait, descriptor.italicTrait]; + + // try cache + firstMatch = [self.fontMatchCache objectForKey:cacheKey]; + + if (firstMatch) + { + DTCoreTextFontDescriptor *retMatch = [firstMatch copy]; + retMatch.pointSize = descriptor.pointSize; + return retMatch; + } + + // need to search + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"fontFamily BEGINSWITH[cd] %@ and boldTrait == %d and italicTrait == %d", descriptor.fontFamily, descriptor.boldTrait, descriptor.italicTrait]; + + NSArray *matchingDescriptors = [self.fontDescriptors filteredArrayUsingPredicate:predicate]; + + //NSLog(@"%@", matchingDescriptors); + + if ([matchingDescriptors count]) + { + firstMatch = [matchingDescriptors objectAtIndex:0]; + [self.fontMatchCache setObject:firstMatch forKey:cacheKey]; + + DTCoreTextFontDescriptor *retMatch = [firstMatch copy]; + + retMatch.pointSize = descriptor.pointSize; + return retMatch; + } + + return nil; +} + +#pragma mark Properties + +- (NSArray *)fontDescriptors +{ + if (!_fontDescriptors) + { + CTFontCollectionRef fonts = CTFontCollectionCreateFromAvailableFonts(NULL); + + CFArrayRef matchingFonts = CTFontCollectionCreateMatchingFontDescriptors(fonts); + + if (matchingFonts) + { + // convert all to our objects + NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; + + for (NSInteger i=0; i + +/** + This class describes the attributes of a font. It is used to represent fonts throughout the parsing and when needed is able to generated matching `CTFont` instances. + */ +@interface DTCoreTextFontDescriptor : NSObject + +/** + @name Creating Font Descriptors + */ + +/** + Convenience method to create a font descriptor from a font attributes dictionary + @param attributes The dictionary of font attributes + @returns An initialized font descriptor + */ ++ (DTCoreTextFontDescriptor *)fontDescriptorWithFontAttributes:(NSDictionary *)attributes; + +/** + Convenience method for creates a font descriptor from a Core Text font + @param ctFont The Core Text font + @returns An initialized font descriptor + */ ++ (DTCoreTextFontDescriptor *)fontDescriptorForCTFont:(CTFontRef)ctFont; + +/** + Creates a font descriptor from a font attributes dictionary + @param attributes The dictionary of font attributes + @returns An initialized font descriptor + */ +- (id)initWithFontAttributes:(NSDictionary *)attributes; + +/** + Creates a font descriptor from a Core Text font descriptor + @param ctFontDescriptor The Core Text font descriptor + @returns An initialized font descriptor + */ +- (id)initWithCTFontDescriptor:(CTFontDescriptorRef)ctFontDescriptor; + +/** + Creates a font descriptor from a Core Text font + @param ctFont The Core Text font + @returns An initialized font descriptor + */ +- (id)initWithCTFont:(CTFontRef)ctFont; + + +/** + @name Creating Fonts from Font Descriptors + */ + +/** + Creates a `CTFont` matching the receiver's attribute + @returns a +1 owning reference of a Core Text font + */ +- (CTFontRef)newMatchingFont; + +/** + @name Specifying Font Attributes + */ + + +/** + Sets the font attributes from a dictionary + @param newAttributes The font attributes dictionary + */ +- (void)setFontAttributes:(NSDictionary *)newAttributes; + +/** + Retrieves a dictionary of font attributes + */ +- (NSDictionary *)fontAttributes; + + +/** + The font family name of the described font + */ +@property (nonatomic, copy) NSString *fontFamily; + +/** + The font name of the described font + */ +@property (nonatomic, copy) NSString *fontName; + +/** + The point size of the described font + */ +@property (nonatomic) CGFloat pointSize; + +/** + Whether the described font has the bold trait + */ +@property (nonatomic) BOOL boldTrait; + +/** + Whether the described font has the italic trait + */ +@property (nonatomic) BOOL italicTrait; + +/** + Whether the described font has the expanded trait + */ +@property (nonatomic) BOOL expandedTrait; + +/** + Whether the described font has the condensed trait + */ +@property (nonatomic) BOOL condensedTrait; + +/** + Whether the described font has the monospace trait + */ +@property (nonatomic) BOOL monospaceTrait; + +/** + Whether the described font has the vertical trait + */ +@property (nonatomic) BOOL verticalTrait; + +/** + Whether the described font is optimized for use in User Interfaces + */ +@property (nonatomic) BOOL UIoptimizedTrait; + +/** + The symbolic traits of the receiver + */ +@property (nonatomic) CTFontSymbolicTraits symbolicTraits; + +/** + The stylistic class of the receiver + */ +@property (nonatomic) CTFontStylisticClass stylisticClass; + +/** + `YES` if the small caps style is enabled, `NO` if not + */ +@property (nonatomic) BOOL smallCapsFeature; + +/** + Determining if the font described by the receiver has native small caps support + @returns `YES` if this font supports native small caps + */ +- (BOOL)supportsNativeSmallCaps; + +/** + Working with CSS + */ + +/** + The CSS style sheet representation of the receiver + @returns A CSS style string + */ +- (NSString *)cssStyleRepresentation; + + +/** + @name Global Font Overriding + */ + +/** + A call to the method is ideally placed into your app delegate. This loads all available system fonts into a look up table to allow DTCoreText to quickly find a specific combination of font-family and italic and bold attributes. Please refer to the [Programming Guide](../docs/Programming%20Guide.html) for information when you should be using this. + + Calling this does not replace entries already existing in the lookup table, for example loaded from the `DTCoreTextFontOverrides.plist` included in the app bundle. + */ ++ (void)asyncPreloadFontLookupTable; + +/** + Sets the font family to use if the font family in a font descriptor is invalid. + + The fallback font family cannot be `nil` and must be a valid font family. The default is **Times New Roman**. + @param fontFamily The font family + */ ++ (void)setFallbackFontFamily:(NSString *)fontFamily; + +/** + Returns the font family to use if the font family in a font descriptor is invalid. The default is **Times New Roman**. + @returns The font family + */ ++ (NSString *)fallbackFontFamily; + +/** + Sets the global font name override to use when encountering a font family with given bold and italic attributes. + @param fontName The font name to use + @param fontFamily The font family to use this for + @param bold The bold trait + @param italic The italic trait + */ ++ (void)setOverrideFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; + +/** + Retrieves the gobal font name override for a given font family with bold and italic traits. + @param fontFamily The font family to retrieve the override for + @param bold The bold trait + @param italic The italic trait + @returns The font name to use for this combination of parameters + */ ++ (NSString *)overrideFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; + +/** + Sets the global font name override to use when encountering small caps text in a font family with given bold and italic attributes. + @param fontName The font name to use + @param fontFamily The font family to use this for + @param bold The bold trait + @param italic The italic trait + */ ++ (void)setSmallCapsFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; + +/** + Retrieves the gobal font name override to use for small caps text for a given font family with bold and italic traits. + @param fontFamily The font family to retrieve the override for + @param bold The bold trait + @param italic The italic trait + @returns The font name to use for this combination of parameters + */ ++ (NSString *)smallCapsFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFontDescriptor.m b/Pods/DTCoreText/Core/Source/DTCoreTextFontDescriptor.m new file mode 100644 index 0000000..98d2875 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFontDescriptor.m @@ -0,0 +1,1003 @@ +// +// DTCoreTextFontDescriptor.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/26/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextFontDescriptor.h" +#import "DTCoreTextFontCollection.h" +#import "DTCompatibility.h" +#import "DTCoreTextConstants.h" + +static NSCache *_fontCache = nil; +static NSMutableDictionary *_fontOverrides = nil; + +// font family to use if no font can be found with the given font-family +static NSString *_fallbackFontFamily = @"Times New Roman"; + +// adds "STHeitiSC-Light" font for cascading fix on iOS 5 +static BOOL _needsChineseFontCascadeFix = NO; + +@interface DTCoreTextFontDescriptor () + +// gets descriptors of all available fonts from system ++ (void)_createDictionaryOfAllAvailableFontOverrideNamesWithCompletion:(void(^)(NSDictionary *dictionary))completion; + +@end + + +@implementation DTCoreTextFontDescriptor +{ + NSString *_fontFamily; + NSString *_fontName; + + CGFloat _pointSize; + + CTFontSymbolicTraits _stylisticTraits; + CTFontStylisticClass _stylisticClass; + + BOOL _smallCapsFeature; +} + ++ (void)initialize +{ + // only this class (and not subclasses) do this + if (self != [DTCoreTextFontDescriptor class]) + { + return; + } + + _fontCache = [[NSCache alloc] init]; + + // init/load of overrides + _fontOverrides = [[NSMutableDictionary alloc] init]; + + // then - if it exists - we override from the plist + NSString *path = [[NSBundle bundleForClass:self] pathForResource:@"DTCoreTextFontOverrides" ofType:@"plist"]; + NSArray *fileArray = [NSArray arrayWithContentsOfFile:path]; + + for (NSDictionary *oneOverride in fileArray) + { + NSString *fontFamily = [oneOverride objectForKey:@"FontFamily"]; + NSString *overrideFontName = [oneOverride objectForKey:@"OverrideFontName"]; + BOOL bold = [[oneOverride objectForKey:@"Bold"] boolValue]; + BOOL italic = [[oneOverride objectForKey:@"Italic"] boolValue]; + BOOL smallcaps = [[oneOverride objectForKey:@"SmallCaps"] boolValue]; + + if (smallcaps) + { + [DTCoreTextFontDescriptor setSmallCapsFontName:overrideFontName forFontFamily:fontFamily bold:bold italic:italic]; + } + else + { + [DTCoreTextFontDescriptor setOverrideFontName:overrideFontName forFontFamily:fontFamily bold:bold italic:italic]; + } + } + +#if TARGET_OS_IPHONE + // workaround for iOS 5.x bug: global font cascade table has incorrect bold font for Chinese characters in Chinese locale + if (NSFoundationVersionNumber < DTNSFoundationVersionNumber_iOS_6_0) + { + _needsChineseFontCascadeFix = YES; + } +#endif +} + +// preloads all available system fonts for faster font matching ++ (void)asyncPreloadFontLookupTable +{ + // asynchronically load all available fonts into override table + [self _createDictionaryOfAllAvailableFontOverrideNamesWithCompletion:^(NSDictionary *dictionary) { + + // now we're done and we can merge the new dictionary synchronized + + @synchronized(_fontOverrides) + { + [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *overrideFontName, BOOL *stop) { + + // only add the overrides where there is no previous setting, either from plist of user setting it + if (![_fontOverrides objectForKey:key]) + { + [_fontOverrides setObject:overrideFontName forKey:key]; + } + }]; + } + }]; +} + +// get font names of all available fonts from system ++ (void)_createDictionaryOfAllAvailableFontOverrideNamesWithCompletion:(void(^)(NSDictionary *dictionary))completion +{ + NSParameterAssert(completion); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + + // get all font descriptors + DTCoreTextFontCollection *allFonts = [DTCoreTextFontCollection availableFontsCollection]; + + NSMutableDictionary *tmpDictionary = [NSMutableDictionary dictionaryWithCapacity:[allFonts.fontDescriptors count]]; + + // sort font descriptors by name so that shorter names are preferred + NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"fontFamily" ascending:YES]; + NSSortDescriptor *sort2 = [NSSortDescriptor sortDescriptorWithKey:@"fontName" ascending:YES]; + NSArray *sortedFonts = [[allFonts fontDescriptors] sortedArrayUsingDescriptors:[NSArray arrayWithObjects:sort1, sort2, nil]]; + + + for (DTCoreTextFontDescriptor *oneFontDescriptor in sortedFonts) + { + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", oneFontDescriptor.fontFamily, oneFontDescriptor.boldTrait, oneFontDescriptor.italicTrait]; + + NSString *existingOverride = [tmpDictionary objectForKey:key]; + + if (!existingOverride) + { + [tmpDictionary setObject:oneFontDescriptor.fontName forKey:key]; + } + else + { + // prefer fonts with shorter name, there are probably "more correct". e.g. Helvetica-Oblique instead of Helvetica-LightOblique + if ([existingOverride length]>[oneFontDescriptor.fontName length]) + { + [tmpDictionary setObject:oneFontDescriptor.fontName forKey:key]; + } + } + } + + completion([tmpDictionary copy]); + }); +} + +#pragma mark - Global Font Overriding + ++ (void)setFallbackFontFamily:(NSString *)fontFamily +{ + if (!fontFamily) + { + [NSException raise:DTCoreTextFontDescriptorException format:@"Fallback Font Family cannot be nil"]; + } + + // make sure that only valid font families can be registered + NSDictionary *attributes = [NSDictionary dictionaryWithObject:fontFamily forKey:(id)kCTFontFamilyNameAttribute]; + CTFontDescriptorRef fontDesc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)(attributes)); + CTFontRef font = CTFontCreateWithFontDescriptor(fontDesc, 12, NULL); + + BOOL isValid = NO; + + if (font) + { + NSString *usedFontFamily = CFBridgingRelease(CTFontCopyFamilyName(font)); + + if ([usedFontFamily isEqualToString:fontFamily]) + { + isValid = YES; + } + + CFRelease(fontDesc); + CFRelease(font); + } + + if (!isValid) + { + [NSException raise:DTCoreTextFontDescriptorException format:@"Fallback Font Family '%@' not registered on the system", fontFamily]; + } + + _fallbackFontFamily = [fontFamily copy]; +} + ++ (NSString *)fallbackFontFamily +{ + return _fallbackFontFamily; +} + ++ (void)setSmallCapsFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + @synchronized(_fontOverrides) + { + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic]; + [_fontOverrides setObject:fontName forKey:key]; + } +} + ++ (NSString *)smallCapsFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + @synchronized(_fontOverrides) + { + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic]; + return [_fontOverrides objectForKey:key]; + } +} + ++ (void)setOverrideFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + @synchronized(_fontOverrides) + { + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic]; + [_fontOverrides setObject:fontName forKey:key]; + }; +} + ++ (NSString *)overrideFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + @synchronized(_fontOverrides) + { + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic]; + return [_fontOverrides objectForKey:key]; + } +} + +#pragma mark Initializing + ++ (DTCoreTextFontDescriptor *)fontDescriptorWithFontAttributes:(NSDictionary *)attributes +{ + return [[DTCoreTextFontDescriptor alloc] initWithFontAttributes:attributes]; +} + ++ (DTCoreTextFontDescriptor *)fontDescriptorForCTFont:(CTFontRef)ctFont +{ + return [[DTCoreTextFontDescriptor alloc] initWithCTFont:ctFont]; +} + +- (id)initWithFontAttributes:(NSDictionary *)attributes +{ + self = [super init]; + if (self) + { + [self setFontAttributes:attributes]; + } + + return self; +} + +- (id)initWithCTFontDescriptor:(CTFontDescriptorRef)ctFontDescriptor +{ + self = [super init]; + if (self) + { + CFDictionaryRef dict = CTFontDescriptorCopyAttributes(ctFontDescriptor); + + CFDictionaryRef traitsDict = CTFontDescriptorCopyAttribute(ctFontDescriptor, kCTFontTraitsAttribute); + CTFontSymbolicTraits traitsValue = [[(__bridge NSDictionary *)traitsDict objectForKey:(id)kCTFontSymbolicTrait] unsignedIntValue]; + CFRelease(traitsDict); + + self.symbolicTraits = traitsValue; + + [self setFontAttributes:CFBridgingRelease(dict)]; + + // also get family name + + CFStringRef familyName = CTFontDescriptorCopyAttribute(ctFontDescriptor, kCTFontFamilyNameAttribute); + self.fontFamily = CFBridgingRelease(familyName); + } + + return self; +} + +- (id)initWithCTFont:(CTFontRef)ctFont +{ + NSParameterAssert(ctFont); + + self = [super init]; + if (self) + { + CTFontDescriptorRef fd = CTFontCopyFontDescriptor(ctFont); + CFDictionaryRef dict = CTFontDescriptorCopyAttributes(fd); + + CFDictionaryRef traitsDict = CTFontDescriptorCopyAttribute(fd, kCTFontTraitsAttribute); + CTFontSymbolicTraits traitsValue = [[(__bridge NSDictionary *)traitsDict objectForKey:(id)kCTFontSymbolicTrait] unsignedIntValue]; + CFRelease(traitsDict); + CFRelease(fd); + + self.symbolicTraits = traitsValue; + + [self setFontAttributes:CFBridgingRelease(dict)]; + + // also get the family while we're at it + CFStringRef cfStr = CTFontCopyFamilyName(ctFont); + + if (cfStr) + { + self.fontFamily = CFBridgingRelease(cfStr); + } + + // look if this has synthetic italics + CGAffineTransform transform = CTFontGetMatrix(ctFont); + + if (!CGAffineTransformIsIdentity(transform)) + { + self.italicTrait = YES; + } + } + + return self; +} + +#ifndef COVERAGE +// exclude method from coverage testing + +- (NSString *)description +{ + NSMutableString *string = [NSMutableString string]; + + [string appendFormat:@"<%@", [self class]]; + + + if (self.fontName) + { + [string appendFormat:@" name=\'%@\'", self.fontName]; + } + + if (_fontFamily) + { + [string appendFormat:@" family=\'%@\'", _fontFamily]; + } + + [string appendFormat:@" size:%.0f", _pointSize]; + + NSMutableArray *tmpTraits = [NSMutableArray array]; + + if (_stylisticTraits & kCTFontBoldTrait) + { + [tmpTraits addObject:@" bold"]; + } + + if (_stylisticTraits & kCTFontItalicTrait) + { + [tmpTraits addObject:@" italic"]; + } + + if (_stylisticTraits & kCTFontMonoSpaceTrait) + { + [tmpTraits addObject:@" monospace"]; + } + + if (_stylisticTraits & kCTFontCondensedTrait) + { + [tmpTraits addObject:@" condensed"]; + } + + if (_stylisticTraits & kCTFontExpandedTrait) + { + [tmpTraits addObject:@" expanded"]; + } + + if (_stylisticTraits & kCTFontVerticalTrait) + { + [tmpTraits addObject:@"vertical"]; + } + + if (_stylisticTraits & kCTFontUIOptimizedTrait) + { + [tmpTraits addObject:@" UI optimized"]; + } + + + if ([tmpTraits count]) + { + [string appendString:@" attributes="]; + [string appendString:[tmpTraits componentsJoinedByString:@", "]]; + } + + [string appendString:@">"]; + + return string; +} + +#endif + +- (NSDictionary *)fontAttributes +{ + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + NSMutableDictionary *traitsDict = [NSMutableDictionary dictionary]; + + CTFontSymbolicTraits theSymbolicTraits = _stylisticTraits | _stylisticClass; + + if (theSymbolicTraits) + { + [traitsDict setObject:[NSNumber numberWithUnsignedInt:theSymbolicTraits] forKey:(id)kCTFontSymbolicTrait]; + } + + if ([traitsDict count]) + { + [tmpDict setObject:traitsDict forKey:(id)kCTFontTraitsAttribute]; + } + + if (_fontFamily) + { + [tmpDict setObject:_fontFamily forKey:(id)kCTFontFamilyNameAttribute]; + } + + if (_fontName) + { + [tmpDict setObject:_fontName forKey:(id)kCTFontNameAttribute]; + } + + // we need size because that's what makes a font unique, for searching it's ignored anyway + [tmpDict setObject:DTNSNumberFromCGFloat(_pointSize) forKey:(id)kCTFontSizeAttribute]; + + if (_smallCapsFeature) + { + NSNumber *typeNum = [NSNumber numberWithInteger:3]; + NSNumber *selNum = [NSNumber numberWithInteger:3]; + + NSDictionary *setting = [NSDictionary dictionaryWithObjectsAndKeys:selNum, (id)kCTFontFeatureSelectorIdentifierKey, + typeNum, (id)kCTFontFeatureTypeIdentifierKey, nil]; + + NSArray *featureSettings = [NSArray arrayWithObject:setting]; + + [tmpDict setObject:featureSettings forKey:(id)kCTFontFeatureSettingsAttribute]; + } + + if (!self.boldTrait && _needsChineseFontCascadeFix) + { + CTFontDescriptorRef desc = CTFontDescriptorCreateWithNameAndSize(CFSTR("STHeitiSC-Light"), self.pointSize); + + [tmpDict setObject:[NSArray arrayWithObject:(__bridge_transfer id) desc] forKey:(id)kCTFontCascadeListAttribute]; + } + + //return [NSDictionary dictionaryWithDictionary:tmpDict]; + // converting to non-mutable costs 42% of entire method + return tmpDict; +} + +- (NSDictionary *)fontAttributesWithOverrideFontName:(NSString *)overrideFontName +{ + NSMutableDictionary *tmpAttributes = [[self fontAttributes] mutableCopy]; + + // remove family + [tmpAttributes removeObjectForKey:(id)kCTFontFamilyNameAttribute]; + + // replace font name + [tmpAttributes setObject:overrideFontName forKey:(id)kCTFontNameAttribute]; + + return tmpAttributes; +} + +- (BOOL)supportsNativeSmallCaps +{ + if ([DTCoreTextFontDescriptor smallCapsFontNameforFontFamily:_fontFamily bold:self.boldTrait italic:self.italicTrait]) + { + return YES; + } + + BOOL smallCapsSupported = NO; + + CTFontRef tmpFont = [self newMatchingFont]; + + if (tmpFont) + { + // check if this font supports small caps + CFArrayRef fontFeatures = CTFontCopyFeatures(tmpFont); + + if (fontFeatures) + { + for (NSDictionary *oneFeature in (__bridge NSArray *)fontFeatures) + { + NSInteger featureTypeIdentifier = [[oneFeature objectForKey:(id)kCTFontFeatureTypeIdentifierKey] integerValue]; + + if (featureTypeIdentifier == 3) // Letter Case + { + NSArray *featureSelectors = [oneFeature objectForKey:(id)kCTFontFeatureTypeSelectorsKey]; + + for (NSDictionary *oneFeatureSelector in featureSelectors) + { + NSInteger featureSelectorIdentifier = [[oneFeatureSelector objectForKey:(id)kCTFontFeatureSelectorIdentifierKey] integerValue]; + + if (featureSelectorIdentifier == 3) // Small Caps + { + // hooray, small caps supported! + smallCapsSupported = YES; + + break; + } + } + + break; + } + } + + CFRelease(fontFeatures); + } + + CFRelease(tmpFont); + } + + return smallCapsSupported; +} + +#pragma mark Finding Font + +- (BOOL)_fontIsOblique:(CTFontRef)font +{ + NSDictionary *traits = (__bridge_transfer NSDictionary *)CTFontCopyTraits(font); + + CGFloat slant = [[traits objectForKey:(id)kCTFontSlantTrait] floatValue]; + BOOL hasItalicTrait = ([[traits objectForKey:(id)kCTFontSymbolicTrait] unsignedIntValue] & kCTFontItalicTrait) ==kCTFontItalicTrait; + + if (!hasItalicTrait || slant<0.01) + { + return NO; + } + + // font HAS italic trait AND sufficient slant angle + return YES; + +} + +- (CTFontRef)_findOrMakeMatchingFont +{ + CTFontDescriptorRef searchingFontDescriptor = NULL; + CTFontDescriptorRef matchingFontDescriptor = NULL; + CTFontRef matchingFont = NULL; + + // check the cache first + NSNumber *cacheKey = [NSNumber numberWithUnsignedInteger:[self hash]]; + + CTFontRef cachedFont = (__bridge_retained CTFontRef)[_fontCache objectForKey:cacheKey]; + + if (cachedFont) + { + return cachedFont; + } + + // check the override table that has all preinstalled fonts plus the ones the user registered + NSString *overrideName = nil; + + if (_fontFamily && !_fontName) // forced font name takes precedence + { + if (_smallCapsFeature) + { + overrideName = [DTCoreTextFontDescriptor smallCapsFontNameforFontFamily:_fontFamily bold:self.boldTrait italic:self.italicTrait]; + } + else + { + overrideName = [DTCoreTextFontDescriptor overrideFontNameforFontFamily:_fontFamily bold:self.boldTrait italic:self.italicTrait]; + } + } + + // if we use the chinese font cascade fix we cannot use fast method as it does not allow specifying the custom cascade list + BOOL useFastFontCreation = !(_needsChineseFontCascadeFix && !self.boldTrait); + + if (useFastFontCreation && (_fontName || overrideName)) + { + // we can create a font directly from the name + NSString *usedName = overrideName?overrideName:_fontName; + + matchingFont = CTFontCreateWithName((__bridge CFStringRef)usedName, _pointSize, NULL); + } + else + { + // we need to search for a suitable font + + NSDictionary *fontAttributes; + + if (overrideName) + { + fontAttributes = [self fontAttributesWithOverrideFontName:overrideName]; + } + else + { + fontAttributes = [self fontAttributes]; + } + + // the descriptor we are looking for + searchingFontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)fontAttributes); + + // the attributes that are mandatory + NSMutableSet *mandatoryAttributes = [NSMutableSet setWithObject:(id)kCTFontTraitsAttribute]; + + if (_fontFamily) + { + [mandatoryAttributes addObject:(id)kCTFontFamilyNameAttribute]; + } + + if (_smallCapsFeature) + { + [mandatoryAttributes addObject:(id)kCTFontFeaturesAttribute]; + } + + // do the search + matchingFontDescriptor = CTFontDescriptorCreateMatchingFontDescriptor(searchingFontDescriptor, (__bridge CFSetRef)mandatoryAttributes); + + if (!matchingFontDescriptor) + { + // try without traits + NSMutableDictionary *mutableAttributes = [fontAttributes mutableCopy]; + [mutableAttributes removeObjectForKey:(id)kCTFontTraitsAttribute]; + + CFRelease(searchingFontDescriptor); + searchingFontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)mutableAttributes); + + // do the relaxed search + matchingFontDescriptor = CTFontDescriptorCreateMatchingFontDescriptor(searchingFontDescriptor, NULL); + } + + if (!matchingFontDescriptor) + { + // try with fallback font family + NSMutableDictionary *mutableAttributes = [fontAttributes mutableCopy]; + [mutableAttributes setObject:_fallbackFontFamily forKey:(id)kCTFontFamilyNameAttribute]; + + CFRelease(searchingFontDescriptor); + searchingFontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)mutableAttributes); + + // do the relaxed search + matchingFontDescriptor = CTFontDescriptorCreateMatchingFontDescriptor(searchingFontDescriptor, NULL); + } + } + + // any search was successful + if (matchingFontDescriptor) + { + matchingFont = CTFontCreateWithFontDescriptor(matchingFontDescriptor, _pointSize, NULL); + + CFRelease(matchingFontDescriptor); + } + + if (searchingFontDescriptor) + { + CFRelease(searchingFontDescriptor); + } + + // check if we indeed got an oblique font if we wanted one + if (matchingFont && self.italicTrait && ![self _fontIsOblique:matchingFont]) + { + // need to synthesize slant + CGAffineTransform slantMatrix = { 1, 0, 0.25, 1, 0, 0 }; + + CTFontRef slantedFont = CTFontCreateCopyWithAttributes(matchingFont, _pointSize, &slantMatrix, NULL); + CFRelease(matchingFont); + + matchingFont = slantedFont; + } + + // add found font to cache + if (matchingFont) + { + [_fontCache setObject:(__bridge id)(matchingFont) forKey:cacheKey]; + } + + return matchingFont; // returns a +1 reference +} + +- (CTFontRef)newMatchingFont +{ + return [self _findOrMakeMatchingFont]; +} + +// two font descriptors are equal if their attributes has identical hash codes +- (NSUInteger)hash +{ + NSUInteger calcHash = 7; + + calcHash = calcHash*31 + (NSUInteger)_pointSize; + calcHash = calcHash*31 + (_stylisticClass | _stylisticTraits); + calcHash = calcHash*31 + [_fontName hash]; + calcHash = calcHash*31 + [_fontFamily hash]; + + return calcHash; +} + +- (BOOL)isEqual:(id)object +{ + if (!object) + { + return NO; + } + + if (object == self) + { + return YES; + } + + if (![object isKindOfClass:[DTCoreTextFontDescriptor class]]) + { + return NO; + } + + DTCoreTextFontDescriptor *otherFontDescriptor = object; + + if (_pointSize != otherFontDescriptor->_pointSize) + { + return NO; + } + + if (_stylisticClass != otherFontDescriptor->_stylisticClass) + { + return NO; + } + + if (_stylisticTraits != otherFontDescriptor->_stylisticTraits) + { + return NO; + } + + if (_fontName != otherFontDescriptor->_fontName) + { + if (![_fontName isEqualToString:_fontName]) + { + return NO; + } + } + + if (_fontFamily != otherFontDescriptor->_fontFamily) + { + if (![_fontFamily isEqualToString:_fontFamily]) + { + return NO; + } + } + + return YES; +} + + +#pragma mark NSCoding + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeObject:self.fontName forKey:@"FontName"]; + [encoder encodeObject:self.fontFamily forKey:@"FontFamily"]; + [encoder encodeBool:self.boldTrait forKey:@"BoldTrait"]; + [encoder encodeBool:self.italicTrait forKey:@"ItalicTrait"]; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + + if (self) + { + self.fontName = [decoder decodeObjectForKey:@"FontName"]; + self.fontFamily = [decoder decodeObjectForKey:@"FontFamily"]; + self.boldTrait = [decoder decodeBoolForKey:@"BoldTrait"]; + self.italicTrait = [decoder decodeBoolForKey:@"ItalicTrait"]; + } + + return self; +} + + +#pragma mark Copying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCoreTextFontDescriptor *newDesc = [[DTCoreTextFontDescriptor allocWithZone:zone] initWithFontAttributes:[self fontAttributes]]; + newDesc.pointSize = self.pointSize; + + return newDesc; +} + + +#pragma mark Properties +- (void)setStylisticClass:(CTFontStylisticClass)newClass +{ + self.fontFamily = nil; + + _stylisticClass = newClass; +} + + +- (void)setFontAttributes:(NSDictionary *)attributes +{ + if (!attributes) + { + self.fontFamily = nil; + self.fontName = nil; + self.pointSize = 12; + + _stylisticTraits = 0; + _stylisticClass = 0; + } + + NSDictionary *traitsDict = [attributes objectForKey:(id)kCTFontTraitsAttribute]; + + if (traitsDict) + { + CTFontSymbolicTraits traitsValue = [[traitsDict objectForKey:(id)kCTFontSymbolicTrait ] unsignedIntValue]; + self.symbolicTraits = traitsValue; + } + + NSNumber *pointNum = [attributes objectForKey:(id)kCTFontSizeAttribute]; + if (pointNum) + { + _pointSize = [pointNum floatValue]; + } + + NSString *family = [attributes objectForKey:(id)kCTFontFamilyNameAttribute]; + + if (family) + { + self.fontFamily = family; + } + + NSString *name = [attributes objectForKey:(id)kCTFontNameAttribute]; + + if (name) + { + self.fontName = name; + } +} + +- (CTFontSymbolicTraits)symbolicTraits +{ + // symbolic traits include both stylistic traits as well as stylistic class + return _stylisticTraits | _stylisticClass; +} + +- (void)setSymbolicTraits:(CTFontSymbolicTraits)theSymbolicTraits +{ + // symbolic traits include both stylistic traits as well as stylistic class + _stylisticTraits = theSymbolicTraits & ~kCTFontClassMaskTrait; + _stylisticClass = theSymbolicTraits & kCTFontClassMaskTrait; +} + +// a representation of this font in CSS style +- (NSString *)cssStyleRepresentation +{ + NSMutableString *retString = [NSMutableString string]; + + if (_fontFamily) + { + [retString appendFormat:@"font-family:'%@';", _fontFamily]; + } + + [retString appendFormat:@"font-size:%.0fpx;", _pointSize]; + + if (self.italicTrait) + { + [retString appendString:@"font-style:italic;"]; + } + + if (self.boldTrait) + { + [retString appendString:@"font-weight:bold;"]; + } + + // return nil if no content + if ([retString length]) + { + return retString; + } + else + { + return nil; + } +} + +- (void)setBoldTrait:(BOOL)boldTrait +{ + if (boldTrait) + { + _stylisticTraits |= kCTFontBoldTrait; + } + else + { + _stylisticTraits &= ~kCTFontBoldTrait; + } +} + +- (BOOL)boldTrait +{ + return (_stylisticTraits & kCTFontBoldTrait)!=0; +} + +- (void)setItalicTrait:(BOOL)italicTrait +{ + if (italicTrait) + { + _stylisticTraits |= kCTFontItalicTrait; + } + else + { + _stylisticTraits &= ~kCTFontItalicTrait; + } +} + +- (BOOL)italicTrait +{ + return (_stylisticTraits & kCTFontItalicTrait)!=0; +} + +- (void)setExpandedTrait:(BOOL)expandedTrait +{ + if (expandedTrait) + { + _stylisticTraits |= kCTFontExpandedTrait; + } + else + { + _stylisticTraits &= ~kCTFontExpandedTrait; + } +} + +- (BOOL)expandedTrait +{ + return (_stylisticTraits & kCTFontExpandedTrait)!=0; +} + +- (void)setCondensedTrait:(BOOL)condensedTrait +{ + if (condensedTrait) + { + _stylisticTraits |= kCTFontCondensedTrait; + } + else + { + _stylisticTraits &= ~kCTFontCondensedTrait; + } +} + +- (BOOL)condensedTrait +{ + return (_stylisticTraits & kCTFontCondensedTrait)!=0; +} + +- (void)setMonospaceTrait:(BOOL)monospaceTrait +{ + if (monospaceTrait) + { + _stylisticTraits |= kCTFontMonoSpaceTrait; + } + else + { + _stylisticTraits &= ~kCTFontMonoSpaceTrait; + } +} + +- (BOOL)monospaceTrait +{ + return (_stylisticTraits & kCTFontMonoSpaceTrait)!=0; +} + +- (void)setVerticalTrait:(BOOL)verticalTrait +{ + if (verticalTrait) + { + _stylisticTraits |= kCTFontVerticalTrait; + } + else + { + _stylisticTraits &= ~kCTFontVerticalTrait; + } +} + +- (BOOL)verticalTrait +{ + return (_stylisticTraits & kCTFontVerticalTrait)!=0; +} + +- (void)setUIoptimizedTrait:(BOOL)UIoptimizedTrait +{ + if (UIoptimizedTrait) + { + _stylisticTraits |= kCTFontUIOptimizedTrait; + } + else + { + _stylisticTraits &= ~kCTFontUIOptimizedTrait; + } +} + +- (BOOL)UIoptimizedTrait +{ + return (_stylisticTraits & kCTFontUIOptimizedTrait)!=0; +} + +- (void)setPointSize:(CGFloat)pointSize +{ + _pointSize = round(pointSize); +} + +@synthesize fontFamily = _fontFamily; +@synthesize fontName = _fontName; +@synthesize pointSize = _pointSize; + +@dynamic symbolicTraits; + +@synthesize stylisticClass = _stylisticClass; +@synthesize smallCapsFeature = _smallCapsFeature; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.h b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.h new file mode 100644 index 0000000..c8675e8 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.h @@ -0,0 +1,59 @@ +// +// DTCoreTextFunctions.h +// DTCoreText +// +// Created by Oliver Drobnik on 21.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCompatibility.h" + +#if TARGET_OS_IPHONE +/** + Creates a CTFont from a UIFont + @param font The `UIFont` + @returns The matching CTFont + */ +CTFontRef DTCTFontCreateWithUIFont(UIFont *font); +#endif + +/** + Converts an NSLineBreakMode into CoreText line truncation type + */ +CTLineTruncationType DTCTLineTruncationTypeFromNSLineBreakMode(NSLineBreakMode lineBreakMode); + +/** + Rounds the passed value according to the specifed content scale. + + With contentScale 1 the results are identical to roundf, with Retina content scale 2 the results are multiples of 0.5. + */ +CGFloat DTRoundWithContentScale(CGFloat value, CGFloat contentScale); + +/** + Rounds up the passed value according to the specifed content scale. + + With contentScale 1 the results are identical to roundf, with Retina content scale 2 the results are multiples of 0.5. + */ +CGFloat DTCeilWithContentScale(CGFloat value, CGFloat contentScale); + +/** + Rounds down the passed value according to the specifed content scale. + + With contentScale 1 the results are identical to roundf, with Retina content scale 2 the results are multiples of 0.5. + */ +CGFloat DTFloorWithContentScale(CGFloat value, CGFloat contentScale); + +#pragma mark - Alignment Conversion + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES +/** + Converts from NSTextAlignment to CTTextAligment + */ +CTTextAlignment DTNSTextAlignmentToCTTextAlignment(NSTextAlignment nsTextAlignment); + +/** + Converts from CTTextAlignment to NSTextAligment + */ +NSTextAlignment DTNSTextAlignmentFromCTTextAlignment(CTTextAlignment ctTextAlignment); +#endif + diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.m b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.m new file mode 100644 index 0000000..5a61f65 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.m @@ -0,0 +1,194 @@ +// +// DTCoreTextFunctions.m +// DTCoreText +// +// Created by Oliver Drobnik on 21.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextFunctions.h" +#import "DTLog.h" + +#if TARGET_OS_IPHONE +CTFontRef DTCTFontCreateWithUIFont(UIFont *font) +{ + return CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL); +} +#endif + +CTLineTruncationType DTCTLineTruncationTypeFromNSLineBreakMode(NSLineBreakMode lineBreakMode) +{ +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 + switch (lineBreakMode) + { + case UILineBreakModeHeadTruncation: + return kCTLineTruncationStart; + + case UILineBreakModeMiddleTruncation: + return kCTLineTruncationMiddle; + + default: + return kCTLineTruncationEnd; + } +#else + switch (lineBreakMode) + { + case NSLineBreakByTruncatingHead: + return kCTLineTruncationStart; + + case NSLineBreakByTruncatingMiddle: + return kCTLineTruncationMiddle; + + default: + return kCTLineTruncationEnd; + } +#endif +} + +CGFloat DTRoundWithContentScale(CGFloat value, CGFloat contentScale) +{ + return round(value*contentScale)/contentScale; +} + +CGFloat DTCeilWithContentScale(CGFloat value, CGFloat contentScale) +{ + return ceil(value*contentScale)/contentScale; +} + +CGFloat DTFloorWithContentScale(CGFloat value, CGFloat contentScale) +{ + return floor(value*contentScale)/contentScale; +} + +#pragma mark - Alignment Functions + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + +CTTextAlignment DTNSTextAlignmentToCTTextAlignment(NSTextAlignment nsTextAlignment) +{ + switch (nsTextAlignment) + { +#if TARGET_OS_IPHONE + case NSTextAlignmentLeft: + { + return kCTTextAlignmentLeft; + } + + case NSTextAlignmentRight: + { + return kCTTextAlignmentRight; + } + + case NSTextAlignmentCenter: + { + return kCTTextAlignmentCenter; + } + + case NSTextAlignmentJustified: + { + return kCTTextAlignmentJustified; + } + + case NSTextAlignmentNatural: + { + return kCTTextAlignmentNatural; + } +#else + case NSLeftTextAlignment: + { + return kCTTextAlignmentLeft; + } + + case NSRightTextAlignment: + { + return kCTTextAlignmentRight; + } + + case NSCenterTextAlignment: + { + return kCTTextAlignmentCenter; + } + + case NSJustifiedTextAlignment: + { + return kCTTextAlignmentJustified; + } + + case NSNaturalTextAlignment: + { + return kCTTextAlignmentNatural; + } +#endif + + default: + { + DTLogError(@"Unknown alignment %d", (int)nsTextAlignment); + return 0; + } + } +} + +NSTextAlignment DTNSTextAlignmentFromCTTextAlignment(CTTextAlignment ctTextAlignment) +{ +#if TARGET_OS_IPHONE + switch (ctTextAlignment) + { + case kCTTextAlignmentLeft: + { + return NSTextAlignmentLeft; + } + + case kCTTextAlignmentRight: + { + return NSTextAlignmentRight; + } + + case kCTTextAlignmentCenter: + { + return NSTextAlignmentCenter; + } + + case kCTTextAlignmentJustified: + { + return NSTextAlignmentJustified; + } + + case kCTTextAlignmentNatural: + { + return NSTextAlignmentNatural; + } + } +#else + switch (ctTextAlignment) + { + case kCTTextAlignmentLeft: + { + return NSLeftTextAlignment; + } + + case kCTTextAlignmentRight: + { + return NSRightTextAlignment; + } + + case kCTTextAlignmentCenter: + { + return NSCenterTextAlignment; + } + + case kCTTextAlignmentJustified: + { + return NSJustifiedTextAlignment; + } + + case kCTTextAlignmentNatural: + { + return NSNaturalTextAlignment; + } + } +#endif +} + +#endif + + diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.h b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.h new file mode 100644 index 0000000..5f0b975 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.h @@ -0,0 +1,153 @@ +// +// DTCoreTextGlyphRun.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/25/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@class DTCoreTextLayoutLine; +@class DTTextAttachment; + + +/** + This class is an Objective-C wrapper around `CTRun` and represents a glyph run. That is, a number of characters from the original `NSAttributedString` that share the same characteristics and attributes. + */ + +@interface DTCoreTextGlyphRun : NSObject +{ + NSRange _stringRange; +} + +/** + @name Creating Glyph Runs + */ + +/** + Creates a new glyph run from a `CTRun`, belonging to a given layout line and with a given offset from the left line origin. + @param run The Core Text glyph run to wrap + @param layoutLine The layout line that this glyph run belongs to + @param offset The offset from the left line origin to place the glyph run at + @returns An initialized DTCoreTextGlyphRun + */ +- (id)initWithRun:(CTRunRef)run layoutLine:(DTCoreTextLayoutLine *)layoutLine offset:(CGFloat)offset; + +/** + @name Drawing + */ + +/** + Draws the receiver into the given context with the position that it derives from the layout line it belongs to. + @see drawDecorationInContext: for drawing the receiver's decoration + @param context The graphics context to draw into + */ +- (void)drawInContext:(CGContextRef)context; + +/** + Draws the receiver's decoration into the given context with the position that it derives from the layout line it belongs to. Decoration is background highlighting, underline and strike-through. + @param context The graphics context to draw into + */ +- (void)drawDecorationInContext:(CGContextRef)context; + +/** + Creates a `CGPath` containing the shapes of all glyphs in the receiver + */ +- (CGPathRef)newPathWithGlyphs; + +/** + @name Getting Information + */ + +/** + Determines the frame of a specific glyph + @param index The index of the glyph + @return The frame of the glyph + */ +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index; + +/** + The bounds of an image encompassing the entire run. + @param context The graphics context used for the measurement + @returns The rectangle containing the result + */ +- (CGRect)imageBoundsInContext:(CGContextRef)context; + +/** + The string range (of the attributed string) that is represented by the receiver + @returns The range + */ +- (NSRange)stringRange; + +/** + The string indices of the receiver + @returns An array of string indices + */ +- (NSArray *)stringIndices; + +/** + The frame rectangle of the glyph run, relative to the layout frame coordinate system + */ +@property (nonatomic, readonly) CGRect frame; + +/** + The number of glyphs that the receiver is made up of + */ +@property (nonatomic, readonly) NSInteger numberOfGlyphs; + +/** + The Core Text attributes that are shared by all gylphs of the receiver + */ +@property (nonatomic, readonly) NSDictionary *attributes; + +/** + Returns `YES` if the receiver is part of a hyperlink, `NO` otherwise + */ +@property (nonatomic, assign, readonly, getter=isHyperlink) BOOL hyperlink; + +/** + Returns `YES` if the receiver represents trailing whitespace in a line. + + This can be used to avoid drawing of background color, strikeout or underline for empty trailing white space glyph runs. + */ +- (BOOL)isTrailingWhitespace; + +/** + The ascent (height above the baseline) of the receiver + */ +@property (nonatomic, readonly) CGFloat ascent; + +/** + The descent (height below the baseline) of the receiver + */ +@property (nonatomic, readonly) CGFloat descent; + +/** + The leading (additional space above the ascent) of the receiver + */ +@property (nonatomic, readonly) CGFloat leading; + +/** + The width of the receiver + */ +@property (nonatomic, readonly) CGFloat width; + +/** + `YES` if the writing direction is Right-to-Left, otherwise `NO` + */ +@property (nonatomic, readonly) BOOL writingDirectionIsRightToLeft; + +/** + The text attachment of the receiver, or `nil` if there is none + */ +@property (nonatomic, readonly) DTTextAttachment *attachment; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.m b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.m new file mode 100644 index 0000000..64c81a3 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.m @@ -0,0 +1,539 @@ +// +// DTCoreTextGlyphRun.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/25/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextGlyphRun.h" +#import "DTCoreTextLayoutLine.h" +#import "DTTextAttachment.h" +#import "DTCoreTextConstants.h" +#import "DTCoreTextParagraphStyle.h" +#import "DTCoreTextFunctions.h" +#import "NSDictionary+DTCoreText.h" +#import "DTWeakSupport.h" +#import "DTLog.h" + +@implementation DTCoreTextGlyphRun +{ + CTRunRef _run; + CGRect _frame; + + CGFloat _offset; // x distance from line origin + CGFloat _ascent; + CGFloat _descent; + CGFloat _leading; + CGFloat _width; + + BOOL _writingDirectionIsRightToLeft; + BOOL _isTrailingWhitespace; + + NSInteger _numberOfGlyphs; + + const CGPoint *_glyphPositionPoints; + + DT_WEAK_VARIABLE DTCoreTextLayoutLine *_line; // retain cycle, since these objects are retained by the _line + DT_WEAK_VARIABLE NSDictionary *_attributes; // weak because it is owned by _run IVAR + NSArray *_stringIndices; + + DTTextAttachment *_attachment; + BOOL _hyperlink; + + BOOL _didCheckForAttachmentInAttributes; + BOOL _didCheckForHyperlinkInAttributes; + BOOL _didCalculateMetrics; + BOOL _didDetermineTrailingWhitespace; +} + +- (id)initWithRun:(CTRunRef)run layoutLine:(DTCoreTextLayoutLine *)layoutLine offset:(CGFloat)offset +{ + self = [super init]; + + if (self) + { + _run = run; + CFRetain(_run); + + _offset = offset; + _line = layoutLine; + } + + return self; +} + +- (void)dealloc +{ + if (_run) + { + CFRelease(_run); + } +} + +#ifndef COVERAGE +// exclude method from coverage testing + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ glyphs=%ld %@>", [self class], (long)[self numberOfGlyphs], NSStringFromCGRect(_frame)]; +} + +#endif + +#pragma mark - Drawing + +- (void)drawInContext:(CGContextRef)context +{ + if (!_run || !context) + { + return; + } + + CGAffineTransform textMatrix = CTRunGetTextMatrix(_run); + + if (CGAffineTransformIsIdentity(textMatrix)) + { + CTRunDraw(_run, context, CFRangeMake(0, 0)); + } + else + { + CGPoint pos = CGContextGetTextPosition(context); + + // set tx and ty to current text pos according to docs + textMatrix.tx = pos.x; + textMatrix.ty = pos.y; + + CGContextSetTextMatrix(context, textMatrix); + + CTRunDraw(_run, context, CFRangeMake(0, 0)); + + // restore identity + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + } +} + +- (void)drawDecorationInContext:(CGContextRef)context +{ + // get the scaling factor of the current translation matrix + CGAffineTransform ctm = CGContextGetCTM(context); + CGFloat contentScale = MAX(ctm.a, -ctm.d); // needed for rounding operations + + if (contentScale<1 || contentScale>2) + { + contentScale = 2; + } + + CGFloat smallestPixelWidth = 1.0f/contentScale; + + DTColor *backgroundColor = [self.attributes backgroundColor]; + + // -------------- Line-Out, Underline, Background-Color + BOOL drawStrikeOut = [[_attributes objectForKey:DTStrikeOutAttribute] boolValue]; + BOOL drawUnderline = [[_attributes objectForKey:(id)kCTUnderlineStyleAttributeName] boolValue]; + + if (drawStrikeOut||drawUnderline||backgroundColor) + { + // calculate area covered by non-whitespace + CGRect lineFrame = _line.frame; + + // LTR line frames include trailing whitespace in width + // we need to subtract it so that we don't highlight/underline it + if (!_line.writingDirectionIsRightToLeft) + { + lineFrame.size.width -= _line.trailingWhitespaceWidth; + } + + // exclude trailing whitespace so that we don't underline too much + CGRect runStrokeBounds = CGRectIntersection(lineFrame, self.frame); + + NSInteger superscriptStyle = [[_attributes objectForKey:(id)kCTSuperscriptAttributeName] integerValue]; + + switch (superscriptStyle) + { + case 1: + { + runStrokeBounds.origin.y -= _ascent * 0.47f; + break; + } + case -1: + { + runStrokeBounds.origin.y += _ascent * 0.25f; + break; + } + default: + break; + } + + if (backgroundColor) + { + CGRect backgroundColorRect = CGRectIntegral(CGRectMake(runStrokeBounds.origin.x, lineFrame.origin.y, runStrokeBounds.size.width, lineFrame.size.height)); + + CGContextSetFillColorWithColor(context, backgroundColor.CGColor); + CGContextFillRect(context, backgroundColorRect); + } + + if (drawStrikeOut || drawUnderline) + { + BOOL didDrawSomething = NO; + + CGContextSaveGState(context); + + CTFontRef usedFont = (__bridge CTFontRef)([_attributes objectForKey:(id)kCTFontAttributeName]); + + CGFloat fontUnderlineThickness; + + if (usedFont) + { + fontUnderlineThickness = CTFontGetUnderlineThickness(usedFont) * smallestPixelWidth; + } + else + { + fontUnderlineThickness = smallestPixelWidth; + } + + CGFloat usedUnderlineThickness = DTCeilWithContentScale(fontUnderlineThickness, contentScale); + + CGContextSetLineWidth(context, usedUnderlineThickness); + + if (drawStrikeOut) + { + CGFloat y; + + if (usedFont) + { + CGFloat strokePosition = CTFontGetXHeight(usedFont)/(CGFloat)2.0; + y = DTRoundWithContentScale(runStrokeBounds.origin.y + _ascent - strokePosition, contentScale); + } + else + { + y = DTRoundWithContentScale((runStrokeBounds.origin.y + self.frame.size.height/2.0f + 1), contentScale); + } + + if ((int)(usedUnderlineThickness/smallestPixelWidth)%2) // odd line width + { + y += smallestPixelWidth/2.0f; // shift down half a pixel to avoid aliasing + } + + CGContextMoveToPoint(context, runStrokeBounds.origin.x, y); + CGContextAddLineToPoint(context, runStrokeBounds.origin.x + runStrokeBounds.size.width, y); + + didDrawSomething = YES; + } + + // only draw underlines if Core Text didn't draw them yet + if (drawUnderline && !DTCoreTextDrawsUnderlinesWithGlyphs()) + { + CGFloat y; + + // use lowest underline position of all glyph runs in same line + CGFloat underlinePosition = [_line underlineOffset]; + + y = DTRoundWithContentScale(_line.baselineOrigin.y + underlinePosition - fontUnderlineThickness/2.0f, contentScale); + + if ((int)(usedUnderlineThickness/smallestPixelWidth)%2) // odd line width + { + y += smallestPixelWidth/2.0f; // shift down half a pixel to avoid aliasing + } + + CGContextMoveToPoint(context, runStrokeBounds.origin.x, y); + CGContextAddLineToPoint(context, runStrokeBounds.origin.x + runStrokeBounds.size.width, y); + + didDrawSomething = YES; + } + + if (didDrawSomething) + { + CGContextStrokePath(context); + } + + CGContextRestoreGState(context); // restore antialiasing + } + } +} + +- (CGPathRef)newPathWithGlyphs +{ + CTFontRef font = (__bridge CTFontRef)[self.attributes objectForKey:(id)kCTFontAttributeName]; + + if (!font) + { + DTLogError(@"CTFont missing on %@", self); + return NULL; + } + + const CGGlyph *glyphs = CTRunGetGlyphsPtr(_run); + const CGPoint *positions = CTRunGetPositionsPtr(_run); + + CGMutablePathRef mutablePath = CGPathCreateMutable(); + + for (NSUInteger i = 0; i < CTRunGetGlyphCount(_run); i++) + { + CGGlyph glyph = glyphs[i]; + CGPoint position = positions[i]; + + CGAffineTransform glyphTransform = CTRunGetTextMatrix(_run); + + glyphTransform = CGAffineTransformScale(glyphTransform, 1, -1); + + + CGPathRef glyphPath = CTFontCreatePathForGlyph(font, glyph, &glyphTransform); + + CGAffineTransform posTransform = CGAffineTransformMakeTranslation(position.x, position.y); + CGPathAddPath(mutablePath, &posTransform, glyphPath); + + CGPathRelease(glyphPath); + } + + return mutablePath; +} + +#pragma mark - Calculations +- (void)calculateMetrics +{ + // calculate metrics + @synchronized(self) + { + if (!_didCalculateMetrics) + { + _width = (CGFloat)CTRunGetTypographicBounds((CTRunRef)_run, CFRangeMake(0, 0), &_ascent, &_descent, &_leading); + _didCalculateMetrics = YES; + } + } +} + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + if (!_glyphPositionPoints) + { + // this is a pointer to the points inside the run, thus no retain necessary + _glyphPositionPoints = CTRunGetPositionsPtr(_run); + } + + if (!_glyphPositionPoints || index >= self.numberOfGlyphs) + { + return CGRectNull; + } + + CGPoint glyphPosition = _glyphPositionPoints[index]; + + CGRect rect = CGRectMake(_line.baselineOrigin.x + glyphPosition.x, _line.baselineOrigin.y - _ascent, _offset + _width - glyphPosition.x, _ascent + _descent); + if (index < self.numberOfGlyphs-1) + { + rect.size.width = _glyphPositionPoints[index+1].x - glyphPosition.x; + } + + return rect; +} + +// TODO: fix indices if the stringRange is modified +- (NSArray *)stringIndices +{ + if (!_stringIndices) + { + const CFIndex *indices = CTRunGetStringIndicesPtr(_run); + NSInteger count = self.numberOfGlyphs; + NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; + NSInteger i; + for (i = 0; i < count; i++) + { + [array addObject:[NSNumber numberWithInteger:indices[i]]]; + } + _stringIndices = array; + } + return _stringIndices; +} + +// bounds of an image encompassing the entire run +- (CGRect)imageBoundsInContext:(CGContextRef)context +{ + return CTRunGetImageBounds(_run, context, CFRangeMake(0, 0)); +} + +// range of the characters from the original string +- (NSRange)stringRange +{ + if (!_stringRange.length) + { + CFRange range = CTRunGetStringRange(_run); + + _stringRange = NSMakeRange(range.location + _line.stringLocationOffset, range.length); + } + + return _stringRange; +} + +- (void)fixMetricsFromAttachment +{ + if (self.attachment) + { + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + _descent = 0; + _ascent = self.attachment.displaySize.height; + } +} + +- (BOOL)isTrailingWhitespace +{ + if (_didDetermineTrailingWhitespace) + { + return _isTrailingWhitespace; + } + + BOOL isTrailing; + + if (_line.writingDirectionIsRightToLeft) + { + isTrailing = (self == [[_line glyphRuns] objectAtIndex:0]); + } + else + { + isTrailing = (self == [[_line glyphRuns] lastObject]); + } + + if (isTrailing) + { + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + // this is trailing whitespace if it matches the lines's trailing whitespace + if (_line.trailingWhitespaceWidth >= _width) + { + _isTrailingWhitespace = YES; + } + } + + _didDetermineTrailingWhitespace = YES; + return _isTrailingWhitespace; +} + +#pragma mark Properites +- (NSInteger)numberOfGlyphs +{ + if (!_numberOfGlyphs) + { + _numberOfGlyphs = CTRunGetGlyphCount(_run); + } + + return _numberOfGlyphs; +} + +- (NSDictionary *)attributes +{ + if (!_attributes) + { + _attributes = (__bridge NSDictionary *)CTRunGetAttributes(_run); + } + + return _attributes; +} + +- (DTTextAttachment *)attachment +{ + if (!_attachment) + { + if (!_didCheckForAttachmentInAttributes) + { + _attachment = [self.attributes objectForKey:NSAttachmentAttributeName]; + + _didCheckForAttachmentInAttributes = YES; + } + } + + return _attachment; +} + +- (BOOL)isHyperlink +{ + if (!_hyperlink) + { + if (!_didCheckForHyperlinkInAttributes) + { + _hyperlink = [self.attributes objectForKey:DTLinkAttribute]!=nil; + + _didCheckForHyperlinkInAttributes = YES; + } + } + + return _hyperlink; +} + +- (CGRect)frame +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return CGRectMake(_line.baselineOrigin.x + _offset, _line.baselineOrigin.y - _ascent, _width, _ascent + _descent); +} + +- (CGFloat)width +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _width; +} + +- (CGFloat)ascent +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _ascent; +} + +- (CGFloat)descent +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _descent; +} + +- (CGFloat)leading +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _leading; +} + +- (BOOL)writingDirectionIsRightToLeft +{ + CTRunStatus status = CTRunGetStatus(_run); + + return (status & kCTRunStatusRightToLeft)!=0; +} + +@synthesize frame = _frame; +@synthesize numberOfGlyphs = _numberOfGlyphs; +@synthesize attributes = _attributes; + +@synthesize ascent = _ascent; +@synthesize descent = _descent; +@synthesize leading = _leading; +@synthesize attachment = _attachment; +@synthesize writingDirectionIsRightToLeft = _writingDirectionIsRightToLeft; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame+Cursor.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame+Cursor.h new file mode 100644 index 0000000..254a03c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame+Cursor.h @@ -0,0 +1,33 @@ +// +// DTCoreTextLayoutFrame+Cursor.h +// DTCoreText +// +// Created by Oliver Drobnik on 10.07.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayoutFrame.h" + +/** + The **Cursor** category extends DTCoreTextLayoutFrame for working with a caret and determine the string index of touch coordinates. + */ + +@interface DTCoreTextLayoutFrame (Cursor) + +/** + Determines the closest string index to a point in the receiver's frame. + + This can be used to find the cursor position to position an input caret at. + @param point The point + @returns The resulting string index + */ +- (NSInteger)closestCursorIndexToPoint:(CGPoint)point; + +/** + The rectangle to draw a caret for a given index + @param index The string index for which to determine a cursor frame + @returns The cursor rectangle + */ +- (CGRect)cursorRectAtIndex:(NSInteger)index; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame+Cursor.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame+Cursor.m new file mode 100644 index 0000000..ae603fe --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame+Cursor.m @@ -0,0 +1,114 @@ +// +// DTCoreTextLayoutFrame+Cursor.m +// DTCoreText +// +// Created by Oliver Drobnik on 10.07.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayoutFrame+Cursor.h" +#import "DTCoreTextLayoutLine.h" + +@implementation DTCoreTextLayoutFrame (Cursor) + +- (NSInteger)closestCursorIndexToPoint:(CGPoint)point +{ + NSArray *lines = self.lines; + + if (![lines count]) + { + return NSNotFound; + } + + DTCoreTextLayoutLine *firstLine = [lines objectAtIndex:0]; + if (point.y < CGRectGetMinY(firstLine.frame)) + { + return 0; + } + + DTCoreTextLayoutLine *lastLine = [lines lastObject]; + if (point.y > CGRectGetMaxY(lastLine.frame)) + { + NSRange stringRange = [self visibleStringRange]; + + if (stringRange.length) + { + return NSMaxRange([self visibleStringRange])-1; + } + } + + // find closest line + DTCoreTextLayoutLine *closestLine = nil; + CGFloat closestDistance = CGFLOAT_MAX; + + for (DTCoreTextLayoutLine *oneLine in lines) + { + // line contains point + if (CGRectGetMinY(oneLine.frame) <= point.y && CGRectGetMaxY(oneLine.frame) >= point.y) + { + closestLine = oneLine; + break; + } + + CGFloat top = CGRectGetMinY(oneLine.frame); + CGFloat bottom = CGRectGetMaxY(oneLine.frame); + + CGFloat distance = CGFLOAT_MAX; + + if (top > point.y) + { + distance = top - point.y; + } + else if (bottom < point.y) + { + distance = point.y - bottom; + } + + if (distance < closestDistance) + { + closestLine = oneLine; + closestDistance = distance; + } + } + + if (!closestLine) + { + return NSNotFound; + } + + NSInteger closestIndex = [closestLine stringIndexForPosition:point]; + + NSInteger maxIndex = NSMaxRange([closestLine stringRange])-1; + + if (closestIndex > maxIndex) + { + closestIndex = maxIndex; + } + + if (closestIndex>=0) + { + return closestIndex; + } + + return NSNotFound; +} + +- (CGRect)cursorRectAtIndex:(NSInteger)index +{ + DTCoreTextLayoutLine *line = [self lineContainingIndex:index]; + + if (!line) + { + return CGRectZero; + } + + CGFloat offset = [line offsetForStringIndex:index]; + + CGRect rect = line.frame; + rect.size.width = 3.0; + rect.origin.x += offset; + + return rect; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.h new file mode 100644 index 0000000..c4adfed --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.h @@ -0,0 +1,377 @@ +// +// DTCoreTextLayoutFrame.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +#import "DTCoreTextConstants.h" + +@class DTCoreTextLayoutLine; +@class DTTextBlock; + + +typedef void (^DTCoreTextLayoutFrameTextBlockHandler)(DTTextBlock *textBlock, CGRect frame, CGContextRef context, BOOL *shouldDrawDefaultBackground); + +/** + The drawing options for DTCoreTextLayoutFrame + */ +typedef NS_ENUM(NSUInteger, DTCoreTextLayoutFrameDrawingOptions) +{ + /** + The default method for drawing draws links and attachments. Links are drawn non-highlighted + */ + DTCoreTextLayoutFrameDrawingDefault = 1<<0, + + /** + Links are not drawn, e.g. if they are displayed via custom buttons + */ + DTCoreTextLayoutFrameDrawingOmitLinks = 1<<1, + + /** + Text attachments are omitted from drawing, e.g. if they are displayed via custom views + */ + DTCoreTextLayoutFrameDrawingOmitAttachments = 1<<2, + + /** + If links are drawn they are displayed with the highlighted variant + */ + DTCoreTextLayoutFrameDrawingDrawLinksHighlighted = 1<<3 +} ; + + +@class DTCoreTextLayouter; + +/** + This class represents a single frame of text and basically wraps CTFrame. It provides an array of text lines that fit in the given rectangle. + + Both styles of layouting are supported: open ended (suitable for scroll views) and limited to a given rectangle. To use the open-ended style specify `CGFLOAT_HEIGHT_UNKNOWN` for the height when creating a layout frame. + + The array of lines is built lazily the first time it is accessed or - for open-ended frames - when the frame property is being queried. + */ +@interface DTCoreTextLayoutFrame : NSObject +{ + CGRect _frame; + + NSArray *_lines; + NSArray *_paragraphRanges; + + NSArray *_textAttachments; + NSAttributedString *_attributedStringFragment; +} + + +/** + @name Creating Layout Frames + */ + + +/** + Creates a Layout Frame with the given frame using the attributed string loaded into the layouter. + + @param frame The rectangle specifying origin and size of available for text. Specify `CGFLOAT_WIDTH_UNKNOWN` to not limit the width. Specify `CGFLOAT_HEIGHT_UNKNOWN` to not limit the height. + @param layouter A reference to the layouter for this text box. + */ +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter; + + +/** + Creates a Layout Frame with the given frame using the attributed string loaded into the layouter. + + @param frame The rectangle specifying origin and size of available for text. Specify `CGFLOAT_WIDTH_UNKNOWN` to not limit the width. Specify `CGFLOAT_HEIGHT_UNKNOWN` to not limit the height. + @param layouter A reference to the layouter for the receiver. Note: The layouter owns the attributed string. + @param range The range within the attributed string to layout into the receiver. + */ +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter range:(NSRange)range; + + +/** + @name Getting Information + */ + + +/** + The string range that is visible i.e. fits into the given rectangle. For open-ended frames this is typically the entire string. For frame-contrained layout frames it is the substring that fits. + */ +- (NSRange)visibleStringRange; + + +/** + This is a copy of the attributed string owned by the layouter of the receiver. +*/ +- (NSAttributedString *)attributedStringFragment; + + +/** + An array that maps glyphs with string indices. + */ +- (NSArray *)stringIndices; + + +/** + The frame rectangle for the layout frame. + */ + @property (nonatomic, assign, readonly) CGRect frame; + + +/** + Calculates the frame that is covered by the text content. + + The result is calculated by enumerating over all lines and creating a union over all their frames. This is different than the frame property since this gets calculated. + @returns The area that is covered by the text content. + @note The width depends on how many glyphs Core Text was able to fit into a line. A line that gets broken might not have glyphs all the way to the margin. The y origin is always adjusted to be the same as frame since the first line might have some leading. The height is the minimum height that fits all layout lines. + */ +- (CGRect)intrinsicContentFrame; + + +/** + @name Drawing + */ + + +/** + Draws the receiver into the given graphics context. + + @warning This method is deprecated, use -[DTCoreTextLayoutFrame drawInContext:options:] instead + @param context A graphics context to draw into + @param drawImages Whether images should be drawn together with the text. If you specify `NO` then space is left blank where images would go and you have to add your own views to display these images. + @param drawLinks Whether hyperlinks should be drawn together with the text. If you specify `NO` then space is left blank where links would go and you have to add your own views to display these images. + */ +- (void)drawInContext:(CGContextRef)context drawImages:(BOOL)drawImages drawLinks:(BOOL)drawLinks __attribute__((deprecated("use -[DTCoreTextLayoutFrame drawInContext:options:] instead"))); + + +/** + Draws the receiver into the given graphics context. + + @param context A graphics context to draw into + @param options The drawing options. See DTCoreTextLayoutFrameDrawingOptions for available options. + */ +- (void)drawInContext:(CGContextRef)context options:(DTCoreTextLayoutFrameDrawingOptions)options; + + +/** + Set a custom handler to be executed before text belonging to a text block is drawn. Of type . +*/ +@property (nonatomic, copy) DTCoreTextLayoutFrameTextBlockHandler textBlockHandler; + + +/** + @name Working with Glyphs + */ + + +/** + Retrieves the index of the text line that contains the given glyph index. + + @param index The index of the glyph + @returns The index of the line containing this glyph + */ +- (NSInteger)lineIndexForGlyphIndex:(NSInteger)index; + + +/** + Retrieves the frame of the glyph at the given glyph index. + + @param index The index of the glyph + @returns The frame of this glyph + */ +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index; + + +/** + @name Working with Text Lines + */ + + +/** + The text lines that belong to the receiver. + */ +@property (nonatomic, strong, readonly) NSArray *lines; + + +/** + The text lines that are visible inside the given rectangle. Also incomplete lines are included. + + @param rect The rectangle + @returns An array, sorted from top to bottom, of lines at least partially visible + */ +- (NSArray *)linesVisibleInRect:(CGRect)rect; + + +/** + The text lines that are visible inside the given rectangle. Only fully visible lines are included. + + @param rect The rectangle + @returns An array, sorted from top to bottom, of lines fully visible + */ +- (NSArray *)linesContainedInRect:(CGRect)rect; + + +/** + The layout line that contains the given string index. + + @param index The string index + @returns The layout line that this index belongs to + */ +- (DTCoreTextLayoutLine *)lineContainingIndex:(NSUInteger)index; + + +/** + Determins if the given line is the first in a paragraph. + + This is needed for example to determine whether paragraphSpaceBefore needs to be applied before it. + @param line The Line + @returns `YES` if the given line is the first in a paragraph + */ +- (BOOL)isLineFirstInParagraph:(DTCoreTextLayoutLine *)line; + + +/** + Determins if the given line is the last in a paragraph. + + This is needed for example to determine whether paragraph spacing needs to be applied after it. + @param line The Line + @returns `YES` if the given line is the last in a paragraph + */ +- (BOOL)isLineLastInParagraph:(DTCoreTextLayoutLine *)line; + + +/** + Finds the appropriate baseline origin for a line to position it at the correct distance from a previous line. + + Support Layout options are: + + - DTCoreTextLayoutFrameLinePositioningAlgorithmWebKit, + - DTCoreTextLayoutFrameLinePositioningAlgorithmLegacy + + @param line The line + @param previousLine The line after which to position the line. + @param options The layout options to employ for positioning lines + @returns The correct baseline origin for the line. + */ +- (CGPoint)baselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine options:(DTCoreTextLayoutFrameLinePositioningOptions)options; + +/** + Finds the appropriate baseline origin for a line to position it at the correct distance from a previous line using the DTCoreTextLayoutFrameLinePositioningOptionAlgorithmLegacy algorithm. + + @warning This method is deprecated, use -[baselineOriginToPositionLine:afterLine:algorithm:] instead + @param line The line + @param previousLine The line after which to position the line. + @returns The correct baseline origin for the line. + */ +- (CGPoint)baselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine __attribute__((deprecated("use use -[baselineOriginToPositionLine:afterLine:algorithm:] instead")));; + +/** + The ratio to decide when to create a justified line + */ +@property (nonatomic, readwrite) CGFloat justifyRatio; + +/** + @name Text Attachments + */ + + +/** + The array of all instances that belong to the receiver. + @returns All text attachments of the receiver. + */ +- (NSArray *)textAttachments; + + +/** + The array of all DTTextAttachment instances that belong to the receiver which also match the specified predicate. + + @param predicate A predicate that uses properties of to reduce the returned array + @returns A filtered array of text attachments. + */ +- (NSArray *)textAttachmentsWithPredicate:(NSPredicate *)predicate; + + +/** + @name Getting Paragraph Info + */ + + +/** + Finding which paragraph a given string index belongs to. + + @param stringIndex The index in the string to look for + @returns The index of the paragraph, numbered from 0 + */ +- (NSUInteger)paragraphIndexContainingStringIndex:(NSUInteger)stringIndex; + + +/** + Determines the paragraph range (of paragraph indexes) that encompass the entire given string Range. + + @param stringRange The string range for which the paragraph range is sought for + @returns The range of paragraphs that fully enclose the string range + */ +- (NSRange)paragraphRangeContainingStringRange:(NSRange)stringRange; + + +/** + The text lines that belong to the specified paragraph. + + @param index The index of the paragraph + @returns An array, sorted from top to bottom, of lines in this paragraph + */ +- (NSArray *)linesInParagraphAtIndex:(NSUInteger)index; + + +/** + An array of `NSRange` values encapsulated in `NSValue` instances. Each range is the string range contained in the corresponding paragraph. +*/ +@property (nonatomic, strong, readonly) NSArray *paragraphRanges; + + +/** + @name Debugging + */ + + +/** + Switches on the debug drawing mode where individual glph runs, baselines, et ceter get individually marked. + + @param debugFrames if the debug drawing should occur + */ ++ (void)setShouldDrawDebugFrames:(BOOL)debugFrames; + + +/** + @returns the current value of the debug frame drawing + */ ++ (BOOL)shouldDrawDebugFrames; + +/** + @name Truncation + */ + + +/** + Maximum number of lines to display before truncation. Default is 0 which indicates no limit. + */ +@property(nonatomic, assign) NSInteger numberOfLines; + + +/** + Line break mode used to indicate how truncation should occur + */ +@property(nonatomic, assign) NSLineBreakMode lineBreakMode; + + +/** + Optional attributed string tu use as truncation indicator. If nil, will use "…" w/ attributes taken from text being truncated + */ +@property(nonatomic, strong)NSAttributedString *truncationString; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.m new file mode 100644 index 0000000..36b4e67 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.m @@ -0,0 +1,1851 @@ +// +// DTCoreTextLayoutFrame.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTCoreTextConstants.h" +#import "DTCoreTextLayoutFrame.h" +#import "NSDictionary+DTCoreText.h" +#import "DTLog.h" + +// global flag that shows debug frames +static BOOL _DTCoreTextLayoutFramesShouldDrawDebugFrames = NO; + +@implementation DTCoreTextLayoutFrame +{ + CTFrameRef _textFrame; + CTFramesetterRef _framesetter; + + NSRange _requestedStringRange; + NSRange _stringRange; + + CGFloat _additionalPaddingAtBottom; // when last line in a text block with padding + + NSInteger _numberLinesFitInFrame; + DTCoreTextLayoutFrameTextBlockHandler _textBlockHandler; + + CGFloat _longestLayoutLineWidth; +} + +// makes a frame for a specific part of the attributed string of the layouter +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter range:(NSRange)range +{ + self = [super init]; + + if (self) + { + _frame = frame; + + _attributedStringFragment = [layouter.attributedString mutableCopy]; + + // determine correct target range + _requestedStringRange = range; + NSUInteger stringLength = [_attributedStringFragment length]; + + if (_requestedStringRange.location >= stringLength) + { + return nil; + } + + if (_requestedStringRange.length==0 || NSMaxRange(_requestedStringRange) > stringLength) + { + _requestedStringRange.length = stringLength - _requestedStringRange.location; + } + + CFRange cfRange = CFRangeMake(_requestedStringRange.location, _requestedStringRange.length); + _framesetter = layouter.framesetter; + + if (_framesetter) + { + CFRetain(_framesetter); + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, frame); + + _textFrame = CTFramesetterCreateFrame(_framesetter, cfRange, path, NULL); + + CGPathRelease(path); + } + else + { + // Strange, should have gotten a valid framesetter + return nil; + } + + _justifyRatio = 0.6f; + } + + return self; +} + +// makes a frame for the entire attributed string of the layouter +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter +{ + return [self initWithFrame:frame layouter:layouter range:NSMakeRange(0, 0)]; +} + +- (void)dealloc +{ + if (_textFrame) + { + CFRelease(_textFrame); + } + + if (_framesetter) + { + CFRelease(_framesetter); + } +} + +#ifndef COVERAGE +// exclude method from coverage testing + +- (NSString *)description +{ + return [self.lines description]; +} + +#endif + +#pragma mark - Positioning Lines + +- (CGPoint)_algorithmLegacy_BaselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine +{ + CGPoint lineOrigin = previousLine.baselineOrigin; + + NSInteger lineStartIndex = line.stringRange.location; + + CTParagraphStyleRef lineParagraphStyle = (__bridge CTParagraphStyleRef)[_attributedStringFragment + attribute:(id)kCTParagraphStyleAttributeName + atIndex:lineStartIndex effectiveRange:NULL]; + + //Meet the first line in this frame + if (!previousLine) + { + // The first line may or may not be the start of paragraph. It depends on the the range passing to + // - (DTCoreTextLayoutFrame *)layoutFrameWithRect:(CGRect)frame range:(NSRange)range; + // So Check it in a safe way: + if ([self isLineFirstInParagraph:line]) + { + + CGFloat paraSpacingBefore = 0; + + if (CTParagraphStyleGetValueForSpecifier(lineParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(paraSpacingBefore), ¶SpacingBefore)) + { + lineOrigin.y += paraSpacingBefore; + } + + // preserve own baseline x + lineOrigin.x = line.baselineOrigin.x; + + // origins are rounded + lineOrigin.y = ceil(lineOrigin.y); + + return lineOrigin; + + } + + } + + // get line height in px if it is specified for this line + CGFloat lineHeight = 0; + CGFloat minLineHeight = 0; + CGFloat maxLineHeight = 0; + BOOL usesForcedLineHeight = NO; + + CGFloat usedLeading = line.leading; + + if (usedLeading == 0.0f) + { + // font has no leading, so we fake one (e.g. Helvetica) + CGFloat tmpHeight = line.ascent + line.descent; + usedLeading = ceil(0.2f * tmpHeight); + + if (usedLeading>20) + { + // we have a large image increasing the ascender too much for this calc to work + usedLeading = 0; + } + } + else + { + // make sure that we don't have less than 10% of line height as leading + usedLeading = ceil(MAX((line.ascent + line.descent)*0.1f, usedLeading)); + } + + if (CTParagraphStyleGetValueForSpecifier(lineParagraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(minLineHeight), &minLineHeight)) + { + usesForcedLineHeight = YES; + + if (lineHeight0.0f) + { + lineHeight *= lineHeightMultiplier; + } + } + + if (CTParagraphStyleGetValueForSpecifier(lineParagraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(maxLineHeight), &maxLineHeight)) + { + if (maxLineHeight>0 && lineHeight>maxLineHeight) + { + lineHeight = maxLineHeight; + } + } + + lineOrigin.y += lineHeight; + + // preserve own baseline x + lineOrigin.x = line.baselineOrigin.x; + + // prevent overlap of a line with small font size with line before it + if (!usesForcedLineHeight) + { + // only if there IS a line before it AND the line height is not fixed + CGFloat previousLineBottom = CGRectGetMaxY(previousLine.frame); + + if (lineOrigin.y - line.ascent < previousLineBottom) + { + // move baseline origin down far enough + lineOrigin.y = previousLineBottom + line.ascent; + } + } + + // origins are rounded + lineOrigin.y = ceil(lineOrigin.y); + + return lineOrigin; +} + +// determins the "half leading" +- (CGFloat)_algorithmWebKit_halfLeadingOfLine:(DTCoreTextLayoutLine *)line +{ + CGFloat maxFontSize = [line lineHeight]; + + DTCoreTextParagraphStyle *paragraphStyle = [line paragraphStyle]; + + if (paragraphStyle.minimumLineHeight && paragraphStyle.minimumLineHeight > maxFontSize) + { + maxFontSize = paragraphStyle.minimumLineHeight; + } + + if (paragraphStyle.maximumLineHeight && paragraphStyle.maximumLineHeight < maxFontSize) + { + maxFontSize = paragraphStyle.maximumLineHeight; + } + + CGFloat leading; + + if (paragraphStyle.lineHeightMultiple > 0) + { + leading = maxFontSize * paragraphStyle.lineHeightMultiple; + } + else + { + // reasonable "normal" + leading = maxFontSize * 1.1f; + } + + // subtract inline box height + CGFloat inlineBoxHeight = line.ascent + line.descent; + + return (leading - inlineBoxHeight)/2.0f; +} + + +- (CGPoint)_algorithmWebKit_BaselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine +{ + CGPoint baselineOrigin = previousLine.baselineOrigin; + + if (previousLine) + { + baselineOrigin.y = CGRectGetMaxY(previousLine.frame); + + CGFloat halfLeadingFromText = [self _algorithmWebKit_halfLeadingOfLine:previousLine]; + + if (previousLine.attachments) + { + // only add half leading if there are no attachments, this prevents line from being shifted up due to negative half leading + if (halfLeadingFromText>0) + { + baselineOrigin.y += halfLeadingFromText; + } + } + else + { + baselineOrigin.y += halfLeadingFromText; + } + + // add previous line's after paragraph spacing + if ([self isLineLastInParagraph:previousLine]) + { + DTCoreTextParagraphStyle *paragraphStyle = [previousLine paragraphStyle]; + baselineOrigin.y += paragraphStyle.paragraphSpacing; + } + } + else + { + // first line in frame + baselineOrigin = _frame.origin; + } + + baselineOrigin.y += line.ascent; + + CGFloat halfLeadingFromText = [self _algorithmWebKit_halfLeadingOfLine:line]; + + if (line.attachments) + { + // only add half leading if there are no attachments, this prevents line from being shifted up due to negative half leading + if (halfLeadingFromText>0) + { + baselineOrigin.y += halfLeadingFromText; + } + } + else + { + baselineOrigin.y += halfLeadingFromText; + } + + DTCoreTextParagraphStyle *paragraphStyle = [line paragraphStyle]; + + // add current line's before paragraph spacing + if ([self isLineFirstInParagraph:line]) + { + baselineOrigin.y += paragraphStyle.paragraphSpacingBefore; + } + + // add padding for closed text blocks + for (DTTextBlock *previousTextBlock in previousLine.textBlocks) + { + if (![line.textBlocks containsObject:previousTextBlock]) + { + baselineOrigin.y += previousTextBlock.padding.bottom; + } + } + + // add padding for newly opened text blocks + for (DTTextBlock *currentTextBlock in line.textBlocks) + { + if (![previousLine.textBlocks containsObject:currentTextBlock]) + { + baselineOrigin.y += currentTextBlock.padding.top; + } + } + + // origins are rounded + baselineOrigin.y = ceil(baselineOrigin.y); + + return baselineOrigin; +} + + +- (CGPoint)baselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine options:(DTCoreTextLayoutFrameLinePositioningOptions)options +{ + if (options & DTCoreTextLayoutFrameLinePositioningOptionAlgorithmWebKit) + { + return [self _algorithmWebKit_BaselineOriginToPositionLine:line afterLine:previousLine]; + } + + if (options & DTCoreTextLayoutFrameLinePositioningOptionAlgorithmLegacy) + { + return [self _algorithmLegacy_BaselineOriginToPositionLine:line afterLine:previousLine]; + } + + return CGPointZero; +} + +// deprecated +- (CGPoint)baselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine +{ + return [self baselineOriginToPositionLine:line afterLine:previousLine options:DTCoreTextLayoutFrameLinePositioningOptionAlgorithmWebKit]; +} + +#pragma mark - Building the Lines + +/* + Builds the array of lines with the internal typesetter of our framesetter. No need to correct line origins in this case because they are placed correctly in the first place. This version supports text boxes. + */ +- (void)_buildLinesWithTypesetter +{ + // framesetter keeps internal reference, no need to retain + CTTypesetterRef typesetter = CTFramesetterGetTypesetter(_framesetter); + + NSMutableArray *typesetLines = [NSMutableArray array]; + + DTCoreTextLayoutLine *previousLine = nil; + + // need the paragraph ranges to know if a line is at the beginning of paragraph + NSMutableArray *paragraphRanges = [[self paragraphRanges] mutableCopy]; + + NSRange currentParagraphRange = [[paragraphRanges objectAtIndex:0] rangeValue]; + + // we start out in the requested range, length will be set by the suggested line break function + NSRange lineRange = _requestedStringRange; + + // maximum values for abort of loop + CGFloat maxY = CGRectGetMaxY(_frame); + NSUInteger maxIndex = NSMaxRange(_requestedStringRange); + NSUInteger fittingLength = 0; + BOOL shouldTruncateLine = NO; + + do // for each line + { + while (lineRange.location >= (currentParagraphRange.location+currentParagraphRange.length)) + { + // we are outside of this paragraph, so we go to the next + [paragraphRanges removeObjectAtIndex:0]; + + currentParagraphRange = [[paragraphRanges objectAtIndex:0] rangeValue]; + } + + BOOL isAtBeginOfParagraph = (currentParagraphRange.location == lineRange.location); + + CGFloat headIndent = 0; + CGFloat tailIndent = 0; + + // get the paragraph style at this index + CTParagraphStyleRef paragraphStyle = (__bridge CTParagraphStyleRef)[_attributedStringFragment attribute:(id)kCTParagraphStyleAttributeName atIndex:lineRange.location effectiveRange:NULL]; + + if (isAtBeginOfParagraph) + { + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(headIndent), &headIndent); + } + else + { + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(headIndent), &headIndent); + } + + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(tailIndent), &tailIndent); + + // add left padding to offset + CGFloat lineOriginX; + CGFloat availableSpace; + + NSArray *textBlocks = [_attributedStringFragment attribute:DTTextBlocksAttribute atIndex:lineRange.location effectiveRange:NULL]; + CGFloat totalLeftPadding = 0; + CGFloat totalRightPadding = 0; + + for (DTTextBlock *oneTextBlock in textBlocks) + { + totalLeftPadding += oneTextBlock.padding.left; + totalRightPadding += oneTextBlock.padding.right; + } + + if (tailIndent<=0) + { + // negative tail indent is measured from trailing margin (we assume LTR here) + availableSpace = _frame.size.width - headIndent - totalRightPadding + tailIndent - totalLeftPadding; + } + else + { + availableSpace = tailIndent - headIndent - totalLeftPadding - totalRightPadding; + } + + + CGFloat offset = totalLeftPadding; + + // if first character is a tab, then it is positioned without the indentation + if (![[[_attributedStringFragment string] substringWithRange:NSMakeRange(lineRange.location, 1)] isEqualToString:@"\t"]) + { + offset += headIndent; + } + + // find how many characters we get into this line + lineRange.length = CTTypesetterSuggestLineBreak(typesetter, lineRange.location, availableSpace); + + if (NSMaxRange(lineRange) > maxIndex) + { + // only layout as much as was requested + lineRange.length = maxIndex - lineRange.location; + } + + + // determine whether this is a normal line or if it should be truncated + shouldTruncateLine = ((self.numberOfLines>0 && [typesetLines count]+1==self.numberOfLines) || (_numberLinesFitInFrame>0 && _numberLinesFitInFrame==[typesetLines count]+1)); + + CTLineRef line; + BOOL isHyphenatedString = NO; + + if (!shouldTruncateLine) + { + static const unichar softHypen = 0x00AD; + NSString *lineString = [[_attributedStringFragment attributedSubstringFromRange:lineRange] string]; + unichar lastChar = [lineString characterAtIndex:[lineString length] - 1]; + if (softHypen == lastChar) + { + NSMutableAttributedString *hyphenatedString = [[_attributedStringFragment attributedSubstringFromRange:lineRange] mutableCopy]; + NSRange replaceRange = NSMakeRange(hyphenatedString.length - 1, 1); + [hyphenatedString replaceCharactersInRange:replaceRange withString:@"-"]; + line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)hyphenatedString); + isHyphenatedString = YES; + } + else + { + // create a line to fit + line = CTTypesetterCreateLine(typesetter, CFRangeMake(lineRange.location, lineRange.length)); + } + } + else + { + // extend the line to the end of the current paragraph + // if we extend to the entire to the entire text range + // it is possible to pull lines up from paragraphs below us + NSRange oldLineRange = lineRange; + lineRange.length = NSMaxRange(currentParagraphRange)-lineRange.location; + CTLineRef baseLine = CTTypesetterCreateLine(typesetter, CFRangeMake(lineRange.location, lineRange.length)); + + // convert lineBreakMode to CoreText type + CTLineTruncationType truncationType = DTCTLineTruncationTypeFromNSLineBreakMode(self.lineBreakMode); + + // prepare truncation string + NSAttributedString * attribStr = self.truncationString; + if(attribStr == nil) + { + NSRange range; + NSInteger index = oldLineRange.location; + if (truncationType == kCTLineTruncationEnd) + { + index += (oldLineRange.length > 0 ? oldLineRange.length - 1 : 0); + } + else if (truncationType == kCTLineTruncationMiddle) + { + index += (oldLineRange.length > 1 ? (oldLineRange.length/2.0 - 1) : 0); + } + NSDictionary * attributes = [_attributedStringFragment attributesAtIndex:index effectiveRange:&range]; + attribStr = [[NSAttributedString alloc] initWithString:@"…" attributes:attributes]; + } + + CTLineRef elipsisLineRef = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)(attribStr)); + + // create the truncated line + line = CTLineCreateTruncatedLine(baseLine, availableSpace, truncationType, elipsisLineRef); + + // clean up + CFRelease(baseLine); + CFRelease(elipsisLineRef); + } + + // we need all metrics so get the at once + CGFloat currentLineWidth = (CGFloat)CTLineGetTypographicBounds(line, NULL, NULL, NULL); + + // adjust lineOrigin based on paragraph text alignment + CTTextAlignment textAlignment; + + if (!CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierAlignment, sizeof(textAlignment), &textAlignment)) + { + textAlignment = kCTNaturalTextAlignment; + } + + // determine writing direction + BOOL isRTL = NO; + CTWritingDirection baseWritingDirection; + + if (CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(baseWritingDirection), &baseWritingDirection)) + { + isRTL = (baseWritingDirection == kCTWritingDirectionRightToLeft); + } + else + { + baseWritingDirection = kCTWritingDirectionNatural; + } + + switch (textAlignment) + { + case kCTLeftTextAlignment: + { + lineOriginX = _frame.origin.x + offset; + // nothing to do + break; + } + + case kCTNaturalTextAlignment: + { + lineOriginX = _frame.origin.x + offset; + + if (baseWritingDirection != kCTWritingDirectionRightToLeft) + { + break; + } + + // right alignment falls through + } + + case kCTRightTextAlignment: + { + lineOriginX = _frame.origin.x + offset + (CGFloat)CTLineGetPenOffsetForFlush(line, 1.0, availableSpace); + + break; + } + + case kCTCenterTextAlignment: + { + lineOriginX = _frame.origin.x + offset + (CGFloat)CTLineGetPenOffsetForFlush(line, 0.5, availableSpace); + + break; + } + + case kCTJustifiedTextAlignment: + { + BOOL isAtEndOfParagraph = (currentParagraphRange.location+currentParagraphRange.length <= lineRange.location+lineRange.length || + [[_attributedStringFragment string] characterAtIndex:lineRange.location+lineRange.length-1]==0x2028); + + // only justify if not last line, not
, and if the line width is longer than _justifyRatio of the frame + // avoids over-stretching + if( !isAtEndOfParagraph && (currentLineWidth > _justifyRatio * _frame.size.width) ) + { + // create a justified line and replace the current one with it + CTLineRef justifiedLine = CTLineCreateJustifiedLine(line, 1.0f, availableSpace); + + // CTLineCreateJustifiedLine sometimes fails if the line ends with 0x00AD (soft hyphen) and contains cyrillic chars + if (justifiedLine) + { + CFRelease(line); + line = justifiedLine; + } + } + + if (isRTL) + { + // align line with right margin + lineOriginX = _frame.origin.x + offset + (CGFloat)CTLineGetPenOffsetForFlush(line, 1.0, availableSpace); + } + else + { + // align line with left margin + lineOriginX = _frame.origin.x + offset; + } + + break; + } + } + + if (!line) + { + continue; + } + + // wrap it + DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:line + stringLocationOffset:isHyphenatedString ? lineRange.location : 0]; + newLine.writingDirectionIsRightToLeft = isRTL; + CFRelease(line); + + // determine position of line based on line before it + + CGPoint newLineBaselineOrigin = [self _algorithmWebKit_BaselineOriginToPositionLine:newLine afterLine:previousLine]; + newLineBaselineOrigin.x = lineOriginX; + newLine.baselineOrigin = newLineBaselineOrigin; + + // abort layout if we left the configured frame + CGFloat lineBottom = CGRectGetMaxY(newLine.frame); + + if (lineBottom>maxY) + { + if ([typesetLines count] && self.lineBreakMode) + { + _numberLinesFitInFrame = [typesetLines count]; + [self _buildLinesWithTypesetter]; + + return; + } + else + { + // doesn't fit any more + break; + } + } + + [typesetLines addObject:newLine]; + fittingLength += lineRange.length; + + lineRange.location += lineRange.length; + previousLine = newLine; + } + while (lineRange.location < maxIndex && !shouldTruncateLine); + + _lines = typesetLines; + + if (![_lines count]) + { + // no lines fit + _stringRange = NSMakeRange(0, 0); + + return; + } + + // now we know how many characters fit + _stringRange.location = _requestedStringRange.location; + _stringRange.length = fittingLength; + + // at this point we can correct the frame if it is open-ended + if (_frame.size.height == CGFLOAT_HEIGHT_UNKNOWN) + { + DTCoreTextLayoutLine *lastLine = [_lines lastObject]; + + CGFloat totalPadding = 0; + + for (DTTextBlock *oneTextBlock in lastLine.textBlocks) + { + totalPadding += oneTextBlock.padding.bottom; + } + + // need to add bottom padding if in text block + _additionalPaddingAtBottom = totalPadding; + } +} + +/** + DEPRECATED: this was the original way of getting the lines + */ + +- (void)_buildLinesWithStandardFramesetter +{ + // get lines (don't own it so no release) + CFArrayRef cflines = CTFrameGetLines(_textFrame); + + if (!cflines) + { + // probably no string set + return; + } + + CGPoint *origins = malloc(sizeof(CGPoint)*CFArrayGetCount(cflines)); + CTFrameGetLineOrigins(_textFrame, CFRangeMake(0, 0), origins); + + NSMutableArray *tmpLines = [[NSMutableArray alloc] initWithCapacity:CFArrayGetCount(cflines)]; + + NSInteger lineIndex = 0; + + for (id oneLine in (__bridge NSArray *)cflines) + { + CGPoint lineOrigin = origins[lineIndex]; + + lineOrigin.y = _frame.size.height - lineOrigin.y + _frame.origin.y; + lineOrigin.x += _frame.origin.x; + + DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:(__bridge CTLineRef)oneLine]; + newLine.baselineOrigin = lineOrigin; + + [tmpLines addObject:newLine]; + + lineIndex++; + } + free(origins); + + _lines = tmpLines; + + // need to get the visible range here + CFRange fittingRange = CTFrameGetStringRange(_textFrame); + _stringRange.location = fittingRange.location; + _stringRange.length = fittingRange.length; +} + +- (void)_buildLines +{ + // only build lines if frame is legal + if (_frame.size.width<=0) + { + return; + } + + // note: building line by line with typesetter + [self _buildLinesWithTypesetter]; + + //[self _buildLinesWithStandardFramesetter]; +} + +- (NSArray *)lines +{ + if (!_lines) + { + [self _buildLines]; + } + + return _lines; +} + +- (NSArray *)linesVisibleInRect:(CGRect)rect +{ + NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:[self.lines count]]; + + CGFloat minY = CGRectGetMinY(rect); + CGFloat maxY = CGRectGetMaxY(rect); + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + CGRect lineFrame = oneLine.frame; + + // lines before the rect + if (CGRectGetMaxY(lineFrame) maxY) + { + break; + } + + // CGRectIntersectsRect returns false if the frame has 0 width, which + // lines that consist only of line-breaks have. Set the min-width + // to one to work-around. + lineFrame.size.width = lineFrame.size.width>1?lineFrame.size.width:1; + + if (CGRectIntersectsRect(rect, lineFrame)) + { + [tmpArray addObject:oneLine]; + } + } + + return tmpArray; +} + +- (NSArray *)linesContainedInRect:(CGRect)rect +{ + NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:[self.lines count]]; + + CGFloat minY = CGRectGetMinY(rect); + CGFloat maxY = CGRectGetMaxY(rect); + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + CGRect lineFrame = oneLine.frame; + + // lines before the rect + if (CGRectGetMaxY(lineFrame) maxY) + { + break; + } + + if (CGRectContainsRect(rect, lineFrame)) + { + [tmpArray addObject:oneLine]; + } + } + + return tmpArray; +} + + +#pragma mark - Text Block Helpers + +// determines the frame to use for a text block with a given effect range at a specific block level +- (CGRect)_blockFrameForEffectiveRange:(NSRange)effectiveRange level:(NSUInteger)level +{ + CGRect blockFrame; + + // we know extent of block, get frame + DTCoreTextLayoutLine *firstBlockLine = [self lineContainingIndex:effectiveRange.location]; + DTCoreTextLayoutLine *lastBlockLine = [self lineContainingIndex:NSMaxRange(effectiveRange)-1]; + + // start with frame spanned from these lines + blockFrame.origin = firstBlockLine.frame.origin; + blockFrame.origin.x = _frame.origin.x; + blockFrame.size.width = _frame.size.width; + blockFrame.size.height = CGRectGetMaxY(lastBlockLine.frame) - blockFrame.origin.y; + + // top paddings we get from first line + for (NSInteger i = [firstBlockLine.textBlocks count]-1; i>=level;i--) + { + if (i<0) + { + break; + } + + DTTextBlock *oneTextBlock = [firstBlockLine.textBlocks objectAtIndex:i]; + + blockFrame.origin.y -= oneTextBlock.padding.top; + blockFrame.size.height += oneTextBlock.padding.top; + } + + // top padding we get from last line + for (NSInteger i = [lastBlockLine.textBlocks count]-1; i>=level;i--) + { + if (i<0) + { + break; + } + + DTTextBlock *oneTextBlock = [lastBlockLine.textBlocks objectAtIndex:i]; + + blockFrame.size.height += oneTextBlock.padding.bottom; + } + + // adjust left and right margins with block stack padding + for (int i=0; i drawableAttachment = (id)attachment; + + // frame might be different due to image vertical alignment + CGFloat ascender = [attachment ascentForLayout]; + CGRect rect = CGRectMake(oneRun.frame.origin.x, oneLine.baselineOrigin.y - ascender, attachment.displaySize.width, attachment.displaySize.height); + + [drawableAttachment drawInRect:rect context:context]; + } + + if (!drawLinks && oneRun.isHyperlink) + { + continue; + } + + // don't draw decorations on images + if (attachment) + { + continue; + } + + // don't draw background, strikout or underline for trailing white space + if ([oneRun isTrailingWhitespace]) + { + continue; + } + + [self _setForgroundColorInContext:context forGlyphRun:oneRun options:options]; + + [oneRun drawDecorationInContext:context]; + } + } + + // Flip the coordinate system + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextScaleCTM(context, 1.0, -1.0); + CGContextTranslateCTM(context, 0, -self.frame.size.height); + + // instead of using the convenience method to draw the entire frame, we draw individual glyph runs + + for (DTCoreTextLayoutLine *oneLine in visibleLines) + { + for (DTCoreTextGlyphRun *oneRun in oneLine.glyphRuns) + { + if (!CGRectIntersectsRect(rect, oneRun.frame)) + { + continue; + } + + if (!drawLinks && oneRun.isHyperlink) + { + continue; + } + + CGPoint textPosition = CGPointMake(oneLine.frame.origin.x, self.frame.size.height - oneRun.frame.origin.y - oneRun.ascent); + + NSInteger superscriptStyle = [[oneRun.attributes objectForKey:(id)kCTSuperscriptAttributeName] integerValue]; + + NSNumber *ascentMultiplier = [oneRun.attributes objectForKey:(id)DTAscentMultiplierAttribute]; + + + switch (superscriptStyle) + { + case 1: + { + textPosition.y += oneRun.ascent * (ascentMultiplier ? [ascentMultiplier floatValue] : 0.47f); + break; + } + case -1: + { + textPosition.y -= oneRun.ascent * (ascentMultiplier ? [ascentMultiplier floatValue] : 0.25f); + break; + } + default: + break; + } + + CGContextSetTextPosition(context, textPosition.x, textPosition.y); + + if (!oneRun.attachment) + { + NSArray *shadows = [oneRun.attributes objectForKey:DTShadowsAttribute]; + + if (shadows) + { + CGContextSaveGState(context); + + NSUInteger numShadows = [shadows count]; + + if (numShadows == 1) + { + // single shadow, we only draw the glyph run with the shadow, no clipping magic + NSDictionary *singleShadow = [shadows objectAtIndex:0]; + [self _setShadowInContext:context fromDictionary:singleShadow additionalOffset:CGSizeZero]; + + [oneRun drawInContext:context]; + } + else // multiple shadows, we shift the text away and then draw a single glyph run over it + { + // get the run bounds, Core Text has bottom left 0,0 so we flip it + CGRect runBoundsFlipped = oneRun.frame; + runBoundsFlipped.origin.y = self.frame.size.height - runBoundsFlipped.origin.y - runBoundsFlipped.size.height; + + // assume that shadows would never be more than 100 pixels away from glyph run frame or outside of frame + CGRect clipRect = CGRectIntersection(CGRectInset(runBoundsFlipped, -100, -100), self.frame); + + // clip to the rect + CGContextAddRect(context, clipRect); + CGContextClipToRect(context, clipRect); + + // Move the text outside of the clip rect so that only the shadow is visisble + CGContextSetTextPosition(context, textPosition.x + clipRect.size.width, textPosition.y); + + // draw each shadow + [shadows enumerateObjectsUsingBlock:^(NSDictionary *shadowDict, NSUInteger idx, BOOL *stop) { + BOOL isLastShadow = (idx == (numShadows-1)); + + if (isLastShadow) + { + // last shadow draws the original text + [self _setShadowInContext:context fromDictionary:shadowDict additionalOffset:CGSizeZero]; + + // ... so we put text position back + CGContextSetTextPosition(context, textPosition.x, textPosition.y); + } + else + { + [self _setShadowInContext:context fromDictionary:shadowDict additionalOffset:CGSizeMake(-clipRect.size.width, 0)]; + } + + [oneRun drawInContext:context]; + }]; + } + + CGContextRestoreGState(context); + } + else // no shadows + { + [self _setForgroundColorInContext:context forGlyphRun:oneRun options:options]; + + [oneRun drawInContext:context]; + } + } + } + } + + if (_textFrame) + { + CFRelease(_textFrame); + } + +#if TARGET_OS_IPHONE + UIGraphicsPopContext(); +#endif + + CGContextRestoreGState(context); +} + +#pragma mark - Text Attachments + +- (NSArray *)textAttachments +{ + if (!_textAttachments) + { + NSMutableArray *tmpAttachments = [NSMutableArray array]; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + for (DTCoreTextGlyphRun *oneRun in oneLine.glyphRuns) + { + DTTextAttachment *attachment = [oneRun attachment]; + + if (attachment) + { + [tmpAttachments addObject:attachment]; + } + } + } + + _textAttachments = [[NSArray alloc] initWithArray:tmpAttachments]; + } + + + return _textAttachments; +} + +- (NSArray *)textAttachmentsWithPredicate:(NSPredicate *)predicate +{ + return [[self textAttachments] filteredArrayUsingPredicate:predicate]; +} + +#pragma mark - Calculations + +- (NSRange)visibleStringRange +{ + if (!_textFrame) + { + return NSMakeRange(0, 0); + } + + if (!_lines) + { + // need to build lines to know range + [self _buildLines]; + } + + return _stringRange; +} + +- (NSArray *)stringIndices +{ + NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self.lines count]]; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + [array addObjectsFromArray:[oneLine stringIndices]]; + } + + return array; +} + +- (NSInteger)lineIndexForGlyphIndex:(NSInteger)index +{ + NSInteger retIndex = 0; + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + NSInteger count = [oneLine numberOfGlyphs]; + if (index >= count) + { + index -= count; + } + else + { + return retIndex; + } + + retIndex++; + } + + return retIndex; +} + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index +{ + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + NSInteger count = [oneLine numberOfGlyphs]; + if (index >= count) + { + index -= count; + } + else + { + return [oneLine frameOfGlyphAtIndex:index]; + } + } + + return CGRectNull; +} + +- (CGRect)frame +{ + if (!_lines) + { + [self _buildLines]; + } + + if (![self.lines count]) + { + return CGRectZero; + } + + if (_frame.size.height == CGFLOAT_HEIGHT_UNKNOWN) + { + // actual frame is spanned between first and last lines + DTCoreTextLayoutLine *lastLine = [_lines lastObject]; + + _frame.size.height = ceil((CGRectGetMaxY(lastLine.frame) - _frame.origin.y + 1.5f + _additionalPaddingAtBottom)); + } + + if (_frame.size.width == CGFLOAT_WIDTH_UNKNOWN) + { + // actual frame width is maximum value of lines + CGFloat maxWidth = 0; + + for (DTCoreTextLayoutLine *oneLine in _lines) + { + CGFloat lineWidthFromFrameOrigin = CGRectGetMaxX(oneLine.frame) - _frame.origin.x; + maxWidth = MAX(maxWidth, lineWidthFromFrameOrigin); + } + + _frame.size.width = ceil(maxWidth); + } + + return _frame; +} + +- (CGRect)intrinsicContentFrame +{ + if (!_lines) + { + [self _buildLines]; + } + + if (![self.lines count]) + { + return CGRectZero; + } + + DTCoreTextLayoutLine *firstLine = [_lines objectAtIndex:0]; + + CGRect outerFrame = self.frame; + + CGRect frameOverAllLines = firstLine.frame; + + // move up to frame origin because first line usually does not go all the ways up + frameOverAllLines.origin.y = outerFrame.origin.y; + + for (DTCoreTextLayoutLine *oneLine in _lines) + { + // need to limit frame to outer frame, otherwise HR causes too long lines + CGRect frame = CGRectIntersection(oneLine.frame, outerFrame); + + frameOverAllLines = CGRectUnion(frame, frameOverAllLines); + } + + // extend height same method as frame + frameOverAllLines.size.height = ceil(frameOverAllLines.size.height + 1.5f + _additionalPaddingAtBottom); + + return CGRectIntegral(frameOverAllLines); +} + +- (DTCoreTextLayoutLine *)lineContainingIndex:(NSUInteger)index +{ + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + if (NSLocationInRange(index, [oneLine stringRange])) + { + return oneLine; + } + } + + return nil; +} + +- (NSArray *)linesInParagraphAtIndex:(NSUInteger)index +{ + NSArray *paragraphRanges = self.paragraphRanges; + + NSAssert(index < [paragraphRanges count], @"index parameter out of range"); + + NSRange range = [[paragraphRanges objectAtIndex:index] rangeValue]; + + NSMutableArray *tmpArray = [NSMutableArray array]; + + // find lines that are in this range + + BOOL insideParagraph = NO; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + if (NSLocationInRange([oneLine stringRange].location, range)) + { + insideParagraph = YES; + [tmpArray addObject:oneLine]; + } + else + { + if (insideParagraph) + { + // that means we left the range + + break; + } + } + } + + // return array only if there is something in it + if ([tmpArray count]) + { + return tmpArray; + } + else + { + return nil; + } +} + +// returns YES if the given line is the first in a paragraph +- (BOOL)isLineFirstInParagraph:(DTCoreTextLayoutLine *)line +{ + NSRange lineRange = line.stringRange; + + if (lineRange.location == 0) + { + return YES; + } + + NSInteger prevLineLastUnicharIndex =lineRange.location - 1; + unichar prevLineLastUnichar = [[_attributedStringFragment string] characterAtIndex:prevLineLastUnicharIndex]; + + return [[NSCharacterSet newlineCharacterSet] characterIsMember:prevLineLastUnichar]; +} + +// returns YES if the given line is the last in a paragraph +- (BOOL)isLineLastInParagraph:(DTCoreTextLayoutLine *)line +{ + NSString *lineString = [[_attributedStringFragment string] substringWithRange:line.stringRange]; + + if ([lineString hasSuffix:@"\n"]) + { + return YES; + } + + return NO; +} + +#pragma mark - Paragraphs +- (NSUInteger)paragraphIndexContainingStringIndex:(NSUInteger)stringIndex +{ + for (NSValue *oneValue in self.paragraphRanges) + { + NSRange range = [oneValue rangeValue]; + + if (NSLocationInRange(stringIndex, range)) + { + return [self.paragraphRanges indexOfObject:oneValue]; + } + } + + return NSNotFound; +} + +- (NSRange)paragraphRangeContainingStringRange:(NSRange)stringRange +{ + NSUInteger firstParagraphIndex = [self paragraphIndexContainingStringIndex:stringRange.location]; + NSUInteger lastParagraphIndex; + + if (stringRange.length) + { + lastParagraphIndex = [self paragraphIndexContainingStringIndex:NSMaxRange(stringRange)-1]; + } + else + { + // range is in a single position, i.e. last paragraph has to be same as first + lastParagraphIndex = firstParagraphIndex; + } + + return NSMakeRange(firstParagraphIndex, lastParagraphIndex - firstParagraphIndex + 1); +} + +#pragma mark - Debugging ++ (void)setShouldDrawDebugFrames:(BOOL)debugFrames +{ + _DTCoreTextLayoutFramesShouldDrawDebugFrames = debugFrames; +} + ++ (BOOL)shouldDrawDebugFrames +{ + return _DTCoreTextLayoutFramesShouldDrawDebugFrames; +} + +#pragma mark - Properties +- (NSAttributedString *)attributedStringFragment +{ + return _attributedStringFragment; +} + +// builds an array +- (NSArray *)paragraphRanges +{ + if (!_paragraphRanges) + { + NSString *plainString = [[self attributedStringFragment] string]; + NSUInteger length = [plainString length]; + + NSRange paragraphRange = [plainString rangeOfParagraphsContainingRange:NSMakeRange(0, 0) parBegIndex:NULL parEndIndex:NULL]; + + NSMutableArray *tmpArray = [NSMutableArray array]; + + while (paragraphRange.length) + { + NSValue *value = [NSValue valueWithRange:paragraphRange]; + [tmpArray addObject:value]; + + NSUInteger nextParagraphBegin = NSMaxRange(paragraphRange); + + if (nextParagraphBegin>=length) + { + break; + } + + // next paragraph + paragraphRange = [plainString rangeOfParagraphsContainingRange:NSMakeRange(nextParagraphBegin, 0) parBegIndex:NULL parEndIndex:NULL]; + } + + _paragraphRanges = tmpArray; // no copy for performance + } + + return _paragraphRanges; +} + +- (void)setNumberOfLines:(NSInteger)numberOfLines +{ + if( _numberOfLines != numberOfLines ) + { + _numberOfLines = numberOfLines; + // clear lines cache + _lines = nil; + } +} + +- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode +{ + if( _lineBreakMode != lineBreakMode ) + { + _lineBreakMode = lineBreakMode; + // clear lines cache + _lines = nil; + } +} + +- (void)setTruncationString:(NSAttributedString *)truncationString +{ + if( ![_truncationString isEqualToAttributedString:truncationString] ) + { + _truncationString = truncationString; + + if( self.numberOfLines > 0 ) + { + // clear lines cache + _lines = nil; + } + } +} + +- (void)setJustifyRatio:(CGFloat)justifyRatio +{ + if (_justifyRatio != justifyRatio) + { + _justifyRatio = justifyRatio; + + // clear lines cache + _lines = nil; + } +} + +@synthesize numberOfLines = _numberOfLines; +@synthesize lineBreakMode = _lineBreakMode; +@synthesize truncationString = _truncationString; +@synthesize frame = _frame; +@synthesize lines = _lines; +@synthesize paragraphRanges = _paragraphRanges; +@synthesize textBlockHandler = _textBlockHandler; +@synthesize justifyRatio = _justifyRatio; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.h new file mode 100644 index 0000000..48e5f08 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.h @@ -0,0 +1,31 @@ +// +// DTCoreTextLayoutFrameAccessibilityElementGenerator.h +// DTCoreText +// +// Created by Austen Green on 3/13/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import +#import "DTAccessibilityElement.h" + +@class DTCoreTextLayoutFrame, DTTextAttachment; + +typedef id(^DTAttachmentViewProvider)(DTTextAttachment *textAttachment); + +/** + Generates an array of objects conforming to the UIAccessibility informal protocol based on a . + */ +@interface DTCoreTextLayoutFrameAccessibilityElementGenerator : NSObject + +/** + The designated initializer. The DTAttachmentViewProvider block may be used to provide custom subviews in place of a static accessibility element. + @param frame The to generate accessibility elements for. + @param view The logical superview of the elements - the view that owns the local coordinate system for drawing the frame. + @param block A callback block which takes a object and returns an object that conforms to the UIAccessibility informal protocol. + @returns Returns an array of objects conforming to the UIAccessibility informal protocol, suitable for presentation fo the VoiceOver system. + */ + +- (NSArray *)accessibilityElementsForLayoutFrame:(DTCoreTextLayoutFrame *)frame view:(UIView *)view attachmentViewProvider:(DTAttachmentViewProvider)block; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m new file mode 100644 index 0000000..f30428b --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m @@ -0,0 +1,119 @@ +// +// DTCoreTextLayoutFrameAccessibilityElementGenerator.m +// DTCoreText +// +// Created by Austen Green on 3/13/13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayoutFrameAccessibilityElementGenerator.h" +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextGlyphRun.h" +#import "DTAccessibilityElement.h" +#import "DTCoreTextConstants.h" +#import "DTTextAttachment.h" + +@implementation DTCoreTextLayoutFrameAccessibilityElementGenerator + +- (NSArray *)accessibilityElementsForLayoutFrame:(DTCoreTextLayoutFrame *)frame view:(UIView *)view attachmentViewProvider:(DTAttachmentViewProvider)block +{ + NSMutableArray *elements = [NSMutableArray array]; + + for (NSUInteger idx = 0; idx < frame.paragraphRanges.count; idx++) + { + NSArray *paragraphElements = [self accessibilityElementsInParagraphAtIndex:idx layoutFrame:frame view:view attachmentViewProvider:block]; + [elements addObjectsFromArray:paragraphElements]; + } + + return elements; +} + +- (NSArray *)accessibilityElementsInParagraphAtIndex:(NSUInteger)index layoutFrame:(DTCoreTextLayoutFrame *)frame view:(UIView *)view attachmentViewProvider:(DTAttachmentViewProvider)block +{ + NSMutableArray *elements = [NSMutableArray array]; + + [self enumerateAccessibleGroupsInFrame:frame forParagraphAtIndex:index usingBlock:^(NSDictionary *attrs, NSRange substringRange, BOOL *stop, NSArray *runs) { + id element = [self accessibilityElementForTextInAttributedString:frame.attributedStringFragment atRange:substringRange attributes:attrs run:runs view:view attachmentViewProvider:block]; + if (element) + [elements addObject:element]; + }]; + + return elements; +} + +- (void)enumerateAccessibleGroupsInFrame:(DTCoreTextLayoutFrame *)frame forParagraphAtIndex:(NSUInteger)index usingBlock:(void(^)(NSDictionary *attrs, NSRange substringRange, BOOL *stop, NSArray *runs))block +{ + NSValue *value = [frame.paragraphRanges objectAtIndex:index]; + NSRange paragraphRange = value.rangeValue; + NSArray *lines = [frame linesInParagraphAtIndex:index]; + + [frame.attributedStringFragment enumerateAttributesInRange:paragraphRange options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + NSMutableArray *runs = [NSMutableArray array]; + for (DTCoreTextLayoutLine *line in lines) + { + [runs addObjectsFromArray:[line glyphRunsWithRange:range]]; + } + + block(attrs, range, stop, runs); + }]; +} + +- (id)accessibilityElementForTextInAttributedString:(NSAttributedString *)attributedString atRange:(NSRange)range attributes:(NSDictionary *)attributes run:(NSArray *)runs view:(UIView *)view attachmentViewProvider:(DTAttachmentViewProvider)block +{ + DTTextAttachment *attachment = [attributes objectForKey:NSAttachmentAttributeName]; + + if (attachment != nil) + return [self viewForAttachment:attachment attachmentViewProvider:block]; + else + return [self accessibilityElementForTextInAttributedString:attributedString atRange:range attributes:attributes run:runs view:view]; +} + +- (DTAccessibilityElement *)accessibilityElementForTextInAttributedString:(NSAttributedString *)attributedString atRange:(NSRange)range attributes:(NSDictionary *)attributes run:(NSArray *)runs view:(UIView *)view +{ + NSString *text = [attributedString.string substringWithRange:range]; + + DTAccessibilityElement *element = [[DTAccessibilityElement alloc] initWithParentView:view]; + element.accessibilityLabel = text; + element.localCoordinateAccessibilityFrame = [self frameForRuns:runs]; + + // We're trying to keep the accessibility frame behavior consistent with UIWebView, which seems to do a union of the rects for all the runs composing a single accessibility group, + // even if that spans across multiple lines. Set the local coordinate activation point to support multi-line links. A link that is at the end of one line and + // wraps to the beginning of the next would have a rect that's the size of both lines combined. The center of that rect would be outside the hit areas for either of the + // runs individually, so we set the accessibility activation point to be the origin of the first run. + if (runs.count > 1) + { + DTCoreTextGlyphRun *run = [runs objectAtIndex:0]; + element.localCoordinateAccessibilityActivationPoint = run.frame.origin; + } + + element.accessibilityTraits = UIAccessibilityTraitStaticText; + + if ([attributes objectForKey:DTLinkAttribute]) + element.accessibilityTraits |= UIAccessibilityTraitLink; + + return element; +} + +- (UIView *)viewForAttachment:(DTTextAttachment *)attachment attachmentViewProvider:(DTAttachmentViewProvider)block +{ + UIView *view = nil; + + if (block) + { + view = block(attachment); + } + + return view; +} + +- (CGRect)frameForRuns:(NSArray *)runs +{ + CGRect frame = CGRectNull; + for (DTCoreTextGlyphRun *run in runs) + frame = CGRectUnion(frame, run.frame); + + return frame; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.h new file mode 100644 index 0000000..648fae2 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.h @@ -0,0 +1,219 @@ +// +// DTCoreTextLayoutLine.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@class DTCoreTextLayoutFrame; +@class DTCoreTextParagraphStyle; +@class DTTextBlock; + +/** + This class represents one layouted line and contains a number of glyph runs. + */ +@interface DTCoreTextLayoutLine : NSObject +{ + // IVAR required by DTRichTextEditor, used in category + NSInteger _stringLocationOffset; // offset to modify internal string location to get actual location +} + +/** + @name Creating Layout Lines + */ + +/** + Creates a layout line from a given `CTLine` + @param line The Core Text line to wrap + @returns A prepared layout line + */ +- (id)initWithLine:(CTLineRef)line; + +/** + Creates a layout line from a given `CTLine` + @param line The Core Text line to wrap + @param stringLocationOffset Offset to modify internal string location to get actual location + @returns A prepared layout line + */ + +- (id)initWithLine:(CTLineRef)line stringLocationOffset:(NSInteger)stringLocationOffset; + +/** + @name Drawing Layout Lines + */ + +/** + Draws the receiver in a given graphics context + @param context The graphics context to draw into + */ +- (void)drawInContext:(CGContextRef)context; + +/** + Creates a `CGPath` containing the shapes of all glyphs in the line + */ +- (CGPathRef)newPathWithGlyphs; + +/** + @name Getting Information about Layout Lines + */ + +/** + The range in the original string that is represented by the receiver + @returns The string strange + */ +- (NSRange)stringRange; + +/** + The number of glyphs the receiver consists of + @returns the number of glyphs + */ +- (NSInteger)numberOfGlyphs; + +/** + Determines the frame of a specific glyph + @param index The index of the glyph + @return The frame of the glyph + */ +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index; + +/** + Retrieves the glyphRuns with a given range + @param range The range + @returns An array of glyph runs + */ +- (NSArray *)glyphRunsWithRange:(NSRange)range; + +/** + The frame of a number of glyphs with a given range + @param range The range + @returns The rectangle containing the result + */ +- (CGRect)frameOfGlyphsWithRange:(NSRange)range; + +/** + The bounds of an image encompassing the entire run. + @param context The graphics context used for the measurement + @returns The rectangle containing the result + */ +- (CGRect)imageBoundsInContext:(CGContextRef)context; + +/** + The string indices of the receiver + @returns An array of string indices + */ +- (NSArray *)stringIndices; + +/** + Determins the graphical offset for a given string index + @param index The string index + @returns The offset + */ +- (CGFloat)offsetForStringIndex:(NSInteger)index; + +/** + Determines the string index that is closest to a given point + @param position The position to determine the string index for + @returns The string index + */ +- (NSInteger)stringIndexForPosition:(CGPoint)position; + +/** + The frame of the receiver relative to the layout frame + */ +@property (nonatomic, assign) CGRect frame; + +/** + The glyph runs that the line contains. + */ +@property (nonatomic, readonly) NSArray *glyphRuns; + +/** + The ascent (height above the baseline) of the receiver + */ +@property (nonatomic, assign) CGFloat ascent; // needs to be modifyable + +/** + The descent (height below the baseline) of the receiver + */ +@property (nonatomic, readonly) CGFloat descent; + +/** + The leading (additional space above the ascent) of the receiver + */ +@property (nonatomic, readonly) CGFloat leading; + +/** + The width of the traling whitespace of the receiver + */ +@property (nonatomic, readonly) CGFloat trailingWhitespaceWidth; + +/** + The offset for the underline in positive points measured from the baseline. This is the maximum underline value of the fonts of all glyph runs of the receiver. + */ +@property (nonatomic, readonly) CGFloat underlineOffset; + +/** + The line height of the line. This is determined by getting the maximum font size of all glyph runs of the receiver. + */ +@property (nonatomic, readonly) CGFloat lineHeight; + +/** + The paragraph style of the paragraph this line belongs to. All lines in a paragraph are supposed to have the same paragraph style, so this takes the paragraph style of the first glyph run + */ +@property (nonatomic, readonly) DTCoreTextParagraphStyle *paragraphStyle; + +/** + The text blocks that the receiver belongs to. + */ +@property (nonatomic, readonly) NSArray *textBlocks; + +/** + The text attachments occuring in glyph runs of the receiver. + */ +@property (nonatomic, readonly) NSArray *attachments; + +/** + The baseline origin of the receiver + */ +@property (nonatomic, assign) CGPoint baselineOrigin; + +/** + `YES` if the writing direction is Right-to-Left, otherwise `NO` + */ +@property (nonatomic, assign) BOOL writingDirectionIsRightToLeft; + +/** + The offset to modify internal string location to get actual location +*/ + +@property (nonatomic, readonly) NSInteger stringLocationOffset; + +/** + Method to efficiently determine if the receiver is a horizontal rule. + + Note: This is used to shortcut drawing of text lines and to allow a horizontal rule line have an "endlessly wide" width so that it gets picked up by [DTCoreTextLayoutFrame linesVisibleInRect:]. + */ +- (BOOL)isHorizontalRule; + + +/** + @name Creating Variants + */ + +/** + Creates a version of the receiver that is justified to the given width. + + @param justificationFactor Full or partial justification. When set to `1.0` or greater, full justification is performed. If this parameter is set to less than `1.0`, varying degrees of partial justification are performed. If it is set to `0` or less, no justification is performed. + @param justificationWidth The width to which the resultant line is justified. If justificationWidth is less than the actual width of the line, then negative justification is performed (that is, glyphs are squeezed together). + */ +- (DTCoreTextLayoutLine *)justifiedLineWithFactor:(CGFloat)justificationFactor justificationWidth:(CGFloat)justificationWidth; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.m new file mode 100644 index 0000000..77a0a2e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.m @@ -0,0 +1,570 @@ +// +// DTCoreTextLayoutLine.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextGlyphRun.h" +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayouter.h" +#import "DTTextAttachment.h" +#import "NSDictionary+DTCoreText.h" +#import "DTTextBlock.h" +#import "DTCoreTextConstants.h" +#import "DTCoreTextFunctions.h" + +@interface DTCoreTextLayoutLine () + +@property (nonatomic, strong) NSArray *glyphRuns; + +@end + +@implementation DTCoreTextLayoutLine +{ + CGRect _frame; + CTLineRef _line; + + CGPoint _baselineOrigin; + + CGFloat _ascent; + CGFloat _descent; + CGFloat _leading; + CGFloat _width; + CGFloat _trailingWhitespaceWidth; + + CGFloat _underlineOffset; + CGFloat _lineHeight; + + NSArray *_glyphRuns; + + BOOL _didCalculateMetrics; + + BOOL _writingDirectionIsRightToLeft; + BOOL _needsToDetectWritingDirection; + + BOOL _hasScannedGlyphRunsForValues; +} + +- (id)initWithLine:(CTLineRef)line +{ + return [self initWithLine:line stringLocationOffset:0]; +} + +- (id)initWithLine:(CTLineRef)line stringLocationOffset:(NSInteger)stringLocationOffset +{ + if (!line) + { + return nil; + } + + if ((self = [super init])) + { + _line = line; + CFRetain(_line); + + // writing direction + _needsToDetectWritingDirection = YES; + + _stringLocationOffset = stringLocationOffset; + } + return self; +} + +- (void)dealloc +{ + CFRelease(_line); +} + +#ifndef COVERAGE +// exclude method from coverage testing + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ origin=%@ frame=%@ range=%@", [self class], NSStringFromCGPoint(_baselineOrigin), NSStringFromCGRect(self.frame), NSStringFromRange([self stringRange])]; +} + +#endif + +- (NSRange)stringRange +{ + CFRange range = CTLineGetStringRange(_line); + + // add offset if there is one, i.e. from merged lines + range.location += _stringLocationOffset; + + return NSMakeRange(range.location, range.length); +} + +- (NSInteger)numberOfGlyphs +{ + NSInteger ret = 0; + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + ret += [oneRun numberOfGlyphs]; + } + + return ret; +} + +#pragma mark - Drawing + +- (void)drawInContext:(CGContextRef)context +{ + CTLineDraw(_line, context); +} + +- (CGPathRef)newPathWithGlyphs +{ + // mutable path for the line + CGMutablePathRef mutablePath = CGPathCreateMutable(); + + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + CGPathRef glyphPath = [oneRun newPathWithGlyphs]; + + CGAffineTransform posTransform = CGAffineTransformMakeTranslation(_baselineOrigin.x, _baselineOrigin.y); + CGPathAddPath(mutablePath, &posTransform, glyphPath); + + CGPathRelease(glyphPath); + } + + return mutablePath; +} + +#pragma mark - Creating Variants + +- (DTCoreTextLayoutLine *)justifiedLineWithFactor:(CGFloat)justificationFactor justificationWidth:(CGFloat)justificationWidth +{ + // make this line justified + CTLineRef justifiedLine = CTLineCreateJustifiedLine(_line, justificationFactor, justificationWidth); + + DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:justifiedLine]; + + CFRelease(justifiedLine); + + return newLine; +} + + +#pragma mark - Calculations +- (NSArray *)stringIndices +{ + NSMutableArray *array = [NSMutableArray array]; + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + [array addObjectsFromArray:[oneRun stringIndices]]; + } + return array; +} + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index +{ + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + NSInteger count = [oneRun numberOfGlyphs]; + if (index >= count) + { + index -= count; + } + else + { + return [oneRun frameOfGlyphAtIndex:index]; + } + } + + return CGRectZero; +} + +- (NSArray *)glyphRunsWithRange:(NSRange)range +{ + NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:[self numberOfGlyphs]]; + + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + NSRange runRange = [oneRun stringRange]; + + // intersect these ranges + NSRange intersectionRange = NSIntersectionRange(range, runRange); + + // if intersection is longer than zero length they intersect + if (intersectionRange.length) + { + [tmpArray addObject:oneRun]; + } + } + + return tmpArray; +} + +- (CGRect)frameOfGlyphsWithRange:(NSRange)range +{ + NSArray *glyphRuns = [self glyphRunsWithRange:range]; + + CGRect tmpRect = CGRectMake(CGFLOAT_MAX, CGFLOAT_MAX, 0, 0); + + for (DTCoreTextGlyphRun *oneRun in glyphRuns) + { + CGRect glyphFrame = oneRun.frame; + + if (glyphFrame.origin.x < tmpRect.origin.x) + { + tmpRect.origin.x = glyphFrame.origin.x; + } + + if (glyphFrame.origin.y < tmpRect.origin.y) + { + tmpRect.origin.y = glyphFrame.origin.y; + } + + if (glyphFrame.size.height > tmpRect.size.height) + { + tmpRect.size.height = glyphFrame.size.height; + } + + tmpRect.size.width = glyphFrame.origin.x + glyphFrame.size.width - tmpRect.origin.x; + } + + CGFloat maxX = CGRectGetMaxX(self.frame) - _trailingWhitespaceWidth; + if (CGRectGetMaxX(tmpRect) > maxX) + { + tmpRect.size.width = maxX - tmpRect.origin.x; + } + + return tmpRect; +} + +// bounds of an image encompassing the entire run +- (CGRect)imageBoundsInContext:(CGContextRef)context +{ + return CTLineGetImageBounds(_line, context); +} + +- (CGFloat)offsetForStringIndex:(NSInteger)index +{ + // subtract offset if there is one, i.e. from merged lines + index -= _stringLocationOffset; + + return CTLineGetOffsetForStringIndex(_line, index, NULL); +} + +- (NSInteger)stringIndexForPosition:(CGPoint)position +{ + // position is in same coordinate system as frame + CGPoint adjustedPosition = position; + CGRect frame = self.frame; + adjustedPosition.x -= frame.origin.x; + adjustedPosition.y -= frame.origin.y; + + NSInteger index = CTLineGetStringIndexForPosition(_line, adjustedPosition); + + // add offset if there is one, i.e. from merged lines + index += _stringLocationOffset; + + return index; +} + +- (void)_calculateMetrics +{ + @synchronized(self) + { + if (!_didCalculateMetrics) + { + _width = (CGFloat)CTLineGetTypographicBounds(_line, &_ascent, &_descent, &_leading); + _trailingWhitespaceWidth = (CGFloat)CTLineGetTrailingWhitespaceWidth(_line); + + _didCalculateMetrics = YES; + } + } +} + +- (BOOL)isHorizontalRule +{ + // HR is only a single \n + + if (self.stringRange.length>1) + { + return NO; + } + + NSArray *runs = self.glyphRuns; + + // thus only a single glyphRun + + if ([runs count]>1) + { + return NO; + } + + DTCoreTextGlyphRun *singleRun = [runs lastObject]; + + if ([singleRun.attributes objectForKey:DTHorizontalRuleStyleAttribute]) + { + return YES; + } + + return NO; +} + +#pragma mark Determining Values from the glyph runs + +- (void)_scanGlyphRunsForValues +{ + @synchronized(self) + { + CGFloat maxOffset = 0; + CGFloat maxFontSize = 0; + + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + CTFontRef usedFont = (__bridge CTFontRef)([oneRun.attributes objectForKey:(id)kCTFontAttributeName]); + + if (usedFont) + { + maxOffset = MAX(maxOffset, fabs(CTFontGetUnderlinePosition(usedFont))); + + maxFontSize = MAX(maxFontSize, CTFontGetSize(usedFont)); + } + } + + _underlineOffset = maxOffset; + _lineHeight = maxFontSize; + + _hasScannedGlyphRunsForValues= YES; + } +} + + +#pragma mark - Properties +- (NSArray *)glyphRuns +{ + @synchronized(self) + { + if (!_glyphRuns) + { + // run array is owned by line + CFArrayRef runs = CTLineGetGlyphRuns(_line); + CFIndex runCount = CFArrayGetCount(runs); + + if (runCount) + { + NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithCapacity:runCount]; + + for (CFIndex i=0; i +#elif TARGET_OS_MAC +#import +#endif + +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextGlyphRun.h" + +/** + This class owns an attributed string and is able to create layoutFrames for certain ranges in this string. Optionally it caches these layout frames. + */ +@interface DTCoreTextLayouter : NSObject + +/** + @name Creating a Layouter + */ + +/** + Designated Initializer. Creates a new Layouter with an attributed string + @param attributedString The `NSAttributedString` to layout for + @returns An initialized layouter + */ +- (id)initWithAttributedString:(NSAttributedString *)attributedString; + + +/** + @name Creating Layout Frames +*/ + +/** + Creates a layout frame with a given rectangle and string range. The layouter fills the layout frame with as many lines as fit. You can query [DTCoreTextLayoutFrame visibleStringRange] for the range the fits and create another layout frame that continues the text from there to create multiple pages, for example for an e-book. + @param frame The rectangle to fill with text + @param range The string range to fill, pass {0,0} for the entire string (as much as fits) + */ +- (DTCoreTextLayoutFrame *)layoutFrameWithRect:(CGRect)frame range:(NSRange)range; + +/** + If set to `YES` then the receiver will cache layout frames generated with layoutFrameWithRect:range: for a given rect + */ +@property (nonatomic, assign) BOOL shouldCacheLayoutFrames; + + +/** + @name Getting Information + */ + +/** + The attributed string that the layouter currently owns + */ +@property (nonatomic, strong) NSAttributedString *attributedString; + +/** + The internal framesetter of the receiver + */ +@property (nonatomic, readonly) CTFramesetterRef framesetter; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.m new file mode 100644 index 0000000..e91298a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.m @@ -0,0 +1,152 @@ +// +// DTCoreTextLayouter.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayouter.h" + +@interface DTCoreTextLayouter () + +@property (nonatomic, strong) NSMutableArray *frames; + +- (void)_discardFramesetter; + +@end + + +@implementation DTCoreTextLayouter +{ + CTFramesetterRef _framesetter; + NSAttributedString *_attributedString; + BOOL _shouldCacheLayoutFrames; + NSCache *_layoutFrameCache; +} + +- (id)initWithAttributedString:(NSAttributedString *)attributedString +{ + if ((self = [super init])) + { + if (!attributedString) + { + return nil; + } + + self.attributedString = attributedString; + } + + return self; +} + +- (void)dealloc +{ + [self _discardFramesetter]; +} + +- (DTCoreTextLayoutFrame *)layoutFrameWithRect:(CGRect)frame range:(NSRange)range +{ + DTCoreTextLayoutFrame *newFrame = nil; + NSString *cacheKey = nil; + + // need to have a non zero + if (!(frame.size.width > 0 && frame.size.height > 0)) + { + return nil; + } + + if (_shouldCacheLayoutFrames) + { + cacheKey = [NSString stringWithFormat:@"%lud-%@-%@", (unsigned long)[_attributedString hash], NSStringFromCGRect(frame), NSStringFromRange(range)]; + + DTCoreTextLayoutFrame *cachedLayoutFrame = [_layoutFrameCache objectForKey:cacheKey]; + + if (cachedLayoutFrame) + { + return cachedLayoutFrame; + } + } + + @autoreleasepool + { + newFrame = [[DTCoreTextLayoutFrame alloc] initWithFrame:frame layouter:self range:range]; + }; + + if (newFrame && _shouldCacheLayoutFrames) + { + [_layoutFrameCache setObject:newFrame forKey:cacheKey]; + } + + return newFrame; +} + +- (void)_discardFramesetter +{ + // framesetter needs to go + if (_framesetter) + { + CFRelease(_framesetter); + _framesetter = NULL; + } +} + +#pragma mark Properties + +- (CTFramesetterRef)framesetter +{ + @synchronized(self) + { + if (!_framesetter) + { + _framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self.attributedString); + } + + + return _framesetter; + } +} + +- (void)setAttributedString:(NSAttributedString *)attributedString +{ + @synchronized(self) + { + if (_attributedString != attributedString) + { + _attributedString = attributedString; + + [self _discardFramesetter]; + + // clear the cache + [_layoutFrameCache removeAllObjects]; + } + } +} + +- (NSAttributedString *)attributedString +{ + return _attributedString; +} + +- (void)setShouldCacheLayoutFrames:(BOOL)shouldCacheLayoutFrames +{ + if (_shouldCacheLayoutFrames != shouldCacheLayoutFrames) + { + _shouldCacheLayoutFrames = shouldCacheLayoutFrames; + + if (shouldCacheLayoutFrames) + { + _layoutFrameCache = [[NSCache alloc] init]; + } + else + { + _layoutFrameCache = nil; + } + } +} + +@synthesize attributedString = _attributedString; +@synthesize framesetter = _framesetter; +@synthesize shouldCacheLayoutFrames = _shouldCacheLayoutFrames; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextMacros.h b/Pods/DTCoreText/Core/Source/DTCoreTextMacros.h new file mode 100644 index 0000000..c6845c6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextMacros.h @@ -0,0 +1,17 @@ +// +// DTCoreTextMacros.h +// DTCoreText +// +// Created by Jean-Charles BERTIN on 5/28/14. +// Copyright (c) 2014 Axinoe. All rights reserved. +// + +#import + +#ifndef DT_RETURNS_INNER_POINTER +#if __has_attribute(objc_returns_inner_pointer) +#define DT_RETURNS_INNER_POINTER __attribute__((objc_returns_inner_pointer)) +#else +#define DT_RETURNS_INNER_POINTER +#endif +#endif diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.h b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.h new file mode 100644 index 0000000..b19354a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.h @@ -0,0 +1,210 @@ +// +// DTCoreTextParagraphStyle.h +// DTCoreText +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +/** + `DTCoreTextParagraphStyle` encapsulates the paragraph or ruler attributes used by the NSAttributedString classes on iOS. It is a replacement for `NSParagraphStyle` which is not implemented on iOS. + + Since `NSAttributedString` instances use CTParagraphStyle object there are methods to bridge from and to these. Because of this distinction there is no need for a mutable variant of this class. + */ +@interface DTCoreTextParagraphStyle : NSObject + +/** + @name Creating a DTCoreTextParagraphStyle + */ + +/** + Returns the default paragraph style. + */ ++ (DTCoreTextParagraphStyle *)defaultParagraphStyle; + + + +/** + @name Bridging to and from CTParagraphStyle + */ + +/** + Create a new paragraph style instance from a `CTParagraphStyle`. + + @param ctParagraphStyle the `CTParagraphStyle` from which to copy this new style's attributes. + */ ++ (DTCoreTextParagraphStyle *)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle; + + +/** + Create a new paragraph style instance from a `CTParagraphStyle`. + + @param ctParagraphStyle the `CTParagraphStyle` from which to copy this new style's attributes. + */ +- (id)initWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle; + +/** + Create a new `CTParagraphStyle` from the receiver for use as attribute in `NSAttributedString` + + @returns The `CTParagraphStyle` based on the receiver's attributes. + */ +- (CTParagraphStyleRef)createCTParagraphStyle; + +/** + @name Bridging to and from NSParagraphStyle + */ + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES +/** + Create a new paragraph style instance from an `NSParagraphStyle`. + + Note: on iOS no tab stops are supported. + @param paragraphStyle the `NSParagraphStyle` from which to copy this new style's attributes. + */ ++ (DTCoreTextParagraphStyle *)paragraphStyleWithNSParagraphStyle:(NSParagraphStyle *)paragraphStyle; + +/** + Create a new `NSParagraphStyle` from the receiver for use as attribute in `NSAttributedString`. + + Note: This method is requires iOS 6 or greater. This does not support tab stops. + + @returns The `NSParagraphStyle` based on the receiver's attributes. + */ +- (NSParagraphStyle *)NSParagraphStyle; +#endif + + +/**------------------------------------------------------------------------------------- + @name Accessing Style Information + --------------------------------------------------------------------------------------- + */ + +/** + The indentation of the first line of the receiver. + */ +@property (nonatomic, assign) CGFloat firstLineHeadIndent; + + +/** + The document-wide default tab interval. + + The default tab interval in points. Tabs after the last specified in tabStops are placed at integer multiples of this distance (if positive). Default return value is 0.0. + */ +@property (nonatomic, assign) CGFloat defaultTabInterval; + + +/** + The distance between the paragraph’s top and the beginning of its text content. + */ +@property (nonatomic, assign) CGFloat paragraphSpacingBefore; + + +/** + The space after the end of the paragraph. + */ +@property (nonatomic, assign) CGFloat paragraphSpacing; + + +/** + The line height multiple. + + Internally line height multiples get converted into minimum and maximum line height. + */ +@property (nonatomic, assign) CGFloat lineHeightMultiple; + + +/** + The minimum height in points that any line in the receiver will occupy, regardless of the font size or size of any attached graphic. This value is always nonnegative. + */ +@property (nonatomic, assign) CGFloat minimumLineHeight; + + +/** + The maximum height in points that any line in the receiver will occupy, regardless of the font size or size of any attached graphic. This value is always nonnegative. The default value is 0. + */ +@property (nonatomic, assign) CGFloat maximumLineHeight; + + +/** +The distance in points from the margin of a text container to the end of lines. + + @note This value is negative if it is to be measured from the trailing margin, positive if measured from the same margin as the headIndent. + */ +@property (nonatomic, assign) CGFloat tailIndent; + +/** + The distance in points from the leading margin of a text container to the beginning of lines other than the first. This value is always nonnegative. + */ +@property (nonatomic, assign) CGFloat headIndent; + +/** + The text alignment of the receiver. + + Natural text alignment is realized as left or right alignment depending on the line sweep direction of the first script contained in the paragraph. + */ +@property (nonatomic, assign) CTTextAlignment alignment; + + +/** + The base writing direction for the receiver. + +*/ +@property (nonatomic, assign) CTWritingDirection baseWritingDirection; + + +/**------------------------------------------------------------------------------------- + @name Setting Tab Stops + --------------------------------------------------------------------------------------- + */ + +/** + The CTTextTab objects, sorted by location, that define the tab stops for the paragraph style. + */ +@property (nonatomic, copy) NSArray *tabStops; + + +/** + Adds a tab stop to the receiver. + + @param position the tab stop position + @param alignment the tab alignment for this tab stop + */ +- (void)addTabStopAtPosition:(CGFloat)position alignment:(CTTextAlignment)alignment; + + +/**------------------------------------------------------------------------------------- + @name Interacting with CSS + --------------------------------------------------------------------------------------- + */ + +/** + Create a representation suitable for CSS. + + @returns A string with the receiver's style encoded as CSS. + */ +- (NSString *)cssStyleRepresentation; + + +/**------------------------------------------------------------------------------------- + @name Setting Text Lists + --------------------------------------------------------------------------------------- + */ + +/** + Text lists containing the paragraph, nested from outermost to innermost. Each text list is a DTCSSListStyle object. +*/ +@property (nonatomic, copy) NSArray *textLists; + + +/**------------------------------------------------------------------------------------- + @name Setting Text Blocks + --------------------------------------------------------------------------------------- + */ + +/** + Text blocks containing the paragraph, nested from outermost to innermost, to array. Each text block is a DTTextBlock object. + */ +@property (nonatomic, copy) NSArray *textBlocks; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.m b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.m new file mode 100644 index 0000000..0f554e0 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.m @@ -0,0 +1,635 @@ +// +// DTCoreTextParagraphStyle.m +// DTCoreText +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextParagraphStyle.h" +#import "DTTextBlock.h" +#import "DTCSSListStyle.h" +#import "DTWeakSupport.h" +#import "DTCoreTextFunctions.h" + +@implementation DTCoreTextParagraphStyle +{ + CGFloat _firstLineHeadIndent; + CGFloat _defaultTabInterval; + CGFloat _paragraphSpacingBefore; + CGFloat _paragraphSpacing; + CGFloat _headIndent; + CGFloat _tailIndent; + CGFloat _lineHeightMultiple; + CGFloat _minimumLineHeight; + CGFloat _maximumLineHeight; + + CTTextAlignment _alignment; + CTWritingDirection _baseWritingDirection; + + NSMutableArray *_tabStops; +} + ++ (DTCoreTextParagraphStyle *)defaultParagraphStyle +{ + return [[DTCoreTextParagraphStyle alloc] init]; +} + ++ (DTCoreTextParagraphStyle *)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle +{ + return [[DTCoreTextParagraphStyle alloc] initWithCTParagraphStyle:ctParagraphStyle]; +} + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES ++ (DTCoreTextParagraphStyle *)paragraphStyleWithNSParagraphStyle:(NSParagraphStyle *)paragraphStyle +{ + if ( ! paragraphStyle) { + paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; + } + + DTCoreTextParagraphStyle *retStyle = [[DTCoreTextParagraphStyle alloc] init]; + + retStyle.firstLineHeadIndent = paragraphStyle.firstLineHeadIndent; + retStyle.headIndent = paragraphStyle.headIndent; + + retStyle.paragraphSpacing = paragraphStyle.paragraphSpacing; + retStyle.paragraphSpacingBefore = paragraphStyle.paragraphSpacingBefore; + + retStyle.lineHeightMultiple = paragraphStyle.lineHeightMultiple; + retStyle.minimumLineHeight = paragraphStyle.minimumLineHeight; + retStyle.maximumLineHeight = paragraphStyle.maximumLineHeight; + + retStyle.alignment = DTNSTextAlignmentToCTTextAlignment(paragraphStyle.alignment); + + switch (paragraphStyle.baseWritingDirection) + { + case NSWritingDirectionNatural: + { + retStyle.baseWritingDirection = kCTWritingDirectionNatural; + break; + } + + case NSWritingDirectionLeftToRight: + { + retStyle.baseWritingDirection = kCTWritingDirectionLeftToRight; + break; + } + case NSWritingDirectionRightToLeft: + { + retStyle.baseWritingDirection = kCTWritingDirectionRightToLeft; + break; + } + } + +#if DTCORETEXT_SUPPORT_NSPARAGRAPHSTYLE_TABS + if (NSClassFromString(@"NSTextTab") && [NSParagraphStyle instancesRespondToSelector:@selector(tabStops)]) + { + NSArray *tabStops = [paragraphStyle valueForKey:@"tabStops"]; + + NSMutableArray *tmpArray = [NSMutableArray array]; + + for (NSTextTab *textTab in tabStops) + { + CTTextAlignment alignment = DTNSTextAlignmentToCTTextAlignment(textTab.alignment); + CGFloat location = textTab.location; + + CTTextTabRef tab = CTTextTabCreate(alignment, location, NULL); + + if (tab) + { + [tmpArray addObject:(__bridge id)(tab)]; + CFRelease(tab); + } + } + + if ([tmpArray count]) + { + retStyle.tabStops = tmpArray; + } + } + + retStyle.defaultTabInterval = paragraphStyle.defaultTabInterval; +#endif + + return retStyle; +} +#endif + +- (id)init +{ + if ((self = [super init])) + { + // defaults + _firstLineHeadIndent = 0.0; + _defaultTabInterval = 36.0; + _baseWritingDirection = kCTWritingDirectionNatural; + _alignment = kCTNaturalTextAlignment; + _lineHeightMultiple = 0.0; + _minimumLineHeight = 0.0; + _maximumLineHeight = 0.0; + _paragraphSpacing = 0.0; + } + + return self; +} + + +- (id)initWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle +{ + if ((self = [super init])) + { + // text alignment + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierAlignment,sizeof(_alignment), &_alignment); + + // indents + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(_firstLineHeadIndent), &_firstLineHeadIndent); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(_headIndent), &_headIndent); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(_tailIndent), &_tailIndent); + + // paragraph spacing + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(_paragraphSpacing), &_paragraphSpacing); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore,sizeof(_paragraphSpacingBefore), &_paragraphSpacingBefore); + + + // tab stops + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(_defaultTabInterval), &_defaultTabInterval); + + CFArrayRef stops; // Could use a CFArray too, leave as a reminder how to do this in the future + if (CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierTabStops, sizeof(stops), &stops)) + { + self.tabStops = (__bridge NSArray *) stops; + } + + + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(_baseWritingDirection), &_baseWritingDirection); + + // line height + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(_minimumLineHeight), &_minimumLineHeight); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(_maximumLineHeight), &_maximumLineHeight); + + + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(_lineHeightMultiple), &_lineHeightMultiple); + } + + return self; +} + +- (CTParagraphStyleRef)createCTParagraphStyle +{ + // This just makes it that much easier to track down memory issues with tabstops + CFArrayRef stops = _tabStops ? CFArrayCreateCopy (NULL, (__bridge CFArrayRef)_tabStops) : NULL; + + CTParagraphStyleSetting settings[] = + { + {kCTParagraphStyleSpecifierAlignment, sizeof(_alignment), &_alignment}, + {kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(_firstLineHeadIndent), &_firstLineHeadIndent}, + {kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(_defaultTabInterval), &_defaultTabInterval}, + + {kCTParagraphStyleSpecifierTabStops, sizeof(stops), &stops}, + + {kCTParagraphStyleSpecifierParagraphSpacing, sizeof(_paragraphSpacing), &_paragraphSpacing}, + {kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(_paragraphSpacingBefore), &_paragraphSpacingBefore}, + + {kCTParagraphStyleSpecifierHeadIndent, sizeof(_headIndent), &_headIndent}, + {kCTParagraphStyleSpecifierTailIndent, sizeof(_tailIndent), &_tailIndent}, + {kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(_baseWritingDirection), &_baseWritingDirection}, + {kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(_lineHeightMultiple), &_lineHeightMultiple}, + + {kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(_minimumLineHeight), &_minimumLineHeight}, + {kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(_maximumLineHeight), &_maximumLineHeight} + }; + + CTParagraphStyleRef ret = CTParagraphStyleCreate(settings, 12); + + if (stops) + { + CFRelease(stops); + } + + return ret; +} + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES +- (NSParagraphStyle *)NSParagraphStyle +{ + NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init]; + + [mps setFirstLineHeadIndent:_firstLineHeadIndent]; + + [mps setParagraphSpacing:_paragraphSpacing]; + [mps setParagraphSpacingBefore:_paragraphSpacingBefore]; + + [mps setHeadIndent:_headIndent]; + [mps setTailIndent:_tailIndent]; + + [mps setMinimumLineHeight:_minimumLineHeight]; + [mps setMaximumLineHeight:_maximumLineHeight]; + [mps setLineHeightMultiple:_lineHeightMultiple]; + + [mps setAlignment:DTNSTextAlignmentFromCTTextAlignment(_alignment)]; + + switch (_baseWritingDirection) + { + case kCTWritingDirectionNatural: + { + [mps setBaseWritingDirection:NSWritingDirectionNatural]; + break; + } + + case kCTWritingDirectionLeftToRight: + { + [mps setBaseWritingDirection:NSWritingDirectionLeftToRight]; + break; + } + + case kCTWritingDirectionRightToLeft: + { + [mps setBaseWritingDirection:NSWritingDirectionRightToLeft]; + break; + } + } + +#if DTCORETEXT_SUPPORT_NSPARAGRAPHSTYLE_TABS + if (NSClassFromString(@"NSTextTab") && [NSParagraphStyle instancesRespondToSelector:@selector(tabStops)]) + { + NSMutableArray *tabs = [NSMutableArray array]; + + for (id object in _tabStops) + { + CTTextTabRef tab = (__bridge CTTextTabRef)(object); + + CTTextAlignment alignment = CTTextTabGetAlignment(tab); + NSTextAlignment nsAlignment = DTNSTextAlignmentFromCTTextAlignment(alignment); + CGFloat location = (CGFloat)CTTextTabGetLocation(tab); + + NSTextTab *textTab = [[NSTextTab alloc] initWithTextAlignment:nsAlignment location:location options:[NSDictionary dictionary]]; + + if (!textTab) + { + break; + } + + [tabs addObject:textTab]; + } + + if ([tabs count]) + { + [mps setValue:tabs forKey:@"tabStops"]; + } + + mps.defaultTabInterval = _defaultTabInterval; + } +#endif + + return (NSParagraphStyle *)mps; +} +#endif + +- (void)addTabStopAtPosition:(CGFloat)position alignment:(CTTextAlignment)alignment +{ + CTTextTabRef tab = CTTextTabCreate(alignment, position, NULL); + if(tab) + { + if (!_tabStops) + { + _tabStops = [[NSMutableArray alloc] init]; + } + [_tabStops addObject:CFBridgingRelease(tab)]; + } +} + +#pragma mark HTML Encoding + +// representation of this paragraph style in css (as far as possible) +- (NSString *)cssStyleRepresentation +{ + NSMutableString *retString = [NSMutableString string]; + + switch (_alignment) + { + case kCTLeftTextAlignment: + [retString appendString:@"text-align:left;"]; + break; + case kCTRightTextAlignment: + [retString appendString:@"text-align:right;"]; + break; + case kCTCenterTextAlignment: + [retString appendString:@"text-align:center;"]; + break; + case kCTJustifiedTextAlignment: + [retString appendString:@"text-align:justify;"]; + break; + case kCTNaturalTextAlignment: + // no output, this is default + break; + } + + if (_lineHeightMultiple && _lineHeightMultiple!=1.0f) + { + NSNumber *number = DTNSNumberFromCGFloat(_lineHeightMultiple); + [retString appendFormat:@"line-height:%@em;", number]; + } + + switch (_baseWritingDirection) + { + case kCTWritingDirectionRightToLeft: + [retString appendString:@"direction:rtl;"]; + break; + case kCTWritingDirectionLeftToRight: + [retString appendString:@"direction:ltr;"]; + break; + case kCTWritingDirectionNatural: + // no output, this is default + break; + } + + // Spacing at the bottom + if (_paragraphSpacing!=0.0f) + { + NSNumber *number = DTNSNumberFromCGFloat(_paragraphSpacing); + [retString appendFormat:@"margin-bottom:%@px;", number]; + } + + // Spacing at the top + if (_paragraphSpacingBefore!=0.0f) + { + NSNumber *number = DTNSNumberFromCGFloat(_paragraphSpacingBefore); + [retString appendFormat:@"margin-top:%@px;", number]; + } + + // Spacing at the left + if (_headIndent!=0.0f) + { + NSNumber *number = DTNSNumberFromCGFloat(_headIndent); + [retString appendFormat:@"margin-left:%@px;", number]; + } + + // Spacing at the right + if (_tailIndent!=0.0f) + { + // tail indent is negative if from trailing margin + NSNumber *number = DTNSNumberFromCGFloat(-_tailIndent); + [retString appendFormat:@"margin-right:%@px;", number]; + } + + // return nil if no content + if ([retString length]) + { + return retString; + } + else + { + return nil; + } +} + +#pragma mark Copying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCoreTextParagraphStyle *newObject = [[DTCoreTextParagraphStyle allocWithZone:zone] init]; + + newObject.firstLineHeadIndent = self.firstLineHeadIndent; + newObject.tailIndent = self.tailIndent; + newObject.defaultTabInterval = self.defaultTabInterval; + newObject.paragraphSpacing = self.paragraphSpacing; + newObject.paragraphSpacingBefore = self.paragraphSpacingBefore; + newObject.lineHeightMultiple = self.lineHeightMultiple; + newObject.minimumLineHeight = self.minimumLineHeight; + newObject.maximumLineHeight = self.maximumLineHeight; + newObject.headIndent = self.headIndent; + newObject.alignment = self.alignment; + newObject.baseWritingDirection = self.baseWritingDirection; + newObject.tabStops = self.tabStops; // copy + newObject.textLists = self.textLists; //copy + newObject.textBlocks = self.textBlocks; //copy + + return newObject; +} + +#pragma mark - Comparing + +- (BOOL)isEqual:(id)object +{ + if (object == self) + { + return YES; + } + + if (!object) + { + return NO; + } + + if (![object isKindOfClass:[DTCoreTextParagraphStyle class]]) + { + return NO; + } + + DTCoreTextParagraphStyle *otherStyle = object; + + + if (_firstLineHeadIndent != otherStyle->_firstLineHeadIndent) + { + return NO; + } + + if (_headIndent != otherStyle->_headIndent) + { + return NO; + } + + if (_tailIndent != otherStyle->_tailIndent) + { + return NO; + } + + if (_defaultTabInterval != otherStyle->_defaultTabInterval) + { + return NO; + } + + if (_paragraphSpacing != otherStyle->_paragraphSpacing) + { + return NO; + } + + if (_paragraphSpacingBefore != otherStyle->_paragraphSpacingBefore) + { + return NO; + } + + if (_lineHeightMultiple != otherStyle->_lineHeightMultiple) + { + return NO; + } + + if (_minimumLineHeight != otherStyle->_minimumLineHeight) + { + return NO; + } + + if (_maximumLineHeight != otherStyle->_maximumLineHeight) + { + return NO; + } + + if (_alignment != otherStyle->_alignment) + { + return NO; + } + + if (_baseWritingDirection != otherStyle->_baseWritingDirection) + { + return NO; + } + + if (_textLists && ![_textLists isEqualToArray:otherStyle->_textLists]) + { + return NO; + } + + if (_textBlocks && ![_textBlocks isEqualToArray:otherStyle->_textBlocks]) + { + return NO; + } + + if (_tabStops && ![_tabStops isEqualToArray:otherStyle->_tabStops]) + { + return NO; + } + + return YES; +} + +#pragma mark Properties + +- (void)setTabStops:(NSArray *)tabStops +{ + if (tabStops != _tabStops) + { + _tabStops = [tabStops mutableCopy]; // keep mutability + } +} + +- (void)setFirstLineHeadIndent:(CGFloat)firstLineHeadIndent +{ + if (_firstLineHeadIndent != firstLineHeadIndent) + { + _firstLineHeadIndent = firstLineHeadIndent; + } +} + +- (void)setDefaultTabInterval:(CGFloat)defaultTabInterval +{ + if (_defaultTabInterval != defaultTabInterval) + { + _defaultTabInterval = defaultTabInterval; + } +} + +- (void)setParagraphSpacingBefore:(CGFloat)paragraphSpacingBefore +{ + if (_paragraphSpacingBefore != paragraphSpacingBefore) + { + _paragraphSpacingBefore = paragraphSpacingBefore; + } +} + +- (void)setParagraphSpacing:(CGFloat)paragraphSpacing +{ + if (_paragraphSpacing != paragraphSpacing) + { + _paragraphSpacing = paragraphSpacing; + } +} + +- (void)setLineHeightMultiple:(CGFloat)lineHeightMultiple +{ + if (_lineHeightMultiple != lineHeightMultiple) + { + _lineHeightMultiple = lineHeightMultiple; + } +} + +- (void)setMinimumLineHeight:(CGFloat)minimumLineHeight +{ + if (_minimumLineHeight != minimumLineHeight) + { + _minimumLineHeight = minimumLineHeight; + } +} + +- (void)setMaximumLineHeight:(CGFloat)maximumLineHeight +{ + if (_maximumLineHeight != maximumLineHeight) + { + _maximumLineHeight = maximumLineHeight; + } +} + +- (void)setHeadIndent:(CGFloat)headIndent +{ + if (_headIndent != headIndent) + { + _headIndent = headIndent; + } +} + +- (void)setTailIndent:(CGFloat)tailIndent +{ + if (_tailIndent != tailIndent) + { + _tailIndent = tailIndent; + } +} + +- (void)setAlignment:(CTTextAlignment)alignment +{ + if (_alignment != alignment) + { + _alignment = alignment; + } +} + +- (void)setTextLists:(NSArray *)textLists +{ + if (_textLists != textLists) + { + _textLists = [textLists copy]; + } +} + +- (void)setTextBlocks:(NSArray *)textBlocks +{ + if (_textBlocks != textBlocks) + { + _textBlocks = [textBlocks copy]; + } +} + +- (void)setBaseWritingDirection:(CTWritingDirection)baseWritingDirection +{ + if (_baseWritingDirection != baseWritingDirection) + { + _baseWritingDirection = baseWritingDirection; + } +} + +@synthesize firstLineHeadIndent = _firstLineHeadIndent; +@synthesize defaultTabInterval = _defaultTabInterval; +@synthesize paragraphSpacingBefore = _paragraphSpacingBefore; +@synthesize paragraphSpacing = _paragraphSpacing; + +@synthesize lineHeightMultiple = _lineHeightMultiple; +@synthesize minimumLineHeight = _minimumLineHeight; +@synthesize maximumLineHeight = _maximumLineHeight; +@synthesize headIndent = _headIndent; +@synthesize tailIndent = _tailIndent; +@synthesize alignment = _alignment; +@synthesize textLists = _textLists; +@synthesize textBlocks = _textBlocks; +@synthesize baseWritingDirection = _baseWritingDirection; +@synthesize tabStops = _tabStops; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTDictationPlaceholderTextAttachment.h b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderTextAttachment.h new file mode 100644 index 0000000..4f2f2ad --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderTextAttachment.h @@ -0,0 +1,24 @@ +// +// DTDictationPlaceholderTextAttachment.h +// DTCoreText +// +// Created by Oliver Drobnik on 06.02.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTTextAttachment.h" + +/** + This is a special subclass of DTTextAttachment used to represent the dictation placeholder. + + When encountering such an element DTAttributedTextContentView does not call the delegate to provide a subclass but automatically creates and adds a DTDictationPlaceholderView. + */ + +@interface DTDictationPlaceholderTextAttachment : DTTextAttachment + +/** + The string that inserting the dictation placeholder replaced, used for Undoing + */ +@property (nonatomic, retain) NSAttributedString *replacedAttributedString; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTDictationPlaceholderTextAttachment.m b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderTextAttachment.m new file mode 100644 index 0000000..825ca36 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderTextAttachment.m @@ -0,0 +1,59 @@ +// +// DTDictationPlaceholderTextAttachment.m +// DTCoreText +// +// Created by Oliver Drobnik on 06.02.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTDictationPlaceholderTextAttachment.h" + +@implementation DTDictationPlaceholderTextAttachment +{ + NSAttributedString *_replacedAttributedString; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + _replacedAttributedString = [aDecoder decodeObjectForKey:@"replacedAttributedString"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_replacedAttributedString forKey:@"replacedAttributedString"]; +} + +// if you change any of these then also make sure to adjust the sizes in DTDictationPlaceholderTextAttachment +#define DOT_WIDTH 10.0f +#define DOT_DISTANCE 2.5f +#define DOT_OUTSIDE_MARGIN 3.0f + +// several hard-coded items +- (CGSize)displaySize +{ + return CGSizeMake(DOT_OUTSIDE_MARGIN*2.0f + DOT_WIDTH*3.0f + DOT_DISTANCE*2.0f, DOT_OUTSIDE_MARGIN*2.0f + DOT_WIDTH); +} + +- (CGSize)originalSize +{ + return [self displaySize]; +} + +- (CGFloat)ascentForLayout +{ + return self.displaySize.height; +} + +- (CGFloat)descentForLayout +{ + return 0.0f; +} + +#pragma mark - Properties + +@synthesize replacedAttributedString = _replacedAttributedString; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTDictationPlaceholderView.h b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderView.h new file mode 100644 index 0000000..59a8de2 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderView.h @@ -0,0 +1,27 @@ +// +// DTDictationPlaceholderView.h +// DTRichTextEditor +// +// Created by Oliver Drobnik on 05.02.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +#import + +/** + A dictation placeholder to display in editors between the time the recording is complete until a recognized response is received. + */ + +@interface DTDictationPlaceholderView : UIView + +/** + Creates an appropriately sized DTDictationPlaceholderView with 3 animated purple dots + */ ++ (DTDictationPlaceholderView *)placeholderView; + +/** + The context of the receiver. This can be any object, for example the selection range to replace with the dictation result text + */ +@property (nonatomic, strong) id context; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTDictationPlaceholderView.m b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderView.m new file mode 100644 index 0000000..d4a6216 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTDictationPlaceholderView.m @@ -0,0 +1,118 @@ +// +// DTDictationPlaceholderView.m +// DTRichTextEditor +// +// Created by Oliver Drobnik on 05.02.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +#import "DTDictationPlaceholderView.h" + +// if you change any of these then also make sure to adjust the sizes in DTDictationPlaceholderTextAttachment +#define DOT_WIDTH 10.0f +#define DOT_DISTANCE 2.5f +#define DOT_OUTSIDE_MARGIN 3.0f + +@implementation DTDictationPlaceholderView +{ + NSUInteger _phase; + NSTimer *_phaseTimer; +} + ++ (DTDictationPlaceholderView *)placeholderView; +{ + return [[DTDictationPlaceholderView alloc] initWithFrame:CGRectZero]; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + self.backgroundColor = [UIColor clearColor]; + + [self sizeToFit]; + } + return self; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + return CGSizeMake(DOT_OUTSIDE_MARGIN*2.0f + DOT_WIDTH*3.0f + DOT_DISTANCE*2.0f, DOT_OUTSIDE_MARGIN*2.0f + DOT_WIDTH); +} + +- (UIColor *)_lightDotColor +{ + return [UIColor colorWithRed:(CGFloat)(238.0/255.0) green:(CGFloat)(128.0/255.0) blue:(CGFloat)(238.0/255.0) alpha:1.0]; +} + +- (UIColor *)_darkDotColor +{ + return [UIColor colorWithRed:(CGFloat)(191.0/255.0) green:(CGFloat)(51.0/255.0) blue:(CGFloat)(191.0/255.0) alpha:1.0]; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + [super willMoveToSuperview:newSuperview]; + + [_phaseTimer invalidate], _phaseTimer = nil; + + if (newSuperview) + { + _phaseTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(_phaseTimerTick:) userInfo:nil repeats:YES]; + } +} + + +- (void)drawRect:(CGRect)rect +{ + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + CGRect dotRect = CGRectMake(DOT_OUTSIDE_MARGIN, 4, DOT_WIDTH, DOT_WIDTH); + + if (_phase==0) + { + CGContextSetFillColorWithColor(ctx, [self _darkDotColor].CGColor); + } + else + { + CGContextSetFillColorWithColor(ctx, [self _lightDotColor].CGColor); + } + + CGContextFillEllipseInRect(ctx, dotRect); + + dotRect.origin.x = DOT_DISTANCE + CGRectGetMaxX(dotRect); + + if (_phase==1) + { + CGContextSetFillColorWithColor(ctx, [self _darkDotColor].CGColor); + } + else + { + CGContextSetFillColorWithColor(ctx, [self _lightDotColor].CGColor); + } + + CGContextFillEllipseInRect(ctx, dotRect); + + dotRect.origin.x = DOT_DISTANCE + CGRectGetMaxX(dotRect); + + if (_phase==2) + { + CGContextSetFillColorWithColor(ctx, [self _darkDotColor].CGColor); + } + else + { + CGContextSetFillColorWithColor(ctx, [self _lightDotColor].CGColor); + } + + CGContextFillEllipseInRect(ctx, dotRect); +} + +- (void)_phaseTimerTick:(id)sender +{ + _phase = (_phase+1)%3; + [self setNeedsDisplay]; +} + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h new file mode 100644 index 0000000..71d2e97 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h @@ -0,0 +1,77 @@ +// +// DTHTMLAttributedStringBuilder.h +// DTCoreText +// +// Created by Oliver Drobnik on 21.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParser.h" + +@class DTHTMLElement; + +typedef void(^DTHTMLAttributedStringBuilderWillFlushCallback)(DTHTMLElement *); + + +/** + Class for building an `NSAttributedString` from an HTML document. + */ +@interface DTHTMLAttributedStringBuilder : NSObject + +/** + @name Creating an Attributed String Builder + */ + +/** + Initializes and returns a new `NSAttributedString` object from the HTML contained in the given object and base URL. + + Options can be: + + - DTMaxImageSize: the maximum CGSize that a text attachment can fill + - DTDefaultFontFamily: the default font family to use instead of Times New Roman + - DTDefaultFontName: the default font face to use instead of Times New Roman + - DTDefaultFontSize: the default font size to use instead of 12 + - DTDefaultTextColor: the default text color + - DTDefaultLinkColor: the default color for hyperlink text + - DTDefaultLinkDecoration: the default decoration for hyperlinks + - DTDefaultLinkHighlightColor: the color to show while the hyperlink is highlighted + - DTDefaultTextAlignment: the default text alignment for paragraphs + - DTDefaultLineHeightMultiplier: The multiplier for line heights + - DTDefaultFirstLineHeadIndent: The default indent for left margin on first line + - DTDefaultHeadIndent: The default indent for left margin except first line + - DTDefaultListIndent: The amount by which lists are indented + - DTDefaultStyleSheet: The default style sheet to use + - DTUseiOS6Attributes: use iOS 6 attributes for building (UITextView compatible) + - DTWillFlushBlockCallBack: a block to be executed whenever content is flushed to the output string + - DTIgnoreInlineStylesOption: All inline style information is being ignored and only style blocks used + + @param data The data in HTML format from which to create the attributed string. + @param options Specifies how the document should be loaded. Contains values described in NSAttributedString(HTML). + @param docAttributes Currently not in use. + @returns Returns an initialized object, or `nil` if the data can’t be decoded. + */ +- (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary * __autoreleasing*)docAttributes; + + +/** + @name Generating Attributed Strings + */ + +/** + Creates the attributed string when called the first time. + @returns An `NSAttributedString` representing the HTML document passed in the initializer. + */ +- (NSAttributedString *)generatedAttributedString; + + +/** + This block is called before the element is written to the output attributed string + */ +@property (nonatomic, copy) DTHTMLAttributedStringBuilderWillFlushCallback willFlushCallback; + +/** + Setting this property to `YES` causes the tree of parse nodes to be preserved until the end of the generation process. This allows to output the HTML structure of the document for debugging. + */ +@property (nonatomic, assign) BOOL shouldKeepDocumentNodeTree; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.m b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.m new file mode 100644 index 0000000..8bd6191 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.m @@ -0,0 +1,978 @@ +// +// DTHTMLAttributedStringBuilder.m +// DTCoreText +// +// Created by Oliver Drobnik on 21.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTHTMLAttributedStringBuilder.h" + +#import "DTTextHTMLElement.h" +#import "DTBreakHTMLElement.h" +#import "DTStylesheetHTMLElement.h" +#import "DTTextAttachmentHTMLElement.h" +#import "DTLog.h" + +#if DEBUG_LOG_METRICS +#import "NSString+DTFormatNumbers.h" +#endif + +@interface DTHTMLAttributedStringBuilder () + +- (void)_registerTagStartHandlers; +- (void)_registerTagEndHandlers; + +@end + + +@implementation DTHTMLAttributedStringBuilder +{ + NSData *_data; + NSDictionary *_options; + BOOL _shouldKeepDocumentNodeTree; + + // settings for parsing + CGFloat _textScale; + DTColor *_defaultLinkColor; + DTCSSStylesheet *_globalStyleSheet; + NSURL *_baseURL; + DTCoreTextFontDescriptor *_defaultFontDescriptor; + DTCoreTextParagraphStyle *_defaultParagraphStyle; + + // root node inherits these defaults + DTHTMLElement *_defaultTag; + + // parsing state, accessed from inside blocks + NSMutableAttributedString *_tmpString; + + // GCD + dispatch_queue_t _stringAssemblyQueue; + dispatch_group_t _stringAssemblyGroup; + dispatch_queue_t _dataParsingQueue; + dispatch_group_t _dataParsingGroup; + dispatch_queue_t _treeBuildingQueue;; + dispatch_group_t _treeBuildingGroup; + + // lookup table for blocks that deal with begin and end tags + NSMutableDictionary *_tagStartHandlers; + NSMutableDictionary *_tagEndHandlers; + + DTHTMLAttributedStringBuilderWillFlushCallback _willFlushCallback; + BOOL _shouldProcessCustomHTMLAttributes; + + // new parsing + DTHTMLElement *_rootNode; + DTHTMLElement *_bodyElement; + DTHTMLElement *_currentTag; + BOOL _ignoreParseEvents; // ignores events from parser after first HTML tag was finished + BOOL _ignoreInlineStyles; // ignores style blocks attached on elements +} + +- (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary * __autoreleasing*)docAttributes +{ + self = [super init]; + if (self) + { + _data = data; + _options = options; + + // documentAttributes ignored for now + + // GCD setup + _stringAssemblyQueue = dispatch_queue_create("DTHTMLAttributedStringBuilder", 0); + _stringAssemblyGroup = dispatch_group_create(); + _dataParsingQueue = dispatch_queue_create("DTHTMLAttributedStringBuilderParser", 0); + _dataParsingGroup = dispatch_group_create(); + _treeBuildingQueue = dispatch_queue_create("DTHTMLAttributedStringBuilderParser Tree Queue", 0); + _treeBuildingGroup = dispatch_group_create(); + } + + return self; +} + +- (void)dealloc +{ +#if !OS_OBJECT_USE_OBJC + dispatch_release(_stringAssemblyQueue); + dispatch_release(_stringAssemblyGroup); + dispatch_release(_dataParsingQueue); + dispatch_release(_dataParsingGroup); + dispatch_release(_treeBuildingQueue); + dispatch_release(_treeBuildingGroup); +#endif +} + +- (BOOL)_buildString +{ +#if DEBUG_LOG_METRICS + // metrics: get start time + CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); +#endif + + // only with valid data + if (![_data length]) + { + return NO; + } + + // register default handlers + [self _registerTagStartHandlers]; + [self _registerTagEndHandlers]; + + // Specify the appropriate text encoding for the passed data, default is UTF8 + NSString *textEncodingName = [_options objectForKey:NSTextEncodingNameDocumentOption]; + NSStringEncoding encoding = NSUTF8StringEncoding; // default + + if (textEncodingName) + { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)textEncodingName); + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + + // custom option to use iOS 6 attributes if running on iOS 6 + if ([[_options objectForKey:DTUseiOS6Attributes] boolValue]) + { +#if TARGET_OS_IPHONE + // NS-attributes only supported running on iOS 6.0 or greater + if (floor(NSFoundationVersionNumber) >= DTNSFoundationVersionNumber_iOS_6_0) + { + ___useiOS6Attributes = YES; + } + else + { + ___useiOS6Attributes = NO; + } +#else + // Mac generally supports it + ___useiOS6Attributes = YES; +#endif + } + +#endif + + + // custom option to scale text + _textScale = [[_options objectForKey:NSTextSizeMultiplierDocumentOption] floatValue]; + if (!_textScale) + { + _textScale = 1.0f; + } + + // use baseURL from options if present + _baseURL = [_options objectForKey:NSBaseURLDocumentOption]; + + // the combined style sheet for entire document + _globalStyleSheet = [[DTCSSStylesheet defaultStyleSheet] copy]; + + // do we have a default style sheet passed as option? + DTCSSStylesheet *defaultStylesheet = [_options objectForKey:DTDefaultStyleSheet]; + if (defaultStylesheet) + { + // merge the default styles to the combined style sheet + [_globalStyleSheet mergeStylesheet:defaultStylesheet]; + } + + // for performance reasons we will return this mutable string + _tmpString = [[NSMutableAttributedString alloc] init]; + + // base tag with font defaults + _defaultFontDescriptor = [[DTCoreTextFontDescriptor alloc] initWithFontAttributes:nil]; + + + // set the default font size + CGFloat defaultFontSize = 12.0f; + + NSNumber *defaultFontSizeNumber = [_options objectForKey:DTDefaultFontSize]; + + if (defaultFontSizeNumber) + { + defaultFontSize = [defaultFontSizeNumber floatValue]; + } + + _defaultFontDescriptor.pointSize = defaultFontSize * _textScale; + + NSString *defaultFontFamily = [_options objectForKey:DTDefaultFontFamily]; + + if (defaultFontFamily) + { + _defaultFontDescriptor.fontFamily = defaultFontFamily; + } + else + { + _defaultFontDescriptor.fontFamily = @"Times New Roman"; + } + + NSString *defaultFontName = [_options objectForKey:DTDefaultFontName]; + + if (defaultFontName) { + _defaultFontDescriptor.fontName = defaultFontName; + } + + + _defaultLinkColor = [_options objectForKey:DTDefaultLinkColor]; + + if (_defaultLinkColor) + { + if ([_defaultLinkColor isKindOfClass:[NSString class]]) + { + // convert from string to color + _defaultLinkColor = DTColorCreateWithHTMLName((NSString *)_defaultLinkColor); + } + + // get hex code for the passed color + NSString *colorHex = DTHexStringFromDTColor(_defaultLinkColor); + + // overwrite the style + NSString *styleBlock = [NSString stringWithFormat:@"a {color:#%@;}", colorHex]; + [_globalStyleSheet parseStyleBlock:styleBlock]; + } + + // default is to have A underlined + NSNumber *linkDecorationDefault = [_options objectForKey:DTDefaultLinkDecoration]; + + if (linkDecorationDefault) + { + if (![linkDecorationDefault boolValue]) + { + // remove default decoration + [_globalStyleSheet parseStyleBlock:@"a {text-decoration:none;}"]; + } + } + + DTColor *defaultLinkHighlightColor = [_options objectForKey:DTDefaultLinkHighlightColor]; + + if (defaultLinkHighlightColor) + { + if ([defaultLinkHighlightColor isKindOfClass:[NSString class]]) + { + // convert from string to color + defaultLinkHighlightColor = DTColorCreateWithHTMLName((NSString *)defaultLinkHighlightColor); + } + + // get hex code for the passed color + NSString *colorHex = DTHexStringFromDTColor(defaultLinkHighlightColor); + + // overwrite the style + NSString *styleBlock = [NSString stringWithFormat:@"a:active {color:#%@;}", colorHex]; + [_globalStyleSheet parseStyleBlock:styleBlock]; + } + + // default paragraph style + _defaultParagraphStyle = [DTCoreTextParagraphStyle defaultParagraphStyle]; + + NSNumber *defaultLineHeightMultiplierNum = [_options objectForKey:DTDefaultLineHeightMultiplier]; + + if (defaultLineHeightMultiplierNum) + { + CGFloat defaultLineHeightMultiplier = [defaultLineHeightMultiplierNum floatValue]; + _defaultParagraphStyle.lineHeightMultiple = defaultLineHeightMultiplier; + } + + NSNumber *defaultTextAlignmentNum = [_options objectForKey:DTDefaultTextAlignment]; + + if (defaultTextAlignmentNum) + { + _defaultParagraphStyle.alignment = (CTTextAlignment)[defaultTextAlignmentNum integerValue]; + } + + NSNumber *defaultFirstLineHeadIndent = [_options objectForKey:DTDefaultFirstLineHeadIndent]; + if (defaultFirstLineHeadIndent) + { + _defaultParagraphStyle.firstLineHeadIndent = [defaultFirstLineHeadIndent integerValue]; + } + + NSNumber *defaultHeadIndent = [_options objectForKey:DTDefaultHeadIndent]; + if (defaultHeadIndent) + { + _defaultParagraphStyle.headIndent = [defaultHeadIndent integerValue]; + } + + _defaultTag = [[DTHTMLElement alloc] init]; + _defaultTag.fontDescriptor = _defaultFontDescriptor; + _defaultTag.paragraphStyle = _defaultParagraphStyle; + _defaultTag.textScale = _textScale; + _defaultTag.currentTextSize = _defaultFontDescriptor.pointSize; + +#if DTCORETEXT_FIX_14684188 + // workaround, only necessary while rdar://14684188 is not fixed + _defaultTag.textColor = [UIColor blackColor]; +#endif + + id defaultColor = [_options objectForKey:DTDefaultTextColor]; + if (defaultColor) + { + if ([defaultColor isKindOfClass:[DTColor class]]) + { + // already a DTColor + _defaultTag.textColor = defaultColor; + } + else + { + // need to convert first + _defaultTag.textColor = DTColorCreateWithHTMLName(defaultColor); + } + } + + _shouldProcessCustomHTMLAttributes = [[_options objectForKey:DTProcessCustomHTMLAttributes] boolValue]; + + // ignore inline styles if option is passed + _ignoreInlineStyles = [[_options objectForKey:DTIgnoreInlineStylesOption] boolValue]; + + // create a parser + DTHTMLParser *parser = [[DTHTMLParser alloc] initWithData:_data encoding:encoding]; + parser.delegate = (id)self; + + __block BOOL result; + dispatch_group_async(_dataParsingGroup, _dataParsingQueue, ^{ result = [parser parse]; }); + + // wait until all string assembly is complete + dispatch_group_wait(_dataParsingGroup, DISPATCH_TIME_FOREVER); + dispatch_group_wait(_treeBuildingGroup, DISPATCH_TIME_FOREVER); + dispatch_group_wait(_stringAssemblyGroup, DISPATCH_TIME_FOREVER); + + // clean up handlers because they retained self + _tagStartHandlers = nil; + _tagEndHandlers = nil; + +#if DEBUG_LOG_METRICS + // metrics: get end time + CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent(); + + // output metrics + DTLogInfo((@"DTCoreText created string from %@ HTML in %.2f sec", [NSString stringByFormattingBytes:[_data length]], endTime-startTime); +#endif + + return result; +} + +- (NSAttributedString *)generatedAttributedString +{ + if (!_tmpString) + { + [self _buildString]; + } + + return _tmpString; +} + +#pragma mark GCD + +- (void)_registerTagStartHandlers +{ + if (_tagStartHandlers) + { + return; + } + + _tagStartHandlers = [[NSMutableDictionary alloc] init]; + + + void (^blockquoteBlock)(void) = ^ + { + _currentTag.paragraphStyle.headIndent += (CGFloat)25.0 * _textScale; + _currentTag.paragraphStyle.firstLineHeadIndent = _currentTag.paragraphStyle.headIndent; + _currentTag.paragraphStyle.paragraphSpacing = _defaultFontDescriptor.pointSize; + }; + + [_tagStartHandlers setObject:[blockquoteBlock copy] forKey:@"blockquote"]; + + + void (^aBlock)(void) = ^ + { + if (_currentTag.isColorInherited || !_currentTag.textColor) + { + _currentTag.textColor = _defaultLinkColor; + _currentTag.isColorInherited = NO; + } + + // the name attribute of A becomes an anchor + _currentTag.anchorName = [_currentTag attributeForKey:@"name"]; + + // remove line breaks and whitespace in links + NSString *cleanString = [[_currentTag attributeForKey:@"href"] stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + cleanString = [cleanString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + if (![cleanString length]) + { + // no valid href + return; + } + + NSURL *link = [NSURL URLWithString:cleanString]; + + // deal with relative URL + if (![link scheme]) + { + if ([cleanString length]) + { + link = [NSURL URLWithString:cleanString relativeToURL:_baseURL]; + + if (!link) + { + // NSURL did not like the link, so let's encode it + cleanString = [cleanString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + link = [NSURL URLWithString:cleanString relativeToURL:_baseURL]; + } + } + else + { + link = _baseURL; + } + } + + _currentTag.link = link; + }; + + [_tagStartHandlers setObject:[aBlock copy] forKey:@"a"]; + + void (^listBlock)(void) = ^ + { + _currentTag.paragraphStyle.firstLineHeadIndent = _currentTag.paragraphStyle.headIndent; + + // create the appropriate list style from CSS + DTCSSListStyle *newListStyle = [_currentTag listStyle]; + + // append this list style to the current paragraph style text lists + NSMutableArray *textLists = [_currentTag.paragraphStyle.textLists mutableCopy]; + + if (!textLists) + { + textLists = [NSMutableArray array]; + } + + [textLists addObject:newListStyle]; + + _currentTag.paragraphStyle.textLists = textLists; + }; + + [_tagStartHandlers setObject:[listBlock copy] forKey:@"ul"]; + [_tagStartHandlers setObject:[listBlock copy] forKey:@"ol"]; + + void (^h1Block)(void) = ^ + { + _currentTag.headerLevel = 1; + }; + [_tagStartHandlers setObject:[h1Block copy] forKey:@"h1"]; + + void (^h2Block)(void) = ^ + { + _currentTag.headerLevel = 2; + }; + [_tagStartHandlers setObject:[h2Block copy] forKey:@"h2"]; + + + void (^h3Block)(void) = ^ + { + _currentTag.headerLevel = 3; + }; + [_tagStartHandlers setObject:[h3Block copy] forKey:@"h3"]; + + + void (^h4Block)(void) = ^ + { + _currentTag.headerLevel = 4; + }; + [_tagStartHandlers setObject:[h4Block copy] forKey:@"h4"]; + + + void (^h5Block)(void) = ^ + { + _currentTag.headerLevel = 5; + }; + [_tagStartHandlers setObject:[h5Block copy] forKey:@"h5"]; + + + void (^h6Block)(void) = ^ + { + _currentTag.headerLevel = 6; + }; + [_tagStartHandlers setObject:[h6Block copy] forKey:@"h6"]; + + + void (^fontBlock)(void) = ^ + { + CGFloat pointSize; + + NSString *sizeAttribute = [_currentTag attributeForKey:@"size"]; + + if (sizeAttribute) + { + NSInteger sizeValue = [sizeAttribute intValue]; + + switch (sizeValue) + { + case 1: + pointSize = _textScale * 10.0f; + break; + case 2: + pointSize = _textScale * 13.0f; + break; + case 3: + pointSize = _textScale * 16.0f; + break; + case 4: + pointSize = _textScale * 18.0f; + break; + case 5: + pointSize = _textScale * 24.0f; + break; + case 6: + pointSize = _textScale * 32.0f; + break; + case 7: + pointSize = _textScale * 48.0f; + break; + default: + pointSize = _defaultFontDescriptor.pointSize; + break; + } + } + else + { + // size is inherited + pointSize = _currentTag.fontDescriptor.pointSize; + } + + NSString *face = [_currentTag attributeForKey:@"face"]; + + if (face) + { + // create a temp font with this face + CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)face, pointSize, NULL); + + _currentTag.fontDescriptor = [DTCoreTextFontDescriptor fontDescriptorForCTFont:font]; + + CFRelease(font); + } + else + { + // modify inherited descriptor + _currentTag.fontDescriptor.pointSize = pointSize; + } + + NSString *color = [_currentTag attributeForKey:@"color"]; + + if (color) + { + _currentTag.textColor = DTColorCreateWithHTMLName(color); + } + }; + + [_tagStartHandlers setObject:[fontBlock copy] forKey:@"font"]; + + + void (^pBlock)(void) = ^ + { + _currentTag.paragraphStyle.firstLineHeadIndent = _currentTag.paragraphStyle.headIndent + _defaultParagraphStyle.firstLineHeadIndent; + }; + + [_tagStartHandlers setObject:[pBlock copy] forKey:@"p"]; +} + +- (void)_registerTagEndHandlers +{ + if (_tagEndHandlers) + { + return; + } + + _tagEndHandlers = [[NSMutableDictionary alloc] init]; + + void (^objectBlock)(void) = ^ + { + if ([_currentTag isKindOfClass:[DTTextAttachmentHTMLElement class]]) + { + if ([_currentTag.textAttachment isKindOfClass:[DTObjectTextAttachment class]]) + { + DTObjectTextAttachment *objectAttachment = (DTObjectTextAttachment *)_currentTag.textAttachment; + + // transfer the child nodes to the attachment + objectAttachment.childNodes = [_currentTag.childNodes copy]; + } + } + }; + + [_tagEndHandlers setObject:[objectBlock copy] forKey:@"object"]; + + void (^videoBlock)(void) = ^ + { + if ([_currentTag isKindOfClass:[DTTextAttachmentHTMLElement class]]) + { + DTTextAttachmentHTMLElement *attachmentElement = (DTTextAttachmentHTMLElement *)_currentTag; + + if ([attachmentElement.textAttachment isKindOfClass:[DTVideoTextAttachment class]]) + { + DTVideoTextAttachment *videoAttachment = (DTVideoTextAttachment *)attachmentElement.textAttachment; + + // find first child that has a source + if (!videoAttachment.contentURL) + { + for (DTHTMLElement *child in attachmentElement.childNodes) + { + if ([child.name isEqualToString:@"source"]) + { + NSString *src = [child attributeForKey:@"src"]; + + // content URL + videoAttachment.contentURL = [NSURL URLWithString:src relativeToURL:_baseURL]; + + break; + } + } + } + } + } + }; + + [_tagEndHandlers setObject:[videoBlock copy] forKey:@"video"]; + + void (^styleBlock)(void) = ^ + { + DTCSSStylesheet *localSheet = [(DTStylesheetHTMLElement *)_currentTag stylesheet]; + [_globalStyleSheet mergeStylesheet:localSheet]; + }; + + [_tagEndHandlers setObject:[styleBlock copy] forKey:@"style"]; + + + void (^linkBlock)(void) = ^ + { + NSString *href = [_currentTag attributeForKey:@"href"]; + NSString *type = [[_currentTag attributeForKey:@"type"] lowercaseString]; + + if ([type isEqualToString:@"text/css"]) + { + NSURL *stylesheetURL = [NSURL URLWithString:href relativeToURL:_baseURL]; + if ([stylesheetURL isFileURL]) + { + NSString *stylesheetContent = [NSString stringWithContentsOfURL:stylesheetURL encoding:NSUTF8StringEncoding error:nil]; + if (stylesheetContent) + { + DTCSSStylesheet *localSheet = [[DTCSSStylesheet alloc] initWithStyleBlock:stylesheetContent]; + [_globalStyleSheet mergeStylesheet:localSheet]; + } + } + else + { + DTLogWarning(@"CSS link referencing a non-local target, ignored"); + } + } + }; + + [ _tagEndHandlers setObject:[linkBlock copy] forKey:@"link"]; +} + +#pragma mark DTHTMLParser Delegate + +- (void)parser:(DTHTMLParser *)parser didStartElement:(NSString *)elementName attributes:(NSDictionary *)attributeDict +{ + + dispatch_group_async(_treeBuildingGroup, _treeBuildingQueue, ^{ + + if (_ignoreParseEvents) + { + return; + } + + DTHTMLElement *newNode = [DTHTMLElement elementWithName:elementName attributes:attributeDict options:_options]; + DTHTMLElement *previousLastChild = nil; + + if (_currentTag) + { + // inherit stuff + [newNode inheritAttributesFromElement:_currentTag]; + [newNode interpretAttributes]; + + previousLastChild = [_currentTag.childNodes lastObject]; + + // add as new child of current node + [_currentTag addChildNode:newNode]; + + // remember body node + if (!_bodyElement && [newNode.name isEqualToString:@"body"]) + { + _bodyElement = newNode; + } + + if (_shouldProcessCustomHTMLAttributes) + { + newNode.shouldProcessCustomHTMLAttributes = _shouldProcessCustomHTMLAttributes; + } + } + else + { + NSAssert(!_rootNode, @"Something went wrong, second root node found in document and not ignored."); + + // might be first node ever + if (!_rootNode) + { + _rootNode = newNode; + + [_rootNode inheritAttributesFromElement:_defaultTag]; + [_rootNode interpretAttributes]; + } + } + + // apply style from merged style sheet + NSSet *matchedSelectors; + NSDictionary *mergedStyles = [_globalStyleSheet mergedStyleDictionaryForElement:newNode matchedSelectors:&matchedSelectors ignoreInlineStyle:_ignoreInlineStyles]; + + if (mergedStyles) + { + [newNode applyStyleDictionary:mergedStyles]; + + // do not add the matched class names to 'class' custom attribute + if (matchedSelectors) + { + NSMutableSet *classNamesToIgnoreForCustomAttributes = [NSMutableSet set]; + + for (NSString *oneSelector in matchedSelectors) + { + // class selectors have a period + NSRange periodRange = [oneSelector rangeOfString:@"."]; + + if (periodRange.location != NSNotFound) + { + NSString *className = [oneSelector substringFromIndex:periodRange.location+1]; + + // add this to ignored classes + [classNamesToIgnoreForCustomAttributes addObject:className]; + } + } + + if ([classNamesToIgnoreForCustomAttributes count]) + { + newNode.CSSClassNamesToIgnoreForCustomAttributes = classNamesToIgnoreForCustomAttributes; + } + } + } + + // adding a block element eliminates previous trailing white space text node + // because a new block starts on a new line + if (previousLastChild && newNode.displayStyle != DTHTMLElementDisplayStyleInline) + { + if ([previousLastChild isKindOfClass:[DTTextHTMLElement class]]) + { + DTTextHTMLElement *textElement = (DTTextHTMLElement *)previousLastChild; + + if ([[textElement text] isIgnorableWhitespace]) + { + [_currentTag removeChildNode:textElement]; + } + } + } + + _currentTag = newNode; + + // find block to execute for this tag if any + void (^tagBlock)(void) = [_tagStartHandlers objectForKey:elementName]; + + if (tagBlock) + { + tagBlock(); + } + }); +} + +- (void)parser:(DTHTMLParser *)parser didEndElement:(NSString *)elementName +{ + + dispatch_group_async(_treeBuildingGroup, _treeBuildingQueue, ^{ + @autoreleasepool { + if (_ignoreParseEvents) + { + return; + } + + // output the element if it is direct descendant of body tag, or close of body in case there are direct text nodes + + // find block to execute for this tag if any + void (^tagBlock)(void) = [_tagEndHandlers objectForKey:elementName]; + + if (tagBlock) + { + tagBlock(); + } + + if (_currentTag.displayStyle != DTHTMLElementDisplayStyleNone) + { + if (_currentTag == _bodyElement || _currentTag.parentElement == _bodyElement) + { + DTHTMLElement *theTag = _currentTag; + + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + // has children that have not been output yet + if ([theTag needsOutput]) + { + // caller gets opportunity to modify tag before it is written + if (_willFlushCallback) + { + _willFlushCallback(theTag); + } + + NSAttributedString *nodeString = [theTag attributedString]; + + if (nodeString) + { + // if this is a block element then we need a paragraph break before it + if (theTag.displayStyle != DTHTMLElementDisplayStyleInline) + { + if ([_tmpString length] && ![[_tmpString string] hasSuffix:@"\n"]) + { + // trim off whitespace + while ([[_tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet ignorableWhitespaceCharacterSet]]) + { + [_tmpString deleteCharactersInRange:NSMakeRange([_tmpString length]-1, 1)]; + } + + [_tmpString appendString:@"\n"]; + } + } + + [_tmpString appendAttributedString:nodeString]; + theTag.didOutput = YES; + + if (!_shouldKeepDocumentNodeTree) + { + // we don't need the children any more + [theTag removeAllChildNodes]; + } + } + + } + }); + } + + } + + while (![_currentTag.name isEqualToString:elementName]) + { + // missing end of element, attempt to recover + _currentTag = [_currentTag parentElement]; + } + + // closing the root node, ignore everything afterwards + if (_currentTag == _rootNode) + { + _ignoreParseEvents = YES; + } + + // go back up a level + _currentTag = [_currentTag parentElement]; + } + }); +} + +- (void)parser:(DTHTMLParser *)parser foundCharacters:(NSString *)string +{ + dispatch_group_async(_treeBuildingGroup, _treeBuildingQueue, ^{ + + if (_ignoreParseEvents) + { + return; + } + + NSAssert(_currentTag, @"Cannot add text node without a current node"); + + if (!_currentTag.preserveNewlines && [string isIgnorableWhitespace]) + { + // ignore whitespace as first element of block element + if (_currentTag.displayStyle!=DTHTMLElementDisplayStyleInline && ![_currentTag.childNodes count]) + { + return; + } + + // ignore whitespace following a block element + DTHTMLElement *previousTag = [_currentTag.childNodes lastObject]; + + if (previousTag.displayStyle != DTHTMLElementDisplayStyleInline) + { + return; + } + + // ignore whitespace following a BR + if ([previousTag isKindOfClass:[DTBreakHTMLElement class]]) + { + return; + } + } + + // adds a text node to the current node + DTTextHTMLElement *textNode = [[DTTextHTMLElement alloc] init]; + textNode.text = string; + + [textNode inheritAttributesFromElement:_currentTag]; + [textNode interpretAttributes]; + + // save it for later output + [_currentTag addChildNode:textNode]; + + DTHTMLElement *theTag = _currentTag; + + // text directly contained in body needs to be output right away + if (theTag == _bodyElement) + { + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + [_tmpString appendAttributedString:[textNode attributedString]]; + theTag.didOutput = YES; + }); + + // only add it to current tag if we need it + if (_shouldKeepDocumentNodeTree) + { + [theTag addChildNode:textNode]; + } + + return; + } + + }); +} + +- (void)parser:(DTHTMLParser *)parser foundCDATA:(NSData *)CDATABlock +{ + dispatch_group_async(_treeBuildingGroup, _treeBuildingQueue, ^{ + + if (_ignoreParseEvents) + { + return; + } + + NSAssert(_currentTag, @"Cannot add text node without a current node"); + + NSString *styleBlock = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding]; + + // adds a text node to the current node + DTHTMLParserTextNode *textNode = [[DTHTMLParserTextNode alloc] initWithCharacters:styleBlock]; + + [_currentTag addChildNode:textNode]; + }); +} + +- (void)parserDidEndDocument:(DTHTMLParser *)parser +{ + + dispatch_group_async(_treeBuildingGroup, _treeBuildingQueue, ^{ + NSAssert(!_currentTag, @"Something went wrong, at end of document there is still an open node"); + + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + // trim off white space at end + while ([[_tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]]) + { + [_tmpString deleteCharactersInRange:NSMakeRange([_tmpString length]-1, 1)]; + } + }); + }); +} + +#pragma mark Properties + +@synthesize willFlushCallback = _willFlushCallback; +@synthesize shouldKeepDocumentNodeTree = _shouldKeepDocumentNodeTree; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElement.h b/Pods/DTCoreText/Core/Source/DTHTMLElement.h new file mode 100644 index 0000000..3f5289c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElement.h @@ -0,0 +1,335 @@ +// +// DTHTMLElement.h +// DTCoreText +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +@class DTCoreTextParagraphStyle; +@class DTCoreTextFontDescriptor; +@class DTTextAttachment; +@class DTCSSListStyle; + +#import "DTCoreTextConstants.h" +#import "DTHTMLParserNode.h" +#import "DTTextAttachment.h" +#import "DTCompatibility.h" + +@class DTBreakHTMLElement; + +/** + Class to represent a element (aka "tag") in a HTML document. Structure information - like parent or children - is inherited from its superclass . + */ +@interface DTHTMLElement : DTHTMLParserNode +{ + DTCoreTextFontDescriptor *_fontDescriptor; + DTCoreTextParagraphStyle *_paragraphStyle; + DTTextAttachment *_textAttachment; + DTTextAttachmentVerticalAlignment _textAttachmentAlignment; + NSURL *_link; + NSString *_anchorName; + + DTColor *_textColor; + DTColor *_backgroundColor; + + DTColor *_backgroundStrokeColor; + CGFloat _backgroundStrokeWidth; + CGFloat _backgroundCornerRadius; + + CTUnderlineStyle _underlineStyle; + + NSString *_beforeContent; + + NSString *_linkGUID; + + BOOL _strikeOut; + NSInteger _superscriptStyle; + + NSInteger _headerLevel; + + NSArray *_shadows; + + DTHTMLElementDisplayStyle _displayStyle; + DTHTMLElementFloatStyle _floatStyle; + + BOOL _isColorInherited; + + BOOL _preserveNewlines; + BOOL _containsAppleConvertedSpace; + + DTHTMLElementFontVariant _fontVariant; + + CGFloat _textScale; + CGSize _size; + + NSMutableArray *_children; + + NSDictionary *_styles; + + BOOL _didOutput; + + // margins/padding + DTEdgeInsets _margins; + DTEdgeInsets _padding; + + // indent of lists + CGFloat _listIndent; + + BOOL _shouldProcessCustomHTMLAttributes; +} + +/** + @name Creating HTML Elements + */ + +/** + Designed initializer, creates the appropriate element sub type + @param name The element name + @param attributes The attributes dictionary of the tag + @param options The parsing options dictionary + @returns the initialized element + */ ++ (DTHTMLElement *)elementWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options; + + +/** + @name Creating Attributed Strings + */ + +/** + Creates an `NSAttributedString` that represents the receiver including all its children. This method is typically overwritten in subclasses of that respresent specific HTML elements. + @returns An attributed string that also contains the children + */ +- (NSAttributedString *)attributedString; + +/** + The dictionary of Core Text attributes for creating an `NSAttributedString` representation for the receiver + @returns The dictionary of attributes + */ +- (NSDictionary *)attributesForAttributedStringRepresentation; + +/** + Creates a to match the CSS styles + */ +- (DTCSSListStyle *)listStyle; + + +/** + @name Getting Element Information + */ + +/** + Font Descriptor describing the font state of the receiver + */ +@property (nonatomic, copy) DTCoreTextFontDescriptor *fontDescriptor; + +/** + Paragraph Style describing the paragraph state of the receiver + */ +@property (nonatomic, copy) DTCoreTextParagraphStyle *paragraphStyle; + +/** + Text Attachment of the receiver, or `nil` if there is no attachment + */ +@property (nonatomic, strong) DTTextAttachment *textAttachment; + +/** + Hyperlink URL of the receiver, or `nil` if there is no hyperlink + */ +@property (nonatomic, copy) NSURL *link; + +/** + Anchor name, used by hyperlinks, of the receiver that can be used to scroll to. + */ +@property (nonatomic, copy) NSString *anchorName; + +/** + Foreground text color of the receiver + */ +@property (nonatomic, strong) DTColor *textColor; + +/** + Background color of text in the receiver + */ +@property (nonatomic, strong) DTColor *backgroundColor; + +/** + Background stroke color in the receiver + */ +@property (nonatomic, strong) DTColor *backgroundStrokeColor; + +/** + Background stroke width in the receiver + */ +@property (nonatomic, assign) CGFloat backgroundStrokeWidth; + +/** + Background stroke width in the receiver + */ +@property (nonatomic, assign) CGFloat backgroundCornerRadius; + +/** + The custom letter spacing of the receiver, default is 0px + */ +@property (nonatomic, assign) CGFloat letterSpacing; + +/** + Additional text to be inserted before the text content of the receiver + */ +@property (nonatomic, copy) NSString *beforeContent; + +/** + Array of shadows attached to the text contents of the receiver + */ +@property (nonatomic, copy) NSArray *shadows; + +/** + The underline style of the receiver, at present only none or single line are supported + */ +@property (nonatomic, assign) CTUnderlineStyle underlineStyle; + +/** + The strike-out style of the receiver + */ +@property (nonatomic, assign) BOOL strikeOut; + +/** + The superscript style of the receiver or 0 if it does not have superscript text. + */ +@property (nonatomic, assign) NSInteger superscriptStyle; + +/** + The header level of the receiver, or 0 if it is not a header + */ +@property (nonatomic, assign) NSInteger headerLevel; + +/** + The display style of the receiver. + */ +@property (nonatomic, assign) DTHTMLElementDisplayStyle displayStyle; + +/** + Whether the receiver is marked as float. While floating is not currently supported this can be used to add additional paragraph breaks. + */ +@property (nonatomic, readonly) DTHTMLElementFloatStyle floatStyle; + +/** + Specifies that the textColor was inherited. Assigning textColor sets this flag to `NO` + */ +@property (nonatomic, assign) BOOL isColorInherited; + +/** + Specifies that whitespace and new lines should be preserved. Default is to compress white space. + */ +@property (nonatomic, assign) BOOL preserveNewlines; + +/** + The current font variant of the receiver, normal or small caps. + */ + +@property (nonatomic, assign) DTHTMLElementFontVariant fontVariant; + +/** + The current unscaled font size (used when inheriting font size). You're probably looking for fontDescriptor.pointSize. + */ +@property (nonatomic, assign) CGFloat currentTextSize; + +/** + The scale by which all fonts are scaled + */ +@property (nonatomic, assign) CGFloat textScale; + +/** + The size of the receiver, either from width/height attributes or width/hight styles. + */ +@property (nonatomic, assign) CGSize size; + +/** + The value of the CSS margins. Margin support is incomplete. + */ +@property (nonatomic, assign) DTEdgeInsets margins; + +/** The value of the CSS padding. Padding are added to DTTextBlock instances for block-level elements. + */ +@property (nonatomic, assign) DTEdgeInsets padding; + +/** + Specifies that whitespace contained in the receiver's text has been converted with Apple's algorithm. + */ +@property (nonatomic, assign) BOOL containsAppleConvertedSpace; + +/** + Prevents adding custom HTML attributes to output + */ +@property (nonatomic, assign) BOOL shouldProcessCustomHTMLAttributes; + +/** + @name Working with HTML Attributes + */ + +/** + Retrieves an attribute with a given key + @param key The attribute name to retrieve + @returns the attribute string + */ +- (NSString *)attributeForKey:(NSString *)key; + +/** + Copies and inherits relevant attributes from the given parent element + @param element The element to inherit attributes from + */ +- (void)inheritAttributesFromElement:(DTHTMLElement *)element; + +/** + Interprets the tag attributes for e.g. writing direction. Usually you would call this after inheritAttributesFromElement:. + */ +- (void)interpretAttributes; + +/** + The HTML attributes that should be attached to the generated attributed string. Typically all attributes that were processed by -interpretAttributes are in this list. All other attributes get added to the generated attributed string with the DTCustomAttributesAttribute key. + */ ++ (NSSet *)attributesToIgnoreForCustomAttributesAttribute; + +/** + The CSS class names that are not to be added to the "class" custom attribute in the DTCustomAttributesAttribute key. Those are usually the class names + */ +@property(nonatomic, strong) NSSet *CSSClassNamesToIgnoreForCustomAttributes; + +/** + @name Working with CSS Styles + */ + +/** + Applies the style information contained in a styles dictionary to the receiver + @param styles A style dictionary + */ +- (void)applyStyleDictionary:(NSDictionary *)styles; + + +/** + @name HTML Node Hierarchy + */ + +/** + Returns the parent element. That's the same as the parent node but with adjusted type for convenience. + */ +- (DTHTMLElement *)parentElement; + + +/** + @name Output State (Internal) + */ + +/** + Internal state during string building to mark the receiver als having been flushed + */ +@property (nonatomic, assign) BOOL didOutput; + +/** + Internal method that determins if this element still requires output, based on its own didOutput state and the didOutput state of its children + @returns `YES` if it still requires output + */ +- (BOOL)needsOutput; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElement.m b/Pods/DTCoreText/Core/Source/DTHTMLElement.m new file mode 100644 index 0000000..cc653e6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElement.m @@ -0,0 +1,1644 @@ +// +// DTHTMLElement.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTHTMLElement.h" +#import "DTAnchorHTMLElement.h" +#import "DTTextAttachmentHTMLElement.h" +#import "DTBreakHTMLElement.h" +#import "DTHorizontalRuleHTMLElement.h" +#import "DTListItemHTMLElement.h" +#import "DTStylesheetHTMLElement.h" +#import "DTTextHTMLElement.h" +#import "NSString+DTUtilities.h" +#import "DTColorFunctions.h" +#import "DTLog.h" + +@interface DTHTMLElement () + +@property (nonatomic, strong) NSString *linkGUID; + +// internal initializer +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options; + +@end + +// global variables +BOOL ___shouldUseiOS6Attributes = NO; +NSDictionary *_classesForNames = nil; + + +@implementation DTHTMLElement +{ + NSSet *_CSSClassNamesToIgnoreForCustomAttributes; +} + ++ (void)initialize +{ + // prevent calling from children + if (self != [DTHTMLElement class]) + { + return; + } + + // lookup table so that we quickly get the correct class to instantiate for special tags + NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init]; + + [tmpDict setObject:[DTAnchorHTMLElement class] forKey:@"a"]; + [tmpDict setObject:[DTBreakHTMLElement class] forKey:@"br"]; + [tmpDict setObject:[DTHorizontalRuleHTMLElement class] forKey:@"hr"]; + [tmpDict setObject:[DTListItemHTMLElement class] forKey:@"li"]; + [tmpDict setObject:[DTStylesheetHTMLElement class] forKey:@"style"]; + [tmpDict setObject:[DTTextAttachmentHTMLElement class] forKey:@"img"]; + [tmpDict setObject:[DTTextAttachmentHTMLElement class] forKey:@"object"]; + [tmpDict setObject:[DTTextAttachmentHTMLElement class] forKey:@"video"]; + [tmpDict setObject:[DTTextAttachmentHTMLElement class] forKey:@"iframe"]; + + _classesForNames = [tmpDict copy]; +} + ++ (instancetype)elementWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options +{ + // look for specialized class + Class class = [_classesForNames objectForKey:name]; + + if (!class) + { + // see if this is a custom attachment class + Class attachmentClass = [DTTextAttachment registeredClassForTagName:name]; + + if (attachmentClass) + { + class = [DTTextAttachmentHTMLElement class]; + } + else + { + // use generic of none found + class = [DTHTMLElement class]; + } + } + + DTHTMLElement *element = [[class alloc] initWithName:name attributes:attributes options:options]; + + return element; +} + +- (instancetype)initWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options +{ + // node does not need the options, but it needs the name and attributes + self = [super initWithName:name attributes:attributes]; + if (self) + { + } + + return self; +} + +#pragma mark - Creating Attributed Strings + +- (NSDictionary *)attributesForAttributedStringRepresentation +{ + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + // add text attachment + if (_textAttachment) + { +#if TARGET_OS_IPHONE + // need run delegate for sizing (only supported on iOS) + CTRunDelegateRef embeddedObjectRunDelegate = createEmbeddedObjectRunDelegate(_textAttachment); + [tmpDict setObject:CFBridgingRelease(embeddedObjectRunDelegate) forKey:(id)kCTRunDelegateAttributeName]; +#endif + + // add attachment + [tmpDict setObject:_textAttachment forKey:NSAttachmentAttributeName]; + + // remember original paragraphSpacing + [tmpDict setObject:DTNSNumberFromCGFloat(self.paragraphStyle.paragraphSpacing) forKey:DTAttachmentParagraphSpacingAttribute]; + } + + CTFontRef font = [_fontDescriptor newMatchingFont]; + + if (font) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + UIFont *uiFont = [UIFont fontWithCTFont:font]; + [tmpDict setObject:uiFont forKey:NSFontAttributeName]; + } + else +#endif + { + // __bridge since its already retained elsewhere + [tmpDict setObject:(__bridge id)(font) forKey:(id)kCTFontAttributeName]; + } + + + // use this font to adjust the values needed for the run delegate during layout time + [_textAttachment adjustVerticalAlignmentForFont:font]; + + CFRelease(font); + } + + // add hyperlink + if (_link) + { + [tmpDict setObject:_link forKey:DTLinkAttribute]; + + // add a GUID to group multiple glyph runs belonging to same link + [tmpDict setObject:_linkGUID forKey:DTGUIDAttribute]; + } + + // add anchor + if (_anchorName) + { + [tmpDict setObject:_anchorName forKey:DTAnchorAttribute]; + } + + // add strikout if applicable + if (_strikeOut) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + [tmpDict setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName]; + } + else +#endif + { + [tmpDict setObject:[NSNumber numberWithBool:YES] forKey:DTStrikeOutAttribute]; + } + } + + // set underline style + if (_underlineStyle) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + [tmpDict setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName]; + } + else +#endif + { + [tmpDict setObject:[NSNumber numberWithInteger:_underlineStyle] forKey:(id)kCTUnderlineStyleAttributeName]; + } + + // we could set an underline color as well if we wanted, but not supported by HTML + // [attributes setObject:(id)[DTImage redColor].CGColor forKey:(id)kCTUnderlineColorAttributeName]; + } + + if (_textColor) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + [tmpDict setObject:_textColor forKey:NSForegroundColorAttributeName]; + } + else +#endif + { + [tmpDict setObject:(id)[_textColor CGColor] forKey:(id)kCTForegroundColorAttributeName]; + } + } + + if (_backgroundColor) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + [tmpDict setObject:_backgroundColor forKey:NSBackgroundColorAttributeName]; + } + else +#endif + { + [tmpDict setObject:(id)[_backgroundColor CGColor] forKey:DTBackgroundColorAttribute]; + } + } + + if (_superscriptStyle) + { + [tmpDict setObject:(id)[NSNumber numberWithInteger:_superscriptStyle] forKey:(id)kCTSuperscriptAttributeName]; + } + + // add paragraph style + if (_paragraphStyle) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + NSParagraphStyle *style = [self.paragraphStyle NSParagraphStyle]; + [tmpDict setObject:style forKey:NSParagraphStyleAttributeName]; + } + else +#endif + { + CTParagraphStyleRef newParagraphStyle = [self.paragraphStyle createCTParagraphStyle]; + [tmpDict setObject:CFBridgingRelease(newParagraphStyle) forKey:(id)kCTParagraphStyleAttributeName]; + } + } + + // add shadow array if applicable + if (_shadows) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + // only a single shadow supported + NSDictionary *firstShadow = [_shadows objectAtIndex:0]; + + NSShadow *shadow = [[NSShadow alloc] init]; + shadow.shadowOffset = [[firstShadow objectForKey:@"Offset"] CGSizeValue]; + shadow.shadowColor = [firstShadow objectForKey:@"Color"]; + shadow.shadowBlurRadius = [[firstShadow objectForKey:@"Blur"] floatValue]; + [tmpDict setObject:shadow forKey:NSShadowAttributeName]; + } + else +#endif + { + [tmpDict setObject:_shadows forKey:DTShadowsAttribute]; + } + } + + if (_letterSpacing) + { + NSNumber *letterSpacingNum = DTNSNumberFromCGFloat(_letterSpacing); + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + [tmpDict setObject:letterSpacingNum forKey:NSKernAttributeName]; + } + else +#endif + { + [tmpDict setObject:letterSpacingNum forKey:(id)kCTKernAttributeName]; + } + } + + if (_headerLevel) + { + [tmpDict setObject:[NSNumber numberWithInteger:_headerLevel] forKey:DTHeaderLevelAttribute]; + } + + if (_paragraphStyle.textLists) + { + [tmpDict setObject:_paragraphStyle.textLists forKey:DTTextListsAttribute]; + } + + if (_paragraphStyle.textBlocks) + { + [tmpDict setObject:_paragraphStyle.textBlocks forKey:DTTextBlocksAttribute]; + } + + if (_backgroundStrokeColor) + { + [tmpDict setObject:(id)[_backgroundStrokeColor CGColor] forKey:DTBackgroundStrokeColorAttribute]; + } + + if (_backgroundStrokeWidth) + { + [tmpDict setObject:DTNSNumberFromCGFloat(_backgroundStrokeWidth) forKey:DTBackgroundStrokeWidthAttribute]; + } + + if (_backgroundCornerRadius) + { + [tmpDict setObject:DTNSNumberFromCGFloat(_backgroundCornerRadius) forKey:DTBackgroundCornerRadiusAttribute]; + } + + return tmpDict; +} + +- (BOOL)needsOutput +{ + @synchronized(self) + { + if ([self.childNodes count]) + { + for (DTHTMLElement *oneChild in self.childNodes) + { + // ignore children that have display:none + if (oneChild.displayStyle == DTHTMLElementDisplayStyleNone) + { + continue; + } + + if (!oneChild.didOutput) + { + return YES; + } + } + + return NO; + } + + return YES; + } +} + +- (BOOL)_isNotChildOfList +{ + DTHTMLElement *parent = self.parentElement; + + if (parent.displayStyle == DTHTMLElementDisplayStyleListItem) + { + return NO; + } + + if (parent.displayStyle == DTHTMLElementDisplayStyleBlock) + { + if ([parent.name isEqualToString:@"ol"] || [parent.name isEqualToString:@"ul"]) + { + return NO; + } + } + + return YES; +} + +// adds the attributes that have not been "dealt with" to the DTCustomAttributesAttribute +- (void)_addCustomHTMLAttributesToAttributedString:(NSMutableAttributedString *)attributedString +{ + NSSet *attributesToIgnore = [[self class] attributesToIgnoreForCustomAttributesAttribute]; + NSRange entireString = NSMakeRange(0, [attributedString length]); + + [_attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + + if ([attributesToIgnore containsObject:key]) return; + + if (_CSSClassNamesToIgnoreForCustomAttributes && [key isEqualToString:@"class"]) + { + NSMutableArray *classNamesToKeep = [NSMutableArray array]; + + NSArray *components = nil; + + if ([value isKindOfClass:[NSString class]]) + { + // split the class string + components = [value componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } + else if ([value isKindOfClass:[NSArray class]]) + { + // already an array + components = value; + } + + for (NSString *oneClassName in components) + { + if (![_CSSClassNamesToIgnoreForCustomAttributes containsObject:oneClassName]) + { + [classNamesToKeep addObject:oneClassName]; + } + } + + if ([classNamesToKeep count]) + { + value = [classNamesToKeep componentsJoinedByString:@" "]; + } + else + { + return; // continue enumeration, class attribute would be empty + } + } + + // we preserve existing because they are from children + [attributedString addHTMLAttribute:key value:value range:entireString replaceExisting:NO]; + }]; +} + +- (NSAttributedString *)attributedString +{ + @synchronized(self) + { + if (_displayStyle == DTHTMLElementDisplayStyleNone || self.didOutput) + { + return nil; + } + + NSDictionary *attributes = [self attributesForAttributedStringRepresentation]; + + NSMutableAttributedString *tmpString; + + if (_textAttachment) + { + // ignore text, use unicode object placeholder + tmpString = [[NSMutableAttributedString alloc] initWithString:UNICODE_OBJECT_PLACEHOLDER attributes:attributes]; + } + else + { + // walk through children + tmpString = [[NSMutableAttributedString alloc] init]; + + DTHTMLElement *previousChild = nil; + + for (DTHTMLElement *oneChild in self.childNodes) + { + if (oneChild.displayStyle == DTHTMLElementDisplayStyleNone) + { + continue; + } + + // if previous node was inline and this child is block then we need a newline + if (previousChild && previousChild.displayStyle == DTHTMLElementDisplayStyleInline) + { + if (oneChild.displayStyle == DTHTMLElementDisplayStyleBlock) + { + // trim off whitespace suffix + while ([[tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet ignorableWhitespaceCharacterSet]]) + { + [tmpString deleteCharactersInRange:NSMakeRange([tmpString length]-1, 1)]; + } + + // paragraph break + [tmpString appendString:@"\n"]; + } + } + + NSAttributedString *nodeString = [oneChild attributedString]; + + if (nodeString) + { + if (!oneChild.containsAppleConvertedSpace) + { + // we already have a white space in the string so far + if ([[tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet ignorableWhitespaceCharacterSet]]) + { + // following e.g. a BR we don't want a space or NL + NSCharacterSet *charactersToIgnore = [NSCharacterSet characterSetWithCharactersInString:@" \n\t"]; + + while ([[nodeString string] hasPrefixCharacterFromSet:charactersToIgnore]) + { + NSString *field = [nodeString attribute:DTFieldAttribute atIndex:0 effectiveRange:NULL]; + + // do not trim off field + if ([field isEqualToString:DTListPrefixField]) + { + break; + } + + // do not trim off HR character + BOOL isHR = [[nodeString attribute:DTHorizontalRuleStyleAttribute atIndex:0 effectiveRange:NULL] boolValue]; + + if (isHR) + { + break; + } + + nodeString = [nodeString attributedSubstringFromRange:NSMakeRange(1, [nodeString length]-1)]; + } + } + } + + [tmpString appendAttributedString:nodeString]; + } + + previousChild = oneChild; + } + } + + // block-level elements get space trimmed and a newline + if (_displayStyle != DTHTMLElementDisplayStyleInline) + { + // trim off whitespace prefix + while ([[tmpString string] hasPrefix:@" "]) + { + [tmpString deleteCharactersInRange:NSMakeRange(0, 1)]; + } + + // trim off whitespace suffix + while ([[tmpString string] hasSuffix:@" "]) + { + [tmpString deleteCharactersInRange:NSMakeRange([tmpString length]-1, 1)]; + } + + if (![self.name isEqualToString:@"html"] && ![self.name isEqualToString:@"body"]) + { + if (![[tmpString string] hasSuffix:@"\n"]) + { + if ([tmpString length]) + { + // extend font and paragraph style with the \n + [tmpString appendEndOfParagraph]; + } + else + { + // string is empty, need a new attributed string so that we have the attributes + attributes = [self attributesForAttributedStringRepresentation]; + NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:@"\n" attributes:attributes]; + [tmpString appendAttributedString:attributedString]; + } + } + } + } + + // make sure the last sub-paragraph of this has no less than the specified paragraph spacing of this element + // e.g. last LI needs to inherit the margin-after of the UL + if (self.displayStyle == DTHTMLElementDisplayStyleBlock && [tmpString length]>0) + { + + // if this is latest + if ([self _isNotChildOfList]) + { + NSRange paragraphRange = [[tmpString string] rangeOfParagraphAtIndex:[tmpString length]-1]; + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + NSParagraphStyle *paraStyle = [tmpString attribute:NSParagraphStyleAttributeName atIndex:paragraphRange.location effectiveRange:NULL]; + + DTCoreTextParagraphStyle *paragraphStyle = [DTCoreTextParagraphStyle paragraphStyleWithNSParagraphStyle:paraStyle]; + + if (paragraphStyle.paragraphSpacing < self.paragraphStyle.paragraphSpacing) + { + paragraphStyle.paragraphSpacing = self.paragraphStyle.paragraphSpacing; + + // make new paragraph style + NSParagraphStyle *newParaStyle = [paragraphStyle NSParagraphStyle]; + +#if DTCORETEXT_NEEDS_ATTRIBUTE_REPLACEMENT_LEAK_FIX + if (NSFoundationVersionNumber <= NSFoundationVersionNumber10_6_8) // less than OS X 10.7 and less than iOS 5 + { + // remove old (works around iOS 4.3 leak) + [tmpString removeAttribute:NSParagraphStyleAttributeName range:paragraphRange]; + } +#endif + + // set new + [tmpString addAttribute:NSParagraphStyleAttributeName value:newParaStyle range:paragraphRange]; + } + } + else +#endif + { + CTParagraphStyleRef paraStyle = (__bridge CTParagraphStyleRef)[tmpString attribute:(id)kCTParagraphStyleAttributeName atIndex:paragraphRange.location effectiveRange:NULL]; + + DTCoreTextParagraphStyle *paragraphStyle = [DTCoreTextParagraphStyle paragraphStyleWithCTParagraphStyle:paraStyle]; + + if (paragraphStyle.paragraphSpacing < self.paragraphStyle.paragraphSpacing) + { + paragraphStyle.paragraphSpacing = self.paragraphStyle.paragraphSpacing; + + // make new paragraph style + CTParagraphStyleRef newParaStyle = [paragraphStyle createCTParagraphStyle]; + +#if DTCORETEXT_NEEDS_ATTRIBUTE_REPLACEMENT_LEAK_FIX + if (NSFoundationVersionNumber <= NSFoundationVersionNumber10_6_8) // less than OS X 10.7 and less than iOS 5 + { + // remove old (works around iOS 4.3 leak) + [tmpString removeAttribute:(id)kCTParagraphStyleAttributeName range:paragraphRange]; + } +#endif + + // set new + [tmpString addAttribute:(id)kCTParagraphStyleAttributeName value:(__bridge_transfer id)newParaStyle range:paragraphRange]; + } + } + } + } + + // add the custom attributes + if (_shouldProcessCustomHTMLAttributes) + { + [self _addCustomHTMLAttributesToAttributedString:tmpString]; + } + + return tmpString; + } +} + +#pragma mark - Working with CSS Styles + +// decodes the edgeInsets for padding or margin +- (BOOL)_parseEdgeInsetsFromStyleDictionary:(NSDictionary *)styles forAttributesWithPrefix:(NSString *)prefix writingDirection:(CTWritingDirection)writingDirection intoEdgeInsets:(DTEdgeInsets *)intoEdgeInsets +{ + DTEdgeInsets edgeInsets = {0,0,0,0}; + + // preserve previous values in insets + if (intoEdgeInsets) + { + edgeInsets = *intoEdgeInsets; + } + + BOOL didModify = NO; + + if (![styles count]) + { + return didModify; + } + + BOOL isWebKitAttribute = NO; + + if ([prefix hasPrefix:@"-webkit"]) + { + isWebKitAttribute = YES; + } + + NSString *leftKey = @"-left"; + + if (isWebKitAttribute) + { + if (writingDirection==kCTWritingDirectionRightToLeft) + { + // RTL + leftKey = @"-end"; + } + else + { + // LTR + leftKey = @"-start"; + } + } + + NSString *rightKey = @"-right"; + + if (isWebKitAttribute) + { + if (writingDirection==kCTWritingDirectionRightToLeft) + { + // RTL + rightKey = @"-start"; + } + else + { + // LTR + rightKey = @"-end"; + } + } + + NSString *topKey = isWebKitAttribute?@"-before":@"-top"; + NSString *bottomKey = isWebKitAttribute?@"-after":@"-bottom"; + + for (NSString *oneKey in styles) + { + if (![oneKey hasPrefix:prefix]) + { + continue; + } + + NSString *attributeValue = [styles objectForKey:oneKey]; + + NSRange dashRange = [oneKey rangeOfString:@"-"]; + + if (dashRange.length) + { + if ([oneKey hasSuffix:leftKey]) + { + edgeInsets.left = [attributeValue pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + didModify = YES; + } + else if ([oneKey hasSuffix:bottomKey]) + { + edgeInsets.bottom = [attributeValue pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + didModify = YES; + } + else if ([oneKey hasSuffix:rightKey]) + { + edgeInsets.right = [attributeValue pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + didModify = YES; + } + else if ([oneKey hasSuffix:topKey]) + { + edgeInsets.top = [attributeValue pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + didModify = YES; + } + } + else + { + // shortcut with multiple values + edgeInsets = [attributeValue DTEdgeInsetsRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + didModify = YES; + } + } + + if (didModify && intoEdgeInsets) + { + *intoEdgeInsets = edgeInsets; + } + + return didModify; +} + +- (void)applyStyleDictionary:(NSDictionary *)styles +{ + if (![styles count]) + { + return; + } + + // keep that for later lookup + _styles = styles; + + // writing direction + NSString *directionStr = [_styles objectForKey:@"direction"]; + + if (directionStr) + { + if ([directionStr isEqualToString:@"rtl"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; + } + else if ([directionStr isEqualToString:@"ltr"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + } + else if ([directionStr isEqualToString:@"auto"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionNatural; // that's also default + } + else + { + // other values are invalid and will be ignored + } + } + + // register pseudo-selector contents + self.beforeContent = [[_styles objectForKey:@"before:content"] stringByDecodingCSSContentAttribute]; + + NSString *fontSize = [styles objectForKey:@"font-size"]; + if (fontSize) + { + // absolute sizes based on 12.0 CoreText default size, Safari has 16.0 + + if ([fontSize isEqualToString:@"smaller"]) + { + _fontDescriptor.pointSize /= 1.2f; + } + else if ([fontSize isEqualToString:@"larger"]) + { + _fontDescriptor.pointSize *= 1.2f; + } + else if ([fontSize isEqualToString:@"xx-small"]) + { + _fontDescriptor.pointSize = 9.0f * _textScale; + } + else if ([fontSize isEqualToString:@"x-small"]) + { + _fontDescriptor.pointSize = 10.0f * _textScale; + } + else if ([fontSize isEqualToString:@"small"]) + { + _fontDescriptor.pointSize = 13.0f * _textScale; + } + else if ([fontSize isEqualToString:@"medium"]) + { + _fontDescriptor.pointSize = 16.0f * _textScale; + } + else if ([fontSize isEqualToString:@"large"]) + { + _fontDescriptor.pointSize = 18.0f * _textScale; + } + else if ([fontSize isEqualToString:@"x-large"]) + { + _fontDescriptor.pointSize = 24.0f * _textScale; + } + else if ([fontSize isEqualToString:@"xx-large"]) + { + _fontDescriptor.pointSize = 32.0f * _textScale; + } + else if ([fontSize isEqualToString:@"-webkit-xxx-large"]) + { + _fontDescriptor.pointSize = 48.0f * _textScale; + } + else if ([fontSize isEqualToString:@"inherit"]) + { + _fontDescriptor.pointSize = self.parentElement.fontDescriptor.pointSize; + } + else if ([fontSize isCSSLengthValue]) + { + _fontDescriptor.pointSize = [fontSize pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_currentTextSize textScale:_textScale]; + } + } + + NSString *color = [styles objectForKey:@"color"]; + if (color) + { + self.textColor = DTColorCreateWithHTMLName(color); + } + + NSString *bgColor = [styles objectForKey:@"background-color"]; + if (bgColor) + { + self.backgroundColor = DTColorCreateWithHTMLName(bgColor); + } + + NSString *floatString = [styles objectForKey:@"float"]; + + if (floatString) + { + if ([floatString isEqualToString:@"left"]) + { + _floatStyle = DTHTMLElementFloatStyleLeft; + } + else if ([floatString isEqualToString:@"right"]) + { + _floatStyle = DTHTMLElementFloatStyleRight; + } + else if ([floatString isEqualToString:@"none"]) + { + _floatStyle = DTHTMLElementFloatStyleNone; + } + } + + id fontFamilyStyle = [styles objectForKey:@"font-family"]; + + if (fontFamilyStyle) + { + NSArray *fontFamilies; + + if ([fontFamilyStyle isKindOfClass:[NSString class]]) + { + fontFamilies = [NSArray arrayWithObject:fontFamilyStyle]; + } + else if ([fontFamilyStyle isKindOfClass:[NSArray class]]) + { + fontFamilies = fontFamilyStyle; + } + + BOOL foundFontFamily = NO; + + for (NSString *fontFamily in fontFamilies) + { + _fontDescriptor.fontFamily = fontFamily; + + // check if this is a known font family + CTFontRef font = [_fontDescriptor newMatchingFont]; + + if (font) + { + NSString *foundFamily = CFBridgingRelease(CTFontCopyFamilyName(font)); + CFRelease(font); + + if ([foundFamily isEqualToString:fontFamily]) + { + foundFontFamily = YES; + break; + } + + NSString *lowercaseFontFamily = [fontFamily lowercaseString]; + + if ([lowercaseFontFamily rangeOfString:@"geneva"].length) + { + _fontDescriptor.fontFamily = @"Helvetica"; + foundFontFamily = YES; + } + else if ([lowercaseFontFamily rangeOfString:@"cursive"].length) + { + _fontDescriptor.stylisticClass = kCTFontScriptsClass; + _fontDescriptor.fontFamily = nil; + foundFontFamily = YES; + } + else if ([lowercaseFontFamily rangeOfString:@"sans-serif"].length) + { + // too many matches (24) + // fontDescriptor.stylisticClass = kCTFontSansSerifClass; + _fontDescriptor.fontFamily = @"Helvetica"; + foundFontFamily = YES; + } + else if ([lowercaseFontFamily rangeOfString:@"serif"].length) + { + // kCTFontTransitionalSerifsClass = Baskerville + // kCTFontClarendonSerifsClass = American Typewriter + // kCTFontSlabSerifsClass = Courier New + // + // strangely none of the classes yields Times + _fontDescriptor.fontFamily = @"Times New Roman"; + foundFontFamily = YES; + } + else if ([lowercaseFontFamily rangeOfString:@"fantasy"].length) + { + _fontDescriptor.fontFamily = @"Papyrus"; // only available on iPad + foundFontFamily = YES; + } + else if ([lowercaseFontFamily rangeOfString:@"monospace"].length) + { + _fontDescriptor.monospaceTrait = YES; + _fontDescriptor.fontFamily = @"Courier"; + foundFontFamily = YES; + } + else if ([lowercaseFontFamily rangeOfString:@"times"].length) + { + _fontDescriptor.fontFamily = @"Times New Roman"; + foundFontFamily = YES; + } + else if ([lowercaseFontFamily isEqualToString:@"inherit"]) + { + _fontDescriptor.fontFamily = self.parentElement.fontDescriptor.fontFamily; + foundFontFamily = YES; + } + } + + if (foundFontFamily) + { + break; + } + } + + if (!foundFontFamily) + { + // probably custom font registered in info.plist + _fontDescriptor.fontFamily = [fontFamilies objectAtIndex:0]; + } + } + + NSString *fontStyle = [[styles objectForKey:@"font-style"] lowercaseString]; + if (fontStyle) + { + // remove font name since this would cause font creation to ignore the trait + _fontDescriptor.fontName = nil; + + if ([fontStyle isEqualToString:@"normal"]) + { + _fontDescriptor.italicTrait = NO; + } + else if ([fontStyle isEqualToString:@"italic"] || [fontStyle isEqualToString:@"oblique"]) + { + _fontDescriptor.italicTrait = YES; + } + else if ([fontStyle isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *fontWeight = [[styles objectForKey:@"font-weight"] lowercaseString]; + if (fontWeight) + { + // remove font name since this would cause font creation to ignore the trait + _fontDescriptor.fontName = nil; + + if ([fontWeight isEqualToString:@"normal"]) + { + _fontDescriptor.boldTrait = NO; + } + else if ([fontWeight isEqualToString:@"bold"]) + { + _fontDescriptor.boldTrait = YES; + } + else if ([fontWeight isEqualToString:@"bolder"]) + { + _fontDescriptor.boldTrait = YES; + } + else if ([fontWeight isEqualToString:@"lighter"]) + { + _fontDescriptor.boldTrait = NO; + } + else + { + // can be 100 - 900 + + NSInteger value = [fontWeight intValue]; + + if (value<=600) + { + _fontDescriptor.boldTrait = NO; + } + else + { + _fontDescriptor.boldTrait = YES; + } + } + } + + NSString *decoration = [[styles objectForKey:@"text-decoration"] lowercaseString]; + if (decoration) + { + if ([decoration isEqualToString:@"underline"]) + { + self.underlineStyle = kCTUnderlineStyleSingle; + } + else if ([decoration isEqualToString:@"line-through"]) + { + self.strikeOut = YES; + } + else if ([decoration isEqualToString:@"none"]) + { + // remove all + self.underlineStyle = kCTUnderlineStyleNone; + self.strikeOut = NO; + } + else if ([decoration isEqualToString:@"overline"]) + { + DTLogInfo(@"Note: 'overline' text decoration not supported"); + } + else if ([decoration isEqualToString:@"blink"]) + { + DTLogInfo(@"Note: 'blink' text decoration not supported"); + } + else if ([decoration isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *alignment = [[styles objectForKey:@"text-align"] lowercaseString]; + if (alignment) + { + if ([alignment isEqualToString:@"left"]) + { + self.paragraphStyle.alignment = kCTLeftTextAlignment; + } + else if ([alignment isEqualToString:@"right"]) + { + self.paragraphStyle.alignment = kCTRightTextAlignment; + } + else if ([alignment isEqualToString:@"center"]) + { + self.paragraphStyle.alignment = kCTCenterTextAlignment; + } + else if ([alignment isEqualToString:@"justify"]) + { + self.paragraphStyle.alignment = kCTJustifiedTextAlignment; + } + else if ([alignment isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *verticalAlignment = [[styles objectForKey:@"vertical-align"] lowercaseString]; + if (verticalAlignment) + { + if ([verticalAlignment isEqualToString:@"sub"]) + { + self.superscriptStyle = -1; + } + else if ([verticalAlignment isEqualToString:@"super"]) + { + self.superscriptStyle = +1; + } + else if ([verticalAlignment isEqualToString:@"baseline"]) + { + self.superscriptStyle = 0; + } + else if ([verticalAlignment isEqualToString:@"inherit"]) + { + // nothing to do + } + else if ([verticalAlignment isEqualToString:@"text-top"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentTop; + } + else if ([verticalAlignment isEqualToString:@"middle"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentCenter; + } + else if ([verticalAlignment isEqualToString:@"text-bottom"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentBottom; + } + else if ([verticalAlignment isEqualToString:@"baseline"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentBaseline; + } + } + + NSString *letterSpacing = [[styles objectForKey:@"letter-spacing"] lowercaseString]; + if (letterSpacing) + { + if ([letterSpacing isEqualToString:@"normal"]) + { + _letterSpacing = 0; + } + else if ([letterSpacing isEqualToString:@"inherit"]) + { + // no op, we already inherited it + } + else // interpret as length + { + _letterSpacing = [letterSpacing pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + } + + // if there is a text attachment we transfer the aligment we got + _textAttachment.verticalAlignment = _textAttachmentAlignment; + + id shadow = [styles objectForKey:@"text-shadow"]; + if (shadow) + { + + self.shadows = [shadow arrayOfCSSShadowsWithCurrentTextSize:_fontDescriptor.pointSize currentColor:_textColor]; + } + + NSString *lineHeight = [[styles objectForKey:@"line-height"] lowercaseString]; + if (lineHeight) + { + if ([lineHeight isEqualToString:@"normal"]) + { + self.paragraphStyle.lineHeightMultiple = 0.0; // default + self.paragraphStyle.minimumLineHeight = 0.0; // default + self.paragraphStyle.maximumLineHeight = 0.0; // default + } + else if ([lineHeight isEqualToString:@"inherit"]) + { + // no op, we already inherited it + } + else if ([lineHeight isNumeric]) + { + self.paragraphStyle.lineHeightMultiple = [lineHeight floatValue]; + } + else // interpret as length + { + CGFloat lineHeightValue = [lineHeight pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + self.paragraphStyle.minimumLineHeight = lineHeightValue; + self.paragraphStyle.maximumLineHeight = lineHeightValue; + } + } + + NSString *fontVariantStr = [[styles objectForKey:@"font-variant"] lowercaseString]; + if (fontVariantStr) + { + if ([fontVariantStr isEqualToString:@"small-caps"]) + { + _fontVariant = DTHTMLElementFontVariantSmallCaps; + } + else if ([fontVariantStr isEqualToString:@"inherit"]) + { + _fontVariant = DTHTMLElementFontVariantInherit; + } + else + { + _fontVariant = DTHTMLElementFontVariantNormal; + } + } + + NSString *widthString = [styles objectForKey:@"width"]; + if (widthString && ![widthString isEqualToString:@"auto"]) + { + _size.width = [widthString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + + NSString *heightString = [styles objectForKey:@"height"]; + if (heightString && ![heightString isEqualToString:@"auto"]) + { + _size.height = [heightString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + + NSString *whitespaceString = [styles objectForKey:@"white-space"]; + if ([whitespaceString hasPrefix:@"pre"]) + { + _preserveNewlines = YES; + } + else + { + _preserveNewlines = NO; + } + + NSString *displayString = [styles objectForKey:@"display"]; + if (displayString) + { + if ([displayString isEqualToString:@"none"]) + { + _displayStyle = DTHTMLElementDisplayStyleNone; + } + else if ([displayString isEqualToString:@"block"]) + { + _displayStyle = DTHTMLElementDisplayStyleBlock; + } + else if ([displayString isEqualToString:@"inline"]) + { + _displayStyle = DTHTMLElementDisplayStyleInline; + } + else if ([displayString isEqualToString:@"list-item"]) + { + _displayStyle = DTHTMLElementDisplayStyleListItem; + } + else if ([displayString isEqualToString:@"table"]) + { + _displayStyle = DTHTMLElementDisplayStyleTable; + } + else if ([verticalAlignment isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *borderColor = [styles objectForKey:@"border-color"]; + if (borderColor) + { + self.backgroundStrokeColor = DTColorCreateWithHTMLName(borderColor); + } + NSString *borderWidth = [[styles objectForKey:@"border-width"] lowercaseString]; + if (borderWidth) + { + _backgroundStrokeWidth = [borderWidth floatValue]; + } + else { + _backgroundStrokeWidth = 0.0f; + } + NSString *cornerRadius = [[styles objectForKey:@"border-radius"] lowercaseString]; + if (cornerRadius) + { + _backgroundCornerRadius = [cornerRadius floatValue]; + } + else { + _backgroundCornerRadius = 0.0f; + } + + BOOL needsTextBlock = (_backgroundColor!=nil || _backgroundStrokeColor!=nil || _backgroundCornerRadius > 0 || _backgroundStrokeWidth > 0); + + BOOL hasMargins = NO; + + NSString *allKeys = [[styles allKeys] componentsJoinedByString:@";"]; + + // there can only be padding if the word "margin" occurs in the styles keys + if ([allKeys rangeOfString:@"-webkit-margin"].length) + { + hasMargins = ([self _parseEdgeInsetsFromStyleDictionary:styles forAttributesWithPrefix:@"-webkit-margin" writingDirection:self.paragraphStyle.baseWritingDirection intoEdgeInsets:&_margins] || hasMargins); + } + + if ([allKeys rangeOfString:@"margin"].length) + { + hasMargins = ([self _parseEdgeInsetsFromStyleDictionary:styles forAttributesWithPrefix:@"margin" writingDirection:self.paragraphStyle.baseWritingDirection intoEdgeInsets:&_margins] || hasMargins); + } + + BOOL hasPadding = NO; + + // there can only be padding if the word "padding" occurs in the styles keys + if ([allKeys rangeOfString:@"-webkit-padding"].length) + { + hasPadding = ([self _parseEdgeInsetsFromStyleDictionary:styles forAttributesWithPrefix:@"-webkit-padding" writingDirection:self.paragraphStyle.baseWritingDirection intoEdgeInsets:&_padding] || hasPadding); + } + + if ([allKeys rangeOfString:@"padding"].length) + { + + hasPadding = ([self _parseEdgeInsetsFromStyleDictionary:styles forAttributesWithPrefix:@"padding" writingDirection:self.paragraphStyle.baseWritingDirection intoEdgeInsets:&_padding] || hasPadding); + } + + if (hasPadding) + { + if ([self.name isEqualToString:@"ul"] || [self.name isEqualToString:@"ol"]) + { + _listIndent = _padding.left; + _padding.left = 0; + } + + // if we still have padding we need a block + if (_padding.left>0 || _padding.right>0 || _padding.top>0 || _padding.bottom>0) + { + needsTextBlock = YES; + } + } + + if (_displayStyle == DTHTMLElementDisplayStyleBlock) + { + // we only care for margins of block level elements + if (hasMargins) + { + self.paragraphStyle.paragraphSpacing = _margins.bottom; + + // we increase the inherited values for the time being + self.paragraphStyle.headIndent += _margins.left; + self.paragraphStyle.firstLineHeadIndent = self.paragraphStyle.headIndent; + + // tailIndent from right side is negative + self.paragraphStyle.tailIndent -= _margins.right; + } + + if (needsTextBlock) + { + // need a block + DTTextBlock *newBlock = [[DTTextBlock alloc] init]; + + newBlock.padding = _padding; + + // transfer background color to block + newBlock.backgroundColor = _backgroundColor; + _backgroundColor = nil; + + if (self.paragraphStyle.textBlocks) + { + // make mutable version + NSMutableArray *mutableBlocks = [self.paragraphStyle.textBlocks mutableCopy]; + + // add new block to the array + [mutableBlocks addObject:newBlock]; + + // set non-mutable version + self.paragraphStyle.textBlocks = [mutableBlocks copy]; + } + else + { + // didn't have any blocks before, start new array + NSArray *blocks = [NSArray arrayWithObject:newBlock]; + self.paragraphStyle.textBlocks = blocks; + } + } + } + else if (_displayStyle == DTHTMLElementDisplayStyleListItem) + { + self.paragraphStyle.paragraphSpacing = _margins.bottom; + } + + NSString *coretextFontString = [styles objectForKey:@"-coretext-fontname"]; + if (coretextFontString) + { + _fontDescriptor.fontName = [styles objectForKey:@"-coretext-fontname"]; + } +} + +- (DTCSSListStyle *)listStyle +{ + DTCSSListStyle *style = [[DTCSSListStyle alloc] initWithStyles:_styles]; + + NSString *startingIndex = [_attributes objectForKey:@"start"]; + + // set the starting index if there is one specified + if (startingIndex) + { + style.startingItemNumber = [startingIndex integerValue]; + } + + return style; +} + +#pragma mark - Calulcating Properties + +- (id)valueForKeyPathWithInheritance:(NSString *)keyPath +{ + id value = [self valueForKeyPath:keyPath]; + + // if property is not set we also go to parent + if (!value && self.parentElement) + { + return [self.parentElement valueForKeyPathWithInheritance:keyPath]; + } + + // enum properties have 0 for inherit + if ([value isKindOfClass:[NSNumber class]]) + { + NSNumber *number = value; + + if (([number integerValue]==0) && self.parentElement) + { + return [self.parentElement valueForKeyPathWithInheritance:keyPath]; + } + } + + // string properties have 'inherit' for inheriting + if ([value isKindOfClass:[NSString class]]) + { + NSString *string = value; + + if ([string isEqualToString:@"inherit"] && self.parentElement) + { + return [self.parentElement valueForKeyPathWithInheritance:keyPath]; + } + } + + // obviously not inherited + return value; +} + +#pragma mark - Working with HTML Attributes + ++ (NSSet *)attributesToIgnoreForCustomAttributesAttribute +{ + return [NSSet setWithObjects:@"style", @"dir", @"align", @"src", @"href", @"color", @"face", @"size", @"name", @"height", @"width", nil]; +} + +- (NSString *)attributeForKey:(NSString *)key +{ + return [_attributes objectForKey:key]; +} + +- (void)inheritAttributesFromElement:(DTHTMLElement *)element +{ + _fontDescriptor = [element.fontDescriptor copy]; + _paragraphStyle = [element.paragraphStyle copy]; + + _headerLevel = element.headerLevel; + + _fontVariant = element.fontVariant; + _underlineStyle = element.underlineStyle; + _strikeOut = element.strikeOut; + _superscriptStyle = element.superscriptStyle; + _letterSpacing = element.letterSpacing; + + _shadows = [element.shadows copy]; + + _link = [element.link copy]; + _anchorName = [element.anchorName copy]; + _linkGUID = element.linkGUID; + + _textColor = element.textColor; + _isColorInherited = YES; + + _preserveNewlines = element.preserveNewlines; + + _currentTextSize = element.currentTextSize; + _textScale = element.textScale; + + _backgroundColor = element.backgroundColor; + _backgroundStrokeColor = element.backgroundStrokeColor; + _backgroundStrokeWidth = element.backgroundStrokeWidth; + _backgroundCornerRadius = element.backgroundCornerRadius; + + // only inherit background-color from inline elements + if (element.displayStyle == DTHTMLElementDisplayStyleInline || element.displayStyle == DTHTMLElementDisplayStyleListItem) + { + self.backgroundColor = element.backgroundColor; + } + + _containsAppleConvertedSpace = element.containsAppleConvertedSpace; + + // we copy the link because we might need for it making the custom view + _textAttachment.hyperLinkURL = element.link; +} + +- (void)interpretAttributes +{ + if (!_attributes) + { + // nothing to interpret + return; + } + + // transfer Apple Converted Space tag + if ([[self attributeForKey:@"class"] isEqualToString:@"Apple-converted-space"]) + { + _containsAppleConvertedSpace = YES; + } + + // detect writing direction if set + NSString *directionStr = [self attributeForKey:@"dir"]; + + if (directionStr) + { + NSAssert(_paragraphStyle, @"Found dir attribute, but missing paragraph style on element"); + + if ([directionStr isEqualToString:@"rtl"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; + } + else if ([directionStr isEqualToString:@"ltr"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + } + else if ([directionStr isEqualToString:@"auto"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionNatural; // that's also default + } + else + { + // other values are invalid and will be ignored + } + } + + // handles align="justify" + NSString *align = [self attributeForKey:@"align"]; + + if (align) + { + if ([align isEqualToString: @"justify"]) + { + _paragraphStyle.alignment = kCTTextAlignmentJustified; + } + else if ([align isEqualToString: @"left"]) + { + _paragraphStyle.alignment = kCTTextAlignmentLeft; + } + else if ([align isEqualToString: @"center"]) + { + _paragraphStyle.alignment = kCTTextAlignmentCenter; + } + else if ([align isEqualToString: @"right"]) + { + _paragraphStyle.alignment = kCTTextAlignmentRight; + } + } +} + +#pragma mark - Properties + +- (void)setTextColor:(DTColor *)textColor +{ + if (_textColor != textColor) + { + + _textColor = textColor; + _isColorInherited = NO; + } +} + +- (DTHTMLElementFontVariant)fontVariant +{ + if (_fontVariant == DTHTMLElementFontVariantInherit) + { + if (self.parentElement) + { + return self.parentElement.fontVariant; + } + + return DTHTMLElementFontVariantNormal; + } + + return _fontVariant; +} + +- (void)setAttributes:(NSDictionary *)attributes +{ + [super setAttributes:[attributes copy]]; + + // decode size contained in attributes, might be overridden later by CSS size + _size = CGSizeMake([[self attributeForKey:@"width"] floatValue], [[self attributeForKey:@"height"] floatValue]); +} + +- (void)setTextAttachment:(DTTextAttachment *)textAttachment +{ + textAttachment.verticalAlignment = _textAttachmentAlignment; + _textAttachment = textAttachment; + + // transfer link GUID + _textAttachment.hyperLinkGUID = _linkGUID; + + // transfer size + _textAttachment.displaySize = _size; +} + +- (void)setLink:(NSURL *)link +{ + _linkGUID = [NSString stringWithUUID]; + _link = [link copy]; + + if (_textAttachment) + { + _textAttachment.hyperLinkGUID = _linkGUID; + } +} + +- (void)setDidOutput:(BOOL)didOutput +{ + @synchronized(self) + { + _didOutput = didOutput; + } +} + +- (BOOL)didOutput +{ + @synchronized(self) + { + return _didOutput; + } +} + +- (void)setCurrentTextSize:(CGFloat)currentTextSize { + _currentTextSize = currentTextSize; +} + +- (CGFloat)currentTextSize { + CGFloat textSize = _currentTextSize; + + if ((_currentTextSize == 0) && self.parentElement) { + textSize = self.parentElement.currentTextSize; + } + + return textSize; +} + +- (DTHTMLElement *)parentElement +{ + return (DTHTMLElement *)self.parentNode; +} + +@synthesize fontDescriptor = _fontDescriptor; +@synthesize paragraphStyle = _paragraphStyle; +@synthesize textColor = _textColor; +@synthesize backgroundColor = _backgroundColor; +@synthesize beforeContent = _beforeContent; +@synthesize link = _link; +@synthesize anchorName = _anchorName; +@synthesize underlineStyle = _underlineStyle; +@synthesize textAttachment = _textAttachment; +@synthesize strikeOut = _strikeOut; +@synthesize superscriptStyle = _superscriptStyle; +@synthesize headerLevel = _headerLevel; +@synthesize shadows = _shadows; +@synthesize floatStyle = _floatStyle; +@synthesize isColorInherited = _isColorInherited; +@synthesize preserveNewlines = _preserveNewlines; +@synthesize displayStyle = _displayStyle; +@synthesize fontVariant = _fontVariant; +@synthesize currentTextSize = _currentTextSize; +@synthesize textScale = _textScale; +@synthesize size = _size; +@synthesize margins = _margins; +@synthesize padding = _padding; +@synthesize linkGUID = _linkGUID; +@synthesize containsAppleConvertedSpace = _containsAppleConvertedSpace; +@synthesize CSSClassNamesToIgnoreForCustomAttributes = _CSSClassNamesToIgnoreForCustomAttributes; +@synthesize shouldProcessCustomHTMLAttributes = _shouldProcessCustomHTMLAttributes; +@synthesize backgroundStrokeColor = _backgroundStrokeColor; +@synthesize backgroundStrokeWidth = _backgroundStrokeWidth; +@synthesize backgroundCornerRadius = _backgroundCornerRadius; +@synthesize letterSpacing = _letterSpacing; + +@end + + diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserNode.h b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.h new file mode 100644 index 0000000..0290119 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.h @@ -0,0 +1,76 @@ +// +// DTHTMLParserNode.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTWeakSupport.h" + +@class DTHTMLParserTextNode; + +/** + This class represents one node in an HTML DOM tree. + */ +@interface DTHTMLParserNode : NSObject +{ + NSDictionary *_attributes; +} + +/** + Designated initializer + @param name The element name + @param attributes The attributes dictionary + @returns An initialized parser node. + */ +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes; + +/** + The name of the receiver + */ +@property (nonatomic, copy) NSString *name; + +/** + The attributes of the receiver. + */ +@property (nonatomic, copy) NSDictionary *attributes; + +/** + A weak link to the parent node of the receiver + */ +@property (nonatomic, DT_WEAK_PROPERTY) DTHTMLParserNode *parentNode; + +/** + The child nodes of the receiver + */ +@property (nonatomic, readonly) NSArray *childNodes; + +/** + Adds a child node to the receiver. + @param childNode The child node to be appended to the list of children + */ +- (void)addChildNode:(DTHTMLParserNode *)childNode; + +/** + Removes a child node from the receiver + @param childNode The child node to remove + */ +- (void)removeChildNode:(DTHTMLParserNode *)childNode; + +/** + Removes all child nodes from the receiver +*/ +- (void)removeAllChildNodes; + +/** + Hierarchy representation of the receiver including all attributes and children + */ +- (NSString *)debugDescription; + +/** + Concatenated contents of all text nodes + */ +- (NSString *)text; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserNode.m b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.m new file mode 100644 index 0000000..944fda0 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.m @@ -0,0 +1,167 @@ +// +// DTHTMLParserNode.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParserNode.h" +#import "DTHTMLParserTextNode.h" +#import "DTWeakSupport.h" + +@implementation DTHTMLParserNode +{ + NSString *_name; + DT_WEAK_VARIABLE DTHTMLParserNode *_parentNode; + NSMutableArray *_childNodes; +} + + +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes +{ + self = [super init]; + + if (self) + { + _name = [name copy]; + [self setAttributes:attributes]; // property to allow overriding + } + + return self; +} + +- (void)addChildNode:(DTHTMLParserNode *)childNode +{ + @synchronized(self) + { + // first child creates array + if (!_childNodes) + { + _childNodes = [[NSMutableArray alloc] init]; + } + + childNode.parentNode = self; + [_childNodes addObject:childNode]; + } +} + +- (void)removeChildNode:(DTHTMLParserNode *)childNode +{ + @synchronized(self) + { + [_childNodes removeObject:childNode]; + } +} + +- (void)removeAllChildNodes +{ + @synchronized(self) + { + [_childNodes removeAllObjects]; + } +} + +#ifndef COVERAGE +// exclude methods from coverage testing + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ name='%@'>", NSStringFromClass([self class]), _name]; +} + +#endif + +- (void)_appendHTMLToString:(NSMutableString *)string indentLevel:(NSUInteger)indentLevel +{ + @synchronized(self) + { + // indent to the level + for (NSUInteger i=0; i\n"]; + return; + } + + [string appendFormat:@">\n"]; + + // output attributes + for (DTHTMLParserNode *childNode in _childNodes) + { + [childNode _appendHTMLToString:string indentLevel:indentLevel+1]; + } + + // indent to the level + for (NSUInteger i=0; i\n", _name]; + } +} + +- (NSString *)debugDescription +{ + @synchronized(self) + { + NSMutableString *tmpString = [NSMutableString string]; + + [self _appendHTMLToString:tmpString indentLevel:0]; + + return tmpString; + } +} + +- (NSString *)text +{ + @synchronized(self) + { + NSMutableString *text = [NSMutableString string]; + + for (DTHTMLParserTextNode *oneChild in self.childNodes) + { + if ([oneChild isKindOfClass:[DTHTMLParserTextNode class]]) + { + [text appendString:[oneChild characters]]; + } + } + + return text; + } +} + +#pragma mark - Properties + +- (NSArray *)childNodes +{ + @synchronized(self) + { + return _childNodes; + } +} + +@synthesize name = _name; +@synthesize attributes = _attributes; +@synthesize parentNode = _parentNode; +@synthesize childNodes = _childNodes; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.h b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.h new file mode 100644 index 0000000..8e12d98 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.h @@ -0,0 +1,28 @@ +// +// DTHTMLParserTextNode.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParserNode.h" + +/** + Specialized sub class of that represents text inside a node + */ +@interface DTHTMLParserTextNode : DTHTMLParserNode + +/** + Designated initializer with the characters that make up the text. + @param characters The characters of the string + @returns The initialized text node + */ +- (id)initWithCharacters:(NSString *)characters; + +/** + Returns the receivers character contents + */ +@property (nonatomic, readonly) NSString *characters; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.m b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.m new file mode 100644 index 0000000..7b4a352 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.m @@ -0,0 +1,56 @@ +// +// DTHTMLParserTextNode.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParserTextNode.h" +#import "NSString+HTML.h" + +@implementation DTHTMLParserTextNode +{ + NSString *_characters; +} + +- (id)initWithCharacters:(NSString *)characters +{ + self = [super init]; + + if (self) + { + self.name = @"#TEXT#"; + + _characters = characters; + + } + return self; +} + +#ifndef COVERAGE +// exclude methods from coverage testing + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ content='%@'>", NSStringFromClass([self class]), _characters]; +} + +- (void)_appendHTMLToString:(NSMutableString *)string indentLevel:(NSUInteger)indentLevel +{ + // indent to the level + for (NSUInteger i=0; i"; + } + else + { + return @""; + } + } + else + { + if (listStyle.position == DTCSSListStylePositionInside) + { + typeString = [typeString stringByAppendingString:@" inside"]; + } + else if (listStyle.position == DTCSSListStylePositionOutside) + { + typeString = [typeString stringByAppendingString:@" outside"]; + } + + NSString *blockElement; + if (isOrdered) + { + blockElement = @"ol"; + } + else + { + blockElement = @"ul"; + } + + NSString *listStyleString = [NSString stringWithFormat:@"list-style:'%@';", typeString]; + + if (listPadding>0) + { + listStyleString = [listStyleString stringByAppendingFormat:@"-webkit-padding-start:%.0fpx;padding-left:%.0fpx;", listPadding, listPadding]; + } + + NSString *className = [self _styleClassForElement:blockElement style:listStyleString]; + + if (inlineStyles) + { + return [NSString stringWithFormat:@"<%@ style=\"%@\">", blockElement, listStyleString]; + } + else + { + return [NSString stringWithFormat:@"<%@ class=\"%@\">", blockElement, className]; + } + } +} + +- (void)_buildOutput +{ + [self _buildOutputAsHTMLFragment:NO]; +} + +- (void)_buildOutputAsHTMLFragment:(BOOL)fragment +{ + // reusable styles + _styleLookup = [[NSMutableDictionary alloc] init]; + + NSString *plainString = [_attributedString string]; + + // divide the string into it's blocks (we assume that these are the P) + NSArray *paragraphs = [plainString componentsSeparatedByString:@"\n"]; + + NSMutableString *retString = [NSMutableString string]; + + NSInteger location = 0; + + NSArray *previousListStyles = nil; + + for (NSUInteger i=0; i<[paragraphs count]; i++) + { + NSString *oneParagraph = [paragraphs objectAtIndex:i]; + NSRange paragraphRange = NSMakeRange(location, [oneParagraph length]); + + // skip empty paragraph at the end + if (i==[paragraphs count]-1) + { + if (!paragraphRange.length) + { + continue; + } + } + + __block BOOL needsToRemovePrefix = NO; + + BOOL fontIsBlockLevel = NO; + + // check if font is same in the entire paragraph + NSRange fontEffectiveRange; + CTFontRef paragraphFont = (__bridge CTFontRef)[_attributedString attribute:(id)kCTFontAttributeName atIndex:paragraphRange.location longestEffectiveRange:&fontEffectiveRange inRange:paragraphRange]; + + if (NSEqualRanges(paragraphRange, fontEffectiveRange)) + { + fontIsBlockLevel = YES; + } + + // next paragraph start + location = location + paragraphRange.length + 1; + + NSDictionary *paraAttributes = [_attributedString attributesAtIndex:paragraphRange.location effectiveRange:NULL]; + + // lets see if we have a list style + NSArray *currentListStyles = [paraAttributes objectForKey:DTTextListsAttribute]; + + DTCSSListStyle *effectiveListStyle = [currentListStyles lastObject]; + + // retrieve the paragraph style + DTCoreTextParagraphStyle *paragraphStyle = [paraAttributes paragraphStyle]; + NSString *paraStyleString = nil; + + if (paragraphStyle && !effectiveListStyle) + { + if (_textScale!=1.0f) + { + paragraphStyle.minimumLineHeight = round(paragraphStyle.minimumLineHeight / _textScale); + paragraphStyle.maximumLineHeight = round(paragraphStyle.maximumLineHeight / _textScale); + + paragraphStyle.paragraphSpacing = round(paragraphStyle.paragraphSpacing/ _textScale); + paragraphStyle.paragraphSpacingBefore = round(paragraphStyle.paragraphSpacingBefore / _textScale); + + paragraphStyle.firstLineHeadIndent = round(paragraphStyle.firstLineHeadIndent / _textScale); + paragraphStyle.headIndent = round(paragraphStyle.headIndent / _textScale); + paragraphStyle.tailIndent = round(paragraphStyle.tailIndent / _textScale); + } + + paraStyleString = [paragraphStyle cssStyleRepresentation]; + } + + if (!paraStyleString) + { + paraStyleString = @""; + } + + if (fontIsBlockLevel) + { + if (paragraphFont) + { + DTCoreTextFontDescriptor *desc = [DTCoreTextFontDescriptor fontDescriptorForCTFont:paragraphFont]; + + if (_textScale!=1.0f) + { + desc.pointSize /= _textScale; + } + + NSString *paraFontStyle = [desc cssStyleRepresentation]; + + if (paraFontStyle) + { + paraStyleString = [paraStyleString stringByAppendingString:paraFontStyle]; + } + } + } + + NSString *blockElement; + + // close until we are at current or nil + if ([previousListStyles count]>[currentListStyles count]) + { + NSMutableArray *closingStyles = [previousListStyles mutableCopy]; + + do + { + DTCSSListStyle *closingStyle = [closingStyles lastObject]; + + if (closingStyle == effectiveListStyle) + { + break; + } + + // end of a list block + [retString appendString:[self _tagRepresentationForListStyle:closingStyle closingTag:YES listPadding:0 inlineStyles:fragment]]; + [retString appendString:@"\n"]; + + [closingStyles removeLastObject]; + + previousListStyles = closingStyles; + } + while ([closingStyles count]); + } + + if (effectiveListStyle) + { + // next text needs to have list prefix removed + needsToRemovePrefix = YES; + + + // get lists that need to be opened here + NSArray *listsToOpen = nil; + + if (!previousListStyles) + { + listsToOpen = currentListStyles; + } + else + { + NSMutableArray *tmpArray = [NSMutableArray array]; + + for (DTCSSListStyle *oneList in currentListStyles) + { + NSRange listRange = [_attributedString rangeOfTextList:oneList atIndex:paragraphRange.location]; + + if (listRange.location == paragraphRange.location) + { + // lists starts here + [tmpArray addObject:oneList]; + } + } + + if ([tmpArray count]) + { + listsToOpen = [tmpArray copy]; + } + } + + [listsToOpen enumerateObjectsUsingBlock:^(DTCSSListStyle *oneList, NSUInteger idx, BOOL *stop) { + + // only padding can be reconstructed so far + CGFloat listPadding = (paragraphStyle.headIndent - paragraphStyle.firstLineHeadIndent) / _textScale; + + // beginning of a list block + [retString appendString:[self _tagRepresentationForListStyle:oneList closingTag:NO listPadding:listPadding inlineStyles:fragment]]; + [retString appendString:@"\n"]; + + // all but the effective list need an extra LI + if (oneList != effectiveListStyle) + { + [retString appendString:@"
  • "]; + } + }]; + + blockElement = @"li"; + } + else + { + blockElement = @"p"; + } + + NSNumber *headerLevel = [paraAttributes objectForKey:DTHeaderLevelAttribute]; + + if (headerLevel) + { + blockElement = [NSString stringWithFormat:@"h%d", (int)[headerLevel integerValue]]; + } + + if ([paragraphs lastObject] == oneParagraph) + { + // last paragraph in string + + if (![plainString hasSuffix:@"\n"]) + { + // not a whole paragraph, so we don't put it in P + blockElement = @"span"; + } + } + + // find which custom attributes are for the entire paragraph + NSDictionary *HTMLAttributes = [_attributedString HTMLAttributesAtIndex:paragraphRange.location]; + NSMutableDictionary *paragraphLevelHTMLAttributes = [NSMutableDictionary dictionary]; + + [HTMLAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + + // check if range is longer than current paragraph + NSRange attributeEffectiveRange = [_attributedString rangeOfHTMLAttribute:key atIndex:paragraphRange.location]; + + if (NSIntersectionRange(attributeEffectiveRange, paragraphRange).length == paragraphRange.length) + { + [paragraphLevelHTMLAttributes setObject:value forKey:key]; + } + }]; + + // Add dir="auto" if the writing direction is unknown + if (paragraphStyle) + { + switch (paragraphStyle.baseWritingDirection) + { + case kCTWritingDirectionNatural: + { + [paragraphLevelHTMLAttributes setObject:@"auto" forKey:@"dir"]; + break; + } + + case kCTWritingDirectionRightToLeft: + { + [paragraphLevelHTMLAttributes setObject:@"rtl" forKey:@"dir"]; + break; + } + + case kCTWritingDirectionLeftToRight: + { + // this is default, so we omit it + break; + } + } + } + + + // start paragraph start tag + [retString appendFormat:@"<%@", blockElement]; + + // do we have style info? + if ([paraStyleString length]) + { + if (fragment) + { + // stays style for fragment mode + [paragraphLevelHTMLAttributes setObject:paraStyleString forKey:@"style"]; + } + else + { + // compress style for document mode + NSString *className = [self _styleClassForElement:blockElement style:paraStyleString]; + + NSString *existingClasses = [paragraphLevelHTMLAttributes objectForKey:@"class"]; + + if (existingClasses) + { + NSMutableArray *individualClasses = [[existingClasses componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy]; + + // insert compressed class at index 0 + [individualClasses insertObject:className atIndex:0]; + + // rejoin + className = [individualClasses componentsJoinedByString:@" "]; + } + + [paragraphLevelHTMLAttributes setObject:className forKey:@"class"]; + } + } + + // add paragraph level attributes + [paragraphLevelHTMLAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + [retString appendFormat:@" %@=\"%@\"", key, value]; + }]; + + // end paragraph start tag + [retString appendString:@">"]; + + // add the attributed string ranges in this paragraph to the paragraph container + + __block NSRange currentLinkRange = {NSNotFound, 0}; + + __block NSMutableDictionary *linkLevelHTMLAttributes = nil; + + // ----- SPAN enumeration + + [_attributedString enumerateAttributesInRange:paragraphRange options:0 usingBlock:^(NSDictionary *attributes, NSRange spanRange, BOOL *stopEnumerateAttributes) { + + NSURL *spanURL = [attributes objectForKey:DTLinkAttribute]; + NSString *spanAnchorName = [attributes objectForKey:DTAnchorAttribute]; + + BOOL isFirstPartOfHyperlink = NO; + BOOL isLastPartOfHyperlink = NO; + + if ((spanURL || spanAnchorName) && (currentLinkRange.location == NSNotFound)) + { + if (spanURL) + { + currentLinkRange = [_attributedString rangeOfLinkAtIndex:spanRange.location URL:NULL]; + } + else if (spanAnchorName) + { + currentLinkRange = [_attributedString rangeOfAnchorNamed:spanAnchorName]; + } + + isFirstPartOfHyperlink = YES; + + // build the attributes for the A tag + linkLevelHTMLAttributes = [NSMutableDictionary dictionary]; + + if (spanURL) + { + [linkLevelHTMLAttributes setObject:[spanURL relativeString] forKey:@"href"]; + } + + // add anchor name if present + if (spanAnchorName) + { + [linkLevelHTMLAttributes setObject:spanAnchorName forKey:@"name"]; + } + + // find which custom attributes are for the link + NSDictionary *localHTMLAttributes = [_attributedString HTMLAttributesAtIndex:currentLinkRange.location]; + + [localHTMLAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stopEnumerateKeysAndObjects) { + + // check if range is longer than current paragraph + NSRange attributeEffectiveRange = [_attributedString rangeOfHTMLAttribute:key atIndex:currentLinkRange.location]; + + if (NSEqualRanges(attributeEffectiveRange, currentLinkRange)) + { + [linkLevelHTMLAttributes setObject:value forKey:key]; + } + }]; + } + + // check if previous link is over yet + if (NSMaxRange(spanRange) >= NSMaxRange(currentLinkRange)) + { + isLastPartOfHyperlink = YES; + } + + NSString *plainSubString =[plainString substringWithRange:spanRange]; + + if (effectiveListStyle && needsToRemovePrefix) + { + NSRange prefixRange = [_attributedString rangeOfFieldAtIndex:spanRange.location]; + + if (prefixRange.location != NSNotFound) + { + if (NSMaxRange(prefixRange) persistableAttachment = (id)attachment; + + NSString *HTMLString = [persistableAttachment stringByEncodingAsHTML]; + + if (HTMLString) + { + [retString appendString:HTMLString]; + } + } + + if (isLastPartOfHyperlink) + { + currentLinkRange = NSMakeRange(NSNotFound, 0); + } + + return; + } + + NSString *fontStyle = nil; + if (!fontIsBlockLevel) + { + DTCoreTextFontDescriptor *fontDescriptor = [attributes fontDescriptor]; + + if (fontDescriptor) + { + if (_textScale!=1.0f) + { + fontDescriptor.pointSize /= _textScale; + } + + fontStyle = [fontDescriptor cssStyleRepresentation]; + } + } + + if (!fontStyle) + { + fontStyle = @""; + } + + CGFloat kerning = [attributes kerning] / _textScale; + + if (kerning) + { + fontStyle = [fontStyle stringByAppendingFormat:@"letter-spacing:%.0fpx;", kerning]; + } + + DTColor *textColor = [attributes foregroundColor]; + + if (textColor) + { + fontStyle = [fontStyle stringByAppendingFormat:@"color:#%@;", DTHexStringFromDTColor(textColor)]; + } + + DTColor *backgroundColor = [attributes backgroundColor]; + + if (backgroundColor) + { + fontStyle = [fontStyle stringByAppendingFormat:@"background-color:#%@;", DTHexStringFromDTColor(backgroundColor)]; + } + + NSNumber *underline = [attributes objectForKey:(id)kCTUnderlineStyleAttributeName]; + if (underline) + { + fontStyle = [fontStyle stringByAppendingString:@"text-decoration:underline;"]; + } + else + { + // there can be no underline and strike-through at the same time + NSNumber *strikout = [attributes objectForKey:DTStrikeOutAttribute]; + if ([strikout boolValue]) + { + fontStyle = [fontStyle stringByAppendingString:@"text-decoration:line-through;"]; + } + } + + NSNumber *superscript = [attributes objectForKey:(id)kCTSuperscriptAttributeName]; + if (superscript) + { + NSInteger style = [superscript integerValue]; + + switch (style) + { + case 1: + { + fontStyle = [fontStyle stringByAppendingString:@"vertical-align:super;"]; + break; + } + + case -1: + { + fontStyle = [fontStyle stringByAppendingString:@"vertical-align:sub;"]; + break; + } + + default: + { + // all other are baseline because we don't support anything else for text + fontStyle = [fontStyle stringByAppendingString:@"vertical-align:baseline;"]; + + break; + } + } + } + + NSString *spanTagName = @"span"; + + __block BOOL needsSpanTag = NO; + + // find which custom attributes are only for this span + NSDictionary *localHTMLAttributes = [attributes objectForKey:DTCustomAttributesAttribute]; + NSMutableDictionary *spanLevelHTMLAttributes = [NSMutableDictionary dictionary]; + + [localHTMLAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + + // check if there is already an identical paragraph attribute + id valueForParagraph = [paragraphLevelHTMLAttributes objectForKey:key]; + + if (valueForParagraph) + { + if (fragment) + { + if ([valueForParagraph isEqual:value]) + { + return; + } + } + else + { + // need to check components + NSArray *paragraphClassComponents = [valueForParagraph componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + if ([paragraphClassComponents containsObject:value]) + { + return; + } + } + } + + NSRange attributeEffectiveRange = [_attributedString rangeOfHTMLAttribute:key atIndex:spanRange.location]; + + if (currentLinkRange.location==NSNotFound || !NSEqualRanges(attributeEffectiveRange, currentLinkRange)) + { + [spanLevelHTMLAttributes setObject:value forKey:key]; + needsSpanTag = YES; + } + }]; + + if ([fontStyle length]) + { + needsSpanTag = YES; + + if (fragment) + { + // stays style for fragment mode + [spanLevelHTMLAttributes setObject:fontStyle forKey:@"style"]; + } + else + { + // compress style for document mode + NSString *className = [self _styleClassForElement:spanTagName style:fontStyle]; + + NSString *existingClasses = [spanLevelHTMLAttributes objectForKey:@"class"]; + + if (existingClasses) + { + NSMutableArray *individualClasses = [[existingClasses componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy]; + + // insert compressed class at index 0 + [individualClasses insertObject:className atIndex:0]; + + // rejoin + className = [individualClasses componentsJoinedByString:@" "]; + } + + [spanLevelHTMLAttributes setObject:className forKey:@"class"]; + } + } + + if (isFirstPartOfHyperlink) + { + // start link start tag + [retString appendString:@""]; + } + + + if (needsSpanTag) + { + // start span start tag + [retString appendFormat:@"<%@", spanTagName]; + + // add span level attributes + [spanLevelHTMLAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + [retString appendFormat:@" %@=\"%@\"", key, value]; + }]; + + // end span start tag + [retString appendString:@">"]; + } + + // add string in span + [retString appendString:subString]; + + if (needsSpanTag) + { + // span end tag + [retString appendFormat:@"", spanTagName]; + } + + if (isLastPartOfHyperlink) + { + [retString appendFormat:@""]; + currentLinkRange = NSMakeRange(NSNotFound, 0); + } + }]; // end of SPAN loop + + + if ([blockElement isEqualToString:@"li"]) + { + BOOL shouldCloseLI = YES; + + NSUInteger nextParagraphStart = NSMaxRange([plainString paragraphRangeForRange:paragraphRange]); + + if (nextParagraphStart < [plainString length]) + { + NSArray *nextListStyles = [_attributedString attribute:DTTextListsAttribute atIndex:nextParagraphStart effectiveRange:NULL]; + + // LI are only closed if there is not a deeper list level following + if (nextListStyles && ([nextListStyles indexOfObjectIdenticalTo:effectiveListStyle]!=NSNotFound) && [nextListStyles count] > [currentListStyles count]) + { + // deeper list following + shouldCloseLI = NO; + } + } + + if (shouldCloseLI) + { + [retString appendString:@"
  • "]; + } + } + else + { + // other blocks are always closed + [retString appendFormat:@"\n", blockElement]; + } + + previousListStyles = [currentListStyles copy]; + } // end of P loop + + + // close list if still open + if ([previousListStyles count]) + { + NSMutableArray *closingStyles = [previousListStyles mutableCopy]; + + do + { + DTCSSListStyle *closingStyle = [closingStyles lastObject]; + + // end of a list block + [retString appendString:[self _tagRepresentationForListStyle:closingStyle closingTag:YES listPadding:0 inlineStyles:fragment]]; + [retString appendString:@"\n"]; + + if ([closingStyles count]>1) + { + [retString appendString:@""]; + } + + [closingStyles removeLastObject]; + } + while ([closingStyles count]); + } + + NSMutableString *output = [NSMutableString string]; + + BOOL hasTab = ([retString rangeOfString:@"\t"].location != NSNotFound); + + if (!fragment) + { + // append style block before text + NSMutableString *styleBlock = [NSMutableString string]; + + NSArray *keys = [[_styleLookup allKeys] sortedArrayUsingSelector:@selector(compare:)]; + + for (NSString *oneKey in keys) + { + NSArray *styleArray = [_styleLookup objectForKey:oneKey]; + + [styleArray enumerateObjectsUsingBlock:^(NSString *style, NSUInteger idx, BOOL *stop) { + NSString *className = [NSString stringWithFormat:@"%@%d", [oneKey substringToIndex:1], (int)idx+1]; + [styleBlock appendFormat:@"%@.%@ {%@}\n", oneKey, className, style]; + }]; + } + + if (hasTab) + { + [styleBlock appendString:@"span.Apple-tab-span {white-space:pre;}"]; + } + + [output appendFormat:@"\n\n\n\n\n\n\n\n\n", styleBlock]; + } + + if (hasTab) + { + NSRange range = NSMakeRange(0, [retString length]); + + if (fragment) + { + [retString replaceOccurrencesOfString:@"\t" withString:@"\t" options:0 range:range]; + } + else + { + [retString replaceOccurrencesOfString:@"\t" withString:@"\t" options:0 range:range]; + } + } + + if (_useAppleConvertedSpace) + { + NSString *convertedSpaces = [retString stringByAddingAppleConvertedSpace]; + + [output appendString:convertedSpaces]; + } + else + { + [output appendString:retString]; + } + + if (!fragment) + { + [output appendString:@"\n\n"]; + } + + _HTMLString = output; +} + +#pragma mark - Public + +- (NSString *)HTMLString +{ + if (!_HTMLString) + { + [self _buildOutput]; + } + + return _HTMLString; +} + +- (NSString *)HTMLFragment +{ + if (!_HTMLString) + { + [self _buildOutputAsHTMLFragment:true]; + } + + return _HTMLString; +} + +#pragma mark - Properties + +@synthesize attributedString = _attributedString; +@synthesize textScale = _textScale; +@synthesize useAppleConvertedSpace = _useAppleConvertedSpace; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHorizontalRuleHTMLElement.h b/Pods/DTCoreText/Core/Source/DTHorizontalRuleHTMLElement.h new file mode 100644 index 0000000..adb8545 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHorizontalRuleHTMLElement.h @@ -0,0 +1,17 @@ +// +// DTHTMLElementHR.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +/** + Specialized subclass of that deals with list items. + */ + +@interface DTHorizontalRuleHTMLElement : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHorizontalRuleHTMLElement.m b/Pods/DTCoreText/Core/Source/DTHorizontalRuleHTMLElement.m new file mode 100644 index 0000000..f63e73b --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHorizontalRuleHTMLElement.m @@ -0,0 +1,30 @@ +// +// DTHTMLElementHR.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHorizontalRuleHTMLElement.h" + +@implementation DTHorizontalRuleHTMLElement + +- (NSDictionary *)attributesForAttributedStringRepresentation +{ + NSMutableDictionary *dict = [[super attributesForAttributedStringRepresentation] mutableCopy]; + [dict setObject:[NSNumber numberWithBool:YES] forKey:DTHorizontalRuleStyleAttribute]; + + return dict; +} + +- (NSAttributedString *)attributedString +{ + @synchronized(self) + { + NSDictionary *attributes = [self attributesForAttributedStringRepresentation]; + return [[NSAttributedString alloc] initWithString:@"\n" attributes:attributes]; + } +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTIframeTextAttachment.h b/Pods/DTCoreText/Core/Source/DTIframeTextAttachment.h new file mode 100644 index 0000000..7972a74 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTIframeTextAttachment.h @@ -0,0 +1,17 @@ +// +// DTTextAttachmentIframe.h +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTTextAttachment.h" + +/** + A specialized subclass in the DTTextAttachment class cluster to represent an IFRAME + */ + +@interface DTIframeTextAttachment : DTTextAttachment + +@end diff --git a/Pods/DTCoreText/Core/Source/DTIframeTextAttachment.m b/Pods/DTCoreText/Core/Source/DTIframeTextAttachment.m new file mode 100644 index 0000000..dd8b9de --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTIframeTextAttachment.m @@ -0,0 +1,112 @@ +// +// DTTextAttachmentIframe.m +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" + +@implementation DTIframeTextAttachment + +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + self = [super initWithElement:element options:options]; + + if (self) + { + // get base URL + NSURL *baseURL = [options objectForKey:NSBaseURLDocumentOption]; + NSString *src = [element.attributes objectForKey:@"src"]; + + // prepend http: if URL string starts with // (seems to do with youtube iframes as standard) + if ([[src substringToIndex:2] isEqualToString:@"//"]) { + src = [@"http:" stringByAppendingString:src]; + } + + // content URL + _contentURL = [NSURL URLWithString:src relativeToURL:baseURL]; + } + + return self; +} + +#pragma mark - DTTextAttachmentHTMLEncoding + +- (NSString *)stringByEncodingAsHTML +{ + NSMutableString *retString = [NSMutableString string]; + + [retString appendString:@"0) + { + [styleString appendFormat:@"width:%.0fpx;", _originalSize.width]; + } + + if (_originalSize.height>0) + { + [styleString appendFormat:@"height:%.0fpx;", _originalSize.height]; + } + + // add local style for size, since sizes might vary quite a bit + if ([styleString length]) + { + [retString appendFormat:@" style=\"%@\"", styleString]; + } + + // attach the attributes dictionary + NSMutableDictionary *tmpAttributes = [_attributes mutableCopy]; + + // remove src,style, width and height we already have these + [tmpAttributes removeObjectForKey:@"src"]; + [tmpAttributes removeObjectForKey:@"style"]; + [tmpAttributes removeObjectForKey:@"width"]; + [tmpAttributes removeObjectForKey:@"height"]; + + for (__strong NSString *oneKey in [tmpAttributes allKeys]) + { + oneKey = [oneKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *value = [[tmpAttributes objectForKey:oneKey] stringByAddingHTMLEntities]; + [retString appendFormat:@" %@=\"%@\"", oneKey, value]; + } + + [retString appendString:@" />"]; + + return retString; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTImage+HTML.h b/Pods/DTCoreText/Core/Source/DTImage+HTML.h new file mode 100644 index 0000000..e1ee5fc --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTImage+HTML.h @@ -0,0 +1,42 @@ +// +// DTImage+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#if TARGET_OS_IPHONE + +/** + Category used to have the same method available for unit testing on Mac on iOS. + */ +@interface UIImage (HTML) + +/** + Retrieve the NSData representation of a UIImage. Used to encode UIImages in DTTextAttachments. + + @returns The NSData representation of the UIImage instance receiving this message. Convenience method for UIImagePNGRepresentation(). + */ +- (NSData *)dataForPNGRepresentation; + +@end + +#else + +/** + Category used to have the same method available for unit testing on Mac on iOS. + */ +@interface NSImage (HTML) + + +/** + Retrieve the NSData representation of a NSImage. + + @returns The NSData representation of the NSImage instance receiving this message. + */ +- (NSData *)dataForPNGRepresentation; + +@end + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTImage+HTML.m b/Pods/DTCoreText/Core/Source/DTImage+HTML.m new file mode 100644 index 0000000..849284f --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTImage+HTML.m @@ -0,0 +1,37 @@ +// +// DTImage+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 31.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTImage+HTML.h" + +#if TARGET_OS_IPHONE + +@implementation UIImage (HTML) + +- (NSData *)dataForPNGRepresentation +{ + return UIImagePNGRepresentation(self); +} + +@end + +#else + +@implementation NSImage (HTML) + +- (NSData *)dataForPNGRepresentation +{ + [self lockFocus]; + NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, self.size.width, self.size.height)]; + [self unlockFocus]; + + return [bitmapRep representationUsingType:NSPNGFileType properties:Nil]; +} + +@end + +#endif \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/DTImageTextAttachment.h b/Pods/DTCoreText/Core/Source/DTImageTextAttachment.h new file mode 100644 index 0000000..6053ba0 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTImageTextAttachment.h @@ -0,0 +1,41 @@ +// +// DTTextAttachmentImage.h +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTTextAttachment.h" + +@class DTImage; + +/** + A specialized subclass in the DTTextAttachment class cluster to represent an embedded image + */ + +@interface DTImageTextAttachment : DTTextAttachment + +/** + The designated initializer which will be called by [DTTextAttachment textAttachmentWithElement:options:] for image attachments. + @param element A DTHTMLElement that must have a valid tag name and should have a size. Any element attributes are copied to the text attachment's elements. + @param options An NSDictionary of options. Used to specify the max image size with the key DTMaxImageSize. + */ +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options; + +/** + @name Alternate Representations + */ + +/** + Retrieves a string which is in the format "data:image/png;base64,%@" with this DTTextAttachment's content's data representation encoded in Base64 string encoding. For image contents only. + @returns A Base64 encoded string of the png data representation of this text attachment's image contents. + */ +- (NSString *)dataURLRepresentation; + +/** + The image represented by the receiver + */ +@property (nonatomic, strong) DTImage *image; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTImageTextAttachment.m b/Pods/DTCoreText/Core/Source/DTImageTextAttachment.m new file mode 100644 index 0000000..c899460 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTImageTextAttachment.m @@ -0,0 +1,490 @@ +// +// DTTextAttachmentImage.m +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTBase64Coding.h" + +#if TARGET_OS_IPHONE + #import "DTAnimatedGIF.h" +#endif + +static NSCache *imageCache = nil; + +@interface DTImageTextAttachment () // private stuff + ++ (NSCache *)sharedImageCache; + +@end + + +@implementation DTImageTextAttachment +{ + DTImage *_image; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + _image = [aDecoder decodeObjectForKey:@"image"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_image forKey:@"image"]; +} + +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + self = [super initWithElement:element options:options]; + + if (self) + { + [self _decodeImageFromElement:element options:options]; + } + + return self; +} + +- (id)initWithImage:(DTImage *)image +{ + self = [super init]; + + if (self) + { + self.image = image; + } + + return self; +} + + +- (void)_decodeImageFromElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + // get base URL + NSURL *baseURL = [options objectForKey:NSBaseURLDocumentOption]; + NSString *src = [element.attributes objectForKey:@"src"]; + + NSURL *contentURL = nil; + + // decode content URL + if ([src length]) // guard against img with no src + { + if ([src hasPrefix:@"data:"]) + { + NSString *cleanStr = [[src componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""]; + + NSURL *dataURL = [NSURL URLWithString:cleanStr]; + + // try native decoding first + NSData *decodedData = [NSData dataWithContentsOfURL:dataURL]; + + // try own base64 decoding + if (!decodedData) + { + NSRange range = [cleanStr rangeOfString:@"base64,"]; + + if (range.length) + { + NSString *encodedData = [cleanStr substringFromIndex:range.location + range.length]; + + decodedData = [DTBase64Coding dataByDecodingString:encodedData]; + } + } + + // if we have image data, get the default display size + if (decodedData) + { + DTImage *decodedImage = [[DTImage alloc] initWithData:decodedData]; + + // we don't know the content scale from such images, need to infer it from size in style + NSString *styles = [element.attributes objectForKey:@"style"]; + + // that only works if there is a style dictionary + if (styles) + { + NSDictionary *attributes = [styles dictionaryOfCSSStyles]; + + NSString *widthStr = attributes[@"width"]; + NSString *heightStr = attributes[@"height"]; + + if ([widthStr hasSuffix:@"px"] && [heightStr hasSuffix:@"px"]) + { + CGSize sizeAccordingToStyle; + + // those style size values are the original image size + sizeAccordingToStyle.width = [widthStr pixelSizeOfCSSMeasureRelativeToCurrentTextSize:0 textScale:1]; + sizeAccordingToStyle.height = [heightStr pixelSizeOfCSSMeasureRelativeToCurrentTextSize:0 textScale:1]; + + // if _orgiginal width and height are a fraction of decode image size, it must be a scaled image + if (sizeAccordingToStyle.width && sizeAccordingToStyle.width < decodedImage.size.width && + sizeAccordingToStyle.height && sizeAccordingToStyle.height < decodedImage.size.height) + { + // determine image scale + CGFloat scale = round(decodedImage.size.width/sizeAccordingToStyle.width); + + // sanity check, accept from @2x - @5x + if (scale>=2.0 && scale<=5.0) + { +#if TARGET_OS_IPHONE + // on iOS change the scale by making a new image with same pixels + decodedImage = [DTImage imageWithCGImage:decodedImage.CGImage scale:scale orientation:decodedImage.imageOrientation]; +#else + // on OS X we can set the size + [decodedImage setSize:sizeAccordingToStyle]; +#endif + } + } + } + } + + self.image = decodedImage; + + // prevent remote loading of image + _contentURL = nil; + } + } + else // normal URL + { + contentURL = [NSURL URLWithString:src]; + + if(!contentURL) + { + src = [src stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + contentURL = [NSURL URLWithString:src relativeToURL:baseURL]; + } + + if (![contentURL scheme]) + { + // possibly a relative url + if (baseURL) + { + contentURL = [NSURL URLWithString:src relativeToURL:baseURL]; + } + else + { + // file in app bundle + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *path = [bundle pathForResource:src ofType:nil]; + + if (path) + { + // Prevent a crash if path turns up nil. + contentURL = [NSURL fileURLWithPath:path]; + } + else + { + // might also be in a different bundle, e.g. when unit testing + bundle = [NSBundle bundleForClass:[DTTextAttachment class]]; + + path = [bundle pathForResource:src ofType:nil]; + if (path) + { + // Prevent a crash if path turns up nil. + contentURL = [NSURL fileURLWithPath:path]; + } + } + } + } + } + } + + // if it's a local file we need to inspect it to get it's dimensions + if (!_displaySize.width || !_displaySize.height) + { + DTImage *image = _image; + + // let's check if we have a cached image already then we can inspect that + if (!_image) + { + image = [[DTImageTextAttachment sharedImageCache] objectForKey:[contentURL absoluteString]]; + } + + if (!image) + { + // only local files we can directly load without punishment + if ([contentURL isFileURL]) + { +#if TARGET_OS_IPHONE + NSString *ext = [[[contentURL lastPathComponent] pathExtension] lowercaseString]; + + if ([ext isEqualToString:@"gif"]) + { + image = DTAnimatedGIFFromFile([contentURL path]); + } + else +#endif + { + image = [[DTImage alloc] initWithContentsOfFile:[contentURL path]]; + } + } + + // cache that for later + if (image) + { + [[DTImageTextAttachment sharedImageCache] setObject:image forKey:[contentURL absoluteString]]; + } + } + + // we have an image, so we can set the original size and default display size + if (image) + { + _contentURL = nil; + [self _updateSizesFromImage:image]; + } + } + + // only remote images should have a URL + _contentURL = contentURL; +} + +- (void)_updateSizesFromImage:(DTImage *)image +{ + // set original size if there is none set yet + if (CGSizeEqualToSize(_originalSize, CGSizeZero)) + { + _originalSize = image.size; + } + else + { + // get the other dimension if one is missing + + if (!_originalSize.width && _originalSize.height) + { + CGFloat factor = _originalSize.height/image.size.height; + _originalSize.width = image.size.height * factor; + } + else if (_originalSize.width && !_originalSize.height) + { + CGFloat factor = _originalSize.width/image.size.width; + _originalSize.height = image.size.width * factor; + } + } + + // initial display size matches original + if (CGSizeEqualToSize(CGSizeZero, _displaySize)) + { + [self setDisplaySize:_originalSize withMaxDisplaySize:_maxImageSize]; + } + else + { + // get the other dimension if one is missing + + if (!_displaySize.width && _displaySize.height) + { + CGSize newDisplaySize = _displaySize; + + CGFloat factor = _displaySize.height/_originalSize.height; + newDisplaySize.width = _originalSize.height * factor; + + [self setDisplaySize:newDisplaySize withMaxDisplaySize:_maxImageSize]; + } + else if (_displaySize.width && !_displaySize.height) + { + CGSize newDisplaySize = _displaySize; + + CGFloat factor = _displaySize.width/_originalSize.width; + newDisplaySize.height = _originalSize.width * factor; + + [self setDisplaySize:newDisplaySize withMaxDisplaySize:_maxImageSize]; + } + } +} + ++ (NSCache *)sharedImageCache { + if (imageCache) return imageCache; + + static dispatch_once_t onceToken; // lock + dispatch_once(&onceToken, ^{ // this block run only once + imageCache = [[NSCache alloc] init]; + }); + return imageCache; +} + +#pragma mark - Alternative Representations + +// makes a data URL of the image +- (NSString *)dataURLRepresentation +{ + DTImage *image = self.image; + + if (!image) + { + return nil; + } + + NSData *data = [image dataForPNGRepresentation]; + NSString *encoded = [DTBase64Coding stringByEncodingData:data]; + + return [@"data:image/png;base64," stringByAppendingString:encoded]; +} + +#pragma mark - DTTextAttachmentDrawing + +- (void)drawInRect:(CGRect)rect context:(CGContextRef)context +{ +#if TARGET_OS_IPHONE + [self.image drawInRect:rect]; +#endif +} + +#pragma mark - DTTextAttachmentHTMLEncoding + +- (NSString *)stringByEncodingAsHTML +{ + NSMutableString *retString = [NSMutableString string]; + NSString *urlString; + + if (_contentURL) + { + + if ([_contentURL isFileURL]) + { + NSString *path = [_contentURL path]; + + NSRange range = [path rangeOfString:@".app/"]; + + if (range.length) + { + urlString = [path substringFromIndex:NSMaxRange(range)]; + } + else + { + urlString = [_contentURL absoluteString]; + } + } + else + { + urlString = [_contentURL relativeString]; + } + } + else + { + urlString = [self dataURLRepresentation]; + } + + // output tag start + [retString appendString:@"0) + { + [styleString appendFormat:@"width:%.0fpx;", _originalSize.width]; + } + + if (_originalSize.height>0) + { + [styleString appendFormat:@"height:%.0fpx;", _originalSize.height]; + } + + // add local style for size, since sizes might vary quite a bit + if ([styleString length]) + { + [retString appendFormat:@" style=\"%@\"", styleString]; + } + + [retString appendFormat:@" src=\"%@\"", urlString]; + + // attach the attributes dictionary + NSMutableDictionary *tmpAttributes = [_attributes mutableCopy]; + + // remove src,style, width and height we already have these + [tmpAttributes removeObjectForKey:@"src"]; + [tmpAttributes removeObjectForKey:@"style"]; + [tmpAttributes removeObjectForKey:@"width"]; + [tmpAttributes removeObjectForKey:@"height"]; + + for (__strong NSString *oneKey in [tmpAttributes allKeys]) + { + oneKey = [oneKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *value = [[tmpAttributes objectForKey:oneKey] stringByAddingHTMLEntities]; + [retString appendFormat:@" %@=\"%@\"", oneKey, value]; + } + + // end + [retString appendString:@" />"]; + + return retString; +} + +#pragma mark - Properties + +/** + Accessor for the contents instance variable. If the content type is DTTextAttachmentTypeImage this returns a DTImage instance of the contents. + @returns Contents. If it is an image, a DTImage instance is returned. Otherwise it is returned as is. + */ +- (DTImage *)image +{ + if (!_image) + { + if (_contentURL) + { + DTImage *image = [[DTImageTextAttachment sharedImageCache] objectForKey:[_contentURL absoluteString]]; + + // only local files can be loaded into cache + if (!image && [_contentURL isFileURL]) + { + image = [[DTImage alloc] initWithContentsOfFile:[_contentURL path]]; + + // cache it + if (image) + { + [[DTImageTextAttachment sharedImageCache] setObject:image forKey:[_contentURL absoluteString]]; + } + } + + return image; + } + } + + return _image; +} + +- (void)setImage:(DTImage *)image +{ + if (_image != image) + { + _image = image; + + [self _updateSizesFromImage:_image]; + } +} + +- (void)setDisplaySize:(CGSize)displaySize +{ + _displaySize = displaySize; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTLazyImageView.h b/Pods/DTCoreText/Core/Source/DTLazyImageView.h new file mode 100644 index 0000000..02a8855 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTLazyImageView.h @@ -0,0 +1,75 @@ +// +// DTLazyImageView.h +// DTCoreText +// +// Created by Oliver Drobnik on 5/20/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTWeakSupport.h" + +@class DTLazyImageView; + +// Notifications +extern NSString * const DTLazyImageViewWillStartDownloadNotification; +extern NSString * const DTLazyImageViewDidFinishDownloadNotification; + +/** + Protocol for delegates of to inform them about the downloaded image dimensions. + */ +@protocol DTLazyImageViewDelegate +@optional + +/** + Method that informs the delegate about the image size so that it can re-layout text. + @param lazyImageView The image view + @param size The image size that is now known + */ +- (void)lazyImageView:(DTLazyImageView *)lazyImageView didChangeImageSize:(CGSize)size; +@end + +/** + This `UIImageView` subclass lazily loads an image from a URL and informs a delegate once the size of the image is known. + */ + +@interface DTLazyImageView : UIImageView + +/** + @name Providing Content + */ + +/** + The URL of the remote image + */ +@property (nonatomic, strong) NSURL *url; + +/** + The URL Request that is to be used for downloading the image. If this is left `nil` the a new URL Request will be created + */ +@property (nonatomic, strong) NSMutableURLRequest *urlRequest; + +/** + @name Getting Information + */ + +/** + Set to `YES` to support progressive display of progressive downloads + */ +@property (nonatomic, assign) BOOL shouldShowProgressiveDownload; + +/** + The delegate, conforming to , to inform when the image dimensions were determined + */ +@property (nonatomic, DT_WEAK_PROPERTY) id delegate; + + +/** + @name Cancelling Download +*/ + +/** + Cancels the image downloading + */ +- (void)cancelLoading; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTLazyImageView.m b/Pods/DTCoreText/Core/Source/DTLazyImageView.m new file mode 100644 index 0000000..22ca8d5 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTLazyImageView.m @@ -0,0 +1,320 @@ +// +// DTLazyImageView.m +// DTCoreText +// +// Created by Oliver Drobnik on 5/20/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import +#import "DTLazyImageView.h" +#import "DTLog.h" + +static NSCache *_imageCache = nil; + +NSString * const DTLazyImageViewWillStartDownloadNotification = @"DTLazyImageViewWillStartDownloadNotification"; +NSString * const DTLazyImageViewDidFinishDownloadNotification = @"DTLazyImageViewDidFinishDownloadNotification"; + +@interface DTLazyImageView () + +- (void)_notifyDelegate; + +@end + +@implementation DTLazyImageView +{ + NSURL *_url; + NSMutableURLRequest *_urlRequest; + + NSURLConnection *_connection; + NSMutableData *_receivedData; + + /* For progressive download */ + CGImageSourceRef _imageSource; + CGFloat _fullHeight; + CGFloat _fullWidth; + NSUInteger _expectedSize; + + BOOL shouldShowProgressiveDownload; + + DT_WEAK_VARIABLE id _delegate; +} + +- (void)dealloc +{ + _delegate = nil; // to avoid late notification + [_connection cancel]; + + if (_imageSource) CFRelease(_imageSource); +} + +- (void)loadImageAtURL:(NSURL *)url +{ + // local files we don't need to get asynchronously + if ([url isFileURL] || [url.scheme isEqualToString:@"data"]) + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSData *data = [NSData dataWithContentsOfURL:url]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self completeDownloadWithData:data]; + }); + }); + + return; + } + + @autoreleasepool + { + if (!_urlRequest) + { + _urlRequest = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10.0]; + } + else + { + [_urlRequest setCachePolicy:NSURLRequestReturnCacheDataElseLoad]; + [_urlRequest setTimeoutInterval:10.0]; + } + + _connection = [[NSURLConnection alloc] initWithRequest:_urlRequest delegate:self startImmediately:NO]; + [_connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + + [[NSNotificationCenter defaultCenter] postNotificationName:DTLazyImageViewWillStartDownloadNotification object:self]; + + [_connection start]; + } +} + +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; + + if (!self.image && (_url || _urlRequest) && !_connection && self.superview) + { + UIImage *image = [_imageCache objectForKey:_url]; + + if (image) + { + self.image = image; + _fullWidth = image.size.width; + _fullHeight = image.size.height; + + // this has to be synchronous + [self _notifyDelegate]; + + return; + } + + [self loadImageAtURL:_url]; + } +} + +- (void)cancelLoading +{ + [_connection cancel]; + _connection = nil; + + _receivedData = nil; +} + +#pragma mark Progressive Image +-(CGImageRef)newTransitoryImage:(CGImageRef)partialImg +{ + const size_t height = CGImageGetHeight(partialImg); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + size_t lFullWidth = (size_t)ceil(_fullWidth); + size_t lFullHeight = (size_t)ceil(_fullHeight); + CGContextRef bmContext = CGBitmapContextCreate(NULL, lFullWidth, lFullHeight, 8, lFullWidth * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(colorSpace); + if (!bmContext) + { + // fail creating context + return NULL; + } + CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = _fullWidth, .size.height = height}, partialImg); + CGImageRef goodImageRef = CGBitmapContextCreateImage(bmContext); + CGContextRelease(bmContext); + return goodImageRef; +} + +- (void)createAndShowProgressiveImage +{ + if (!_imageSource) + { + return; + } + + /* For progressive download */ + const NSUInteger totalSize = [_receivedData length]; + CGImageSourceUpdateData(_imageSource, (__bridge CFDataRef)_receivedData, (totalSize == _expectedSize) ? true : false); + + if (_fullHeight > 0 && _fullWidth > 0) + { + CGImageRef image = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL); + if (image) + { + CGImageRef imgTmp = [self newTransitoryImage:image]; // iOS fix to correctly handle JPG see : http://www.cocoabyss.com/mac-os-x/progressive-image-download-imageio/ + if (imgTmp) + { + UIImage *uimage = [[UIImage alloc] initWithCGImage:imgTmp]; + CGImageRelease(imgTmp); + + dispatch_async(dispatch_get_main_queue(), ^{ self.image = uimage; } ); + } + CGImageRelease(image); + } + } + else + { + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL); + if (properties) + { + CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); + if (val) + CFNumberGetValue(val, kCFNumberFloatType, &_fullHeight); + val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); + if (val) + CFNumberGetValue(val, kCFNumberFloatType, &_fullWidth); + CFRelease(properties); + } + } +} + +#pragma mark NSURL Loading + +- (void)_notifyDelegate +{ + if ([self.delegate respondsToSelector:@selector(lazyImageView:didChangeImageSize:)]) { + [self.delegate lazyImageView:self didChangeImageSize:CGSizeMake(_fullWidth, _fullHeight)]; + } +} + +- (void)completeDownloadWithData:(NSData *)data +{ + UIImage *image = [[UIImage alloc] initWithData:data]; + + self.image = image; + _fullWidth = image.size.width; + _fullHeight = image.size.height; + + [self _notifyDelegate]; + + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _imageCache = [[NSCache alloc] init]; + }); + + if (_url) + { + if (image) + { + // cache image + [_imageCache setObject:image forKey:_url]; + } + else + { + DTLogWarning(@"Warning, %@ did not get an image for %@", NSStringFromClass([self class]), [_url absoluteString]); + } + } +} + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + // every time we get an response it might be a forward, so we discard what data we have + _receivedData = nil; + + // does not fire for local file URLs + if ([response isKindOfClass:[NSHTTPURLResponse class]]) + { + NSHTTPURLResponse *httpResponse = (id)response; + + if (![[httpResponse MIMEType] hasPrefix:@"image"]) + { + [self cancelLoading]; + return; + } + } + + /* For progressive download */ + _fullWidth = _fullHeight = -1.0f; + _expectedSize = (NSUInteger)[response expectedContentLength]; + + _receivedData = [[NSMutableData alloc] init]; +} + +-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [_receivedData appendData:data]; + + if (!CGImageSourceCreateIncremental || !shouldShowProgressiveDownload) + { + // don't show progressive + return; + } + + if (!_imageSource) + { + _imageSource = CGImageSourceCreateIncremental(NULL); + } + + [self createAndShowProgressiveImage]; +} + + +- (void)removeFromSuperview +{ + [super removeFromSuperview]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + if (_receivedData) + { + [self performSelectorOnMainThread:@selector(completeDownloadWithData:) withObject:_receivedData waitUntilDone:YES]; + + _receivedData = nil; + } + + _connection = nil; + + /* For progressive download */ + if (_imageSource) + CFRelease(_imageSource), _imageSource = NULL; + + CFRunLoopStop(CFRunLoopGetCurrent()); + + // success = no userInfo + [[NSNotificationCenter defaultCenter] postNotificationName:DTLazyImageViewDidFinishDownloadNotification object:self]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + _connection = nil; + _receivedData = nil; + + /* For progressive download */ + if (_imageSource) + CFRelease(_imageSource), _imageSource = NULL; + + CFRunLoopStop(CFRunLoopGetCurrent()); + + // send completion notification, pack in error as well + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:error forKey:@"Error"]; + [[NSNotificationCenter defaultCenter] postNotificationName:DTLazyImageViewDidFinishDownloadNotification object:self userInfo:userInfo]; +} + +#pragma mark Properties + +- (void) setUrlRequest:(NSMutableURLRequest *)request +{ + _urlRequest = request; + self.url = [_urlRequest URL]; +} + +@synthesize delegate=_delegate; +@synthesize shouldShowProgressiveDownload; +@synthesize url = _url; +@synthesize urlRequest = _urlRequest; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTLinkButton.h b/Pods/DTCoreText/Core/Source/DTLinkButton.h new file mode 100644 index 0000000..ff0c1af --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTLinkButton.h @@ -0,0 +1,47 @@ +// +// DTLinkButton.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/16/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +/** + Constant for highlighting notification + */ + +extern NSString *DTLinkButtonDidHighlightNotification; + +/** + A button that corresponds to a hyperlink. + + Multiple parts of the same hyperlink synchronize their looks through the guid. You can show link text in a different color for normal and highlighted mode by setting the button images for these states. + */ +@interface DTLinkButton : UIButton + + +/** + The URL that this button corresponds to. + */ +@property (nonatomic, copy) NSURL *URL; + + +/** + The unique identifier (GUID) that all parts of the same hyperlink have in common. + */ +@property (nonatomic, copy) NSString *GUID; + + +/** + The minimum size that the receiver should respond on hits with. Adjusts the bounds if they are smaller than the passed size. + */ +@property (nonatomic, assign) CGSize minimumHitSize; + + +/** + A Boolean value that determines whether tapping the button causes it to show a gray rounded rectangle. Default is YES. + */ +@property(nonatomic) BOOL showsTouchWhenHighlighted; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTLinkButton.m b/Pods/DTCoreText/Core/Source/DTLinkButton.m new file mode 100644 index 0000000..c7273ac --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTLinkButton.m @@ -0,0 +1,180 @@ +// +// DTLinkButton.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/16/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTLinkButton.h" +#import "DTCoreText.h" + +// constant for notification +NSString *DTLinkButtonDidHighlightNotification = @"DTLinkButtonDidHighlightNotification"; + + +@interface DTLinkButton () + +- (void)highlightNotification:(NSNotification *)notification; + +@end + + +@implementation DTLinkButton +{ + NSURL *_URL; + NSString *_GUID; + + CGSize _minimumHitSize; + BOOL _showsTouchWhenHighlighted; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + self.userInteractionEnabled = YES; + self.enabled = YES; + self.opaque = NO; + + _showsTouchWhenHighlighted = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(highlightNotification:) name:DTLinkButtonDidHighlightNotification object:nil]; + } + + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark Drawing the Run + +- (void)drawRect:(CGRect)rect +{ + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + if (self.highlighted) + { + if (_showsTouchWhenHighlighted) + { + CGRect imageRect = [self contentRectForBounds:self.bounds]; + + UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:3.0f]; + CGContextSetGrayFillColor(ctx, 0.73f, 0.4f); + [roundedPath fill]; + } + } +} + +#pragma mark Utilitiy + +- (void)_adjustBoundsIfNecessary +{ + CGRect bounds = self.bounds; + CGFloat widthExtend = 0; + CGFloat heightExtend = 0; + + if (bounds.size.width < _minimumHitSize.width) + { + widthExtend = _minimumHitSize.width - bounds.size.width; + } + + if (bounds.size.height < _minimumHitSize.height) + { + heightExtend = _minimumHitSize.height - bounds.size.height; + } + + if (widthExtend>0 || heightExtend>0) + { + UIEdgeInsets edgeInsets = UIEdgeInsetsMake(ceil(heightExtend/2.0f), ceil(widthExtend/2.0f), ceil(heightExtend/2.0f), ceil(widthExtend/2.0f)); + + // extend bounds by the calculated necessary edge insets + bounds.size.width += edgeInsets.left + edgeInsets.right; + bounds.size.height += edgeInsets.top + edgeInsets.bottom; + + // apply bounds and insets + self.bounds = bounds; + self.contentEdgeInsets = edgeInsets; + } + else + { + self.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); + } +} + +#pragma mark Notifications +- (void)highlightNotification:(NSNotification *)notification +{ + if ([notification object] == self) + { + // that was me + return; + } + + NSDictionary *userInfo = [notification userInfo]; + + NSString *guid = [userInfo objectForKey:@"GUID"]; + + if ([guid isEqualToString:_GUID]) + { + BOOL highlighted = [[userInfo objectForKey:@"Highlighted"] boolValue]; + [super setHighlighted:highlighted]; + [self setNeedsDisplay]; + } +} + + + +#pragma mark Properties + +- (void)setHighlighted:(BOOL)highlighted +{ + [super setHighlighted:highlighted]; + [self setNeedsDisplay]; + + // notify other parts of the same link + if (_GUID) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:highlighted], @"Highlighted", _GUID, @"GUID", nil]; + + [[NSNotificationCenter defaultCenter] postNotificationName:DTLinkButtonDidHighlightNotification object:self userInfo:userInfo]; + } +} + +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; + + if (CGRectIsEmpty(frame)) + { + return; + } + + [self _adjustBoundsIfNecessary]; +} + + +- (void)setMinimumHitSize:(CGSize)minimumHitSize +{ + if (CGSizeEqualToSize(_minimumHitSize, minimumHitSize)) + { + return; + } + + _minimumHitSize = minimumHitSize; + + [self _adjustBoundsIfNecessary]; +} + +@synthesize URL = _URL; +@synthesize GUID = _GUID; + +@synthesize minimumHitSize = _minimumHitSize; +@synthesize showsTouchWhenHighlighted = _showsTouchWhenHighlighted; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTListItemHTMLElement.h b/Pods/DTCoreText/Core/Source/DTListItemHTMLElement.h new file mode 100644 index 0000000..ec5b451 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTListItemHTMLElement.h @@ -0,0 +1,16 @@ +// +// DTHTMLElementLI.h +// DTCoreText +// +// Created by Oliver Drobnik on 27.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" + +/** + Specialized subclass of that deals with list items. + */ +@interface DTListItemHTMLElement : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTListItemHTMLElement.m b/Pods/DTCoreText/Core/Source/DTListItemHTMLElement.m new file mode 100644 index 0000000..e6c3577 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTListItemHTMLElement.m @@ -0,0 +1,311 @@ +// +// DTHTMLElementLI.m +// DTCoreText +// +// Created by Oliver Drobnik on 27.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTListItemHTMLElement.h" + +@implementation DTListItemHTMLElement + +- (NSUInteger)_indexOfListItemInListRoot:(DTHTMLElement *)listRoot +{ + @synchronized(self) + { + NSInteger index = -1; + + NSArray *childNodes = [listRoot.childNodes copy]; + for (DTHTMLElement *oneElement in childNodes) + { + if ([oneElement isKindOfClass:[DTListItemHTMLElement class]]) + { + index++; + } + + if (oneElement == self) + { + break; + } + } + + return index; + } +} + +// calculates the accumulated list indent +- (CGFloat)_sumOfListIndents +{ + CGFloat indent = 0; + + DTHTMLElement *element = self.parentElement; + + while (element) + { + if ([element.name isEqualToString:@"ul"] || [element.name isEqualToString:@"ol"]) + { + indent += element->_listIndent; + } + else if (element.displayStyle == DTHTMLElementDisplayStyleListItem) + { + // we accept these + indent += element.padding.left; + } + else + { + break; + } + + element = element.parentElement; + } + + return indent; +} + +- (void)applyStyleDictionary:(NSDictionary *)styles +{ + [super applyStyleDictionary:styles]; + + CGFloat parentPadding = self.parentElement->_listIndent; + CGFloat listIndents = [self _sumOfListIndents]; + + self.paragraphStyle.headIndent = listIndents + _padding.left + _margins.left; + self.paragraphStyle.firstLineHeadIndent = self.paragraphStyle.headIndent; + + _margins.left += parentPadding; +} + +// creates an attributed list prefix +- (NSAttributedString *)_listPrefix +{ + DTCoreTextParagraphStyle *paragraphStyle = [[self attributesForAttributedStringRepresentation] paragraphStyle]; + NSParameterAssert(paragraphStyle); + + DTCoreTextFontDescriptor *fontDescriptor = [[self attributesForAttributedStringRepresentation] fontDescriptor]; + NSParameterAssert(fontDescriptor); + + DTCSSListStyle *effectiveList = [self.paragraphStyle.textLists lastObject]; + DTHTMLElement *listRoot = self.parentElement; + NSUInteger listCounter = [self _indexOfListItemInListRoot:listRoot]+effectiveList.startingItemNumber; + + // make a temporary version of self that has same font attributes as list root + DTListItemHTMLElement *tmpCopy = [[DTListItemHTMLElement alloc] init]; + [tmpCopy inheritAttributesFromElement:self]; + + // take the parents text color + tmpCopy.textColor = listRoot.textColor; + + // check for list-style:none modifier + NSDictionary *styles = [[self attributeForKey:@"style"] dictionaryOfCSSStyles]; + + if (styles) + { + // make a temp copy + effectiveList = [effectiveList copy]; + + // update from styles + [effectiveList updateFromStyleDictionary:styles]; + } + + NSDictionary *attributes = [tmpCopy attributesForAttributedStringRepresentation]; + + // modify paragraph style + paragraphStyle.firstLineHeadIndent = self.paragraphStyle.headIndent - _margins.left - _padding.left;; // first line has prefix and starts at list indent; + paragraphStyle.defaultTabInterval = 100; + + // resets tabs + paragraphStyle.tabStops = nil; + + // set tab stops + if (effectiveList.type != DTCSSListStyleTypeNone) + { + if (_margins.left<=0) + { + return nil; + } + + // first tab is to right-align bullet, numbering against + CGFloat tabOffset = paragraphStyle.headIndent - (CGFloat)5.0; // TODO: change with font size + [paragraphStyle addTabStopAtPosition:tabOffset alignment:kCTRightTextAlignment]; + } + + // second tab is for the beginning of first line after bullet + [paragraphStyle addTabStopAtPosition:paragraphStyle.headIndent alignment:kCTLeftTextAlignment]; + + NSMutableDictionary *newAttributes = [NSMutableDictionary dictionary]; + + // make a font without italic or bold + fontDescriptor.boldTrait = NO; + fontDescriptor.italicTrait = NO; + + CTFontRef font = [fontDescriptor newMatchingFont]; + + if (font) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + UIFont *uiFont = [UIFont fontWithCTFont:font]; + [newAttributes setObject:uiFont forKey:NSFontAttributeName]; + + CFRelease(font); + } + else +#endif + { + [newAttributes setObject:CFBridgingRelease(font) forKey:(id)kCTFontAttributeName]; + } + } + + CGColorRef textColor = (__bridge CGColorRef)[attributes objectForKey:(id)kCTForegroundColorAttributeName]; + + if (textColor) + { + [newAttributes setObject:(__bridge id)textColor forKey:(id)kCTForegroundColorAttributeName]; + } +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + else if (___useiOS6Attributes) + { + DTColor *uiColor = [attributes foregroundColor]; + + if (uiColor) + { + [newAttributes setObject:uiColor forKey:NSForegroundColorAttributeName]; + } + } +#endif + + // add paragraph style (this has the tabs) + if (paragraphStyle) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + NSParagraphStyle *style = [paragraphStyle NSParagraphStyle]; + [newAttributes setObject:style forKey:NSParagraphStyleAttributeName]; + } + else +#endif + { + CTParagraphStyleRef newParagraphStyle = [paragraphStyle createCTParagraphStyle]; + [newAttributes setObject:CFBridgingRelease(newParagraphStyle) forKey:(id)kCTParagraphStyleAttributeName]; + } + } + + // add textBlock if there's one (this has padding and background color) + NSArray *textBlocks = [attributes objectForKey:DTTextBlocksAttribute]; + if (textBlocks) + { + [newAttributes setObject:textBlocks forKey:DTTextBlocksAttribute]; + } + + // transfer all lists so that + NSArray *lists = [attributes objectForKey:DTTextListsAttribute]; + if (lists) + { + [newAttributes setObject:lists forKey:DTTextListsAttribute]; + } + + // add a marker so that we know that this is a field/prefix + [newAttributes setObject:DTListPrefixField forKey:DTFieldAttribute]; + + NSString *prefix = [effectiveList prefixWithCounter:listCounter]; + + if (!prefix) + { + return nil; + } + + DTImage *image = nil; + + if (effectiveList.imageName) + { + image = [DTImage imageNamed:effectiveList.imageName]; + + if (!image) + { + // image invalid + effectiveList.imageName = nil; + + prefix = [effectiveList prefixWithCounter:listCounter]; + } + } + + NSMutableAttributedString *tmpStr = [[NSMutableAttributedString alloc] initWithString:prefix attributes:newAttributes]; + + if (image) + { + // make an attachment for the image + DTImageTextAttachment *attachment = [[DTImageTextAttachment alloc] init]; + attachment.image = image; + attachment.displaySize = image.size; + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES && TARGET_OS_IPHONE + // need run delegate for sizing + CTRunDelegateRef embeddedObjectRunDelegate = createEmbeddedObjectRunDelegate(attachment); + [newAttributes setObject:CFBridgingRelease(embeddedObjectRunDelegate) forKey:(id)kCTRunDelegateAttributeName]; +#endif + + // add attachment + [newAttributes setObject:attachment forKey:NSAttachmentAttributeName]; + + if (effectiveList.position == DTCSSListStylePositionInside) + { + [tmpStr setAttributes:newAttributes range:NSMakeRange(2, 1)]; + } + else + { + [tmpStr setAttributes:newAttributes range:NSMakeRange(1, 1)]; + } + } + + // estimate width of the prefix + NSString *trimmedPrefix = [prefix stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSAttributedString *tmpAttributedString = [[NSAttributedString alloc] initWithString:trimmedPrefix attributes:newAttributes]; + + CTLineRef tmpLine = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)(tmpAttributedString)); + double width = CTLineGetTypographicBounds(tmpLine, NULL, NULL, NULL); + CFRelease(tmpLine); + + // if the non-whitespace characters are too wide then we omit the prefix + if ((width+5.0)>_margins.left) + { + return nil; + } + + return tmpStr; +} + +- (NSAttributedString *)attributedString +{ + NSMutableAttributedString *tmpString = [[NSMutableAttributedString alloc] init]; + + // append child elements + NSAttributedString *childrenString = [super attributedString]; + + // apend list prefix + NSAttributedString *listPrefix = [self _listPrefix]; + + if (listPrefix) + { + [tmpString appendAttributedString:listPrefix]; + + // add NL if there is immediately another list prefix following + NSString *field = [childrenString attribute:DTFieldAttribute atIndex:0 effectiveRange:NULL]; + + if ([field isEqualToString:DTListPrefixField]) + { + [tmpString appendEndOfParagraph]; + } + } + + if (childrenString) + { + [tmpString appendAttributedString:childrenString]; + } + + return tmpString; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTObjectTextAttachment.h b/Pods/DTCoreText/Core/Source/DTObjectTextAttachment.h new file mode 100644 index 0000000..166766a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTObjectTextAttachment.h @@ -0,0 +1,22 @@ +// +// DTTextAttachmentObject.h +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTTextAttachment.h" + +/** + A specialized subclass in the DTTextAttachment class cluster to represent an generic object + */ + +@interface DTObjectTextAttachment : DTTextAttachment + +/** + The DTHTMLElement child nodes of the receiver. This array is only used for object tags at the moment. + */ +@property (nonatomic, strong) NSArray *childNodes; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTObjectTextAttachment.m b/Pods/DTCoreText/Core/Source/DTObjectTextAttachment.m new file mode 100644 index 0000000..e0c2daa --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTObjectTextAttachment.m @@ -0,0 +1,139 @@ +// +// DTTextAttachmentObject.m +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" + +@implementation DTObjectTextAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + _childNodes = [aDecoder decodeObjectForKey:@"childNodes"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_childNodes forKey:@"childNodes"]; +} + +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + self = [super initWithElement:element options:options]; + + if (self) + { + // get base URL + NSURL *baseURL = [options objectForKey:NSBaseURLDocumentOption]; + NSString *src = [element.attributes objectForKey:@"src"]; + + // content URL + _contentURL = [NSURL URLWithString:src relativeToURL:baseURL]; + } + + return self; +} + +#pragma mark - DTTextAttachmentHTMLEncoding + +- (NSString *)stringByEncodingAsHTML +{ + NSMutableString *retString = [NSMutableString string]; + + [retString appendString:@"0) + { + [styleString appendFormat:@"width:%.0fpx;", _originalSize.width]; + } + + if (_originalSize.height>0) + { + [styleString appendFormat:@"height:%.0fpx;", _originalSize.height]; + } + + // add local style for size, since sizes might vary quite a bit + if ([styleString length]) + { + [retString appendFormat:@" style=\"%@\"", styleString]; + } + + // attach the attributes dictionary + NSMutableDictionary *tmpAttributes = [_attributes mutableCopy]; + + // remove src,style, width and height we already have these + [tmpAttributes removeObjectForKey:@"src"]; + [tmpAttributes removeObjectForKey:@"style"]; + [tmpAttributes removeObjectForKey:@"width"]; + [tmpAttributes removeObjectForKey:@"height"]; + + for (__strong NSString *oneKey in [tmpAttributes allKeys]) + { + oneKey = [oneKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *value = [[tmpAttributes objectForKey:oneKey] stringByAddingHTMLEntities]; + [retString appendFormat:@" %@=\"%@\"", oneKey, value]; + } + + if (_childNodes) + { + [retString appendString:@">"]; + + for (DTHTMLElement *oneChild in _childNodes) + { + [retString appendString:[oneChild debugDescription]]; + } + + [retString appendString:@""]; + } + else + { + [retString appendString:@" />"]; + } + + + return retString; +} + +#pragma mark - Properties + +@synthesize childNodes = _childNodes; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTStylesheetHTMLElement.h b/Pods/DTCoreText/Core/Source/DTStylesheetHTMLElement.h new file mode 100644 index 0000000..b7d0469 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTStylesheetHTMLElement.h @@ -0,0 +1,23 @@ +// +// DTHTMLElementStylesheet.h +// DTCoreText +// +// Created by Oliver Drobnik on 29.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +@class DTCSSStylesheet; + +/** + This is a specialized subclass of representing a style block. + */ +@interface DTStylesheetHTMLElement : DTHTMLElement + +/** + Parses the text children and assembles the resulting stylesheet. + */ +- (DTCSSStylesheet *)stylesheet; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTStylesheetHTMLElement.m b/Pods/DTCoreText/Core/Source/DTStylesheetHTMLElement.m new file mode 100644 index 0000000..3263ebf --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTStylesheetHTMLElement.m @@ -0,0 +1,26 @@ +// +// DTHTMLElementStylesheet.m +// DTCoreText +// +// Created by Oliver Drobnik on 29.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTStylesheetHTMLElement.h" +#import "DTCSSStylesheet.h" + +@implementation DTStylesheetHTMLElement + +- (NSAttributedString *)attributedString +{ + return nil; +} + +- (DTCSSStylesheet *)stylesheet +{ + NSString *text = [self text]; + + return [[DTCSSStylesheet alloc] initWithStyleBlock:text]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextAttachment.h b/Pods/DTCoreText/Core/Source/DTTextAttachment.h new file mode 100644 index 0000000..3e2d6d9 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextAttachment.h @@ -0,0 +1,196 @@ +// +// DTTextAttachment.h +// DTCoreText +// +// Created by Oliver on 14.01.11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@class DTHTMLElement; + +/** + Text Attachment vertical alignment + */ +typedef NS_ENUM(NSUInteger, DTTextAttachmentVerticalAlignment) +{ + /** + Baseline alignment (default) + */ + DTTextAttachmentVerticalAlignmentBaseline = 0, + + /** + Align with top edge + */ + DTTextAttachmentVerticalAlignmentTop, + + /** + Align with center + */ + DTTextAttachmentVerticalAlignmentCenter, + + /** + Align with bottom edge + */ + DTTextAttachmentVerticalAlignmentBottom +}; + +/** + Methods to implement for attachments to support inline drawing. + */ +@protocol DTTextAttachmentDrawing + +/** + Draws the contents of the receiver into a graphics context + @param rect The rectangle to draw the receiver into + @param context The graphics context + */ +- (void)drawInRect:(CGRect)rect context:(CGContextRef)context; + +@end + + +/** + Methods to implement for attachments to support output to HTML. + */ +@protocol DTTextAttachmentHTMLPersistence + +/** + Creates a HTML representation of the receiver + @returns A HTML string with the receiver encoded as HTML + */ +- (NSString *)stringByEncodingAsHTML; + +@end + + +/** + An object to represent an attachment in an HTML/rich text view. + */ +@interface DTTextAttachment : NSObject +{ + CGSize _displaySize; // the display dimensions of the attachment + CGSize _originalSize; // the original dimensions of the attachment + CGSize _maxImageSize; // the maximum dimensions to size to + NSURL *_contentURL; + NSDictionary *_attributes; // attributes transferred from HTML element + DTTextAttachmentVerticalAlignment _verticalAlignment; // alignment in relation to the baseline +} + +/** + @name Creating Text Attachments + */ + +/** + Initialize and return a DTTextAttachment with the specified DTHTMLElement and options. Convenience initializer. + The element must have a valid tagName. The size of the returned text attachment is determined by the element, constrained by the option's key for DTMaxImageSize. Any valid image resource included in the element (denoted by the method attributeForKey: "src") is loaded and determines the text attachment size if it was not known before. If a size is too large the image is downsampled with sizeThatFitsKeepingAspectRatio() which preserves the aspect ratio. + @param element A DTHTMLElement that must have a valid tag name and should have a size. Any element attributes are copied to the text attachment's elements. + @param options An NSDictionary of options. Used to specify the max image size with the key DTMaxImageSize. + @returns Returns the appropriate subclass of the class cluster + */ ++ (DTTextAttachment *)textAttachmentWithElement:(DTHTMLElement *)element options:(NSDictionary *)options; + +/** + The designated initializer for members of the DTTextAttachment class cluster. If you need additional setup for custom subclasses then you should override this initializer. + @param element A DTHTMLElement that must have a valid tag name and should have a size. Any element attributes are copied to the text attachment's elements. + @param options An NSDictionary of options. Used to specify the max image size with the key DTMaxImageSize. + @returns Returns an initialized DTTextAttachment built using the element and options parameters. */ +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options; + +/** + @name Vertical Alignment + */ + +/** + Inspects the given font and records the font's ascent, descent and leading. These values are then used during layout to properly respond with ascentForLayout, descentForLayout for the receiver's verticalAlignment. + @param font The font to inspect + */ +- (void)adjustVerticalAlignmentForFont:(CTFontRef)font; + +/** + The ascent to use during layout so that the receiver can be display at its verticalAlignment. + */ +- (CGFloat)ascentForLayout; + +/** + The descent to use during layout so that the receiver can be display at its verticalAlignment. + */ +- (CGFloat)descentForLayout; + +/** + The vertical alignment of the receiver + */ +@property (nonatomic, assign) DTTextAttachmentVerticalAlignment verticalAlignment; + + +/** + @name Retrieving Information about Attachments + */ + +/** + The size of the receiver according to width/height HTML attribute or CSS style attribute + */ +@property (nonatomic, assign) CGSize originalSize; + +/** + The size to use for displaying/layouting the receiver + */ +@property (nonatomic, assign) CGSize displaySize; + +/** + Updates the display size optionally passing a maximum size that it should not exceed. + + This method in contrast to using the displaySize property will use the originalSize and max display size to calculate missing dimensions. + @param displaySize The new size to display the content with + @param maxDisplaySize the maximum size that the content should be scaled to fit + */ +- (void)setDisplaySize:(CGSize)displaySize withMaxDisplaySize:(CGSize)maxDisplaySize; + +/** + The URL representing the content + */ +@property (nonatomic, strong) NSURL *contentURL; + +/** + The hyperlink URL of the receiver. + */ +@property (nonatomic, strong) NSURL *hyperLinkURL; + +/** + The GUID of the hyperlink that this is a part of. All parts of a hyperlink have the same GUID so that they can highlight together. + */ +@property (nonatomic, strong) NSString *hyperLinkGUID; + +/** + The attributes dictionary of the attachment. + + If initialized from HTML, the values of this dictionary are transferred from the give HTML element. If you wish to add custom attribute values to be written to and read from HTML, be aware that the attribute name will be lowercased in compliance with W3C recommendations. Therefore, you may set a camel-case name and persist to HTML, but you will receive a lowercase name when the HTML is transformed into an attributed string. + */ +@property (nonatomic, strong) NSDictionary *attributes; + +/** + @name Customizing Attachments + */ + +/** + Registers your own class for use when encountering a specific tag Name. If you register a class for a previously registered class (or one of the predefined ones (img, iframe, object, video) then this replaces this with the newer registration. + + These registrations are permanent during the run time of your app. Custom attachment classes must implement the initWithElement:options: initializer and can implement the DTTextAttachmentDrawing and/or DTTextAttachmentHTMLPersistence protocols. + @param theClass The class to instantiate in textAttachmentWithElement:options: when encountering a tag with this name + @param tagName The tag name to use this class for + */ ++ (void)registerClass:(Class)theClass forTagName:(NSString *)tagName; + +/** + The class to use for a tag name + @param tagName The tag name + @returns The class to use for attachments with with tag name, or `nil` if this should not be an attachment + */ ++ (Class)registeredClassForTagName:(NSString *)tagName; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextAttachment.m b/Pods/DTCoreText/Core/Source/DTTextAttachment.m new file mode 100644 index 0000000..ac3d845 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextAttachment.m @@ -0,0 +1,262 @@ +// +// DTTextAttachment.m +// DTCoreText +// +// Created by Oliver on 14.01.11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTTextAttachment.h" +#import "DTCoreText.h" +#import "DTCoreGraphicsUtils.h" + +#import "DTBase64Coding.h" +#import "DTDictationPlaceholderTextAttachment.h" +#import "DTIframeTextAttachment.h" +#import "DTImageTextAttachment.h" +#import "DTObjectTextAttachment.h" +#import "DTVideoTextAttachment.h" +#import "DTLog.h" +#import "NSCoder+DTCompatibility.h" + +static NSMutableDictionary *_classForTagNameLookup = nil; + +@interface DTTextAttachment () + +@end + +@implementation DTTextAttachment +{ + NSURL *_hyperLinkURL; + NSString *_hyperLinkGUID; + + CGFloat _fontLeading; + CGFloat _fontAscent; + CGFloat _fontDescent; +} + ++ (void)initialize +{ + // this gets called from each subclass + // prevent calling from children + if (self != [DTTextAttachment class]) + { + return; + } + + _classForTagNameLookup = [[NSMutableDictionary alloc] init]; + + // register standard tags + [DTTextAttachment registerClass:[DTImageTextAttachment class] forTagName:@"img"]; + [DTTextAttachment registerClass:[DTVideoTextAttachment class] forTagName:@"video"]; + [DTTextAttachment registerClass:[DTIframeTextAttachment class] forTagName:@"iframe"]; + [DTTextAttachment registerClass:[DTObjectTextAttachment class] forTagName:@"object"]; +} + ++ (DTTextAttachment *)textAttachmentWithElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + Class class = [DTTextAttachment registeredClassForTagName:element.name]; + + if (!class) + { + return nil; + } + + DTTextAttachment *attachment = [class alloc]; + + return [attachment initWithElement:element options:options]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self) { + _displaySize = [aDecoder decodeCGSizeForKey:@"displaySize"]; + _originalSize = [aDecoder decodeCGSizeForKey:@"originalSize"]; + _maxImageSize = [aDecoder decodeCGSizeForKey:@"maxImageSize"]; + _contentURL = [aDecoder decodeObjectForKey:@"contentURL"]; + _attributes = [aDecoder decodeObjectForKey:@"attributes"]; + _verticalAlignment = [aDecoder decodeIntegerForKey:@"verticalAlignment"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeCGSize:_displaySize forKey:@"displaySize"]; + [aCoder encodeCGSize:_originalSize forKey:@"originalSize"]; + [aCoder encodeCGSize:_maxImageSize forKey:@"maxImageSize"]; + [aCoder encodeObject:_contentURL forKey:@"contentURL"]; + [aCoder encodeObject:_attributes forKey:@"attributes"]; + [aCoder encodeInteger:_verticalAlignment forKey:@"verticalAlignment"]; +} + +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + self = [super init]; + + if (self) + { + // width, height from tag + _originalSize = element.size; // initially not known + + // determine if there is a display size restriction + _maxImageSize = CGSizeZero; + + NSValue *maxImageSizeValue =[options objectForKey:DTMaxImageSize]; + if (maxImageSizeValue) + { +#if TARGET_OS_IPHONE + _maxImageSize = [maxImageSizeValue CGSizeValue]; +#else + _maxImageSize = [maxImageSizeValue sizeValue]; +#endif + } + + // set the display size from the original size, restricted to the max size + [self setDisplaySize:_originalSize withMaxDisplaySize:_maxImageSize]; + + _attributes = element.attributes; + } + + return self; +} + +- (void)adjustVerticalAlignmentForFont:(CTFontRef)font +{ + _fontLeading = CTFontGetLeading(font); + _fontAscent = CTFontGetAscent(font); + _fontDescent = CTFontGetDescent(font); +} + +- (CGFloat)ascentForLayout +{ + switch (_verticalAlignment) + { + case DTTextAttachmentVerticalAlignmentBaseline: + { + return _displaySize.height; + } + case DTTextAttachmentVerticalAlignmentTop: + { + return _fontAscent; + } + case DTTextAttachmentVerticalAlignmentCenter: + { + CGFloat halfHeight = (_fontAscent + _fontDescent) / 2.0f; + + return halfHeight - _fontDescent + _displaySize.height/2.0f; + } + case DTTextAttachmentVerticalAlignmentBottom: + { + return _displaySize.height - _fontDescent; + } + } +} + +- (CGFloat)descentForLayout +{ + switch (_verticalAlignment) + { + case DTTextAttachmentVerticalAlignmentBaseline: + { + return 0; + } + case DTTextAttachmentVerticalAlignmentTop: + { + return _displaySize.height - _fontAscent; + } + case DTTextAttachmentVerticalAlignmentCenter: + { + CGFloat halfHeight = (_fontAscent + _fontDescent) / 2.0f; + + return halfHeight - _fontAscent + _displaySize.height/2.0f; + } + case DTTextAttachmentVerticalAlignmentBottom: + { + return _fontDescent; + } + } +} + +#pragma mark - Subclass Customization + ++ (void)registerClass:(Class)class forTagName:(NSString *)tagName +{ + Class previousClass = [DTTextAttachment registeredClassForTagName:tagName]; + + if (previousClass) + { + DTLogWarning(@"Replacing previously registered class '%@' for tag name '%@' with '%@'", NSStringFromClass(previousClass), tagName, NSStringFromClass(class)); + } + + [_classForTagNameLookup setObject:class forKey:tagName]; +} + ++ (Class)registeredClassForTagName:(NSString *)tagName +{ + return [_classForTagNameLookup objectForKey:tagName]; +} + +#pragma mark Properties +/** Mutator for originalSize. Sets displaySize to the same value as originalSize. + @param originalSize The CGSize to store in originalSize. */ +- (void)setOriginalSize:(CGSize)originalSize +{ + if (!CGSizeEqualToSize(originalSize, _originalSize)) + { + _originalSize = originalSize; + + if (!_displaySize.width || !_displaySize.height) + { + [self setDisplaySize:_originalSize withMaxDisplaySize:_maxImageSize]; + } + } +} + +- (void)setDisplaySize:(CGSize)displaySize withMaxDisplaySize:(CGSize)maxDisplaySize +{ + if (_originalSize.width && _originalSize.height) + { + // width and/or height missing + if (displaySize.width==0 && displaySize.height==0) + { + displaySize = _originalSize; + } + else if (!displaySize.width && displaySize.height) + { + // width missing, calculate it + CGFloat factor = _originalSize.height / displaySize.height; + displaySize.width = round(_originalSize.width / factor); + } + else if (displaySize.width>0 && displaySize.height==0) + { + // height missing, calculate it + CGFloat factor = _originalSize.width / displaySize.width; + displaySize.height = round(_originalSize.height / factor); + } + } + + if (maxDisplaySize.width>0 && maxDisplaySize.height>0) + { + if (maxDisplaySize.width < displaySize.width || maxDisplaySize.height < displaySize.height) + { + displaySize = DTCGSizeThatFitsKeepingAspectRatio(displaySize, maxDisplaySize); + } + } + + _displaySize = displaySize; +} + +- (void)setDisplaySize:(CGSize)displaySize +{ + _displaySize = displaySize; +} + +@synthesize originalSize = _originalSize; +@synthesize displaySize = _displaySize; +@synthesize contentURL = _contentURL; +@synthesize hyperLinkURL = _hyperLinkURL; +@synthesize attributes = _attributes; +@synthesize verticalAlignment = _verticalAlignment; +@synthesize hyperLinkGUID = hyperLinkGUID; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextAttachmentHTMLElement.h b/Pods/DTCoreText/Core/Source/DTTextAttachmentHTMLElement.h new file mode 100644 index 0000000..264387a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextAttachmentHTMLElement.h @@ -0,0 +1,17 @@ +// +// DTHTMLElementAttachment.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +/** + Specialized subclass of for dealing with instances, e.g. images. + */ + +@interface DTTextAttachmentHTMLElement : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextAttachmentHTMLElement.m b/Pods/DTCoreText/Core/Source/DTTextAttachmentHTMLElement.m new file mode 100644 index 0000000..e9b070a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextAttachmentHTMLElement.m @@ -0,0 +1,104 @@ +// +// DTHTMLElementAttachment.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTTextAttachmentHTMLElement.h" + +#import "DTHTMLElement.h" +#import "DTTextAttachment.h" +#import "DTCoreTextParagraphStyle.h" +#import "NSMutableAttributedString+HTML.h" + +@implementation DTTextAttachmentHTMLElement +{ + CGSize _maxDisplaySize; +} + +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options +{ + self = [super initWithName:name attributes:attributes]; + + if (self) + { + // make appropriate attachment + DTTextAttachment *attachment = [DTTextAttachment textAttachmentWithElement:self options:options]; + + // add it to tag + _textAttachment = attachment; + + // to avoid much too much space before the image + _paragraphStyle.lineHeightMultiple = 1; + + // specifiying line height interfers with correct positioning + _paragraphStyle.minimumLineHeight = 0; + _paragraphStyle.maximumLineHeight = 0; + + // remember the maximum display size + _maxDisplaySize = CGSizeZero; + + NSValue *maxImageSizeValue =[options objectForKey:DTMaxImageSize]; + if (maxImageSizeValue) + { +#if TARGET_OS_IPHONE + _maxDisplaySize = [maxImageSizeValue CGSizeValue]; +#else + _maxDisplaySize = [maxImageSizeValue sizeValue]; +#endif + } + } + + return self; +} + +- (NSAttributedString *)attributedString +{ + @synchronized(self) + { + NSDictionary *attributes = [self attributesForAttributedStringRepresentation]; + + // ignore text, use unicode object placeholder + NSMutableAttributedString *tmpString = [[NSMutableAttributedString alloc] initWithString:UNICODE_OBJECT_PLACEHOLDER attributes:attributes]; + + // block-level elements get space trimmed and a newline + if (self.displayStyle != DTHTMLElementDisplayStyleInline) + { + [tmpString appendString:@"\n"]; + } + + return tmpString; + } +} + +// workaround, because we don't support float yet. float causes the image to be its own block +- (DTHTMLElementDisplayStyle)displayStyle +{ + if ([super floatStyle]==DTHTMLElementFloatStyleNone) + { + return [super displayStyle]; + } + + return DTHTMLElementDisplayStyleBlock; +} + +- (void)applyStyleDictionary:(NSDictionary *)styles +{ + // element size is determined in super (tag attribute and style) + [super applyStyleDictionary:styles]; + + // at this point we have the size from width/height attribute or style in _size + + // set original size if it was previously unknown + if (CGSizeEqualToSize(CGSizeZero, _textAttachment.originalSize)) + { + _textAttachment.originalSize = _size; + } + + // update the display size + [_textAttachment setDisplaySize:_size withMaxDisplaySize:_maxDisplaySize]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextBlock.h b/Pods/DTCoreText/Core/Source/DTTextBlock.h new file mode 100644 index 0000000..f22808e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextBlock.h @@ -0,0 +1,28 @@ +// +// DTTextBlock.h +// DTCoreText +// +// Created by Oliver Drobnik on 04.03.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCompatibility.h" + +/** + Class that represents a block of text with attributes like padding or a background color. + */ +@interface DTTextBlock : NSObject + +/** + The space to be applied between the layouted text and the edges of the receiver + */ +@property (nonatomic, assign) DTEdgeInsets padding; + + +/** + The background color to paint behind the text in the receiver + */ +@property (nonatomic, strong) DTColor *backgroundColor; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextBlock.m b/Pods/DTCoreText/Core/Source/DTTextBlock.m new file mode 100644 index 0000000..eec71a4 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextBlock.m @@ -0,0 +1,86 @@ +// +// DTTextBlock.m +// DTCoreText +// +// Created by Oliver Drobnik on 04.03.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTTextBlock.h" +#import "DTCoreText.h" +#import "NSCoder+DTCompatibility.h" + +@implementation DTTextBlock +{ + DTEdgeInsets _padding; + DTColor *_backgroundColor; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self) { + _padding = [aDecoder decodeDTEdgeInsetsForKey:@"padding"]; + _backgroundColor = [aDecoder decodeObjectForKey:@"backgroundColor"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeDTEdgeInsets:_padding forKey:@"padding"]; + [aCoder encodeObject:_backgroundColor forKey:@"backgroundColor"]; +} + +- (NSUInteger)hash +{ + NSUInteger calcHash = 7; + + calcHash = calcHash*31 + [_backgroundColor hash]; + calcHash = calcHash*31 + (NSUInteger)_padding.left; + calcHash = calcHash*31 + (NSUInteger)_padding.top; + calcHash = calcHash*31 + (NSUInteger)_padding.right; + calcHash = calcHash*31 + (NSUInteger)_padding.bottom; + + return calcHash; +} + +- (BOOL)isEqual:(id)object +{ + if (!object) + { + return NO; + } + + if (object == self) + { + return YES; + } + + if (![object isKindOfClass:[DTTextBlock class]]) + { + return NO; + } + + DTTextBlock *other = object; + + if (_padding.left != other->_padding.left || + _padding.top != other->_padding.top || + _padding.right != other->_padding.right || + _padding.bottom != other->_padding.bottom) + { + return NO; + } + + if (other->_backgroundColor == _backgroundColor) + { + return YES; + } + + return [other->_backgroundColor isEqual:_backgroundColor]; +} + +#pragma mark Properties + +@synthesize padding = _padding; +@synthesize backgroundColor = _backgroundColor; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextHTMLElement.h b/Pods/DTCoreText/Core/Source/DTTextHTMLElement.h new file mode 100644 index 0000000..ddf2a59 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextHTMLElement.h @@ -0,0 +1,22 @@ +// +// DTHTMLElementText.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +/** + Specialized subclass of that deals with text. It represents a text node. The text inside a DTHTMLElement can consist of any number of such text nodes. + */ + +@interface DTTextHTMLElement : DTHTMLElement + +/** + The text content of the element. + */ +@property (nonatomic, strong) NSString *text; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTTextHTMLElement.m b/Pods/DTCoreText/Core/Source/DTTextHTMLElement.m new file mode 100644 index 0000000..3442ed4 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTTextHTMLElement.m @@ -0,0 +1,119 @@ +// +// DTHTMLElementText.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTTextHTMLElement.h" +#import "NSString+HTML.h" +#import "DTCoreTextFontDescriptor.h" +#import "NSAttributedString+SmallCaps.h" + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES && TARGET_OS_IPHONE +#import "UIFont+DTCoreText.h" +#endif + +@implementation DTTextHTMLElement +{ + NSString *_text; +} + +- (void)_appendHTMLToString:(NSMutableString *)string indentLevel:(NSUInteger)indentLevel +{ + // indent to the level + for (NSUInteger i=0; i + +@end diff --git a/Pods/DTCoreText/Core/Source/DTVideoTextAttachment.m b/Pods/DTCoreText/Core/Source/DTVideoTextAttachment.m new file mode 100644 index 0000000..b60a911 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTVideoTextAttachment.m @@ -0,0 +1,107 @@ +// +// DTTextAttachmentVideo.m +// DTCoreText +// +// Created by Oliver Drobnik on 22.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" + +@implementation DTVideoTextAttachment + +- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options +{ + self = [super initWithElement:element options:options]; + + if (self) + { + // get base URL + NSURL *baseURL = [options objectForKey:NSBaseURLDocumentOption]; + NSString *src = [element.attributes objectForKey:@"src"]; + + // content URL + _contentURL = [NSURL URLWithString:src relativeToURL:baseURL]; + } + + return self; +} + +#pragma mark - DTTextAttachmentHTMLEncoding + +- (NSString *)stringByEncodingAsHTML +{ + NSMutableString *retString = [NSMutableString string]; + + [retString appendString:@"0) + { + [styleString appendFormat:@"width:%.0fpx;", _originalSize.width]; + } + + if (_originalSize.height>0) + { + [styleString appendFormat:@"height:%.0fpx;", _originalSize.height]; + } + + // add local style for size, since sizes might vary quite a bit + if ([styleString length]) + { + [retString appendFormat:@" style=\"%@\"", styleString]; + } + + // attach the attributes dictionary + NSMutableDictionary *tmpAttributes = [_attributes mutableCopy]; + + // remove src,style, width and height we already have these + [tmpAttributes removeObjectForKey:@"src"]; + [tmpAttributes removeObjectForKey:@"style"]; + [tmpAttributes removeObjectForKey:@"width"]; + [tmpAttributes removeObjectForKey:@"height"]; + + for (__strong NSString *oneKey in [tmpAttributes allKeys]) + { + oneKey = [oneKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *value = [[tmpAttributes objectForKey:oneKey] stringByAddingHTMLEntities]; + [retString appendFormat:@" %@=\"%@\"", oneKey, value]; + } + + [retString appendString:@" />"]; + + return retString; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTWebVideoView.h b/Pods/DTCoreText/Core/Source/DTWebVideoView.h new file mode 100644 index 0000000..5d49ffb --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTWebVideoView.h @@ -0,0 +1,52 @@ +// +// DTWebVideoView.h +// DTCoreText +// +// Created by Oliver Drobnik on 8/5/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTWeakSupport.h" + +@class DTWebVideoView; +@class DTTextAttachment; + +/** + Protocol for delegates of +*/ +@protocol DTWebVideoViewDelegate + +@optional + +/** + Asks the delegate if an external URL may be opened + @param videoView The web video view + @param url The external URL that is asked to be opened + @returns `YES` if the app may be left to open the external URL + */ + +- (BOOL)videoView:(DTWebVideoView *)videoView shouldOpenExternalURL:(NSURL *)url; + +@end + + +/** + The class represents a custom subview for use in to represent an embedded video. + + Embedded videos work by loading the video URL in a UIWebView which iOS then replaces with the built-in media player view. The URL of the embed script depends on the service and needs to be added to the webView:shouldStartLoadWithRequest:navigationType:. You want to allow the URL for the embed script, but disallow all other requests which for example occur if a user taps on the YouTube logo. If you were to allow this type of navigation then the YouTube website would be loaded in the video view. For these scenarios there is the videoView:shouldOpenExternalURL: method in DTWebVideoViewDelegate. If you respond with `YES` (which is default if the method is not implemented) then the URL will be opened in Safari. + + To add additional video services please add them in the mentioned location and submit a pull request for the addition. + */ +@interface DTWebVideoView : UIView + +/** + The delegate of the video view + */ +@property (nonatomic, DT_WEAK_PROPERTY) id delegate; + +/** + The text attachment representing an embedded video. + */ +@property (nonatomic, strong) DTTextAttachment *attachment; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTWebVideoView.m b/Pods/DTCoreText/Core/Source/DTWebVideoView.m new file mode 100644 index 0000000..57a93ab --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTWebVideoView.m @@ -0,0 +1,127 @@ +// +// DTWebVideoView.m +// DTCoreText +// +// Created by Oliver Drobnik on 8/5/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" + +@interface DTWebVideoView () + +- (void)disableScrolling; + +@end + + +@implementation DTWebVideoView +{ + DTTextAttachment *_attachment; + + DT_WEAK_VARIABLE id _delegate; + + UIWebView *_webView; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + self.userInteractionEnabled = YES; + + _webView = [[UIWebView alloc] initWithFrame:self.bounds]; + _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:_webView]; + + [self disableScrolling]; + + _webView.delegate = self; + + if ([_webView respondsToSelector:@selector(setAllowsInlineMediaPlayback:)]) + { + _webView.allowsInlineMediaPlayback = YES; + } + } + return self; +} + +- (void)dealloc +{ + _webView.delegate = nil; + +} + + +- (void)disableScrolling +{ + // find scrollview and disable scrolling + for (UIView *oneView in _webView.subviews) + { + if ([oneView isKindOfClass:[UIScrollView class]]) + { + UIScrollView *scrollView = (id)oneView; + + scrollView.scrollEnabled = NO; + scrollView.bounces = NO; + + return; + } + } +} + +#pragma mark UIWebViewDelegate + +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType +{ + // allow the embed request for YouTube + if (NSNotFound != [[[request URL] absoluteString] rangeOfString:@"www.youtube.com/embed/"].location) + { + return YES; + } + + // allow the embed request for DailyMotion Cloud + if (NSNotFound != [[[request URL] absoluteString] rangeOfString:@"api.dmcloud.net/player/embed/"].location) + { + return YES; + } + + BOOL shouldOpenExternalURL = YES; + + if ([_delegate respondsToSelector:@selector(videoView:shouldOpenExternalURL:)]) + { + shouldOpenExternalURL = [_delegate videoView:self shouldOpenExternalURL:[request URL]]; + } + + if (shouldOpenExternalURL) + { + [[UIApplication sharedApplication] openURL:[request URL]]; + } + + return NO; +} + + + +#pragma mark Properties + +- (void)setAttachment:(DTTextAttachment *)attachment +{ + if (_attachment != attachment) + { + + _attachment = attachment; + + if ([attachment isKindOfClass:[DTIframeTextAttachment class]]) + { + NSURLRequest *request = [NSURLRequest requestWithURL:attachment.contentURL]; + [_webView loadRequest:request]; + } + } +} + +@synthesize delegate = _delegate; +@synthesize attachment = _attachment; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSAttributedString+DTCoreText.h b/Pods/DTCoreText/Core/Source/NSAttributedString+DTCoreText.h new file mode 100644 index 0000000..98b29da --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedString+DTCoreText.h @@ -0,0 +1,139 @@ +// +// NSAttributedString+DTCoreText.h +// DTCoreText +// +// Created by Oliver Drobnik on 2/1/12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +@class DTCSSListStyle; +@class DTTextBlock; + +/** + Convenience Methods that mimick similar methods available on Mac + */ +@interface NSAttributedString (DTCoreText) + +/** + @name Working with Text Attachments + */ + +/** + Retrieves the DTTextAttachment objects that match the given predicate. + + With this method you can for example find all images that have a certain URL. + + @param predicate The predicate to apply for filtering or `nil` to not filter by attachment + @param theClass The class that attachments need to have, or `nil` for all attachments regardless of class + @returns The filtered array of attachments + */ +- (NSArray *)textAttachmentsWithPredicate:(NSPredicate *)predicate class:(Class)theClass; + +/** + @name Calculating Ranges + */ + + +/** + Returns the index of the item at the given location within the list. + + @param list The text list. + @param location The location of the item. + @returns Returns the index within the list. +*/ +- (NSInteger)itemNumberInTextList:(DTCSSListStyle *)list atIndex:(NSUInteger)location; + + +/** + Returns the range of the given text list that contains the given location. + + @param list The text list. + @param location The location in the text. + @returns The range of the given text list containing the location. + */ +- (NSRange)rangeOfTextList:(DTCSSListStyle *)list atIndex:(NSUInteger)location; + +/** + Returns the range of the given text block that contains the given location. + + @param textBlock The text block. + @param location The location in the text. + @returns The range of the given text block containing the location. + */ +- (NSRange)rangeOfTextBlock:(DTTextBlock *)textBlock atIndex:(NSUInteger)location; + +/** + Returns the range of the given href anchor. + + @param anchorName The name of the anchor. + @returns The range of the given anchor. + */ +- (NSRange)rangeOfAnchorNamed:(NSString *)anchorName; + +/** + Returns the range of the hyperlink at the given index. + + @param location The location to query + @param URL The URL that is found at this location or `NULL` if this is not needed + @returns The range of the given hyperlink. + */ +- (NSRange)rangeOfLinkAtIndex:(NSUInteger)location URL:(NSURL * __autoreleasing*)URL; + +/** + Returns the range of a field at the given index. + + @param location The location of the field + @returns The range of the field. If there is no field at this location it returns {NSNotFound, 0}. + */ +- (NSRange)rangeOfFieldAtIndex:(NSUInteger)location; + +#ifndef COVERAGE +// exclude method from coverage testing, those are just convenience methods + +/** + @name Converting to Other Representations + */ + +/** + Encodes the receiver into a generic HTML prepresentation. + + @returns An HTML string. + */ +- (NSString *)htmlString; + + +/** + Encodes the receiver into a generic HTML fragment representation. Styles are inlined and no html or head tags are included. + + @returns An HTML string. + */ +- (NSString *)htmlFragment; + +/** + Converts the receiver into plain text. + + This is different from the `string` method of `NSAttributedString` by also erasing placeholders for text attachments. + + @returns The receiver converted to plain text. + */ +- (NSString *)plainTextString; + +#endif + +/** + @name Creating Special Attributed Strings + */ + + +/** + Create a prefix for a paragraph in a list + + @param listCounter The value for the list item. + @param listStyle The list style + @param listIndent The amount in px to indent the list + @param attributes The attribute dictionary for the text to be prefixed + @returns An attributed string with the list prefix + */ ++ (NSAttributedString *)prefixForListItemWithCounter:(NSUInteger)listCounter listStyle:(DTCSSListStyle *)listStyle listIndent:(CGFloat)listIndent attributes:(NSDictionary *)attributes; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSAttributedString+DTCoreText.m b/Pods/DTCoreText/Core/Source/NSAttributedString+DTCoreText.m new file mode 100644 index 0000000..a9966bd --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedString+DTCoreText.m @@ -0,0 +1,512 @@ +// +// NSAttributedString+DTCoreText.m +// DTCoreText +// +// Created by Oliver Drobnik on 2/1/12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "NSAttributedString+DTCoreText.h" +#import "DTHTMLWriter.h" +#import "NSURL+DTComparing.h" + +@implementation NSAttributedString (DTCoreText) + +#pragma mark Text Attachments +- (NSArray *)textAttachmentsWithPredicate:(NSPredicate *)predicate class:(Class)class +{ + if (![self length]) + { + return nil; + } + + NSMutableArray *foundAttachments = [NSMutableArray array]; + + NSRange entireRange = NSMakeRange(0, [self length]); + [self enumerateAttribute:NSAttachmentAttributeName inRange:entireRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(DTTextAttachment *attachment, NSRange range, BOOL *stop) { + + if (attachment == nil) + { + // no attachment value + return; + } + + if (predicate && ![predicate evaluateWithObject:attachment]) + { + // doesn't fit predicate, next + return; + } + + if (class && ![attachment isKindOfClass:class]) + { + // doesn't fit class, next + return; + } + + [foundAttachments addObject:attachment]; + }]; + + if ([foundAttachments count]) + { + return foundAttachments; + } + + return nil; +} + + +#pragma mark Calculating Ranges + +- (NSInteger)itemNumberInTextList:(DTCSSListStyle *)list atIndex:(NSUInteger)location +{ + NSRange effectiveRange; + NSArray *textListsAtIndex = [self attribute:DTTextListsAttribute atIndex:location effectiveRange:&effectiveRange]; + + if (!textListsAtIndex) + { + return 0; + } + + // get outermost list + DTCSSListStyle *outermostList = [textListsAtIndex objectAtIndex:0]; + + // get the range of all lists + NSRange totalRange = [self rangeOfTextList:outermostList atIndex:location]; + + // get naked NSString + NSString *string = [[self string] substringWithRange:totalRange]; + + // entire string + NSRange range = NSMakeRange(0, [string length]); + + NSMutableDictionary *countersPerList = [NSMutableDictionary dictionary]; + + // enumerating through the paragraphs in the plain text string + [string enumerateSubstringsInRange:range options:NSStringEnumerationByParagraphs usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) + { + NSRange paragraphListRange; + NSArray *textLists = [self attribute:DTTextListsAttribute atIndex:substringRange.location + totalRange.location effectiveRange:¶graphListRange]; + + DTCSSListStyle *currentEffectiveList = [textLists lastObject]; + + NSNumber *key = [NSNumber numberWithInteger:(NSInteger)currentEffectiveList]; // list address is identifier + NSNumber *currentCounterNum = [countersPerList objectForKey:key]; + + NSInteger currentCounter=0; + + if (!currentCounterNum) + { + currentCounter = currentEffectiveList.startingItemNumber; + } + else + { + currentCounter = [currentCounterNum integerValue]+1; + } + + currentCounterNum = [NSNumber numberWithInteger:currentCounter]; + [countersPerList setObject:currentCounterNum forKey:key]; + + // calculate the actual range + NSRange actualRange = enclosingRange; // includes a potential \n + actualRange.location += totalRange.location; + + if (NSLocationInRange(location, actualRange)) + { + *stop = YES; + } + } + ]; + + NSNumber *key = [NSNumber numberWithInteger:(NSInteger)list]; // list address is identifier + NSNumber *currentCounterNum = [countersPerList objectForKey:key]; + + return [currentCounterNum integerValue]; +} + +- (NSRange)_rangeOfObject:(id)object inArrayBehindAttribute:(NSString *)attribute atIndex:(NSUInteger)location +{ + @synchronized(self) + { + NSUInteger stringLength = [self length]; + NSUInteger searchIndex = location; + + NSArray *arrayAtIndex; + + NSRange totalRange = NSMakeRange(NSNotFound, 0); + + BOOL foundList = NO; + + do + { + NSRange effectiveRange; + arrayAtIndex = [self attribute:attribute atIndex:searchIndex effectiveRange:&effectiveRange]; + + if (!arrayAtIndex || [arrayAtIndex indexOfObjectIdenticalTo:object] == NSNotFound) + { + break; + } + + searchIndex = effectiveRange.location; + foundList = YES; + + // enhance found range + if (totalRange.location == NSNotFound) + { + totalRange = effectiveRange; + } + else + { + totalRange = NSUnionRange(totalRange, effectiveRange); + } + + if (searchIndex == 0) + { + // reached beginning of string + break; + } + + searchIndex--; + } + while (foundList); + + // if we didn't find the list at all, return + if (!foundList) + { + return NSMakeRange(NSNotFound, 0); + } + + // now search forward + + searchIndex = NSMaxRange(totalRange); + + while (searchIndex < stringLength) + { + NSRange effectiveRange; + arrayAtIndex = [self attribute:attribute atIndex:searchIndex effectiveRange:&effectiveRange]; + + if (!arrayAtIndex || [arrayAtIndex indexOfObjectIdenticalTo:object] == NSNotFound) + { + break; + } + + searchIndex = NSMaxRange(effectiveRange); + + // enhance found range + totalRange = NSUnionRange(totalRange, effectiveRange); + } + + return totalRange; + } +} + +- (NSRange)rangeOfTextList:(DTCSSListStyle *)list atIndex:(NSUInteger)location +{ + NSParameterAssert(list); + + NSRange listRange = [self _rangeOfObject:list inArrayBehindAttribute:DTTextListsAttribute atIndex:location]; + + if (listRange.location == NSNotFound) + { + // list was not found + return listRange; + } + + // extend list range to full paragraphs to be safe + listRange = [self.string rangeOfParagraphsContainingRange:listRange parBegIndex:NULL parEndIndex:NULL]; + + return listRange; +} + +- (NSRange)rangeOfTextBlock:(DTTextBlock *)textBlock atIndex:(NSUInteger)location +{ + NSParameterAssert(textBlock); + + return [self _rangeOfObject:textBlock inArrayBehindAttribute:DTTextBlocksAttribute atIndex:location]; +} + +- (NSRange)rangeOfAnchorNamed:(NSString *)anchorName +{ + __block NSRange foundRange = NSMakeRange(NSNotFound, 0); + + [self enumerateAttribute:DTAnchorAttribute inRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(NSString *value, NSRange range, BOOL *stop) { + if ([value isEqualToString:anchorName]) + { + *stop = YES; + foundRange = range; + } + }]; + + return foundRange; +} + +- (NSRange)rangeOfLinkAtIndex:(NSUInteger)location URL:(NSURL * __autoreleasing*)URL +{ + NSRange rangeSoFar; + + NSURL *foundURL = [self attribute:DTLinkAttribute atIndex:location effectiveRange:&rangeSoFar]; + + if (!foundURL) + { + return NSMakeRange(NSNotFound, 0); + } + + // search towards beginning + while (rangeSoFar.location>0) + { + NSRange extendedRange; + NSURL *extendedURL = [self attribute:DTLinkAttribute atIndex:rangeSoFar.location-1 effectiveRange:&extendedRange]; + + // abort search if key not found or value not identical + if (!extendedURL || ![extendedURL isEqualToURL:foundURL]) + { + break; + } + + rangeSoFar = NSUnionRange(rangeSoFar, extendedRange); + } + + NSUInteger length = [self length]; + + // search towards end + while (NSMaxRange(rangeSoFar) __IPHONE_5_1 + if (___useiOS6Attributes) + { + UIFont *uiFont = [UIFont fontWithCTFont:font]; + [newAttributes setObject:uiFont forKey:NSFontAttributeName]; + + CFRelease(font); + } + else +#endif + { + [newAttributes setObject:CFBridgingRelease(font) forKey:(id)kCTFontAttributeName]; + } + } + } + + CGColorRef textColor = (__bridge CGColorRef)[attributes objectForKey:(id)kCTForegroundColorAttributeName]; + + if (textColor) + { + [newAttributes setObject:(__bridge id)textColor forKey:(id)kCTForegroundColorAttributeName]; + } +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + else if (___useiOS6Attributes) + { + DTColor *uiColor = [attributes foregroundColor]; + + if (uiColor) + { + [newAttributes setObject:uiColor forKey:NSForegroundColorAttributeName]; + } + } +#endif + + // add paragraph style (this has the tabs) + if (paragraphStyle) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + NSParagraphStyle *style = [paragraphStyle NSParagraphStyle]; + [newAttributes setObject:style forKey:NSParagraphStyleAttributeName]; + } + else +#endif + { + CTParagraphStyleRef newParagraphStyle = [paragraphStyle createCTParagraphStyle]; + [newAttributes setObject:CFBridgingRelease(newParagraphStyle) forKey:(id)kCTParagraphStyleAttributeName]; + } + } + + // add textBlock if there's one (this has padding and background color) + NSArray *textBlocks = [attributes objectForKey:DTTextBlocksAttribute]; + if (textBlocks) + { + [newAttributes setObject:textBlocks forKey:DTTextBlocksAttribute]; + } + + // transfer all lists so that + NSArray *lists = [attributes objectForKey:DTTextListsAttribute]; + if (lists) + { + [newAttributes setObject:lists forKey:DTTextListsAttribute]; + } + + // add a marker so that we know that this is a field/prefix + [newAttributes setObject:DTListPrefixField forKey:DTFieldAttribute]; + + NSString *prefix = [listStyle prefixWithCounter:listCounter]; + + if (prefix) + { + DTImage *image = nil; + + if (listStyle.imageName) + { + image = [DTImage imageNamed:listStyle.imageName]; + + if (!image) + { + // image invalid + listStyle.imageName = nil; + + prefix = [listStyle prefixWithCounter:listCounter]; + } + } + + NSMutableAttributedString *tmpStr = [[NSMutableAttributedString alloc] initWithString:prefix attributes:newAttributes]; + + if (image) + { + // make an attachment for the image + DTImageTextAttachment *attachment = [[DTImageTextAttachment alloc] init]; + attachment.image = image; + attachment.displaySize = image.size; + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES && TARGET_OS_IPHONE + // need run delegate for sizing + CTRunDelegateRef embeddedObjectRunDelegate = createEmbeddedObjectRunDelegate(attachment); + [newAttributes setObject:CFBridgingRelease(embeddedObjectRunDelegate) forKey:(id)kCTRunDelegateAttributeName]; +#endif + + // add attachment + [newAttributes setObject:attachment forKey:NSAttachmentAttributeName]; + + if (listStyle.position == DTCSSListStylePositionInside) + { + [tmpStr setAttributes:newAttributes range:NSMakeRange(2, 1)]; + } + else + { + [tmpStr setAttributes:newAttributes range:NSMakeRange(1, 1)]; + } + } + + return tmpStr; + } + + return nil; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSAttributedString+DTDebug.h b/Pods/DTCoreText/Core/Source/NSAttributedString+DTDebug.h new file mode 100644 index 0000000..fc75f86 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedString+DTDebug.h @@ -0,0 +1,18 @@ +// +// NSAttributedString+DTDebug.h +// DTCoreText +// +// Created by Oliver Drobnik on 29.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import + +/** + The *DTDebug* category contains methods for debugging and dumping attributed strings + */ +@interface NSAttributedString (DTDebug) + +- (void)dumpRangesOfAttribute:(id)attribute; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSAttributedString+DTDebug.m b/Pods/DTCoreText/Core/Source/NSAttributedString+DTDebug.m new file mode 100644 index 0000000..650486a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedString+DTDebug.m @@ -0,0 +1,39 @@ +// +// NSAttributedString+DTDebug.m +// DTCoreText +// +// Created by Oliver Drobnik on 29.04.13. +// Copyright (c) 2013 Drobnik.com. All rights reserved. +// + +#import "NSAttributedString+DTDebug.h" + + +@implementation NSAttributedString (DTDebug) + +- (void)dumpRangesOfAttribute:(id)attribute +{ + NSMutableString *tmpString = [NSMutableString string]; + + NSRange entireRange = NSMakeRange(0, [self length]); + [self enumerateAttribute:attribute inRange:entireRange options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { + NSString *rangeString = [[self string] substringWithRange:range]; + NSString *valueString; + + if ([value isKindOfClass:[NSArray class]]) + { + valueString = [(NSArray *)value componentsJoinedByString:@", "]; + } + else + { + valueString = [value debugDescription]; + } + + [tmpString appendFormat:@"%@ %@ '%@'\n", NSStringFromRange(range), valueString, [rangeString stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]]; + }]; + + printf("%s", [tmpString UTF8String]); +} + + +@end diff --git a/Pods/DTCoreText/Core/Source/NSAttributedString+HTML.h b/Pods/DTCoreText/Core/Source/NSAttributedString+HTML.h new file mode 100644 index 0000000..51c28c0 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedString+HTML.h @@ -0,0 +1,75 @@ +// +// NSAttributedString+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +@class NSAttributedString; + +/** + Methods for generating an `NSAttributedString` from HTML data. Those methods exist on Mac but have not been ported (publicly) to iOS. This project aims to remedy this. + + For a list of available options to pass to any of these methods please refer to [DTHTMLAttributedStringBuilder initWithHTML:options:documentAttributes:]. + */ + +@interface NSAttributedString (HTML) + +/** + @name Creating an NSAttributedString + */ + +/** + Initializes and returns a new `NSAttributedString` object from the HTML contained in the given object and base URL. + @param data The data in HTML format from which to create the attributed string. + @param docAttributes Currently not in used. + @returns Returns an initialized object, or `nil` if the data can’t be decoded. + @see [DTHTMLAttributedStringBuilder initWithHTML:options:documentAttributes:] for a list of available options + */ +- (id)initWithHTMLData:(NSData *)data documentAttributes:(NSDictionary * __autoreleasing*)docAttributes; + +/** + Initializes and returns a new `NSAttributedString` object from the HTML contained in the given object and base URL. + @param data The data in HTML format from which to create the attributed string. + @param baseURL An `NSURL` that represents the base URL for all links within the HTML. + @param docAttributes Currently not in used. + @returns Returns an initialized object, or `nil` if the data can’t be decoded. + @see [DTHTMLAttributedStringBuilder initWithHTML:options:documentAttributes:] for a list of available options + */ +- (id)initWithHTMLData:(NSData *)data baseURL:(NSURL *)baseURL documentAttributes:(NSDictionary * __autoreleasing*)docAttributes; + +/** + Initializes and returns a new `NSAttributedString` object from the HTML contained in the given object and base URL. + + @param data The data in HTML format from which to create the attributed string. + @param options Specifies how the document should be loaded. + @param docAttributes Currently not in used. + @returns Returns an initialized object, or `nil` if the data can’t be decoded. + @see [DTHTMLAttributedStringBuilder initWithHTML:options:documentAttributes:] for a list of available options + */ +- (id)initWithHTMLData:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary * __autoreleasing*)docAttributes; + + +/** + @name Working with Custom HTML Attributes + */ + +/** + Retrieves the dictionary of custom HTML attributes active at the given string index + @param index The string index to query + @returns The custom HTML attributes dictionary or `nil` if there aren't any at this index + */ +- (NSDictionary *)HTMLAttributesAtIndex:(NSUInteger)index; + +/** + Retrieves the range that an attribute with a given name is active for, beginning with the passed index + + Since a custom HTML attribute can occur in multiple individual attribute dictionaries this extends the range from the passed index outwards until the full range of the custom HTML attribute has been found. Those range extentions have to have an identical value, as established by comparing them to the value of the custom attribute at the index with isEqual: + @param name The name of the custom attribute to remove + @param index The string index to query + @returns The custom HTML attributes dictionary or `nil` if there aren't any at this index + */ +- (NSRange)rangeOfHTMLAttribute:(NSString *)name atIndex:(NSUInteger)index; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSAttributedString+HTML.m b/Pods/DTCoreText/Core/Source/NSAttributedString+HTML.m new file mode 100644 index 0000000..b07c10e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedString+HTML.m @@ -0,0 +1,122 @@ +// +// NSAttributedString+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +#import "DTCoreText.h" + +@implementation NSAttributedString (HTML) + +- (id)initWithHTMLData:(NSData *)data documentAttributes:(NSDictionary * __autoreleasing*)docAttributes +{ + return [self initWithHTMLData:data options:nil documentAttributes:docAttributes]; +} + +- (id)initWithHTMLData:(NSData *)data baseURL:(NSURL *)base documentAttributes:(NSDictionary * __autoreleasing*)docAttributes +{ + NSDictionary *optionsDict = nil; + + if (base) + { + optionsDict = [NSDictionary dictionaryWithObject:base forKey:NSBaseURLDocumentOption]; + } + + return [self initWithHTMLData:data options:optionsDict documentAttributes:docAttributes]; +} + +- (id)initWithHTMLData:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary * __autoreleasing*)docAttributes +{ + // only with valid data + if (![data length]) + { + return nil; + } + + DTHTMLAttributedStringBuilder *stringBuilder = [[DTHTMLAttributedStringBuilder alloc] initWithHTML:data options:options documentAttributes:docAttributes]; + + void (^callBackBlock)(DTHTMLElement *element) = [options objectForKey:DTWillFlushBlockCallBack]; + + if (callBackBlock) + { + [stringBuilder setWillFlushCallback:callBackBlock]; + } + + // This needs to be on a seprate line so that ARC can handle releasing the object properly + // return [stringBuilder generatedAttributedString]; shows leak in instruments + id string = [stringBuilder generatedAttributedString]; + + return string; +} + +#pragma mark - Working with Custom HTML Attributes + +- (NSDictionary *)HTMLAttributesAtIndex:(NSUInteger)index +{ + return [self attribute:DTCustomAttributesAttribute atIndex:index effectiveRange:NULL]; +} + +- (NSRange)rangeOfHTMLAttribute:(NSString *)name atIndex:(NSUInteger)index +{ + NSRange rangeSoFar; + + NSDictionary *attributes = [self attribute:DTCustomAttributesAttribute atIndex:index effectiveRange:&rangeSoFar]; + + NSAssert(attributes, @"No custom attribute '%@' at index %d", name, (int)index); + + // check if there is a value for this custom attribute name + id value = [attributes objectForKey:name]; + + if (!attributes || !value) + { + return NSMakeRange(NSNotFound, 0); + } + + // search towards beginning + while (rangeSoFar.location>0) + { + NSRange extendedRange; + attributes = [self attribute:DTCustomAttributesAttribute atIndex:rangeSoFar.location-1 effectiveRange:&extendedRange]; + + id extendedValue = [attributes objectForKey:name]; + + // abort search if key not found or value not identical + if (!extendedValue || ![extendedValue isEqual:value]) + { + break; + } + + rangeSoFar = NSUnionRange(rangeSoFar, extendedRange); + } + + NSUInteger length = [self length]; + + // search towards end + while (NSMaxRange(rangeSoFar) +#elif TARGET_OS_MAC +#import +#endif + +void embeddedObjectDeallocCallback(void *context); +CGFloat embeddedObjectGetAscentCallback(void *context); +CGFloat embeddedObjectGetDescentCallback(void *context); +CGFloat embeddedObjectGetWidthCallback(void *context); +CTRunDelegateRef createEmbeddedObjectRunDelegate(id obj); diff --git a/Pods/DTCoreText/Core/Source/NSAttributedStringRunDelegates.m b/Pods/DTCoreText/Core/Source/NSAttributedStringRunDelegates.m new file mode 100644 index 0000000..232fa39 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSAttributedStringRunDelegates.m @@ -0,0 +1,53 @@ +// +// NSAttributedStringRunDelegates.m +// DTCoreText +// +// Created by Oliver on 14.01.11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "NSAttributedStringRunDelegates.h" +#import "DTTextAttachment.h" + + +void embeddedObjectDeallocCallback(void *context) +{ +} + +CGFloat embeddedObjectGetAscentCallback(void *context) +{ + if ([(__bridge id)context isKindOfClass:[DTTextAttachment class]]) + { + return [(__bridge DTTextAttachment *)context ascentForLayout]; + } + return 0; +} +CGFloat embeddedObjectGetDescentCallback(void *context) +{ + if ([(__bridge id)context isKindOfClass:[DTTextAttachment class]]) + { + return [(__bridge DTTextAttachment *)context descentForLayout]; + } + return 0; +} + +CGFloat embeddedObjectGetWidthCallback(void * context) +{ + if ([(__bridge id)context isKindOfClass:[DTTextAttachment class]]) + { + return [(__bridge DTTextAttachment *)context displaySize].width; + } + return 35; +} + +CTRunDelegateRef createEmbeddedObjectRunDelegate(id obj) +{ + CTRunDelegateCallbacks callbacks; + callbacks.version = kCTRunDelegateCurrentVersion; + callbacks.dealloc = embeddedObjectDeallocCallback; + callbacks.getAscent = embeddedObjectGetAscentCallback; + callbacks.getDescent = embeddedObjectGetDescentCallback; + callbacks.getWidth = embeddedObjectGetWidthCallback; + return CTRunDelegateCreate(&callbacks, (__bridge void *)obj); + return NULL; +} diff --git a/Pods/DTCoreText/Core/Source/NSCharacterSet+HTML.h b/Pods/DTCoreText/Core/Source/NSCharacterSet+HTML.h new file mode 100644 index 0000000..282ab32 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSCharacterSet+HTML.h @@ -0,0 +1,79 @@ +// +// NSCharacterSet+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/15/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import + +/** + Category on NSCharacterSet to create character sets frequently used and relevant to HTML and CSS string manipulations. Each character set is only initialized once. + */ +@interface NSCharacterSet (HTML) + +/** + @name HTML Character Sets + */ + + +/** + Creates an alpha-numeric character set, appropriate for tag names. + @returns An NSCharacterSet with alpha-numeric characters. a-Z, 0-9. + */ ++ (NSCharacterSet *)tagNameCharacterSet; + + +/** + Creates an alpha-numeric character set just as tagNameCharacterSet does but also with colon, dash, and underscore characters, appropriate for tag attribute names. + @returns An NSCharacterSet with alpha-numeric characters and colon :, dash -, and underscore _'. + */ ++ (NSCharacterSet *)tagAttributeNameCharacterSet; + + +/** + Creates a characterset of all whitespace and newline characters that can be ignored between HTML tags + @returns An NSCharacterSet of ignorable characters + */ ++ (NSCharacterSet *)ignorableWhitespaceCharacterSet; + +/** + Creates a character set with the apostrophe character ' (used as single quote agnostic of direction) and double quote character " (agnostic of direction). + @returns An NSCharacterSet with the single quote and double quote characters: ', ". + */ ++ (NSCharacterSet *)quoteCharacterSet; + + +/** + Creates a character set with the characters forward slash / and closing angle bracket aka greater than sign >, then forms the union of this character set with the [whitespace character set](https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSCharacterSet_Class/Reference/Reference.html) which includes space, tab, newline, and nextline characters. Useful to find the end of an attribute. + @returns An NSCharacterSet with the forward slash, closing angle bracket characters, tab, space, newline, and nextline characters. + */ ++ (NSCharacterSet *)nonQuotedAttributeEndCharacterSet; + + +/** + @name CSS Character Sets + */ + +/** + Creates an alpha-numeric character set just as tagNameCharacterSet does but also with the dash and underscore characters. Does not contain the colon character because it will mess up parsing of CSS style attributes. Useful for CSS attribute names. + @returns An NSCharacterSet with alpha-numeric characters, dash, and underscore characters. + */ ++ (NSCharacterSet *)cssStyleAttributeNameCharacterSet; + + +/** + Characterset of characters that make up units in CSS values + @returns An NSCharacterSet + */ ++ (NSCharacterSet *)cssLengthValueCharacterSet; + + +/** + Characterset of characters that make up units in CSS lengths + @returns An NSCharacterSet + */ ++ (NSCharacterSet *)cssLengthUnitCharacterSet; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSCharacterSet+HTML.m b/Pods/DTCoreText/Core/Source/NSCharacterSet+HTML.m new file mode 100644 index 0000000..65c19a5 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSCharacterSet+HTML.m @@ -0,0 +1,139 @@ +// +// NSCharacterSet+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/15/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "NSCharacterSet+HTML.h" +#import "DTCoreTextConstants.h" + +static NSCharacterSet *_tagNameCharacterSet = nil; +static NSCharacterSet *_ignorableWhitespaceCharacterSet = nil; +static NSCharacterSet *_tagAttributeNameCharacterSet = nil; +static NSCharacterSet *_quoteCharacterSet = nil; +static NSCharacterSet *_nonQuotedAttributeEndCharacterSet = nil; +static NSCharacterSet *_cssStyleAttributeNameCharacterSet = nil; +static NSCharacterSet *_cssLengthValueCharacterSet = nil; +static NSCharacterSet *_cssLengthUnitCharacterSet = nil; + + + +@implementation NSCharacterSet (HTML) + ++ (NSCharacterSet *)tagNameCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _tagNameCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"]; + }); + + return _tagNameCharacterSet; +} + ++ (NSCharacterSet *)tagAttributeNameCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _tagAttributeNameCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"-_:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"]; + }); + + return _tagAttributeNameCharacterSet; +} + ++ (NSCharacterSet *)ignorableWhitespaceCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + NSMutableCharacterSet *tmpSet = [NSMutableCharacterSet whitespaceAndNewlineCharacterSet]; + + // remove all special unicode space characters + [tmpSet removeCharactersInString:UNICODE_NON_BREAKING_SPACE]; + [tmpSet removeCharactersInString:UNICODE_OGHAM_SPACE_MARK]; + [tmpSet removeCharactersInString:UNICODE_MONGOLIAN_VOWEL_SEPARATOR]; + [tmpSet removeCharactersInString:UNICODE_EN_QUAD]; + [tmpSet removeCharactersInString:UNICODE_EM_QUAD]; + [tmpSet removeCharactersInString:UNICODE_EN_SPACE]; + [tmpSet removeCharactersInString:UNICODE_EM_SPACE]; + [tmpSet removeCharactersInString:UNICODE_THREE_PER_EM_SPACE]; + [tmpSet removeCharactersInString:UNICODE_FOUR_PER_EM_SPACE]; + [tmpSet removeCharactersInString:UNICODE_SIX_PER_EM_SPACE]; + [tmpSet removeCharactersInString:UNICODE_FIGURE_SPACE]; + [tmpSet removeCharactersInString:UNICODE_PUNCTUATION_SPACE]; + [tmpSet removeCharactersInString:UNICODE_THIN_SPACE]; + [tmpSet removeCharactersInString:UNICODE_HAIR_SPACE]; + [tmpSet removeCharactersInString:UNICODE_ZERO_WIDTH_SPACE]; + [tmpSet removeCharactersInString:UNICODE_NARROW_NO_BREAK_SPACE]; + [tmpSet removeCharactersInString:UNICODE_MEDIUM_MATHEMATICAL_SPACE]; + [tmpSet removeCharactersInString:UNICODE_IDEOGRAPHIC_SPACE]; + [tmpSet removeCharactersInString:UNICODE_ZERO_WIDTH_NO_BREAK_SPACE]; + + _ignorableWhitespaceCharacterSet = [tmpSet copy]; + }); + + return _ignorableWhitespaceCharacterSet; +} + + ++ (NSCharacterSet *)quoteCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _quoteCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"'\""]; + }); + + return _quoteCharacterSet; +} + ++ (NSCharacterSet *)nonQuotedAttributeEndCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + NSMutableCharacterSet *tmpCharacterSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"/>"]; + [tmpCharacterSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + _nonQuotedAttributeEndCharacterSet = [tmpCharacterSet copy]; + }); + + return _nonQuotedAttributeEndCharacterSet; +} + +// NOTE: cannot contain : because otherwise this messes up parsing of CSS style attributes ++ (NSCharacterSet *)cssStyleAttributeNameCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _cssStyleAttributeNameCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"]; + }); + return _cssStyleAttributeNameCharacterSet; +} + + ++ (NSCharacterSet *)cssLengthValueCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _cssLengthValueCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@".0123456789"]; + }); + return _cssLengthValueCharacterSet; +} + ++ (NSCharacterSet *)cssLengthUnitCharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _cssLengthUnitCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"pxtem"]; + }); + return _cssLengthUnitCharacterSet; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSCoder+DTCompatibility.h b/Pods/DTCoreText/Core/Source/NSCoder+DTCompatibility.h new file mode 100644 index 0000000..ee398e7 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSCoder+DTCompatibility.h @@ -0,0 +1,21 @@ +// +// NSCoder+DTCompatibility.h +// DTCoreText +// +// Created by Ryan Johnson on 14/02/19. +// Copyright (c) 2014 Drobnik.com. All rights reserved. +// + +#import "DTCompatibility.h" + +@interface NSCoder (DTCompatibility) + +#if !TARGET_OS_IPHONE +- (void)encodeCGSize:(CGSize)size forKey:(NSString *)key; +- (CGSize)decodeCGSizeForKey:(NSString *)key; +#endif + +- (void)encodeDTEdgeInsets:(DTEdgeInsets)insets forKey:(NSString *)key; +- (DTEdgeInsets)decodeDTEdgeInsetsForKey:(NSString *)key; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSCoder+DTCompatibility.m b/Pods/DTCoreText/Core/Source/NSCoder+DTCompatibility.m new file mode 100644 index 0000000..924a066 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSCoder+DTCompatibility.m @@ -0,0 +1,68 @@ +// +// NSCoder+DTCompatibility.m +// DTCoreText +// +// Created by Ryan Johnson on 14/02/19. +// Copyright (c) 2014 Drobnik.com. All rights reserved. +// + +#import "NSCoder+DTCompatibility.h" + +#if !TARGET_OS_IPHONE +NSString* NSStringFromNSEdgeInsets(NSEdgeInsets insets); +NSEdgeInsets NSEdgeInsetsFromString(NSString *string); +#endif + +@implementation NSCoder (DTCompatibility) + +#if !TARGET_OS_IPHONE +- (void)encodeCGSize:(CGSize)size forKey:(NSString *)key { + [self encodeObject:NSStringFromCGSize(size) forKey:key]; +} + +- (CGSize)decodeCGSizeForKey:(NSString *)key { + return NSSizeToCGSize(NSSizeFromString([self decodeObjectForKey:key])); +} +#endif + +- (void)encodeDTEdgeInsets:(DTEdgeInsets)insets forKey:(NSString *)key { +#if TARGET_OS_IPHONE + [self encodeUIEdgeInsets:insets forKey:key]; +#else + [self encodeObject:NSStringFromNSEdgeInsets(insets) forKey:key]; +#endif +} + +- (DTEdgeInsets)decodeDTEdgeInsetsForKey:(NSString *)key { +#if TARGET_OS_IPHONE + return [self decodeUIEdgeInsetsForKey:key]; +#else + return NSEdgeInsetsFromString([self decodeObjectForKey:key]); +#endif +} + + +@end + +#if !TARGET_OS_IPHONE +NSString* NSStringFromNSEdgeInsets(NSEdgeInsets insets) { + return [NSString stringWithFormat:@"{%f,%f,%f,%f}", insets.top, insets.left, insets.bottom, insets.right]; +} + +NSEdgeInsets NSEdgeInsetsFromString(NSString *string) { + // Cut off curly brackets + string = [string substringWithRange:NSMakeRange(1, string.length - 2)]; + + NSArray *floatStrings = [string componentsSeparatedByString:@","]; + if (floatStrings.count != 4) { + return NSEdgeInsetsMake(0, 0, 0, 0); + } + CGFloat top = [floatStrings[0] floatValue]; + CGFloat left = [floatStrings[1] floatValue]; + CGFloat bottom = [floatStrings[2] floatValue]; + CGFloat right = [floatStrings[3] floatValue]; + + return NSEdgeInsetsMake(top, left, bottom, right); +} + +#endif diff --git a/Pods/DTCoreText/Core/Source/NSDictionary+DTCoreText.h b/Pods/DTCoreText/Core/Source/NSDictionary+DTCoreText.h new file mode 100644 index 0000000..0e50706 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSDictionary+DTCoreText.h @@ -0,0 +1,93 @@ +// +// NSDictionary+DTRichText.h +// DTCoreText +// +// Created by Oliver Drobnik on 7/21/11. +// Copyright 2011 Cocoanetics. All rights reserved. +// + +#import "DTCompatibility.h" + +@class DTCoreTextParagraphStyle; +@class DTCoreTextFontDescriptor; + +/** + Convenience methods for editors dealing with Core Text attribute dictionaries. + */ +@interface NSDictionary (DTCoreText) + +/** + @name Getting State information + */ + +/** + Whether the font in the receiver's attributes is bold. + @returns `YES` if the text has a bold trait + */ +- (BOOL)isBold; + +/** + Whether the font in the receiver's attributes is italic. + @returns `YES` if the text has an italic trait + */ +- (BOOL)isItalic; + +/** + Whether the receiver's attributes contains underlining. + @returns `YES` if the text is underlined + */ +- (BOOL)isUnderline; + +/** + Whether the receiver's attributes contains strike-through. + @returns `YES` if the text is strike-through + */ +- (BOOL)isStrikethrough; + +/** + Whether the receiver's attributes contain a DTTextAttachment + @returns `YES` if ther is an attachment + */ +- (BOOL)hasAttachment; + +/** + The header level of the receiver + @returns The header level (1-6) or 0 if no header level is set + */ +- (NSUInteger)headerLevel; + +/** + @name Getting Style Information + */ + +/** + Retrieves the DTCoreTextParagraphStyle from the receiver's attributes. This supports both `CTParagraphStyle` as well as `NSParagraphStyle` as a possible representation of the text's paragraph style. + @returns The paragraph style + */ +- (DTCoreTextParagraphStyle *)paragraphStyle; + +/** + Retrieves the DTCoreTextFontDescriptor from the receiver's attributes. This supports both `CTFont` as well as `UIFont` as a possible representation of the text's font. + @returns The font descriptor + */ +- (DTCoreTextFontDescriptor *)fontDescriptor; + +/** + Retrieves the foreground color. On iOS as UIColor, on Mac as NSColor. This supports both the CT as well as the NS/UIKit method of specifying the color. If no foreground color is defined in the receiver then black is assumed. + @returns The platform-specific color defined for the foreground + */ +- (DTColor *)foregroundColor; + +/** + Retrieves the background color. On iOS as UIColor, on Mac as NSColor. This supports both the DT as well as the NS/UIKit method of specifying the color. If no background color is defined in the receiver then `nil` is returned + @returns The platform-specific color defined for the background, or `nil` if none is defined + */ +- (DTColor *)backgroundColor; + +/** + The text kerning value + @returns the kerning value + */ +- (CGFloat)kerning; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSDictionary+DTCoreText.m b/Pods/DTCoreText/Core/Source/NSDictionary+DTCoreText.m new file mode 100644 index 0000000..b9eccef --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSDictionary+DTCoreText.m @@ -0,0 +1,266 @@ +// +// NSDictionary+DTCoreText.m +// DTCoreText +// +// Created by Oliver Drobnik on 7/21/11. +// Copyright 2011 Cocoanetics. All rights reserved. +// + + +#import + +#import "NSDictionary+DTCoreText.h" +#import "DTCoreTextFontDescriptor.h" +#import "DTCoreTextConstants.h" +#import "DTCoreTextFunctions.h" +#import "DTCoreTextParagraphStyle.h" + +@implementation NSDictionary (DTCoreText) + +- (BOOL)isBold +{ + DTCoreTextFontDescriptor *desc = [self fontDescriptor]; + + return desc.boldTrait; +} + +- (BOOL)isItalic +{ + DTCoreTextFontDescriptor *desc = [self fontDescriptor]; + + return desc.italicTrait; +} + +- (BOOL)isUnderline +{ + NSNumber *underlineStyle = [self objectForKey:(id)kCTUnderlineStyleAttributeName]; + + if (underlineStyle) + { + return [underlineStyle integerValue] != kCTUnderlineStyleNone; + } + + if (DTCoreTextModernAttributesPossible()) + { + underlineStyle = [self objectForKey:NSUnderlineStyleAttributeName]; + + if (underlineStyle) + { + return [underlineStyle integerValue] != NSUnderlineStyleNone; + } + } + + return NO; +} + +- (BOOL)isStrikethrough +{ + NSNumber *strikethroughStyle = [self objectForKey:DTStrikeOutAttribute]; + + if (strikethroughStyle) + { + return [strikethroughStyle boolValue]; + } + + if (DTCoreTextModernAttributesPossible()) + { + strikethroughStyle = [self objectForKey:NSStrikethroughStyleAttributeName]; + + if (strikethroughStyle) + { + return [strikethroughStyle boolValue]; + } + } + + return NO; +} + +- (NSUInteger)headerLevel +{ + NSNumber *headerLevelNum = [self objectForKey:DTHeaderLevelAttribute]; + + return [headerLevelNum integerValue]; +} + +- (BOOL)hasAttachment +{ + id attachment = [self objectForKey:NSAttachmentAttributeName]; + + if (!attachment) + { + // could also be modern NS-style attachment + attachment = [self objectForKey:@"NSAttachment"]; + } + + return attachment!=nil; +} + +- (DTCoreTextParagraphStyle *)paragraphStyle +{ + if (DTCoreTextModernAttributesPossible()) + { + NSParagraphStyle *nsParagraphStyle = [self objectForKey:NSParagraphStyleAttributeName]; + + if (nsParagraphStyle && [nsParagraphStyle isKindOfClass:[NSParagraphStyle class]]) + { + return [DTCoreTextParagraphStyle paragraphStyleWithNSParagraphStyle:nsParagraphStyle]; + } + } + + CTParagraphStyleRef ctParagraphStyle = (__bridge CTParagraphStyleRef)[self objectForKey:(id)kCTParagraphStyleAttributeName]; + + if (ctParagraphStyle) + { + return [DTCoreTextParagraphStyle paragraphStyleWithCTParagraphStyle:ctParagraphStyle]; + } + + return nil; +} + +- (DTCoreTextFontDescriptor *)fontDescriptor +{ + CTFontRef ctFont = (__bridge CTFontRef)[self objectForKey:(id)kCTFontAttributeName]; + + // on Mac NSFont and CTFont are toll-free bridged, so this works there as well + + if (ctFont) + { + return [DTCoreTextFontDescriptor fontDescriptorForCTFont:ctFont]; + } + + if (DTCoreTextModernAttributesPossible()) + { +#if TARGET_OS_IPHONE + UIFont *uiFont = [self objectForKey:NSFontAttributeName]; + + if (!uiFont) + { + return nil; + } + + // convert font + ctFont = DTCTFontCreateWithUIFont(uiFont); + + if (ctFont) + { + DTCoreTextFontDescriptor *fontDescriptor = [DTCoreTextFontDescriptor fontDescriptorForCTFont:ctFont]; + + CFRelease(ctFont); + + return fontDescriptor; + } +#endif + } + + return nil; +} + +- (DTColor *)foregroundColor +{ + if (DTCoreTextModernAttributesPossible()) + { + DTColor *color = [self objectForKey:NSForegroundColorAttributeName]; + + if (color) + { + return color; + } + } + + CGColorRef cgColor = (__bridge CGColorRef)[self objectForKey:(id)kCTForegroundColorAttributeName]; + + if (cgColor) + { +#if DTCORETEXT_FIX_14684188 + // test if this a valid color, workaround for iOS 7 bug + size_t componentCount = CGColorGetNumberOfComponents(cgColor); + + if (componentCount>0 && componentCount<=4) + { + return [DTColor colorWithCGColor:cgColor]; + } +#else + return [DTColor colorWithCGColor:cgColor]; +#endif + } + + // default foreground is black + return [DTColor blackColor]; +} + +- (DTColor *)backgroundColor +{ + CGColorRef cgColor = (__bridge CGColorRef)[self objectForKey:DTBackgroundColorAttribute]; + + if (cgColor) + { + return [DTColor colorWithCGColor:cgColor]; + } + + if (DTCoreTextModernAttributesPossible()) + { + DTColor *color = [self objectForKey:NSBackgroundColorAttributeName]; + + if (color) + { + return color; + } + } + + // default background is nil + return nil; +} + +- (CGFloat)kerning +{ + if (DTCoreTextModernAttributesPossible()) + { + NSNumber *kerningNum = [self objectForKey:NSKernAttributeName]; + + if (kerningNum) + { + return [kerningNum floatValue]; + } + } + + NSNumber *kerningNum = [self objectForKey:(id)kCTKernAttributeName]; + + return [kerningNum floatValue]; +} + +- (DTColor *)backgroundStrokeColor +{ + CGColorRef cgColor = (__bridge CGColorRef)[self objectForKey:DTBackgroundStrokeColorAttribute]; + + if (cgColor) + { + return [DTColor colorWithCGColor:cgColor]; + } + return nil; +} + +- (CGFloat)backgroundStrokeWidth +{ + NSNumber *num = [self objectForKey:DTBackgroundStrokeWidthAttribute]; + + if (num) + { + return [num floatValue]; + } + + return 0.0f; +} + +- (CGFloat)backgroundCornerRadius +{ + NSNumber *num = [self objectForKey:DTBackgroundCornerRadiusAttribute]; + + if (num) + { + return [num floatValue]; + } + + return 0.0f; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSMutableAttributedString+HTML.h b/Pods/DTCoreText/Core/Source/NSMutableAttributedString+HTML.h new file mode 100644 index 0000000..2f6696f --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSMutableAttributedString+HTML.h @@ -0,0 +1,57 @@ +// +// NSMutableAttributedString+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextConstants.h" + +@class DTCoreTextParagraphStyle, DTCoreTextFontDescriptor; + +/** + Methods for appending `NSString` instances to mutable attributed strings + */ +@interface NSMutableAttributedString (HTML) + +/** + Appends a string with the same attributes as this string to this string. + + If the last character of the receiver contains a placeholder for a it is removed from the appended string. Also fields (e.g. list prefixes) are not extended + @param string The string to be appended to this string. */ +- (void)appendString:(NSString *)string; + +/** + Appends a string with a given paragraph style and font to this string. + @param string The string to be appended to this string. + @param paragraphStyle Paragraph style to be attributed to the appended string. + @param fontDescriptor Font descriptor to be attributed to the appended string. */ +- (void)appendString:(NSString *)string withParagraphStyle:(DTCoreTextParagraphStyle *)paragraphStyle fontDescriptor:(DTCoreTextFontDescriptor *)fontDescriptor; + +/** + Adds the paragraph terminator `\n` and makes sure that the previous font and paragraph styles extend to include it + */ +- (void)appendEndOfParagraph; + +/** + @name Working with Custom HTML Attributes + */ + +/** + Adds the custom HTML attributes with the given value on the given range, optionally replacing occurences of an attribute with the same name. + @param name The name of the custom HTML attribute + @param value The value to set for the custom attribute + @param range The range to add the custom attribute for + @param replaceExisting `YES` if ranges that have an attribute with the same name should be replaced. With `NO` the attribute is only added for ranges where there is no attribute with the given name + */ +- (void)addHTMLAttribute:(NSString *)name value:(id)value range:(NSRange)range replaceExisting:(BOOL)replaceExisting; + +/** + Adds the custom HTML attributes with the given value from the given range. + @param name The name of the custom HTML attribute + @param range The range to add the custom attribute for + */ +- (void)removeHTMLAttribute:(NSString *)name range:(NSRange)range; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSMutableAttributedString+HTML.m b/Pods/DTCoreText/Core/Source/NSMutableAttributedString+HTML.m new file mode 100644 index 0000000..5a78641 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSMutableAttributedString+HTML.m @@ -0,0 +1,291 @@ +// +// NSMutableAttributedString+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "NSMutableAttributedString+HTML.h" + +#import "DTCoreText.h" + +@implementation NSMutableAttributedString (HTML) + + +// appends a plain string extending the attributes at this position +- (void)appendString:(NSString *)string +{ + NSParameterAssert(string); + + NSUInteger length = [self length]; + NSAttributedString *appendString = nil; + + if (length) + { + // get attributes at end of self + NSMutableDictionary *attributes = [[self attributesAtIndex:length-1 effectiveRange:NULL] mutableCopy]; + + // we need to remove the image placeholder (if any) to prevent duplication + [attributes removeObjectForKey:NSAttachmentAttributeName]; + [attributes removeObjectForKey:(id)kCTRunDelegateAttributeName]; + + // we also remove field attribute, because appending plain strings should never extend an field + [attributes removeObjectForKey:DTFieldAttribute]; + + // create a temp attributed string from the appended part + appendString = [[NSAttributedString alloc] initWithString:string attributes:attributes]; + } + else + { + // no attributes to extend + appendString = [[NSAttributedString alloc] initWithString:string]; + } + + [self appendAttributedString:appendString]; +} + +- (void)appendString:(NSString *)string withParagraphStyle:(DTCoreTextParagraphStyle *)paragraphStyle fontDescriptor:(DTCoreTextFontDescriptor *)fontDescriptor +{ + NSUInteger selfLengthBefore = [self length]; + + [self.mutableString appendString:string]; + + NSRange appendedStringRange = NSMakeRange(selfLengthBefore, [string length]); + + if (paragraphStyle || fontDescriptor) + { + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + + if (paragraphStyle) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + NSParagraphStyle *style = [paragraphStyle NSParagraphStyle]; + [attributes setObject:style forKey:NSParagraphStyleAttributeName]; + } + else +#endif + { + CTParagraphStyleRef newParagraphStyle = [paragraphStyle createCTParagraphStyle]; + [attributes setObject:CFBridgingRelease(newParagraphStyle) forKey:(id)kCTParagraphStyleAttributeName]; + } + } + + if (fontDescriptor) + { + CTFontRef newFont = [fontDescriptor newMatchingFont]; + + if (newFont) + { +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES && TARGET_OS_IPHONE + if (___useiOS6Attributes) + { + // convert to UIFont + UIFont *uiFont = [UIFont fontWithCTFont:newFont]; + [attributes setObject:uiFont forKey:NSFontAttributeName]; + + CFRelease(newFont); + } + else +#endif + { + [attributes setObject:CFBridgingRelease(newFont) forKey:(id)kCTFontAttributeName]; + } + } + } + + // Replace attributes + [self setAttributes:attributes range:appendedStringRange]; + } + else + { + // Remove attributes + [self setAttributes:[NSDictionary dictionary] range:appendedStringRange]; + } +} + +- (void)appendEndOfParagraph +{ + NSUInteger length = [self length]; + + NSAssert(length, @"Cannot append end of paragraph to empty string"); + + NSRange effectiveRange; + NSDictionary *attributes = [self attributesAtIndex:length-1 effectiveRange:&effectiveRange]; + + + NSMutableDictionary *appendAttributes = [NSMutableDictionary dictionary]; + + +#if DTCORETEXT_SUPPORT_NS_ATTRIBUTES + if (___useiOS6Attributes) + { + id font = [attributes objectForKey:NSFontAttributeName]; + + if (font) + { + [appendAttributes setObject:font forKey:NSFontAttributeName]; + } + + id paragraphStyle = [attributes objectForKey:NSParagraphStyleAttributeName]; + + if (paragraphStyle) + { + [appendAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName]; + } + } + else +#endif + { + CTFontRef font = (__bridge CTFontRef)[attributes objectForKey:(id)kCTFontAttributeName]; + + if (font) + { + [appendAttributes setObject:(__bridge id)(font) forKey:(id)kCTFontAttributeName]; + } + + CTParagraphStyleRef paragraphStyle = (__bridge CTParagraphStyleRef)[attributes objectForKey:(id)kCTParagraphStyleAttributeName]; + + if (paragraphStyle) + { + [appendAttributes setObject:(__bridge id)(paragraphStyle) forKey:(id)kCTParagraphStyleAttributeName]; + } + } + + // transfer blocks + NSArray *blocks = [attributes objectForKey:DTTextBlocksAttribute]; + + if (blocks) + { + [appendAttributes setObject:blocks forKey:DTTextBlocksAttribute]; + } + + // transfer lists + NSArray *lists = [attributes objectForKey:DTTextListsAttribute]; + + if (lists) + { + [appendAttributes setObject:lists forKey:DTTextListsAttribute]; + } + + // transfer foreground color + id foregroundColor = [attributes objectForKey:(id)kCTForegroundColorAttributeName]; + + if (foregroundColor) + { +#if TARGET_OS_IPHONE + if ([foregroundColor isKindOfClass:[UIColor class]]) + { + [appendAttributes setObject:(id)[foregroundColor CGColor] forKey:(id)kCTForegroundColorAttributeName]; + } + else +#endif + { + [appendAttributes setObject:foregroundColor forKey:(id)kCTForegroundColorAttributeName]; + } + } + + NSAttributedString *newlineString = [[NSAttributedString alloc] initWithString:@"\n" attributes:appendAttributes]; + [self appendAttributedString:newlineString]; +} + + +#pragma mark - Working with Custom HTML Attributes + +- (void)addHTMLAttribute:(NSString *)name value:(id)value range:(NSRange)range replaceExisting:(BOOL)replaceExisting +{ + NSRange safeRange = NSIntersectionRange(range, NSMakeRange(0, [self length])); + + [self beginEditing]; + + NSMutableIndexSet *indexesToSetThis = [NSMutableIndexSet indexSetWithIndexesInRange:range]; + + [self enumerateAttribute:DTCustomAttributesAttribute inRange:safeRange options:0 usingBlock:^(NSDictionary *dictionary, NSRange effectiveRange, BOOL *stop) { + + id existingValue = [dictionary objectForKey:name]; + + if (existingValue && !replaceExisting) + { + // exempt this range + [indexesToSetThis removeIndexesInRange:effectiveRange]; + } + }]; + + // now our mutable index set should contain the ranges where we want to set this + + [indexesToSetThis enumerateRangesInRange:safeRange options:0 usingBlock:^(NSRange indexRange, BOOL *stopEnumerateRanges) { + + // for each such range, we need to add this to the attribute + [self enumerateAttribute:DTCustomAttributesAttribute inRange:indexRange options:0 usingBlock:^(NSDictionary *dictionary, NSRange effectiveRange, BOOL *stopEnumerateAttribute) { + + if (dictionary) + { + // need to make it mutable and add the value + NSMutableDictionary *mutableDictionary = [dictionary mutableCopy]; + [mutableDictionary setObject:value forKey:name]; + + // substitute attribute +#if DTCORETEXT_NEEDS_ATTRIBUTE_REPLACEMENT_LEAK_FIX + if (NSFoundationVersionNumber <= NSFoundationVersionNumber10_6_8) // less than OS X 10.7 and less than iOS 5 + { + // remove old (works around iOS 4.3 leak) + [self removeAttribute:DTCustomAttributesAttribute range:effectiveRange]; + } +#endif + + [self addAttribute:DTCustomAttributesAttribute value:[mutableDictionary copy] range:effectiveRange]; + } + else + { + // create new dictionary with the value + dictionary = [NSDictionary dictionaryWithObject:value forKey:name]; + [self addAttribute:DTCustomAttributesAttribute value:dictionary range:effectiveRange]; + } + }]; + }]; + + [self endEditing]; +} + +- (void)removeHTMLAttribute:(NSString *)name range:(NSRange)range +{ + NSRange safeRange = NSIntersectionRange(range, NSMakeRange(0, [self length])); + + [self beginEditing]; + + [self enumerateAttribute:DTCustomAttributesAttribute inRange:safeRange options:0 usingBlock:^(NSDictionary *dictionary, NSRange effectiveRange, BOOL *stop) { + + id existingValue = [dictionary objectForKey:name]; + + if (existingValue) + { + // need to make it mutable and remove the value + NSMutableDictionary *mutableDictionary = [dictionary mutableCopy]; + [mutableDictionary removeObjectForKey:name]; + + // substitute attribute + + // only re-add modified dictionary if it is not empty + if ([mutableDictionary count]) + { + if (NSFoundationVersionNumber <= NSFoundationVersionNumber10_6_8) // less than OS X 10.7 and less than iOS 5 + { + // remove old (works around iOS 4.3 leak) + [self removeAttribute:DTCustomAttributesAttribute range:effectiveRange]; + } + + [self addAttribute:DTCustomAttributesAttribute value:[mutableDictionary copy] range:effectiveRange]; + } + else + { + [self removeAttribute:DTCustomAttributesAttribute range:effectiveRange]; + } + } + }]; + + [self endEditing]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSMutableString+HTML.h b/Pods/DTCoreText/Core/Source/NSMutableString+HTML.h new file mode 100644 index 0000000..1078308 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSMutableString+HTML.h @@ -0,0 +1,20 @@ +// +// NSMutableString+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 01.02.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + + +/** + Categories needed for modifying mutable strings, as needed for DTCoreText. + */ +@interface NSMutableString (HTML) + +/** + Removes the trailing whitespace from the receiver. + */ +- (void)removeTrailingWhitespace; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSMutableString+HTML.m b/Pods/DTCoreText/Core/Source/NSMutableString+HTML.m new file mode 100644 index 0000000..461cbc4 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSMutableString+HTML.m @@ -0,0 +1,37 @@ +// +// NSMutableString+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 01.02.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "NSMutableString+HTML.h" + + +#define IS_WHITESPACE(_c) (_c == ' ' || _c == '\t' || _c == 0xA || _c == 0xB || _c == 0xC || _c == 0xD || _c == 0x85) + +@implementation NSMutableString (HTML) + +- (void)removeTrailingWhitespace +{ + NSUInteger length = self.length; + + NSInteger lastIndex = length-1; + NSInteger index = lastIndex; + NSInteger whitespaceLength = 0; + + while (index>=0 && IS_WHITESPACE([self characterAtIndex:index])) + { + index--; + whitespaceLength++; + } + + // do the removal once for all whitespace characters + if (whitespaceLength) + { + [self deleteCharactersInRange:NSMakeRange(index+1, whitespaceLength)]; + } +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSScanner+HTML.h b/Pods/DTCoreText/Core/Source/NSScanner+HTML.h new file mode 100644 index 0000000..72f54ea --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSScanner+HTML.h @@ -0,0 +1,53 @@ +// +// NSScanner+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/12/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +@class DTColor; + +/** + Extensions for NSScanner to deal with HTML-specific parsing, primarily CSS-related things + */ +@interface NSScanner (HTML) + +/** + @name Working with CSS + */ + +/** + Scans for a CSS attribute used in CSS style sheets + @param name An optional output parameter that will contain the name of the scanned attribute if successful + @param value An optional output parameter that will contain the value of the scanned attribute if successful. This value may be a string or an array. + @returns `YES` if an URL String could be scanned + */ +- (BOOL)scanCSSAttribute:(NSString * __autoreleasing*)name value:(id __autoreleasing*)value; + + +/** + Scans for URLs used in CSS style sheets + @param urlString An optional output parameter that will contain the scanned URL string if successful + @returns `YES` if an URL String could be scanned + */ +- (BOOL)scanCSSURL:(NSString * __autoreleasing*)urlString; + + +/** + Scans for a typical HTML color, typically either #FFFFFF, rgb(255,255,255) or a HTML color name. + @param color An optional output parameter that will contain the scanned color if successful + @returns `YES` if a color could be scanned + */ +- (BOOL)scanHTMLColor:(DTColor * __autoreleasing*)color; + +/** + Scans for a typical HTML color, typically either #FFFFFF, rgb(255,255,255) or a HTML color name. + @param color An optional output parameter that will contain the scanned color if successful + @param name An optional output parameter that will contain the HTML color string + @returns `YES` if a color could be scanned + */ +- (BOOL)scanHTMLColor:(DTColor * __autoreleasing*)color HTMLName:(NSString * __autoreleasing*)name; + +@end + diff --git a/Pods/DTCoreText/Core/Source/NSScanner+HTML.m b/Pods/DTCoreText/Core/Source/NSScanner+HTML.m new file mode 100644 index 0000000..3a8bfc1 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSScanner+HTML.m @@ -0,0 +1,306 @@ +// +// NSScanner+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/12/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "NSScanner+HTML.h" +#import "NSCharacterSet+HTML.h" +#import "DTColorFunctions.h" + +@implementation NSScanner (HTML) + +#pragma mark CSS + +// scan a single element from a style list +- (BOOL)scanCSSAttribute:(NSString * __autoreleasing*)name value:(id __autoreleasing*)value +{ + NSString *attrName = nil; + + NSInteger initialScanLocation = [self scanLocation]; + + NSCharacterSet *whiteCharacterSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + + NSMutableCharacterSet *nonWhiteCharacterSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy]; + [nonWhiteCharacterSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@";"]]; + [nonWhiteCharacterSet invert]; + + NSMutableCharacterSet *nonWhiteCommaCharacterSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy]; + [nonWhiteCommaCharacterSet formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@";,"]]; + [nonWhiteCommaCharacterSet invert]; + + + // alphanumeric plus - + NSCharacterSet *cssStyleAttributeNameCharacterSet = [NSCharacterSet cssStyleAttributeNameCharacterSet]; + + if (![self scanCharactersFromSet:cssStyleAttributeNameCharacterSet intoString:&attrName]) + { + return NO; + } + + // skip whitespace + [self scanCharactersFromSet:whiteCharacterSet intoString:NULL]; + + // expect : + if (![self scanString:@":" intoString:NULL]) + { + [self setScanLocation:initialScanLocation]; + return NO; + } + + // skip whitespace + [self scanCharactersFromSet:whiteCharacterSet intoString:NULL]; + + NSMutableArray *results = [NSMutableArray array]; + BOOL nextIterationAddsNewEntry = YES; + + while (![self isAtEnd] && ![self scanString:@";" intoString:NULL]) + { + // skip whitespace + [self scanCharactersFromSet:whiteCharacterSet intoString:NULL]; + + NSString *quote = nil; + if ([self scanCharactersFromSet:[NSCharacterSet quoteCharacterSet] intoString:"e]) + { + NSString *quotedValue = nil; + + // attribute is quoted + if (![self scanUpToString:quote intoString:"edValue]) + { + [self setScanLocation:initialScanLocation]; + return NO; + } + else + { + if (nextIterationAddsNewEntry) + { + [results addObject:quotedValue]; + nextIterationAddsNewEntry = NO; + } + else + { + quotedValue = [NSString stringWithFormat:@"%@ %@%@%@", [results lastObject], quote, quotedValue, quote]; + [results removeLastObject]; + [results addObject:quotedValue]; + } + } + + // skip ending quote + [self scanString:quote intoString:NULL]; + + //TODO: decode unicode sequences like "\2022" + } + else + { + // attribute is not quoted, we append elements until we find a ; or the string is at the end + NSString *valueString = nil; + + if ([self scanString:@"rgb(" intoString:&valueString]) + { + if ([valueString isEqualToString:@"rgb("]) + { + [self scanUpToString:@";" intoString:&valueString]; + NSString * formattedRGBString = [NSString stringWithFormat:@"rgb(%@", valueString]; + + if (nextIterationAddsNewEntry) + { + [results addObject:formattedRGBString]; + nextIterationAddsNewEntry = NO; + } + else + { + valueString = [NSString stringWithFormat:@"%@ %@", [results lastObject], formattedRGBString]; + [results removeLastObject]; + [results addObject:valueString]; + } + } + } + else if ([self scanString:@"," intoString:&valueString]) + { + BOOL isStringOnlyCSSProperty = NO; + + if (![valueString isEqualToString:@","]) + { + [results addObject:valueString]; + } + else if ([attrName isEqualToString:@"font"] || + ([attrName rangeOfString:@"color"].location != NSNotFound) || + ([attrName rangeOfString:@"shadow"].location != NSNotFound) || + ([attrName rangeOfString:@"background"].location != NSNotFound)) + { + valueString = [NSString stringWithFormat:@"%@%@", [results lastObject], valueString]; + [results removeLastObject]; + [results addObject:valueString]; + + isStringOnlyCSSProperty = YES; + } + + if ([valueString isEqualToString:@","] && !isStringOnlyCSSProperty) + { + nextIterationAddsNewEntry = YES; + } + } + else if ([self scanCharactersFromSet:nonWhiteCommaCharacterSet intoString:&valueString]) + { + if ([valueString length] && ![valueString isEqualToString:@","]) + { + if (nextIterationAddsNewEntry) { + [results addObject:valueString]; + nextIterationAddsNewEntry = NO; + } else { + valueString = [NSString stringWithFormat:@"%@ %@", [results lastObject], valueString]; + [results removeLastObject]; + [results addObject:valueString]; + } + } + } + } + + // skip whitespace + [self scanCharactersFromSet:whiteCharacterSet intoString:NULL]; + } + + // Success + if (name) + { + *name = attrName; + } + + if (value) + { + if (results.count == 0) { + *value = @""; + } else if (results.count == 1) { + *value = [results objectAtIndex:0]; + } else { + *value = results; + } + } + + return YES; +} + +/* + + Source: http://www.w3.org/TR/CSS1/#url + + The format of a URL value is 'url(' followed by optional white space followed by an optional single quote (') or double quote (") character followed by the URL itself (as defined in [11]) followed by an optional single quote (') or double quote (") character followed by optional whitespace followed by ')'. Quote characters that are not part of the URL itself must be balanced. + + Parentheses, commas, whitespace characters, single quotes (') and double quotes (") appearing in a URL must be escaped with a backslash: '\(', '\)', '\,'. + + Partial URLs are interpreted relative to the source of the style sheet, not relative to the document: +*/ + +// NOTE: Simplified, we assume that there are no quotes in the URL + +- (BOOL)scanCSSURL:(NSString * __autoreleasing*)urlString +{ + if (![self scanString:@"url(" intoString:NULL]) + { + return NO; + } + + + NSCharacterSet *quoteCharacterSet = [NSCharacterSet quoteCharacterSet]; + NSString *quote; + NSString *attrValue; + + if ([self scanCharactersFromSet:quoteCharacterSet intoString:"e]) + { + if ([quote length]==1) + { + [self scanUpToString:quote intoString:&attrValue]; + [self scanString:quote intoString:NULL]; + } + else + { + // most likely e.g. href="" + attrValue = @""; + } + + // decode HTML entities + attrValue = [attrValue stringByReplacingHTMLEntities]; + } + else + { + // non-quoted attribute, ends at ) + if ([self scanUpToString:@")" intoString:&attrValue]) + { + // decode HTML entities + attrValue = [attrValue stringByReplacingHTMLEntities]; + } + } + + if (urlString) + { + *urlString = attrValue; + } + + return YES; +} + +- (BOOL)scanHTMLColor:(DTColor * __autoreleasing*)color +{ + return [self scanHTMLColor:color HTMLName:NULL]; +} + +- (BOOL)scanHTMLColor:(DTColor * __autoreleasing*)color HTMLName:(NSString * __autoreleasing*)name +{ + NSUInteger indexBefore = [self scanLocation]; + + NSString *colorName = nil; + + NSMutableCharacterSet *tokenEndSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy]; + [tokenEndSet addCharactersInString:@","]; + + if ([self scanString:@"#" intoString:NULL]) + { + self.scanLocation = indexBefore; + + [self scanUpToCharactersFromSet:tokenEndSet intoString:&colorName]; + } + else if ([self scanString:@"rgb" intoString:NULL]) + { + if ([self scanUpToString:@")" intoString:NULL]) + { + self.scanLocation++; + colorName = [[self string] substringWithRange:NSMakeRange(indexBefore, self.scanLocation - indexBefore)]; + + colorName = [colorName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } + } + else + { + // could be a plain html color name + [self scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet] intoString:&colorName]; + } + + DTColor *foundColor = nil; + + if (colorName) + { + foundColor = DTColorCreateWithHTMLName(colorName); + } + + if (!foundColor) + { + self.scanLocation = indexBefore; + return NO; + } + + if (color) + { + *color = foundColor; + } + + if (name) { + *name = colorName; + } + + return YES; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSString+CSS.h b/Pods/DTCoreText/Core/Source/NSString+CSS.h new file mode 100755 index 0000000..a7d9381 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSString+CSS.h @@ -0,0 +1,57 @@ +// +// NSString+CSS.h +// DTCoreText +// +// Created by Oliver Drobnik on 31.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +@class DTColor; + +/** + Methods to make dealing with CSS strings easier. Extract shadows from this string, extract CSS styles found in this string, extract the pixel size of a CSS measurement relative to the current text size, and extract the CSS pixel measurement of this string. + */ +@interface NSString (CSS) + +/** + Examine a string for all CSS styles that are applied to it and return a dictionary of those styles. Implemented using scanCSSAttribute: which is defined in NSScanner+HTML.h. + @returns A dictionary of strings containing the CSS styles which are applied to this string. + */ +- (NSDictionary *)dictionaryOfCSSStyles; + +/** + Determines if the receiver contains a CSS length value, that is a number with optional period and unit (em, pt, px). + @returns `YES` if this is a CSS length value + */ +- (BOOL)isCSSLengthValue; + +/** + Calculates a pixel-based length from the receiver based on the current text size in pixels. Used in DTHTMLElement. + @param textSize The current size which the CSS size is relative to. + @param textScale The factor by which absolute sizes are scaled. Set to 1.0f to keep the original value. + @returns A float that is the textSize + */ +- (CGFloat)pixelSizeOfCSSMeasureRelativeToCurrentTextSize:(CGFloat)textSize textScale:(CGFloat)textScale; + +/** + Decodes edge inset values from the CSS attribute string. This is used for margin andpadding which might have varying number of elements. + @param textSize The current size which the CSS size is relative to. + @param textScale The factor by which absolute sizes are scaled. Set to 1.0f to keep the original value. + @returns The edge insets that this describes + */ +- (DTEdgeInsets)DTEdgeInsetsRelativeToCurrentTextSize:(CGFloat)textSize textScale:(CGFloat)textScale; + +/** + Parse CSS shadow styles, consisting of color, blur, and offset, out of this string. The input string must be comma delimited in the format: ? ? where the third length and the color are not required per CSS shadows. To calculate the sizes of the blur and offset pixelSizeOfCSSMeasureRelativeToCurrentTextSize is used. Used in DTHTMLElement. + @param textSize In order to determine the shadow offset we need what text size it will be displayed at. + @param color Used if no shadow attribute color is found. + @returns An array of dictionaries, each of which is a shadow consisting of color, blur, and offset keys value pairs. + */ +- (NSArray *)arrayOfCSSShadowsWithCurrentTextSize:(CGFloat)textSize currentColor:(DTColor *)color; + +/** + Decodes a content attribute which might contained unicode sequences. + */ +- (NSString *)stringByDecodingCSSContentAttribute; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSString+CSS.m b/Pods/DTCoreText/Core/Source/NSString+CSS.m new file mode 100644 index 0000000..95840a1 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSString+CSS.m @@ -0,0 +1,428 @@ +// +// NSString+CSS.m +// DTCoreText +// +// Created by Oliver Drobnik on 31.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "NSString+CSS.h" + +@implementation NSString (CSS) + +#pragma mark CSS + +- (NSDictionary *)dictionaryOfCSSStyles +{ + // font-size:14px; + NSScanner *scanner = [NSScanner scannerWithString:self]; + + NSString *name = nil; + NSString *value = nil; + + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + @autoreleasepool + { + while ([scanner scanCSSAttribute:&name value:&value]) + { + [tmpDict setObject:value forKey:name]; + } + } + + // converting to non-mutable costs 37.5% of method + // return [NSDictionary dictionaryWithDictionary:tmpDict]; + return tmpDict; +} + +- (BOOL)isCSSLengthValue +{ + NSScanner *scanner = [NSScanner scannerWithString:self]; + + NSString *numberStr; + + if (![scanner scanCharactersFromSet:[NSCharacterSet cssLengthValueCharacterSet] intoString:&numberStr]) + { + return NO; + } + + NSString *numberUnitStr; + if (![scanner scanCharactersFromSet:[NSCharacterSet cssLengthUnitCharacterSet] intoString:&numberUnitStr]) + { + return YES; + } + + if ([numberUnitStr isEqualToString:@"em"] | [numberUnitStr isEqualToString:@"px"] | [numberUnitStr isEqualToString:@"pt"]) + { + return YES; + } + + return NO; +} + +- (CGFloat)pixelSizeOfCSSMeasureRelativeToCurrentTextSize:(CGFloat)textSize textScale:(CGFloat)textScale +{ + NSUInteger stringLength = [self length]; + unichar *_characters = calloc(stringLength, sizeof(unichar)); + [self getCharacters:_characters range:NSMakeRange(0, stringLength)]; + + CGFloat value = 0; + + BOOL commaSeen = NO; + BOOL negative = NO; + NSUInteger digitsPastComma = 0; + + NSUInteger i=0; + + for (; i='0' && ch<='9') + { + float digit = (float)(ch-'0'); + value *= 10.0f; + value += digit; + + if (commaSeen) + { + digitsPastComma++; + } + } + else if (ch=='.') + { + commaSeen = YES; + } + else if (ch=='-') + { + negative = YES; + } + else + { + // non-numeric character + break; + } + } + + if (commaSeen) + { + value /= powf(10.0f, digitsPastComma); + } + + // skip whitespace + while (i ? + + if ([scanner scanUpToCharactersFromSet:tokenEndSet intoString:&offsetXString]) + { + if ([scanner scanUpToCharactersFromSet:tokenEndSet intoString:&offsetYString]) + { + // blur is optional + [scanner scanUpToCharactersFromSet:tokenEndSet intoString:&blurString]; + + + CGFloat offset_x = [offsetXString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:textSize textScale:1.0f]; + CGFloat offset_y = [offsetYString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:textSize textScale:1.0f]; + CGSize offset = CGSizeMake(offset_x, offset_y); + CGFloat blur = [blurString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:textSize textScale:1.0f]; + + NSValue *offsetValue; +#if TARGET_OS_IPHONE + offsetValue = [NSValue valueWithCGSize:offset]; +#else + offsetValue = [NSValue valueWithSize:offset]; +#endif + + NSDictionary *shadowDict = [NSDictionary dictionaryWithObjectsAndKeys:offsetValue, @"Offset", + DTNSNumberFromCGFloat(blur), @"Blur", + shadowColor, @"Color", nil]; + + [tmpArray addObject:shadowDict]; + } + } + } + else + { + // format: ? ? + + if ([scanner scanUpToCharactersFromSet:tokenEndSet intoString:&offsetXString]) + { + if ([scanner scanUpToCharactersFromSet:tokenEndSet intoString:&offsetYString]) + { + // blur is optional + if (![scanner scanHTMLColor:&shadowColor]) + { + if ([scanner scanUpToCharactersFromSet:tokenEndSet intoString:&blurString]) + { + if (![scanner scanHTMLColor:&shadowColor]) + { + // invalid color, we ignore this color + } + } + } + + if (!shadowColor) + { + // color is same as color attribute of style + shadowColor = color; + } + + CGFloat offset_x = [offsetXString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:textSize textScale:1.0f]; + CGFloat offset_y = [offsetYString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:textSize textScale:1.0f]; + CGSize offset = CGSizeMake(offset_x, offset_y); + CGFloat blur = [blurString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:textSize textScale:1.0f]; + + NSValue *offsetValue; +#if TARGET_OS_IPHONE + offsetValue = [NSValue valueWithCGSize:offset]; +#else + offsetValue = [NSValue valueWithSize:offset]; +#endif + + NSDictionary *shadowDict = [NSDictionary dictionaryWithObjectsAndKeys:offsetValue, @"Offset", + DTNSNumberFromCGFloat(blur), @"Blur", + shadowColor, @"Color", nil]; + + [tmpArray addObject:shadowDict]; + } + } + } + + // now there should be a comma + if (![scanner scanString:@"," intoString:NULL]) + { + break; + } + } + + // only return array if not empty + if ([tmpArray count]) + { + return tmpArray; + } + + return nil; +} + +- (NSString *)stringByDecodingCSSContentAttribute +{ + NSUInteger length = [self length]; + + unichar *characters = calloc(length, sizeof(unichar)); + unichar *final = calloc(length, sizeof(unichar)); + + [self getCharacters:characters range:NSMakeRange(0, length)]; + + NSUInteger outChars = 0; + + BOOL inEscapedSequence = NO; + unichar decodedChar = 0; + NSUInteger escapedCharacterCount = 0; + + for (NSUInteger idx=0; idx='0' && character<='9') || (character>='A' && character<='F') || (character>='a' && character<='f')) + { + // hex digit + decodedChar *= 16; + + if (character>='0' && character<='9') + { + decodedChar += (character - '0'); + } + else if (character>='A' && character<='F') + { + decodedChar += (character - 'A' + 10); + } + else if (character>='a' && character<='f') + { + decodedChar += (character - 'a' + 10); + } + + escapedCharacterCount++; + } + else + { + // illegal character following slash + final[outChars++] = '\\'; + final[outChars++] = character; + + inEscapedSequence = NO; + } + } + else + { + if (inEscapedSequence) + { + // output what we have decoded so far + final[outChars++] = decodedChar; + } + + if (character == '\\') + { + // begin of escape sequence + decodedChar = 0; + escapedCharacterCount = 0; + inEscapedSequence = YES; + } + else + { + inEscapedSequence = NO; + + // just copy + final[outChars++] = character; + } + } + } + + // if string ended in escaped sequence we still need to output + if (inEscapedSequence) + { + // output what we have decoded so far + final[outChars++] = decodedChar; + } + + free(characters); + NSString *clean = [[NSString alloc] initWithCharacters:final length:outChars]; + free(final); + + return clean; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSString+HTML.h b/Pods/DTCoreText/Core/Source/NSString+HTML.h new file mode 100644 index 0000000..d738620 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSString+HTML.h @@ -0,0 +1,82 @@ +// +// NSString+HTML.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +/** + Methods for making HTML strings easier and quicker to handle. + */ +@interface NSString (HTML) + +/** + Extract the numbers from this string and return them as an NSUInteger. + @returns An NSUInteger of the number characters in this string. + */ +- (NSUInteger)integerValueFromHex; + + +/** + Test whether or not this string is numeric only. + @returns If this string consists only of numeric characters 0-9. + */ +- (BOOL)isNumeric; + +/** + Test whether the entire receiver consists of only whitespace characters. + @returns `YES` if the receiver only has whitespace and newline characters + */ +- (BOOL)isIgnorableWhitespace; + +/** + Read through this string and store the numbers included, then divide them by 100 giving a percentage. + @returns The numbers contained in this string, as a percentage. + */ +- (float)percentValue; + + +/** + Return a copy of this string with all whitespace characters replaced by space characters. + @returns A copy of this string with only space characters for whitespace. + */ +- (NSString *)stringByNormalizingWhitespace; + + +/** + Determines if the first character of this string is in the parameter characterSet. + @param characterSet The character set to compare the first character of this string against. + @returns If the first character of this string is in character set. + */ +- (BOOL)hasPrefixCharacterFromSet:(NSCharacterSet *)characterSet; + + +/** + Determines if the last character of this string is in the parameter characterSet. + @param characterSet The character set to compare the last character of this string against. + @returns If the last character of this string is in the character set. + */ +- (BOOL)hasSuffixCharacterFromSet:(NSCharacterSet *)characterSet; + + +/** + Convert a string into a proper HTML string by converting special characters into HTML entities. For example: an ellipsis `…` is represented by the entity `…` in order to display it correctly across text encodings. + @returns A string containing HTML that now uses proper HTML entities. + */ +- (NSString *)stringByAddingHTMLEntities; + + +/** + Convert a string from HTML entities into correct character representations using UTF8 encoding. For example: an ellipsis entity representy by `…` is converted into `…`. + @returns A string without HTML entities, instead having the actual characters formerly represented by HTML entities. + */ +- (NSString *)stringByReplacingHTMLEntities; + + +/** + Replaces occurences of more two or more spaces with a range of alternating non-breaking space and regular space. It also encloses these parts with a span of class 'Apple-converted-space' + */ +- (NSString *)stringByAddingAppleConvertedSpace; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSString+HTML.m b/Pods/DTCoreText/Core/Source/NSString+HTML.m new file mode 100644 index 0000000..88d8f1d --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSString+HTML.m @@ -0,0 +1,792 @@ +// +// NSString+HTML.m +// DTCoreText +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "NSString+HTML.h" +#import "DTCoreText.h" + + +static NSDictionary *entityLookup = nil; +static NSDictionary *entityReverseLookup = nil; + +@implementation NSString (HTML) + +- (NSUInteger)integerValueFromHex +{ + unsigned long result = 0; + sscanf([self UTF8String], "%lx", &result); + return (NSUInteger)result; +} + +- (BOOL)isNumeric +{ + const char *s = [self UTF8String]; + + for (size_t i=0;i'9') && (s[i] != '.')) + { + return NO; + } + } + + return YES; +} + +- (BOOL)isIgnorableWhitespace +{ + NSCharacterSet *whitespaceCharacterSet = [NSCharacterSet ignorableWhitespaceCharacterSet]; + + NSString *tmpStr = [self stringByTrimmingCharactersInSet:whitespaceCharacterSet]; + + return [tmpStr length]==0; +} + +- (float)percentValue +{ + float result = 1; + sscanf([self UTF8String], "%f", &result); + + return result/100.0f; +} + +- (NSString *)stringByNormalizingWhitespace +{ + NSInteger stringLength = [self length]; + + // reserve buffer, same size as input + unichar *_characters = calloc(stringLength, sizeof(unichar)); + [self getCharacters:_characters range:NSMakeRange(0, stringLength)]; + + NSInteger outputLength = 0; + BOOL inWhite = NO; + + + // we output to the same buffer as the input was + for (NSInteger i = 0; i", [NSNumber numberWithInteger:0x2028], + nil]; + + }); + + NSMutableString *tmpString = [NSMutableString string]; + + for (NSUInteger i = 0; i<[self length]; i++) + { + unichar oneChar = [self characterAtIndex:i]; + + NSNumber *subKey = [NSNumber numberWithInteger:oneChar]; + NSString *entity = [entityReverseLookup objectForKey:subKey]; + + if (entity) + { + [tmpString appendString:entity]; + } + else + { + if (oneChar<=255) + { + // output as is + [tmpString appendFormat:@"%C", oneChar]; + } + else if (CFStringIsSurrogateHighCharacter(oneChar) && i < [self length]-1) + { + i++; + unichar surrogateLowChar = [self characterAtIndex:i]; + UTF32Char u32code = CFStringGetLongCharacterForSurrogatePair(oneChar, surrogateLowChar); + [tmpString appendFormat:@"&#%lu;", (unsigned long)u32code]; + } + else + { + // output encoded + [tmpString appendFormat:@"&#%d;", oneChar]; + } + } + } + + return tmpString; +} + +- (NSString *)stringByReplacingHTMLEntities +{ + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + entityLookup = [[NSDictionary alloc] initWithObjectsAndKeys:@"\x22", @"quot", + @"\x26", @"amp", + @"\x27", @"apos", + @"\x3c", @"lt", + @"\x3e", @"gt", + @"\u00a0", @"nbsp", + @"\u00a1", @"iexcl", + @"\u00a2", @"cent", + @"\u00a3", @"pound", + @"\u00a4", @"curren", + @"\u00a5", @"yen", + @"\u00a6", @"brvbar", + @"\u00a7", @"sect", + @"\u00a8", @"uml", + @"\u00a9", @"copy", + @"\u00aa", @"ordf", + @"\u00ab", @"laquo", + @"\u00ac", @"not", + @"\u00ae", @"reg", + @"\u00af", @"macr", + @"\u00b0", @"deg", + @"\u00b1", @"plusmn", + @"\u00b2", @"sup2", + @"\u00b3", @"sup3", + @"\u00b4", @"acute", + @"\u00b5", @"micro", + @"\u00b6", @"para", + @"\u00b7", @"middot", + @"\u00b8", @"cedil", + @"\u00b9", @"sup1", + @"\u00ba", @"ordm", + @"\u00bb", @"raquo", + @"\u00bc", @"frac14", + @"\u00bd", @"frac12", + @"\u00be", @"frac34", + @"\u00bf", @"iquest", + @"\u00c0", @"Agrave", + @"\u00c1", @"Aacute", + @"\u00c2", @"Acirc", + @"\u00c3", @"Atilde", + @"\u00c4", @"Auml", + @"\u00c5", @"Aring", + @"\u00c6", @"AElig", + @"\u00c7", @"Ccedil", + @"\u00c8", @"Egrave", + @"\u00c9", @"Eacute", + @"\u00ca", @"Ecirc", + @"\u00cb", @"Euml", + @"\u00cc", @"Igrave", + @"\u00cd", @"Iacute", + @"\u00ce", @"Icirc", + @"\u00cf", @"Iuml", + @"\u00d0", @"ETH", + @"\u00d1", @"Ntilde", + @"\u00d2", @"Ograve", + @"\u00d3", @"Oacute", + @"\u00d4", @"Ocirc", + @"\u00d5", @"Otilde", + @"\u00d6", @"Ouml", + @"\u00d7", @"times", + @"\u00d8", @"Oslash", + @"\u00d9", @"Ugrave", + @"\u00da", @"Uacute", + @"\u00db", @"Ucirc", + @"\u00dc", @"Uuml", + @"\u00dd", @"Yacute", + @"\u00de", @"THORN", + @"\u00df", @"szlig", + @"\u00e0", @"agrave", + @"\u00e1", @"aacute", + @"\u00e2", @"acirc", + @"\u00e3", @"atilde", + @"\u00e4", @"auml", + @"\u00e5", @"aring", + @"\u00e6", @"aelig", + @"\u00e7", @"ccedil", + @"\u00e8", @"egrave", + @"\u00e9", @"eacute", + @"\u00ea", @"ecirc", + @"\u00eb", @"euml", + @"\u00ec", @"igrave", + @"\u00ed", @"iacute", + @"\u00ee", @"icirc", + @"\u00ef", @"iuml", + @"\u00f0", @"eth", + @"\u00f1", @"ntilde", + @"\u00f2", @"ograve", + @"\u00f3", @"oacute", + @"\u00f4", @"ocirc", + @"\u00f5", @"otilde", + @"\u00f6", @"ouml", + @"\u00f7", @"divide", + @"\u00f8", @"oslash", + @"\u00f9", @"ugrave", + @"\u00fa", @"uacute", + @"\u00fb", @"ucirc", + @"\u00fc", @"uuml", + @"\u00fd", @"yacute", + @"\u00fe", @"thorn", + @"\u00ff", @"yuml", + @"\u0152", @"OElig", + @"\u0153", @"oelig", + @"\u0160", @"Scaron", + @"\u0161", @"scaron", + @"\u0178", @"Yuml", + @"\u0192", @"fnof", + @"\u02c6", @"circ", + @"\u02dc", @"tilde", + @"\u0393", @"Gamma", + @"\u0394", @"Delta", + @"\u0398", @"Theta", + @"\u039b", @"Lambda", + @"\u039e", @"Xi", + @"\u03a3", @"Sigma", + @"\u03a5", @"Upsilon", + @"\u03a6", @"Phi", + @"\u03a8", @"Psi", + @"\u03a9", @"Omega", + @"\u03b1", @"alpha", + @"\u0391", @"Alpha", + @"\u03b2", @"beta", + @"\u0392", @"Beta", + @"\u03b3", @"gamma", + @"\u03b4", @"delta", + @"\u03b5", @"epsilon", + @"\u0395", @"Epsilon", + @"\u03b6", @"zeta", + @"\u0396", @"Zeta", + @"\u03b7", @"eta", + @"\u0397", @"Eta", + @"\u03b8", @"theta", + @"\u03b9", @"iota", + @"\u0399", @"Iota", + @"\u03ba", @"kappa", + @"\u039a", @"Kappa", + @"\u03bb", @"lambda", + @"\u03bc", @"mu", + @"\u039c", @"Mu", + @"\u03bd", @"nu", + @"\u039d", @"Nu", + @"\u03be", @"xi", + @"\u03bf", @"omicron", + @"\u039f", @"Omicron", + @"\u03c0", @"pi", + @"\u03a0", @"Pi", + @"\u03c1", @"rho", + @"\u03a1", @"Rho", + @"\u03c2", @"sigmaf", + @"\u03c3", @"sigma", + @"\u03c4", @"tau", + @"\u03a4", @"Tau", + @"\u03c5", @"upsilon", + @"\u03c6", @"phi", + @"\u03c7", @"chi", + @"\u03a7", @"Chi", + @"\u03c8", @"psi", + @"\u03c9", @"omega", + @"\u03d1", @"thetasym", + @"\u03d2", @"upsih", + @"\u03d6", @"piv", + @"\u2002", @"ensp", + @"\u2003", @"emsp", + @"\u2009", @"thinsp", + @"\u2013", @"ndash", + @"\u2014", @"mdash", + @"\u2018", @"lsquo", + @"\u2019", @"rsquo", + @"\u201a", @"sbquo", + @"\u201a", @"bsquo", + @"\u201c", @"ldquo", + @"\u201d", @"rdquo", + @"\u201e", @"bdquo", + @"\u2020", @"dagger", + @"\u2021", @"Dagger", + @"\u2022", @"bull", + @"\u2026", @"hellip", + @"\u2030", @"permil", + @"\u2032", @"prime", + @"\u2033", @"Prime", + @"\u2039", @"lsaquo", + @"\u203a", @"rsaquo", + @"\u203e", @"oline", + @"\u2044", @"frasl", + @"\u20ac", @"euro", + @"\u2111", @"image", + @"\u2118", @"weierp", + @"\u211c", @"real", + @"\u2122", @"trade", + @"\u2135", @"alefsym", + @"\u2190", @"larr", + @"\u2191", @"uarr", + @"\u2192", @"rarr", + @"\u2193", @"darr", + @"\u2194", @"harr", + @"\u21b5", @"crarr", + @"\u21d0", @"lArr", + @"\u21d1", @"uArr", + @"\u21d2", @"rArr", + @"\u21d3", @"dArr", + @"\u21d4", @"hArr", + @"\u2200", @"forall", + @"\u2202", @"part", + @"\u2203", @"exist", + @"\u2205", @"empty", + @"\u2207", @"nabla", + @"\u2208", @"isin", + @"\u2209", @"notin", + @"\u220b", @"ni", + @"\u220f", @"prod", + @"\u2211", @"sum", + @"\u2212", @"minus", + @"\u2217", @"lowast", + @"\u221a", @"radic", + @"\u221d", @"prop", + @"\u221e", @"infin", + @"\u2220", @"ang", + @"\u2227", @"and", + @"\u2228", @"or", + @"\u2229", @"cap", + @"\u222a", @"cup", + @"\u222b", @"int", + @"\u2234", @"there4", + @"\u223c", @"sim", + @"\u2245", @"cong", + @"\u2248", @"asymp", + @"\u2260", @"ne", + @"\u2261", @"equiv", + @"\u2264", @"le", + @"\u2265", @"ge", + @"\u2282", @"sub", + @"\u2283", @"sup", + @"\u2284", @"nsub", + @"\u2286", @"sube", + @"\u2287", @"supe", + @"\u2295", @"oplus", + @"\u2297", @"otimes", + @"\u22a5", @"perp", + @"\u22c5", @"sdot", + @"\u2308", @"lceil", + @"\u2309", @"rceil", + @"\u230a", @"lfloor", + @"\u230b", @"rfloor", + @"\u27e8", @"lang", + @"\u27e9", @"rang", + @"\u25ca", @"loz", + @"\u2660", @"spades", + @"\u2663", @"clubs", + @"\u2665", @"hearts", + @"\u2666", @"diams", + nil]; + + }); + + NSScanner *scanner = [NSScanner scannerWithString:self]; + [scanner setCharactersToBeSkipped:nil]; + + NSMutableString *output = [NSMutableString string]; + + + while (![scanner isAtEnd]) + { + NSString *scanned = nil; + + if ([scanner scanUpToString:@"&" intoString:&scanned]) + { + [output appendString:scanned]; + } + + if ([scanner scanString:@"&" intoString:NULL]) + { + NSString *afterAmpersand = nil; + if ([scanner scanUpToString:@";" intoString:&afterAmpersand]) + { + if ([scanner scanString:@";" intoString:NULL]) + { + if ([afterAmpersand hasPrefix:@"#"] && [afterAmpersand length]<=6) + { + unichar ch = (unichar)[[afterAmpersand substringFromIndex:1] integerValue]; + [output appendFormat:@"%C", ch]; + } + else + { + NSString *converted = [entityLookup objectForKey:afterAmpersand]; + + if (converted) + { + [output appendString:converted]; + } + else + { + // not a valid sequence + [output appendString:@"&"]; + [output appendString:afterAmpersand]; + [output appendString:@";"]; + } + } + + } + else + { + // no semicolon + [output appendString:@"&"]; + [output appendString:afterAmpersand]; + } + } + } + } + + + return [NSString stringWithString:output]; +} + +- (NSString *)stringByAddingAppleConvertedSpace +{ + NSMutableString *output = [NSMutableString string]; + + NSScanner *scanner = [NSScanner scannerWithString:self]; + scanner.charactersToBeSkipped = nil; + + NSCharacterSet *spaceSet = [NSCharacterSet characterSetWithCharactersInString:@" "]; + + while (![scanner isAtEnd]) + { + NSString *part; + if ([scanner scanUpToString:@" " intoString:&part]) + { + [output appendString:part]; + } + + NSString *spaces; + if ([scanner scanCharactersFromSet:spaceSet intoString:&spaces]) + { + // first space always output as is + [output appendString:@" "]; + + NSUInteger numSpaces = [spaces length]-1; + + if (numSpaces > 1) + { + [output appendString:@""]; + + // alternate nbsp; and normal space + for (NSUInteger i=0; i"]; + } + } + } + + return output; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/NSString+Paragraphs.h b/Pods/DTCoreText/Core/Source/NSString+Paragraphs.h new file mode 100644 index 0000000..860a2c6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSString+Paragraphs.h @@ -0,0 +1,52 @@ +// +// NSString+Paragraphs.h +// DTRichTextEditor +// +// Created by Oliver Drobnik on 11/11/11. +// Copyright (c) 2011 Cocoanetics. All rights reserved. +// + + +/** + Methods simplifying dealing with text that is in paragraphs. + + The character used to separate paragraphs from each other is '\n'. + */ +@interface NSString (Paragraphs) + +/** + Extends the given range such that it contains only full paragraphs. + + @param range The string range + @param parBegIndex An optional output parameter that is filled with the beginning index of the extended range + @param parEndIndex An optional output parameter that is filled with the ending index of the extended range + @returns The extended string range + */ +- (NSRange)rangeOfParagraphsContainingRange:(NSRange)range parBegIndex:(NSUInteger *)parBegIndex parEndIndex:(NSUInteger *)parEndIndex; + + +/** + Determines if the given index is the first character of a new paragraph. + + This is done by examining the string, index 0 or characters following a newline are considered to be a first character of a new paragraph. + @param index The index to examine + @returns `YES` if the given index is the first character of a new paragraph, `NO` otherwise + */ +- (BOOL)indexIsAtBeginningOfParagraph:(NSUInteger)index; + +/** + Returns the string range of the paragraph with the given index. + + @param index The paragraph index to inspect + @returns The string range of the paragraph + */ +- (NSRange)rangeOfParagraphAtIndex:(NSUInteger)index; + + +/** + Counts the number of paragraphs in the receiver + @returns The number of paragraph characters (\n) in the receiver + */ +- (NSUInteger)numberOfParagraphs; + +@end diff --git a/Pods/DTCoreText/Core/Source/NSString+Paragraphs.m b/Pods/DTCoreText/Core/Source/NSString+Paragraphs.m new file mode 100644 index 0000000..5cdb27c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/NSString+Paragraphs.m @@ -0,0 +1,74 @@ +// +// NSString+Paragraphs.m +// DTRichTextEditor +// +// Created by Oliver Drobnik on 11/11/11. +// Copyright (c) 2011 Cocoanetics. All rights reserved. +// + +#import "NSString+Paragraphs.h" + +@implementation NSString (Paragraphs) + +- (NSRange)rangeOfParagraphsContainingRange:(NSRange)range parBegIndex:(NSUInteger *)parBegIndex parEndIndex:(NSUInteger *)parEndIndex +{ + // get beginning and end of paragraph containing the replaced range + CFIndex beginIndex; + CFIndex endIndex; + + CFStringGetParagraphBounds((__bridge CFStringRef)self, CFRangeMake(range.location, range.length), &beginIndex, &endIndex, NULL); + + if (parBegIndex) + { + *parBegIndex = beginIndex; + } + + if (parEndIndex) + { + *parEndIndex = endIndex; + } + + // endIndex is the first character of the following paragraph, so we don't need to add 1 + + return NSMakeRange(beginIndex, endIndex - beginIndex); +} + +- (BOOL)indexIsAtBeginningOfParagraph:(NSUInteger)index +{ + // index zero is beginning of first paragraph + if (!index) + { + return YES; + } + + // beginning of any other paragraph is after NL + if ([self characterAtIndex:index-1] == '\n') + { + return YES; + } + + // no beginning + return NO; +} + +- (NSRange)rangeOfParagraphAtIndex:(NSUInteger)index +{ + return [self rangeOfParagraphsContainingRange:NSMakeRange(index, 1) parBegIndex:NULL parEndIndex:NULL]; +} + +- (NSUInteger)numberOfParagraphs +{ + NSUInteger retValue = 0; + + for (NSUInteger i=0; i<[self length]; i++) + { + if ([self characterAtIndex:i] == '\n') + { + retValue++; + } + } + + return retValue; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/UIFont+DTCoreText.h b/Pods/DTCoreText/Core/Source/UIFont+DTCoreText.h new file mode 100644 index 0000000..09bdb37 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/UIFont+DTCoreText.h @@ -0,0 +1,22 @@ +// +// UIFont+DTCoreText.h +// DTCoreText +// +// Created by Oliver Drobnik on 11.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +/** + Methods to translate from `CTFont` to `UIFont` + */ + +@interface UIFont (DTCoreText) + +/** + Creates a UIFont that matches the provided CTFont. + @param ctFont a `CTFontRef` + @returns The matching UIFont + */ ++ (UIFont *)fontWithCTFont:(CTFontRef)ctFont; + +@end diff --git a/Pods/DTCoreText/Core/Source/UIFont+DTCoreText.m b/Pods/DTCoreText/Core/Source/UIFont+DTCoreText.m new file mode 100644 index 0000000..4b2c50e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/UIFont+DTCoreText.m @@ -0,0 +1,29 @@ +// +// UIFont+DTCoreText.m +// DTCoreText +// +// Created by Oliver Drobnik on 11.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "UIFont+DTCoreText.h" + +@implementation UIFont (DTCoreText) + ++ (UIFont *)fontWithCTFont:(CTFontRef)ctFont +{ + NSString *fontName = (__bridge_transfer NSString *)CTFontCopyName(ctFont, kCTFontPostScriptNameKey); + + CGFloat fontSize = CTFontGetSize(ctFont); + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + + // fix for missing HelveticaNeue-Italic font in iOS 7.0.x + if (!font && [fontName isEqualToString:@"HelveticaNeue-Italic"]) + { + font = [UIFont fontWithName:@"HelveticaNeue-LightItalic" size:fontSize]; + } + + return font; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/default.css.c b/Pods/DTCoreText/Core/Source/default.css.c new file mode 100644 index 0000000..7905023 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/default.css.c @@ -0,0 +1,293 @@ +unsigned char default_css[] = { + 0x2f, 0x2a, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x78, 0x78, 0x64, 0x20, 0x76, + 0x69, 0x61, 0x20, 0x61, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x20, 0x72, + 0x75, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6d, 0x62, 0x65, + 0x64, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x2f, 0x2a, 0x20, 0x74, + 0x68, 0x65, 0x73, 0x65, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x20, + 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x53, 0x61, + 0x66, 0x61, 0x72, 0x69, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x2f, 0x2a, 0x20, + 0x6e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, + 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, + 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, + 0x79, 0x6c, 0x65, 0x73, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x2f, 0x2a, 0x20, + 0x44, 0x4f, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x66, 0x69, 0x64, 0x64, 0x6c, + 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69, 0x66, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x61, 0x76, 0x65, + 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x73, 0x74, + 0x79, 0x6c, 0x65, 0x73, 0x2c, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, + 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, 0x20, 0x76, + 0x69, 0x61, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x20, + 0x74, 0x6f, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x73, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x68, 0x74, 0x6d, 0x6c, 0x0a, + 0x7b, 0x0a, 0x09, 0x2f, 0x2a, 0x20, 0x62, 0x6c, 0x61, 0x20, 0x2a, 0x2f, + 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x68, 0x65, 0x61, 0x64, + 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, + 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, 0x69, 0x74, + 0x6c, 0x65, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x3a, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, + 0x74, 0x79, 0x6c, 0x65, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x3a, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x0a, 0x7b, 0x0a, 0x09, 0x2f, 0x2a, + 0x20, 0x73, 0x61, 0x66, 0x61, 0x72, 0x69, 0x20, 0x68, 0x61, 0x73, 0x20, + 0x61, 0x20, 0x38, 0x20, 0x70, 0x78, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, + 0x6e, 0x20, 0x68, 0x65, 0x72, 0x65, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x63, 0x6c, 0x65, + 0x2c, 0x61, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x66, 0x6f, 0x6f, 0x74, 0x65, + 0x72, 0x2c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2c, 0x68, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x2c, 0x6e, 0x61, 0x76, 0x2c, 0x73, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x70, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x09, 0x2d, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x31, 0x65, 0x6d, 0x3b, + 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, + 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x3a, 0x31, + 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, + 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, 0x64, + 0x3a, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x75, 0x6c, 0x2c, 0x6d, 0x65, + 0x6e, 0x75, 0x2c, 0x64, 0x69, 0x72, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, + 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x64, 0x69, 0x73, 0x63, 0x3b, 0x0a, + 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, + 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x31, + 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, + 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, + 0x72, 0x3a, 0x31, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, + 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, + 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, + 0x65, 0x6e, 0x64, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, + 0x6b, 0x69, 0x74, 0x2d, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, 0x32, 0x37, 0x70, 0x78, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x6c, 0x69, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x69, 0x74, + 0x65, 0x6d, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x6c, 0x0a, 0x7b, 0x0a, + 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x3b, 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73, 0x74, + 0x79, 0x6c, 0x65, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x64, 0x65, 0x63, + 0x69, 0x6d, 0x61, 0x6c, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, + 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x65, + 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x31, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, + 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, + 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x3a, 0x31, 0x65, 0x6d, 0x3b, + 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, + 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, 0x30, + 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, + 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, 0x64, 0x3a, 0x30, 0x3b, + 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, + 0x32, 0x37, 0x70, 0x78, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x75, 0x6c, 0x20, + 0x75, 0x6c, 0x2c, 0x20, 0x6f, 0x6c, 0x20, 0x75, 0x6c, 0x20, 0x0a, 0x7b, + 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6f, 0x6c, 0x20, 0x6f, 0x6c, 0x20, + 0x75, 0x6c, 0x2c, 0x20, 0x6f, 0x6c, 0x20, 0x75, 0x6c, 0x20, 0x75, 0x6c, + 0x2c, 0x20, 0x75, 0x6c, 0x20, 0x6f, 0x6c, 0x20, 0x75, 0x6c, 0x2c, 0x20, + 0x75, 0x6c, 0x20, 0x75, 0x6c, 0x20, 0x75, 0x6c, 0x20, 0x0a, 0x7b, 0x0a, + 0x09, 0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x0a, 0x7b, 0x0a, + 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, + 0x3a, 0x43, 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x70, 0x72, 0x65, 0x2c, 0x20, 0x78, 0x6d, 0x70, 0x2c, 0x20, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, + 0x6c, 0x79, 0x3a, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x3b, 0x0a, 0x09, 0x77, 0x68, 0x69, 0x74, 0x65, 0x2d, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x3a, 0x20, 0x70, 0x72, 0x65, 0x3b, 0x0a, 0x09, 0x6d, + 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x31, 0x65, 0x6d, 0x20, 0x30, + 0x70, 0x78, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x20, 0x63, 0x6f, + 0x6c, 0x6f, 0x72, 0x3a, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, + 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x2a, 0x2f, 0x0a, 0x61, 0x0a, 0x7b, 0x0a, + 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x23, 0x30, 0x30, 0x30, 0x30, + 0x45, 0x45, 0x3b, 0x20, 0x0a, 0x09, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x64, + 0x65, 0x63, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x75, 0x6e, + 0x64, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x61, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7b, 0x63, 0x6f, + 0x6c, 0x6f, 0x72, 0x3a, 0x23, 0x46, 0x46, 0x30, 0x30, 0x30, 0x30, 0x3b, + 0x7d, 0x20, 0x20, 0x2f, 0x2a, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x77, + 0x68, 0x69, 0x6c, 0x65, 0x20, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x20, + 0x64, 0x6f, 0x77, 0x6e, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x63, 0x65, 0x6e, + 0x74, 0x65, 0x72, 0x0a, 0x7b, 0x0a, 0x09, 0x74, 0x65, 0x78, 0x74, 0x2d, + 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, + 0x3b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x72, + 0x6f, 0x6e, 0x67, 0x2c, 0x62, 0x0a, 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, + 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x62, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x69, 0x2c, 0x65, 0x6d, + 0x0a, 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x79, + 0x6c, 0x65, 0x3a, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x75, 0x0a, 0x7b, 0x0a, 0x09, 0x74, 0x65, 0x78, 0x74, 0x2d, + 0x64, 0x65, 0x63, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x75, + 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x62, 0x69, 0x67, 0x0a, 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, + 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x62, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x0a, 0x7b, + 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, + 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x73, 0x75, 0x62, 0x0a, 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, + 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x3b, 0x0a, 0x09, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x2d, + 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x73, 0x75, 0x62, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x73, 0x75, 0x70, 0x0a, 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, + 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x73, 0x6d, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x3b, 0x0a, 0x09, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, + 0x6c, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x2c, 0x73, 0x74, 0x72, 0x69, + 0x6b, 0x65, 0x2c, 0x64, 0x65, 0x6c, 0x0a, 0x7b, 0x0a, 0x09, 0x74, 0x65, + 0x78, 0x74, 0x2d, 0x64, 0x65, 0x63, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x3a, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x74, 0x68, 0x72, 0x6f, 0x75, + 0x67, 0x68, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, 0x74, 0x2c, 0x63, 0x6f, + 0x64, 0x65, 0x2c, 0x6b, 0x62, 0x64, 0x2c, 0x73, 0x61, 0x6d, 0x70, 0x0a, + 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, + 0x6c, 0x79, 0x3a, 0x6d, 0x6f, 0x6e, 0x6f, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x70, 0x72, 0x65, 0x2c, 0x78, 0x6d, 0x70, + 0x2c, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x6c, + 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, + 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c, + 0x79, 0x3a, 0x6d, 0x6f, 0x6e, 0x6f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3b, + 0x0a, 0x09, 0x77, 0x68, 0x69, 0x74, 0x65, 0x2d, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x3a, 0x70, 0x72, 0x65, 0x3b, 0x0a, 0x09, 0x6d, 0x61, 0x72, 0x67, + 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x31, 0x65, 0x6d, 0x3b, 0x0a, + 0x09, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x31, 0x65, 0x6d, 0x3b, + 0x0a, 0x09, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x6c, 0x65, 0x66, + 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x68, 0x31, 0x0a, 0x7b, + 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, + 0x69, 0x7a, 0x65, 0x3a, 0x32, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x2e, 0x36, 0x37, 0x65, + 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, + 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, + 0x3a, 0x2e, 0x36, 0x37, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, + 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x65, 0x6e, 0x64, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, + 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x62, 0x6f, 0x6c, + 0x64, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x68, 0x32, 0x0a, 0x7b, 0x0a, 0x09, + 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, + 0x65, 0x3a, 0x31, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x2e, 0x38, 0x33, 0x65, + 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, + 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, + 0x3a, 0x2e, 0x38, 0x33, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, + 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x65, 0x6e, 0x64, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, + 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x62, 0x6f, 0x6c, + 0x64, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x68, 0x33, 0x0a, 0x7b, 0x0a, 0x09, + 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, + 0x65, 0x3a, 0x31, 0x2e, 0x31, 0x37, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, + 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, + 0x6e, 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x31, 0x65, 0x6d, + 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, + 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x3a, + 0x31, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, + 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, + 0x64, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x62, 0x6f, 0x6c, 0x64, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x68, 0x34, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, + 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, + 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x31, + 0x2e, 0x33, 0x33, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, + 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x3a, 0x31, 0x2e, 0x33, 0x33, 0x65, 0x6d, 0x3b, + 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, + 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, 0x30, + 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, + 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, 0x64, 0x3a, 0x30, 0x3b, + 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3a, 0x62, 0x6f, 0x6c, 0x64, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x68, + 0x35, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, + 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x2e, 0x38, 0x33, 0x65, 0x6d, + 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, + 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, + 0x3a, 0x31, 0x2e, 0x36, 0x37, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x3a, 0x31, 0x2e, 0x36, 0x37, 0x65, + 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, + 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, + 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, 0x64, 0x3a, + 0x30, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3a, 0x62, 0x6f, 0x6c, 0x64, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x68, 0x36, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x09, 0x66, + 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x2e, 0x36, 0x37, + 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, + 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x65, 0x66, 0x6f, + 0x72, 0x65, 0x3a, 0x32, 0x2e, 0x33, 0x33, 0x65, 0x6d, 0x3b, 0x0a, 0x09, + 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, + 0x69, 0x6e, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x3a, 0x32, 0x2e, 0x33, + 0x33, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, + 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, + 0x64, 0x3a, 0x30, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x62, 0x6f, 0x6c, 0x64, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x64, 0x69, 0x76, 0x20, 0x7b, 0x0a, 0x09, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x7b, 0x0a, + 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x6e, 0x6f, + 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x20, + 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, + 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x68, 0x72, 0x20, 0x7b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0a, 0x09, + 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, + 0x69, 0x6e, 0x2d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3a, 0x20, 0x30, + 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, + 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x65, 0x6d, 0x3b, 0x0a, + 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2d, 0x6d, 0x61, 0x72, + 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3a, 0x20, 0x61, + 0x75, 0x74, 0x6f, 0x3b, 0x0a, 0x09, 0x2d, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x65, 0x6e, 0x64, + 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0a, 0x09, 0x62, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3a, 0x20, 0x69, + 0x6e, 0x73, 0x65, 0x74, 0x3b, 0x0a, 0x09, 0x62, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x70, 0x78, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, + 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x62, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x70, + 0x73, 0x65, 0x3a, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, + 0x73, 0x70, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x32, 0x70, 0x78, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x67, 0x72, 0x61, 0x79, 0x3b, + 0x0a, 0x7d +}; +unsigned int default_css_len = 3470; diff --git a/Pods/DTFoundation/Core/Source/DTAsyncFileDeleter.h b/Pods/DTFoundation/Core/Source/DTAsyncFileDeleter.h new file mode 100644 index 0000000..c19d342 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTAsyncFileDeleter.h @@ -0,0 +1,50 @@ +// +// DTAsyncFileDeleter.h +// DTFoundation +// +// Created by Oliver Drobnik on 2/10/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** This class deletes large amounts of files asynchronously. You should use the sharedInstance to get an instance. On iOS this automatically starts a background task if the app is suspended so that file deletion can complete. + */ + +@interface DTAsyncFileDeleter : NSObject + +/**------------------------------------------------------------------------------------- + @name Creating A File Deleter + --------------------------------------------------------------------------------------- + */ + +/** Creates a shared file deleter. + */ ++ (DTAsyncFileDeleter *)sharedInstance; + +/**------------------------------------------------------------------------------------- + @name Asynchronous Operations + --------------------------------------------------------------------------------------- + */ + +/** Blocks execution of the current thread until the receiver finishes. + */ +- (void)waitUntilFinished; + + +/** Removes the file or directory at the specified path and immediately returns. + + This method moves the given item to a temporary name which is an instant operation. It then schedules an asynchronous background operation to actually remove the item. + + @param path A path string indicating the file or directory to remove. If the path specifies a directory, the contents of that directory are recursively removed. + */ +- (void)removeItemAtPath:(NSString *)path; + + +/** Removes the file or directory at the specified URL. + + This method moves the given item to a temporary name which is an instant operation. It then schedules an asynchronous background operation to actually remove the item. + + @param URL A file URL specifying the file or directory to remove. If the URL specifies a directory, the contents of that directory are recursively removed. + */ +- (void)removeItemAtURL:(NSURL *)URL; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTAsyncFileDeleter.m b/Pods/DTFoundation/Core/Source/DTAsyncFileDeleter.m new file mode 100644 index 0000000..2446989 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTAsyncFileDeleter.m @@ -0,0 +1,133 @@ +// +// DTAsyncFileDeleter.m +// DTFoundation +// +// Created by Oliver Drobnik on 2/10/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTAsyncFileDeleter.h" +#import "NSString+DTPaths.h" + + +static dispatch_queue_t _delQueue; +static dispatch_group_t _delGroup; +static dispatch_once_t onceToken; + +static dispatch_queue_t _renameQueue; + +static DTAsyncFileDeleter *_sharedInstance; + + +// private utilites +@interface DTAsyncFileDeleter () +- (BOOL)_supportsTaskCompletion; +@end + + +@implementation DTAsyncFileDeleter + ++ (DTAsyncFileDeleter *)sharedInstance +{ + static dispatch_once_t instanceOnceToken; + dispatch_once(&instanceOnceToken, ^{ + _sharedInstance = [[DTAsyncFileDeleter alloc] init]; + }); + + return _sharedInstance; +} + +- (id)init +{ + self = [super init]; + if (self) + { + dispatch_once(&onceToken, ^{ + _delQueue = dispatch_queue_create("DTAsyncFileDeleterRemoveQueue", 0); + _delGroup = dispatch_group_create(); + _renameQueue = dispatch_queue_create("DTAsyncFileDeleterRenameQueue", 0); + }); + } + + return self; +} + +- (void)waitUntilFinished +{ + dispatch_group_wait(_delGroup, DISPATCH_TIME_FOREVER); +} + +- (void)removeItemAtPath:(NSString *)path +{ + // make a unique temporary name in tmp folder + NSString *tmpPath = [NSString pathForTemporaryFile]; + + // rename the file, waiting for the rename to finish before async deletion + dispatch_sync(_renameQueue, ^{ + NSFileManager *fileManager = [[NSFileManager alloc] init]; + + if ([fileManager moveItemAtPath:path toPath:tmpPath error:NULL]) + { + // schedule the removal and immediately return + dispatch_group_async(_delGroup, _delQueue, ^{ +#if TARGET_OS_IPHONE + __block UIBackgroundTaskIdentifier backgroundTaskID = UIBackgroundTaskInvalid; + + // block to use for timeout as well as completed task + void (^completionBlock)() = ^{ + [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID]; + backgroundTaskID = UIBackgroundTaskInvalid; + }; + + if ([self _supportsTaskCompletion]) + { + // according to docs this is safe to be called from background threads + backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:completionBlock]; + } +#endif + + // file manager is not used any more in the rename queue, so we reuse it + [fileManager removeItemAtPath:tmpPath error:NULL]; + +#if TARGET_OS_IPHONE + // ... when the task completes: + if (backgroundTaskID != UIBackgroundTaskInvalid) + { + completionBlock(); + } +#endif + }); + } + }); +} + +- (void)removeItemAtURL:(NSURL *)URL +{ + NSAssert([URL isFileURL], @"Parameter URL must be a file URL"); + + [self removeItemAtPath:[URL path]]; +} + +#pragma mark Utilities +- (BOOL)_supportsTaskCompletion +{ +#if TARGET_OS_IPHONE + UIDevice *device = [UIDevice currentDevice]; + + if ([device respondsToSelector:@selector(isMultitaskingSupported)]) + { + if (device.multitaskingSupported) + { + return YES; + } + else + { + return NO; + } + } +#endif + + return NO; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/DTBase64Coding.h b/Pods/DTFoundation/Core/Source/DTBase64Coding.h new file mode 100644 index 0000000..66d877d --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTBase64Coding.h @@ -0,0 +1,37 @@ +// +// DTBase64Coding.h +// DTFoundation +// +// Created by Oliver Drobnik on 04.03.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +/** + Utility class for encoding and decoding data in base64 format. + + This was formerly a category on `NSData` but since Matt Gallagher's category has become so enormously popular people where reporting more and more conflicts. Thus we decided to move it into a properly named class. + + Since all methods are class methods you never need to actually initialize it, doing so will raises a `DTAbstractClassException`. + */ + +@interface DTBase64Coding : NSObject + +/** + Encoding and Decoding + */ + +/** + Encodes data as base64 string. + @param data The data to encode + @returns The encoded string + */ ++ (NSString *)stringByEncodingData:(NSData *)data; + +/** + Encodes data as base64 string. + @param string The string with data encoded in base64 format + @returns data The decoded data + */ ++ (NSData *)dataByDecodingString:(NSString *)string; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTBase64Coding.m b/Pods/DTFoundation/Core/Source/DTBase64Coding.m new file mode 100644 index 0000000..92c6ca0 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTBase64Coding.m @@ -0,0 +1,296 @@ +// +// DTBase64Coding.m +// DTFoundation +// +// Original code from NSData+Base64.m +// +// Created by Matt Gallagher on 2009/06/03. +// Copyright 2009 Matt Gallagher. All rights reserved. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. Permission is granted to anyone to +// use this software for any purpose, including commercial applications, and to +// alter it and redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. + + +#import "DTBase64Coding.h" + + +// Function Prototypes +void *DT__NewBase64Decode(const char *inputBuffer, size_t length, size_t *outputLength); +char *DT__NewBase64Encode(const void *buffer, size_t length, bool separateLines, size_t *outputLength); + +// +// Mapping from 6 bit pattern to ASCII character. +// +static unsigned char base64EncodeLookup[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// +// Definition for "masked-out" areas of the base64DecodeLookup mapping +// +#define xx 65 + +// +// Mapping from ASCII character to 6 bit pattern. +// +static unsigned char base64DecodeLookup[256] = +{ + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, + xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, + xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, +}; + +// +// Fundamental sizes of the binary and base64 encode/decode units in bytes +// +#define BINARY_UNIT_SIZE 3 +#define BASE64_UNIT_SIZE 4 + +// +// NewBase64Decode +// +// Decodes the base64 ASCII string in the inputBuffer to a newly malloced +// output buffer. +// +// inputBuffer - the source ASCII string for the decode +// length - the length of the string or -1 (to specify strlen should be used) +// outputLength - if not-NULL, on output will contain the decoded length +// +// returns the decoded buffer. Must be free'd by caller. Length is given by +// outputLength. +// +void *DT__NewBase64Decode ( const char *inputBuffer, size_t length, size_t *outputLength) +{ + if ((long)length == -1) + { + length = strlen(inputBuffer); + } + + size_t outputBufferSize = + ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE; + unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize); + + size_t i = 0; + size_t j = 0; + while (i < length) + { + // + // Accumulate 4 valid characters (ignore everything else) + // + unsigned char accumulated[BASE64_UNIT_SIZE]; + size_t accumulateIndex = 0; + while (i < length) + { + unsigned char decode = base64DecodeLookup[(int) inputBuffer[i++]]; + if (decode != xx) + { + accumulated[accumulateIndex] = decode; + accumulateIndex++; + + if (accumulateIndex == BASE64_UNIT_SIZE) + { + break; + } + } + } + + // + // Store the 6 bits from each of the 4 characters as 3 bytes + // + // (Uses improved bounds checking suggested by Alexandre Colucci) + // + if(accumulateIndex >= 2) + outputBuffer[j] = (unsigned char)((accumulated[0] << 2) | (accumulated[1] >> 4)); + if(accumulateIndex >= 3) + outputBuffer[j + 1] = (unsigned char)((accumulated[1] << 4) | (accumulated[2] >> 2)); + if(accumulateIndex >= 4) + outputBuffer[j + 2] = (unsigned char)((accumulated[2] << 6) | accumulated[3]); + j += accumulateIndex - 1; + } + + if (outputLength) + { + *outputLength = j; + } + return outputBuffer; +} + +// +// NewBase64Encode +// +// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced +// output buffer. +// +// inputBuffer - the source data for the encode +// length - the length of the input in bytes +// separateLines - if zero, no CR/LF characters will be added. Otherwise +// a CR/LF pair will be added every 64 encoded chars. +// outputLength - if not-NULL, on output will contain the encoded length +// (not including terminating 0 char) +// +// returns the encoded buffer. Must be free'd by caller. Length is given by +// outputLength. +// +char *DT__NewBase64Encode(const void *buffer, size_t length, bool separateLines, size_t *outputLength) +{ + const unsigned char *inputBuffer = (const unsigned char *)buffer; + +#define MAX_NUM_PADDING_CHARS 2 +#define OUTPUT_LINE_LENGTH 64 +#define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE) +#define CR_LF_SIZE 2 + + // + // Byte accurate calculation of final buffer size + // + size_t outputBufferSize = + ((length / BINARY_UNIT_SIZE) + + ((length % BINARY_UNIT_SIZE) ? 1 : 0)) + * BASE64_UNIT_SIZE; + if (separateLines) + { + outputBufferSize += + (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE; + } + + // + // Include space for a terminating zero + // + outputBufferSize += 1; + + // + // Allocate the output buffer + // + char *outputBuffer = (char *)malloc(outputBufferSize); + if (!outputBuffer) + { + return NULL; + } + + size_t i = 0; + size_t j = 0; + const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length; + size_t lineEnd = lineLength; + + while (true) + { + if (lineEnd > length) + { + lineEnd = length; + } + + for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) + { + // + // Inner loop: turn 48 bytes into 64 base64 characters + // + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; + outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) + | ((inputBuffer[i + 1] & 0xF0) >> 4)]; + outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2) + | ((inputBuffer[i + 2] & 0xC0) >> 6)]; + outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F]; + } + + if (lineEnd == length) + { + break; + } + + // + // Add the newline + // + outputBuffer[j++] = '\r'; + outputBuffer[j++] = '\n'; + lineEnd += lineLength; + } + + if (i + 1 < length) + { + // + // Handle the single '=' case + // + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; + outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) + | ((inputBuffer[i + 1] & 0xF0) >> 4)]; + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2]; + outputBuffer[j++] = '='; + } + else if (i < length) + { + // + // Handle the double '=' case + // + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; + outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4]; + outputBuffer[j++] = '='; + outputBuffer[j++] = '='; + } + outputBuffer[j] = 0; + + // + // Set the output length and return the buffer + // + if (outputLength) + { + *outputLength = j; + } + return outputBuffer; +} + +@implementation DTBase64Coding + +// this is abstract and not meant to be actually used +- (id)init +{ + [NSException raise:@"DTAbstractClassException" format:@"You tried to call %@ on an abstract class %@", NSStringFromSelector(_cmd), NSStringFromClass([self class])]; + + return nil; +} + +#pragma mark - Encoding and Decoding + ++ (NSString *)stringByEncodingData:(NSData *)data +{ + size_t outputLength = 0; + char *outputBuffer = DT__NewBase64Encode([data bytes], [data length], true, &outputLength); + + NSString *result = [[NSString alloc] initWithBytes:outputBuffer length:outputLength encoding:NSASCIIStringEncoding]; + free(outputBuffer); + return result; +} + ++ (NSData *)dataByDecodingString:(NSString *)string +{ + NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding]; + size_t outputLength; + void *outputBuffer = DT__NewBase64Decode([data bytes], [data length], &outputLength); + NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength]; + free(outputBuffer); + return result; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/DTBlockFunctions.h b/Pods/DTFoundation/Core/Source/DTBlockFunctions.h new file mode 100644 index 0000000..9392908 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTBlockFunctions.h @@ -0,0 +1,25 @@ +// +// DTBlockFunctions.h +// DTFoundation +// +// Created by Oliver Drobnik on 02.10.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +/** + Block Utility Methods + */ + +/** + Performs a block synchronous if execution is currently on the main thread or dispatches it asynchronously if not + @param block The block to execute + */ +void DTBlockPerformSyncIfOnMainThreadElseAsync(void (^block)(void)); + +/** + Performs a block synchronous on the main thread regardless of the current thread + @param block The block to execute + */ +void DTBlockPerformSyncOnMainThread(void (^block)(void)); + + diff --git a/Pods/DTFoundation/Core/Source/DTBlockFunctions.m b/Pods/DTFoundation/Core/Source/DTBlockFunctions.m new file mode 100644 index 0000000..6117325 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTBlockFunctions.m @@ -0,0 +1,35 @@ +// +// DTBlockFunctions.m +// DTFoundation +// +// Created by Oliver Drobnik on 02.10.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +#import "DTBlockFunctions.h" + +void DTBlockPerformSyncIfOnMainThreadElseAsync(void (^block)(void)) +{ + if ([NSThread isMainThread]) + { + // can perform synchronous on main thread + block(); + } + else + { + // need to perform asynchronous + dispatch_async(dispatch_get_main_queue(), block); + } +} + +void DTBlockPerformSyncOnMainThread(void (^block)(void)) +{ + if ([NSThread isMainThread]) + { + block(); + } + else + { + dispatch_sync(dispatch_get_main_queue(), block); + } +} \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/DTCoreGraphicsUtils.h b/Pods/DTFoundation/Core/Source/DTCoreGraphicsUtils.h new file mode 100644 index 0000000..3ecfc1f --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTCoreGraphicsUtils.h @@ -0,0 +1,64 @@ +// +// DTCoreGraphicsUtils.h +// DTFoundation +// +// Created by Oliver Drobnik on 7/18/10. +// Copyright 2010 Cocoanetics. All rights reserved. +// + +#import + +/** + Various CoreGraphics-related utility functions + */ + +/** + Promotes value to CGFloat type. + */ +#define CGFloat_(__x) ((CGFloat) (__x)) + +/** + Calculates a size that fits an original size into a different size preserving the aspect ratio. + */ +CGSize DTCGSizeThatFitsKeepingAspectRatio(CGSize originalSize, CGSize sizeToFit); + +/** + Calculates a size that fits an original size into a different size preserving the aspect ratio and filling the target size. + */ +CGSize DTCGSizeThatFillsKeepingAspectRatio(CGSize originalSize, CGSize sizeToFit); + +/** + Replacement for buggy CGSizeMakeWithDictionaryRepresentation + @param dict The dictionary containing an encoded `CGSize` + @param size The `CGSize` to decode from the dictionary + @see http://www.cocoanetics.com/2012/09/radar-cgrectmakewithdictionaryrepresentation/ + */ +BOOL DTCGSizeMakeWithDictionaryRepresentation(NSDictionary *dict, CGSize *size); + +/** + Replacement for buggy CGSizeCreateDictionaryRepresentation + @param size The `CGSize` to encode in the returned dictionary + @see http://www.cocoanetics.com/2012/09/radar-cgrectmakewithdictionaryrepresentation/ + */ +NSDictionary *DTCGSizeCreateDictionaryRepresentation(CGSize size); + +/** + Replacement for buggy CGRectMakeWithDictionaryRepresentation + @param dict The dictionary containing an encoded `CGRect` + @param rect The `CGRect` to decode from the dictionary + @see http://www.cocoanetics.com/2012/09/radar-cgrectmakewithdictionaryrepresentation/ + */ +BOOL DTCGRectMakeWithDictionaryRepresentation(NSDictionary *dict, CGRect *rect); + +/** + Replacement for buggy CGRectCreateDictionaryRepresentation + @param rect The `CGRect` to encode in the returned dictionary + @see http://www.cocoanetics.com/2012/09/radar-cgrectmakewithdictionaryrepresentation/ + */ +NSDictionary *DTCGRectCreateDictionaryRepresentation(CGRect rect); + +/** + Convenience method to find the center of a CGRect. Uses CGRectGetMidX and CGRectGetMidY. + @returns The point which is the center of rect. + */ +CGPoint DTCGRectCenter(CGRect rect); diff --git a/Pods/DTFoundation/Core/Source/DTCoreGraphicsUtils.m b/Pods/DTFoundation/Core/Source/DTCoreGraphicsUtils.m new file mode 100644 index 0000000..906b3c1 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTCoreGraphicsUtils.m @@ -0,0 +1,119 @@ +// +// DTCoreGraphicsUtils.m +// DTFoundation +// +// Created by Oliver Drobnik on 7/18/10. +// Copyright 2010 Cocoanetics. All rights reserved. +// + +#import "DTCoreGraphicsUtils.h" + +CGSize DTCGSizeThatFitsKeepingAspectRatio(CGSize originalSize, CGSize sizeToFit) +{ + CGFloat necessaryZoomWidth = sizeToFit.width / originalSize.width; + CGFloat necessaryZoomHeight = sizeToFit.height / originalSize.height; + + CGFloat smallerZoom = MIN(necessaryZoomWidth, necessaryZoomHeight); + + return CGSizeMake(round(originalSize.width*smallerZoom), round(originalSize.height*smallerZoom)); +} + +CGSize DTCGSizeThatFillsKeepingAspectRatio(CGSize originalSize, CGSize sizeToFit) +{ + CGFloat necessaryZoomWidth = sizeToFit.width / originalSize.width; + CGFloat necessaryZoomHeight = sizeToFit.height / originalSize.height; + + CGFloat largerZoom = MAX(necessaryZoomWidth, necessaryZoomHeight); + + return CGSizeMake(round(originalSize.width*largerZoom), round(originalSize.height*largerZoom)); +} + +BOOL DTCGSizeMakeWithDictionaryRepresentation(NSDictionary *dict, CGSize *size) +{ + NSNumber *widthNumber = [dict objectForKey:@"Width"]; + NSNumber *heightNumber = [dict objectForKey:@"Height"]; + + if (!widthNumber || !heightNumber) + { + return NO; + } + + if (size) + { +#if CGFLOAT_IS_DOUBLE + size->width = [widthNumber doubleValue]; + size->height = [heightNumber doubleValue]; +#else + size->width = [widthNumber floatValue]; + size->height = [heightNumber floatValue]; +#endif + } + + return YES; +} + +NSDictionary *DTCGSizeCreateDictionaryRepresentation(CGSize size) +{ +#if CGFLOAT_IS_DOUBLE + NSNumber *widthNumber = [NSNumber numberWithDouble:size.width]; + NSNumber *heightNumber = [NSNumber numberWithDouble:size.height]; +#else + NSNumber *widthNumber = [NSNumber numberWithFloat:size.width]; + NSNumber *heightNumber = [NSNumber numberWithFloat:size.height]; +#endif + + return [NSDictionary dictionaryWithObjectsAndKeys:widthNumber, @"Width", heightNumber, @"Height", nil]; +} + + +BOOL DTCGRectMakeWithDictionaryRepresentation(NSDictionary *dict, CGRect *rect) +{ + NSNumber *widthNumber = [dict objectForKey:@"Width"]; + NSNumber *heightNumber = [dict objectForKey:@"Height"]; + NSNumber *xNumber = [dict objectForKey:@"X"]; + NSNumber *yNumber = [dict objectForKey:@"Y"]; + + if (!widthNumber || !heightNumber || !xNumber || !yNumber) + { + return NO; + } + + if (rect) + { +#if CGFLOAT_IS_DOUBLE + rect->origin.x = [xNumber doubleValue]; + rect->origin.y = [yNumber doubleValue]; + rect->size.width = [widthNumber doubleValue]; + rect->size.height = [heightNumber doubleValue]; +#else + rect->origin.x = [xNumber floatValue]; + rect->origin.y = [yNumber floatValue]; + rect->size.width = [widthNumber floatValue]; + rect->size.height = [heightNumber floatValue]; +#endif + } + + return YES; +} + +NSDictionary *DTCGRectCreateDictionaryRepresentation(CGRect rect) +{ +#if CGFLOAT_IS_DOUBLE + NSNumber *widthNumber = [NSNumber numberWithDouble:rect.size.width]; + NSNumber *heightNumber = [NSNumber numberWithDouble:rect.size.height]; + NSNumber *xNumber = [NSNumber numberWithDouble:rect.origin.x]; + NSNumber *yNumber = [NSNumber numberWithDouble:rect.origin.y]; +#else + NSNumber *widthNumber = [NSNumber numberWithFloat:rect.size.width]; + NSNumber *heightNumber = [NSNumber numberWithFloat:rect.size.height]; + NSNumber *xNumber = [NSNumber numberWithFloat:rect.origin.x]; + NSNumber *yNumber = [NSNumber numberWithFloat:rect.origin.y]; +#endif + + return [NSDictionary dictionaryWithObjectsAndKeys:widthNumber, @"Width", heightNumber, @"Height", xNumber, @"X", yNumber, @"Y", nil]; +} + +CGPoint DTCGRectCenter(CGRect rect) +{ + return (CGPoint){CGRectGetMidX(rect), CGRectGetMidY(rect)}; +} \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/DTExtendedFileAttributes.h b/Pods/DTFoundation/Core/Source/DTExtendedFileAttributes.h new file mode 100644 index 0000000..b6bdb2f --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTExtendedFileAttributes.h @@ -0,0 +1,63 @@ +// +// DTExtendedFileAttributes.h +// DTFoundation +// +// Created by Oliver Drobnik on 3/6/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + This class provides read/write access to extended file attributes of a file or folder. It wraps the standard xattr Posix functions to do that. + + Because the file system does not keep track of the data types saved in extended attributes this API so far reads and writes strings. +*/ +@interface DTExtendedFileAttributes : NSObject + + +/** + @name Creating an Extended File Attribute Manager + */ + + +/** + Creates an Extended File Attribute Manager. + + @param path The file path + */ +- (id)initWithPath:(NSString *)path; + + +/** + @name Reading/Writing Extended Attributes + */ + +/** + Removes an extended file attribute from the receiver. + + @param attribute The name of the attribute. + @returns `YES` if successful. + */ +- (BOOL)removeAttribute:(NSString *)attribute; + + +/** + Sets the value of an extended file attribute for the receiver. + + If the value is `nil` then this is the same as calling . + + @param value The string to save for this attribute. + @param attribute The name of the attribute. + @returns `YES` if successful. + */ +- (BOOL)setValue:(NSString *)value forAttribute:(NSString *)attribute; + + +/** + Gets the value of an extended file attribute from the receiver. + + @param attribute The name of the attribute. + @returns The string for the value or `nil` if the value is not set. + */ +- (NSString *)valueForAttribute:(NSString *)attribute; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTExtendedFileAttributes.m b/Pods/DTFoundation/Core/Source/DTExtendedFileAttributes.m new file mode 100644 index 0000000..67ae047 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTExtendedFileAttributes.m @@ -0,0 +1,100 @@ +// +// DTExtendedFileAttributes.m +// DTFoundation +// +// Created by Oliver Drobnik on 3/6/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTExtendedFileAttributes.h" + +#import + +@implementation DTExtendedFileAttributes +{ + NSString *_path; +} + +- (id)initWithPath:(NSString *)path +{ + self = [super init]; + if (self) + { + if (![path length]) + { + return nil; + } + + _path = path; + } + + return self; +} + +- (BOOL)removeAttribute:(NSString *)attribute +{ + const char *attrName = [attribute UTF8String]; + const char *filePath = [_path fileSystemRepresentation]; + + int result = removexattr(filePath, attrName, 0); + + return (result==0); +} + +- (BOOL)setValue:(NSString *)value forAttribute:(NSString *)attribute +{ + if (![attribute length]) + { + return NO; + } + + if (!value) + { + // remove it instead + return [self removeAttribute:attribute]; + } + + const char *attrName = [attribute UTF8String]; + const char *filePath = [_path fileSystemRepresentation]; + + const char *val = [value UTF8String]; + + int result = setxattr(filePath, attrName, val, strlen(val), 0, 0); + + return (result==0); +} + +- (NSString *)valueForAttribute:(NSString *)attribute +{ + if (![attribute length]) + { + return nil; + } + + const char *attrName = [attribute UTF8String]; + const char *filePath = [_path fileSystemRepresentation]; + + // get size of needed buffer + ssize_t bufferLength = getxattr(filePath, attrName, NULL, 0, 0, 0); + + if (bufferLength<=0) + { + return nil; + } + + // make a buffer of sufficient length + char *buffer = malloc(bufferLength); + + // now actually get the attribute string + getxattr(filePath, attrName, buffer, bufferLength, 0, 0); + + // convert to NSString + NSString *retString = [[NSString alloc] initWithBytes:buffer length:bufferLength encoding:NSUTF8StringEncoding]; + + // release buffer + free(buffer); + + return retString; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/DTFolderMonitor.h b/Pods/DTFoundation/Core/Source/DTFolderMonitor.h new file mode 100644 index 0000000..8139ac4 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTFolderMonitor.h @@ -0,0 +1,48 @@ +// +// DTFolderMonitor.h +// DTFoundation +// +// Created by Oliver Drobnik on 05.08.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +// The block to execute if a monitored folder changes +typedef void (^DTFolderMonitorBlock) (void); + +/** + Class for monitoring changes on a folder. This can be used to monitor the application documents folder for changes in the files there if the user adds or removes files via iTunes file sharing. + */ + +@interface DTFolderMonitor : NSObject + +/** + @name Creating a Folder Monitor + */ + +/** + Creates a new DTFolderMonitor to watch the folder at the given URL. Whenever there is a change on this folder the block is executed. + + The URL must be a file URL. Both the URL and the block parameter are mandatory. The block is being dispatched on a background queue. + + @param URL The monitored folder URL + @param block The block to execute if the folder is being modified + @returns The instantiated monitor in suspended mode. Call -startMonitoring to start monitoring. + */ ++ (DTFolderMonitor *)folderMonitorForURL:(NSURL *)URL block:(DTFolderMonitorBlock)block; + + +/** + @name Starting/Stopping Monitoring + */ + +/** + Start monitoring the folder. A monitor can be started and stopped multiple times. + */ +- (void)startMonitoring; + +/** + Stop monitoring the folder. A monitor can be started and stopped multiple times. + */ +- (void)stopMonitoring; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTFolderMonitor.m b/Pods/DTFoundation/Core/Source/DTFolderMonitor.m new file mode 100644 index 0000000..9bac645 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTFolderMonitor.m @@ -0,0 +1,103 @@ +// +// DTFolderMonitor.m +// DTFoundation +// +// Created by Oliver Drobnik on 05.08.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +#import "DTFolderMonitor.h" + +@implementation DTFolderMonitor +{ + NSURL *_URL; + DTFolderMonitorBlock _block; + + int _fileDescriptor; + dispatch_queue_t _queue; + dispatch_source_t _source; +} + ++ (DTFolderMonitor *)folderMonitorForURL:(NSURL *)URL block:(DTFolderMonitorBlock)block +{ + return [[DTFolderMonitor alloc] initWithURL:URL block:block]; +} + +- (instancetype)initWithURL:(NSURL *)URL block:(DTFolderMonitorBlock)block +{ + NSParameterAssert(URL); + NSParameterAssert(block); + NSAssert([URL isFileURL], @"URL Parameter must be a folder URL"); + + self = [super init]; + + if (self) + { + _URL = URL; + _block = [block copy]; + _queue = dispatch_queue_create("DTFolderMonitor Queue", 0); + } + + return self; +} + +- (void)dealloc +{ + [self stopMonitoring]; + +#if !OS_OBJECT_USE_OBJC + dispatch_release(_queue); +#endif +} + +- (void)startMonitoring +{ + @synchronized(self) + { + if (_source) + { + return; + } + + _fileDescriptor = open([_URL.path fileSystemRepresentation], O_EVTONLY); + + if (!_fileDescriptor) { + return; + } + + // watch the file descriptor for writes + _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, _fileDescriptor, DISPATCH_VNODE_WRITE, _queue); + + // call the passed block if the source is modified + dispatch_source_set_event_handler(_source, _block); + + // close the file descriptor when the dispatch source is cancelled + dispatch_source_set_cancel_handler(_source, ^{ + + close(_fileDescriptor); + }); + + // at this point the dispatch source is paused, so start watching + dispatch_resume(_source); + } +} + +- (void)stopMonitoring +{ + @synchronized(self) + { + if (!_source) + { + return; + } + + dispatch_source_cancel(_source); + +#if !OS_OBJECT_USE_OBJC + dispatch_release(_source); +#endif + _source = nil; + } +} + +@end diff --git a/Pods/DTFoundation/Core/Source/DTFoundationConstants.h b/Pods/DTFoundation/Core/Source/DTFoundationConstants.h new file mode 100644 index 0000000..0600ffa --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTFoundationConstants.h @@ -0,0 +1,11 @@ +// +// DTFoundationConstants.h +// DTFoundation +// +// Created by Stefan Gugarel on 10/18/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + + +// string constant for NSError domain +extern NSString * const DTFoundationErrorDomain; \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/DTFoundationConstants.m b/Pods/DTFoundation/Core/Source/DTFoundationConstants.m new file mode 100644 index 0000000..e4cbe87 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTFoundationConstants.m @@ -0,0 +1,11 @@ +// +// DTFoundationConstants.m +// DTFoundation +// +// Created by Stefan Gugarel on 10/18/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTFoundationConstants.h" + +NSString * const DTFoundationErrorDomain = @"DTFoundation"; \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/DTHTMLParser/DTHTMLParser.h b/Pods/DTFoundation/Core/Source/DTHTMLParser/DTHTMLParser.h new file mode 100644 index 0000000..df2ba1b --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTHTMLParser/DTHTMLParser.h @@ -0,0 +1,192 @@ +// +// DTHTMLParser.h +// DTCoreText +// +// Created by Oliver Drobnik on 1/18/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + + +#import "DTWeakSupport.h" + +@class DTHTMLParser; +/** The DTHTMLParserDelegate protocol defines the optional methods implemented by delegates of DTHTMLParser objects. + + Dependencies: libxml2.dylib + */ +@protocol DTHTMLParserDelegate + +@optional + +/** + Sent by the parser object to the delegate when it begins parsing a document. + + @param parser A parser object. + */ +- (void)parserDidStartDocument:(DTHTMLParser *)parser; + +/** + Sent by the parser object to the delegate when it has successfully completed parsing + + @param parser A parser object. + */ +- (void)parserDidEndDocument:(DTHTMLParser *)parser; + +/** + Sent by a parser object to its delegate when it encounters a start tag for a given element. + + @param parser A parser object. + @param elementName A string that is the name of an element (in its start tag). + @param attributeDict A dictionary that contains any attributes associated with the element. Keys are the names of attributes, and values are attribute values. + */ +- (void)parser:(DTHTMLParser *)parser didStartElement:(NSString *)elementName attributes:(NSDictionary *)attributeDict; + +/** + Sent by a parser object to its delegate when it encounters an end tag for a specific element. + + @param parser A parser object. + @param elementName A string that is the name of an element (in its end tag). + */ +- (void)parser:(DTHTMLParser *)parser didEndElement:(NSString *)elementName; + +/** + Sent by a parser object to provide its delegate with a string representing all or part of the characters of the current element. + + The parser object may send the delegate several parser:foundCharacters: messages to report the characters of an element. Because string may be only part of the total character content for the current element, you should append it to the current accumulation of characters until the element changes. + + @param parser A parser object. + @param string A string representing the complete or partial textual content of the current element. + */ +- (void)parser:(DTHTMLParser *)parser foundCharacters:(NSString *)string; + +/** + Sent by a parser object to its delegate when it encounters a comment in the HTML. + + @param parser A DTHTMLParser object parsing HTML. + @param comment A string that is a the content of a comment in the XML. + */ +- (void)parser:(DTHTMLParser *)parser foundComment:(NSString *)comment; + +/** + Sent by a parser object to its delegate when it encounters a CDATA block. + + Through this method the parser object passes the contents of the block to its delegate in an NSData object. The CDATA block is character data that is ignored by the parser. The encoding of the character data is UTF-8. To convert the data object to a string object, use the NSString method initWithData:encoding:. Note: CSS style blocks are returned as CDATA. + + @param parser A DTHTMLParser object parsing HTML. + @param CDATABlock A data object containing a block of CDATA. + */ +- (void)parser:(DTHTMLParser *)parser foundCDATA:(NSData *)CDATABlock; + +/** + Sent by a parser object to its delegate when it encounters a processing instruction. + + @param parser A DTHTMLParser object parsing HTML. + @param target A string representing the target of a processing instruction. + @param data A string representing the data for a processing instruction. + */ +- (void)parser:(DTHTMLParser *)parser foundProcessingInstructionWithTarget:(NSString *)target data:(NSString *)data; + +/** + Sent by a parser object to its delegate when it encounters a fatal error. + + When this method is invoked, parsing is stopped. For further information about the error, you can query parseError or you can send the parser a parserError message. You can also send the parser lineNumber and columnNumber messages to further isolate where the error occurred. Typically you implement this method to display information about the error to the user. + + @param parser A parser object. + @param parseError An `NSError` object describing the parsing error that occurred. + */ +- (void)parser:(DTHTMLParser *)parser parseErrorOccurred:(NSError *)parseError; + +@end + +/** Instances of this class parse HTML documents (including DTD declarations) in an event-driven manner. A DTHTMLParser notifies its delegate about the items (elements, attributes, CDATA blocks, comments, and so on) that it encounters as it processes an HTML document. It does not itself do anything with those parsed items except report them. It also reports parsing errors. For convenience, an DTHTMLParser object in the following descriptions is sometimes referred to as a parser object. + */ +@interface DTHTMLParser : NSObject + +/**------------------------------------------------------------------------------------- + @name Initializing a Parser Object + --------------------------------------------------------------------------------------- + */ + +/** + Initializes the receiver with the HTML contents encapsulated in a given data object. + + @param data An `NSData` object containing XML markup. + @param encoding The encoding used for encoding the HTML data + @returns An initialized `DTHTMLParser` object or nil if an error occurs. + */ +- (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding; + +/**------------------------------------------------------------------------------------- + @name Parsing + --------------------------------------------------------------------------------------- + */ + +/** + Starts the event-driven parsing operation. + + If you invoke this method, the delegate, if it implements parser:parseErrorOccurred:, is informed of the cancelled parsing operation. + + @returns `YES` if parsing is successful and `NO` in there is an error or if the parsing operation is aborted. + */ +- (BOOL)parse; + +/** + Stops the parser object. + + @see parse + @see parserError + */ +- (void)abortParsing; + +/** + Sets the receiver’s delegate. + + @param delegate An object that is the new delegate. It is not retained. The delegate must conform to the DTHTMLParserDelegate Protocol protocol. + + @see delegate + */ +- (void)setDelegate:(id )delegate; + +/** + Returns the receiver’s delegate. + + @see delegate + */ +- (id )delegate; + +/** + Returns the column number of the XML document being processed by the receiver. + + The column refers to the nesting level of the HTML elements in the document. You may invoke this method once a parsing operation has begun or after an error occurs. + */ +@property (nonatomic, readonly) NSInteger columnNumber; + +/** + Returns the line number of the HTML document being processed by the receiver. + + You may invoke this method once a parsing operation has begun or after an error occurs. + */ +@property (nonatomic, readonly) NSInteger lineNumber; + +/** + Returns an `NSError` object from which you can obtain information about a parsing error. + + You may invoke this method after a parsing operation abnormally terminates to determine the cause of error. + */ +@property (nonatomic, readonly, strong) NSError *parserError; + +/** + Returns the public identifier of the external entity referenced in the HTML document. + + You may invoke this method once a parsing operation has begun or after an error occurs. + */ +@property (nonatomic, readonly) NSString *publicID; + +/** + Returns the system identifier of the external entity referenced in the HTML document. + + You may invoke this method once a parsing operation has begun or after an error occurs. + */ +@property (nonatomic, readonly) NSString *systemID; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTHTMLParser/DTHTMLParser.m b/Pods/DTFoundation/Core/Source/DTHTMLParser/DTHTMLParser.m new file mode 100644 index 0000000..47089b4 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTHTMLParser/DTHTMLParser.m @@ -0,0 +1,484 @@ +// +// DTHTMLParser.m +// DTFoundation +// +// Created by Oliver Drobnik on 1/18/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTHTMLParser.h" +#import + + +@interface DTHTMLParser() + +@property (nonatomic, strong) NSError *parserError; +@property (nonatomic, assign) NSStringEncoding encoding; + +- (void)_resetAccumulateBufferAndReportCharacters; +- (void)_accumulateCharacters:(const xmlChar *)characters length:(int)length; + +@end + + +#pragma mark Event function prototypes + +void _startDocument(void *context); +void _endDocument(void *context); +void _startElement(void *context, const xmlChar *name,const xmlChar **atts); +void _startElement_no_delegate(void *context, const xmlChar *name, const xmlChar **atts); +void _endElement(void *context, const xmlChar *name); +void _endElement_no_delegate(void *context, const xmlChar *chars); +void _characters(void *context, const xmlChar *ch, int len); +void _comment(void *context, const xmlChar *value); +void _dterror(void *context, const char *msg, ...); +void _cdataBlock(void *context, const xmlChar *value, int len); +void _processingInstruction (void *context, const xmlChar *target, const xmlChar *data); + +#pragma mark Event functions +void _startDocument(void *context) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself.delegate parserDidStartDocument:myself]; +} + +void _endDocument(void *context) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself.delegate parserDidEndDocument:myself]; +} + +void _startElement(void *context, const xmlChar *name, const xmlChar **atts) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself _resetAccumulateBufferAndReportCharacters]; + + NSString *nameStr = [NSString stringWithUTF8String:(char *)name]; + + NSMutableDictionary *attributes = nil; + + if (atts) + { + NSString *key = nil; + NSString *value = nil; + + attributes = [[NSMutableDictionary alloc] init]; + + int i = 0; + while (1) + { + char *att = (char *)atts[i++]; + + if (!key) + { + if (!att) + { + // we're done + break; + } + + key = [NSString stringWithUTF8String:att]; + } + else + { + if (att) + { + value = [NSString stringWithUTF8String:att]; + } + else + { + // solo attribute + value = key; + } + + [attributes setObject:value forKey:key]; + + value = nil; + key = nil; + } + } + } + + [myself.delegate parser:myself didStartElement:nameStr attributes:attributes]; +} + +void _startElement_no_delegate(void *context, const xmlChar *name, const xmlChar **atts) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself _resetAccumulateBufferAndReportCharacters]; +} + +void _endElement(void *context, const xmlChar *chars) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself _resetAccumulateBufferAndReportCharacters]; + + NSString *nameStr = [NSString stringWithUTF8String:(char *)chars]; + + [myself.delegate parser:myself didEndElement:nameStr]; +} + +void _endElement_no_delegate(void *context, const xmlChar *chars) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself _resetAccumulateBufferAndReportCharacters]; +} + +// libxml reports characters in batches of at most 1000 at a time +// in addition, entities are reported separately +void _characters(void *context, const xmlChar *chars, int len) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + [myself _accumulateCharacters:chars length:len]; +} + +void _comment(void *context, const xmlChar *chars) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + NSString *string = [NSString stringWithCString:(const char *)chars encoding:myself.encoding]; + + [myself.delegate parser:myself foundComment:string]; +} + +void _dterror(void *context, const char *msg, ...) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + char string[256]; + va_list arg_ptr; + + va_start(arg_ptr, msg); + vsnprintf(string, 256, msg, arg_ptr); + va_end(arg_ptr); + + NSString *errorMsg = [NSString stringWithUTF8String:string]; + + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey]; + myself.parserError = [NSError errorWithDomain:@"DTHTMLParser" code:1 userInfo:userInfo]; + + [myself.delegate parser:myself parseErrorOccurred:myself.parserError]; +} + +void _cdataBlock(void *context, const xmlChar *value, int len) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + NSData *data = [NSData dataWithBytes:(const void *)value length:len]; + + [myself.delegate parser:myself foundCDATA:data]; +} + +void _processingInstruction (void *context, const xmlChar *target, const xmlChar *data) +{ + DTHTMLParser *myself = (__bridge DTHTMLParser *)context; + + NSStringEncoding encoding = myself.encoding; + + NSString *targetStr = [NSString stringWithCString:(const char *)target encoding:encoding]; + NSString *dataStr = [NSString stringWithCString:(const char *)data encoding:encoding]; + + [myself.delegate parser:myself foundProcessingInstructionWithTarget:targetStr data:dataStr]; +} + +@implementation DTHTMLParser +{ + htmlSAXHandler _handler; + + NSData *_data; + NSStringEncoding _encoding; + + DT_WEAK_VARIABLE id _delegate; + htmlParserCtxtPtr _parserContext; + + NSMutableString *_accumulateBuffer; + + BOOL _isAborting; +} + + +- (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding +{ + if (!data) + { + return nil; + } + + self = [super init]; + if (self) + { + _data = data; + _encoding = encoding; + + xmlSAX2InitHtmlDefaultSAXHandler(&_handler); + + // set default handlers as we would crash otherwise + self.delegate = nil; + } + + return self; +} + +- (void)dealloc +{ + if (_parserContext) + { + htmlFreeParserCtxt(_parserContext); + } +} + +- (void)_resetAccumulateBufferAndReportCharacters +{ + if (!_accumulateBuffer.length) + { + // nothing in the buffer + return; + } + + [self.delegate parser:self foundCharacters:_accumulateBuffer]; + + // reset buffer + _accumulateBuffer = nil; +} + +- (void)_accumulateCharacters:(const xmlChar *)characters length:(int)length +{ + if (!_accumulateBuffer) + { + _accumulateBuffer = [[NSMutableString alloc] initWithBytes:characters length:length encoding:NSUTF8StringEncoding]; + } + else + { + // we don't need to use the copy version since _accumulateBuffer will copy characters immediately + [_accumulateBuffer appendString:[[NSString alloc] initWithBytesNoCopy:(void *)characters length:length encoding:NSUTF8StringEncoding freeWhenDone:NO]]; + } +} + +- (BOOL)parse +{ + void *dataBytes = (char *)[_data bytes]; + unsigned long dataSize = [_data length]; + + // detect encoding if necessary + xmlCharEncoding charEnc = XML_CHAR_ENCODING_NONE; + + if (!_encoding) + { + charEnc = xmlDetectCharEncoding(dataBytes, (int)dataSize); + } + else + { + // convert the encoding + CFStringEncoding cfenc = CFStringConvertNSStringEncodingToEncoding(_encoding); + + if (cfenc != kCFStringEncodingInvalidId) + { + CFStringRef cfencstr = CFStringConvertEncodingToIANACharSetName(cfenc); + + if (cfencstr) + { + NSString *NS_VALID_UNTIL_END_OF_SCOPE encstr = [NSString stringWithString:(__bridge NSString*)cfencstr]; + const char *enc = [encstr UTF8String]; + + charEnc = xmlParseCharEncoding(enc); + } + } + } + + // create a parse context + _parserContext = htmlCreatePushParserCtxt(&_handler, (__bridge void *)self, dataBytes, (int)dataSize, NULL, charEnc); + + // set some options + htmlCtxtUseOptions(_parserContext, HTML_PARSE_RECOVER | HTML_PARSE_NONET | HTML_PARSE_COMPACT | HTML_PARSE_NOBLANKS); + + // parse! + int result = htmlParseDocument(_parserContext); + + return (result==0 && !_isAborting); +} + +- (void)abortParsing +{ + if (_parserContext) + { + // apparently this frees it too + xmlStopParser(_parserContext); + _parserContext = NULL; + } + + _isAborting = YES; + + // prevent future callbacks + _handler.startDocument = NULL; + _handler.endDocument = NULL; + _handler.startElement = NULL; + _handler.endElement = NULL; + _handler.characters = NULL; + _handler.comment = NULL; + _handler.error = NULL; + _handler.processingInstruction = NULL; + + // inform delegate + __strong __typeof__(_delegate) delegate = _delegate; + if ([delegate respondsToSelector:@selector(parser:parseErrorOccurred:)]) + { + [delegate parser:self parseErrorOccurred:self.parserError]; + } +} + +#pragma mark Properties + +- (id )delegate +{ + return _delegate; +} + +- (void)setDelegate:(id )delegate +{ + _delegate = delegate; + + if ([delegate respondsToSelector:@selector(parserDidStartDocument:)]) + { + _handler.startDocument = _startDocument; + } + else + { + _handler.startDocument = NULL; + } + + if ([delegate respondsToSelector:@selector(parserDidEndDocument:)]) + { + _handler.endDocument = _endDocument; + } + else + { + _handler.endDocument = NULL; + } + + if ([delegate respondsToSelector:@selector(parser:foundCharacters:)]) + { + _handler.characters = _characters; + } + else + { + _handler.characters = NULL; + } + + if ([delegate respondsToSelector:@selector(parser:didStartElement:attributes:)]) + { + _handler.startElement = _startElement; + } + else + { + // if there is a character handler we need to still report start of elements for accumulation + if (_handler.characters) + { + _handler.startElement = _startElement_no_delegate; + } + else + { + _handler.startElement = NULL; + } + } + + if ([delegate respondsToSelector:@selector(parser:didEndElement:)]) + { + _handler.endElement = _endElement; + } + else + { + // if there is a character handler we need to still report start of elements for accumulation + if (_handler.characters) + { + _handler.endElement = _endElement_no_delegate; + } + else + { + _handler.endElement = NULL; + } + } + + if ([delegate respondsToSelector:@selector(parser:foundComment:)]) + { + _handler.comment = _comment; + } + else + { + _handler.comment = NULL; + } + + if ([delegate respondsToSelector:@selector(parser:parseErrorOccurred:)]) + { + _handler.error = _dterror; + } + else + { + _handler.error = NULL; + } + + if ([delegate respondsToSelector:@selector(parser:foundCDATA:)]) + { + _handler.cdataBlock = _cdataBlock; + } + else + { + _handler.cdataBlock = NULL; + } + + if ([delegate respondsToSelector:@selector(parser:foundProcessingInstructionWithTarget:data:)]) + { + _handler.processingInstruction = _processingInstruction; + } + else + { + _handler.processingInstruction = NULL; + } +} + +- (NSInteger)lineNumber +{ + return xmlSAX2GetLineNumber(_parserContext); +} + +- (NSInteger)columnNumber +{ + return xmlSAX2GetColumnNumber(_parserContext); +} + +- (NSString *)systemID +{ + char *systemID = (char *)xmlSAX2GetSystemId(_parserContext); + + if (!systemID) + { + return nil; + } + + return [NSString stringWithUTF8String:systemID]; +} + +- (NSString *)publicID +{ + char *publicID = (char *)xmlSAX2GetPublicId(_parserContext); + + if (!publicID) + { + return nil; + } + + return [NSString stringWithUTF8String:publicID]; +} + + +@synthesize parserError = _parserError; +@synthesize encoding = _encoding; + + +@end diff --git a/Pods/DTFoundation/Core/Source/DTLog.h b/Pods/DTFoundation/Core/Source/DTLog.h new file mode 100644 index 0000000..62d0596 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTLog.h @@ -0,0 +1,156 @@ +// +// DTLog.h +// DTFoundation +// +// Created by Oliver Drobnik on 06.08.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +/** + Replacement for `NSLog` which can be configured to output certain log levels at run time. + */ + +// block signature called for each log statement +typedef void (^DTLogBlock)(NSUInteger logLevel, NSString *fileName, NSUInteger lineNumber, NSString *methodName, NSString *format, ...); + + +// internal variables needed by macros +extern DTLogBlock DTLogHandler; +extern NSUInteger DTCurrentLogLevel; + +/** + There is a macro for each ASL log level: + + - DTLogEmergency (0) + - DTLogAlert (1) + - DTLogCritical (2) + - DTLogError (3) + - DTLogWarning (4) + - DTLogNotice (5) + - DTLogInfo (6) + - DTLogDebug (7) + */ + +/** + Constants for log levels used by DTLog + */ +typedef NS_ENUM(NSUInteger, DTLogLevel) +{ + /** + Log level for *emergency* messages + */ + DTLogLevelEmergency = 0, + + /** + Log level for *alert* messages + */ + DTLogLevelAlert = 1, + + /** + Log level for *critical* messages + */ + DTLogLevelCritical = 2, + + /** + Log level for *error* messages + */ + DTLogLevelError = 3, + + /** + Log level for *warning* messages + */ + DTLogLevelWarning = 4, + + /** + Log level for *notice* messages + */ + DTLogLevelNotice = 5, + + /** + Log level for *info* messages. This is the default log level for DTLog. + */ + DTLogLevelInfo = 6, + + /** + Log level for *debug* messages + */ + DTLogLevelDebug = 7 +}; + +/** + @name Logging Functions + */ + +/** + Sets the block to be executed for messages with a log level less or equal the currently set log level + @param handler The block to handle log output + */ +void DTLogSetLoggerBlock(DTLogBlock handler); + +/** + Modifies the current log level + @param logLevel The ASL log level (0-7) to set, lower numbers being more important + */ +void DTLogSetLogLevel(NSUInteger logLevel); + +/** + Variant of DTLogMessage that takes a va_list. + @param logLevel The DTLogLevel for the message + @param format The log message format + @param args The va_list of arguments +*/ +void DTLogMessagev(DTLogLevel logLevel, NSString *format, va_list args); + +/** + Same as `NSLog` but allows for setting a message log level + @param logLevel The DTLogLevel for the message + @param format The log message format and optional variables + */ +void DTLogMessage(DTLogLevel logLevel, NSString *format, ...); + +/** + Retrieves the log messages currently available for the running app + @returns an `NSArray` of `NSDictionary` entries + */ +NSArray *DTLogGetMessages(void); + +/** + @name Macros + */ + +// log macro for error level (0) +#define DTLogEmergency(format, ...) DTLogCallHandlerIfLevel(DTLogLevelEmergency, format, ##__VA_ARGS__) + +// log macro for error level (1) +#define DTLogAlert(format, ...) DTLogCallHandlerIfLevel(DTLogLevelAlert, format, ##__VA_ARGS__) + +// log macro for error level (2) +#define DTLogCritical(format, ...) DTLogCallHandlerIfLevel(DTLogLevelCritical, format, ##__VA_ARGS__) + +// log macro for error level (3) +#define DTLogError(format, ...) DTLogCallHandlerIfLevel(DTLogLevelError, format, ##__VA_ARGS__) + +// log macro for error level (4) +#define DTLogWarning(format, ...) DTLogCallHandlerIfLevel(DTLogLevelWarning, format, ##__VA_ARGS__) + +// log macro for error level (5) +#define DTLogNotice(format, ...) DTLogCallHandlerIfLevel(DTLogLevelNotice, format, ##__VA_ARGS__) + +// log macro for info level (6) +#define DTLogInfo(format, ...) DTLogCallHandlerIfLevel(DTLogLevelInfo, format, ##__VA_ARGS__) + +// log macro for debug level (7) +#define DTLogDebug(format, ...) DTLogCallHandlerIfLevel(DTLogLevelDebug, format, ##__VA_ARGS__) + +// macro that gets called by individual level macros +#define DTLogCallHandlerIfLevel(logLevel, format, ...) \ +if (DTLogHandler && DTCurrentLogLevel>=logLevel) DTLogHandler(logLevel, DTLogSourceFileName, DTLogSourceLineNumber, DTLogSourceMethodName, format, ##__VA_ARGS__) + +// helper to get the current source file name as NSString +#define DTLogSourceFileName [[NSString stringWithUTF8String:__FILE__] lastPathComponent] + +// helper to get current method name +#define DTLogSourceMethodName [NSString stringWithUTF8String:__PRETTY_FUNCTION__] + +// helper to get current line number +#define DTLogSourceLineNumber __LINE__ \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/DTLog.m b/Pods/DTFoundation/Core/Source/DTLog.m new file mode 100644 index 0000000..afc9dd7 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTLog.m @@ -0,0 +1,136 @@ +// +// DTLog.m +// DTFoundation +// +// Created by Oliver Drobnik on 06.08.13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +#import "DTLog.h" +#import +#import + +DTLogLevel DTCurrentLogLevel = DTLogLevelInfo; + +#if TARGET_OS_IPHONE +#if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_6_1 && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1 +#define DTLOG_USE_NEW_ASL_METHODS 1 +#endif +#else +#if __MAC_OS_X_VERSION_MIN_REQUIRED > __MAC_10_9 +#define DTLOG_USE_NEW_ASL_METHODS 1 +#endif +#endif + +#if DEBUG + +// set default handler for debug mode +DTLogBlock DTLogHandler = ^(NSUInteger logLevel, NSString *fileName, NSUInteger lineNumber, NSString *methodName, NSString *format, ...) +{ + va_list args; + va_start(args, format); + + DTLogMessagev(logLevel, format, args); + + va_end(args); +}; + +#else + +// set no default handler for non-DEBUG mode +DTLogBlock DTLogHandler = NULL; + +#endif + +#pragma mark - Logging Functions + +void DTLogSetLoggerBlock(DTLogBlock handler) +{ + DTLogHandler = [handler copy]; +} + +void DTLogSetLogLevel(DTLogLevel logLevel) +{ + DTCurrentLogLevel = logLevel; +} + +void DTLogMessagev(DTLogLevel logLevel, NSString *format, va_list args) +{ + NSString *facility = [[NSBundle mainBundle] bundleIdentifier]; + aslclient client = asl_open(NULL, [facility UTF8String], ASL_OPT_STDERR); // also log to stderr + + aslmsg msg = asl_new(ASL_TYPE_MSG); + asl_set(msg, ASL_KEY_READ_UID, "-1"); // without this the message cannot be found by asl_search + + // convert to via NSString, since printf does not know %@ + NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; + + asl_log(client, msg, logLevel, "%s", [message UTF8String]); + + asl_free(msg); + + va_end(args); +} + +void DTLogMessage(DTLogLevel logLevel, NSString *format, ...) +{ + va_list args; + va_start(args, format); + + DTLogMessagev(logLevel, format, args); + + va_end(args); +} + +NSArray *DTLogGetMessages(void) +{ + aslmsg query, message; + int i; + const char *key, *val; + + NSString *facility = [[NSBundle mainBundle] bundleIdentifier]; + + query = asl_new(ASL_TYPE_QUERY); + + // search only for current app messages + asl_set_query(query, ASL_KEY_FACILITY, [facility UTF8String], ASL_QUERY_OP_EQUAL); + + aslresponse response = asl_search(NULL, query); + + NSMutableArray *tmpArray = [NSMutableArray array]; + +#if DTLOG_USE_NEW_ASL_METHODS + while ((message = asl_next(response))) +#else + while ((message = aslresponse_next(response))) +#endif + { + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + for (i = 0; ((key = asl_key(message, i))); i++) + { + NSString *keyString = [NSString stringWithUTF8String:(char *)key]; + + val = asl_get(message, key); + + NSString *string = val?[NSString stringWithUTF8String:val]:@""; + [tmpDict setObject:string forKey:keyString]; + } + + [tmpArray addObject:tmpDict]; + } + + asl_free(query); +#if DTLOG_USE_NEW_ASL_METHODS + asl_release(response); +#else + aslresponse_free(response); +#endif + + if ([tmpArray count]) + { + return [tmpArray copy]; + } + + return nil; +} diff --git a/Pods/DTFoundation/Core/Source/DTVersion.h b/Pods/DTFoundation/Core/Source/DTVersion.h new file mode 100644 index 0000000..9c2510c --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTVersion.h @@ -0,0 +1,158 @@ +// +// DTVersion.h +// DTFoundation +// +// Created by Oliver Drobnik on 11/25/11. +// Copyright (c) 2012 Cocoanetics. All rights reserved. + +/** + Class that represents a version number comprised of major, minor and maintenance number separated by dots. For example "1.2.2". + This encapsulation simplifies comparing versions against each other. Sub-numbers that are omitted on creating a `DTVersion` are assumed to be 0. + */ +@interface DTVersion : NSObject +{ + NSUInteger _major; + NSUInteger _minor; + NSUInteger _maintenance; + NSUInteger _build; +} + +/**------------------------------------------------------------------------------------- + @name Properties + --------------------------------------------------------------------------------------- + */ + +/** + The major version number + */ +@property (nonatomic, readonly) NSUInteger major; + +/** + The minor version number + */ +@property (nonatomic, readonly) NSUInteger minor; + + +/** + The maintenance/hotfix version number + */ +@property (nonatomic, readonly) NSUInteger maintenance; + +/** + The build number + */ +@property (nonatomic, readonly) NSUInteger build; + +/**------------------------------------------------------------------------------------- + @name Creating Versions + --------------------------------------------------------------------------------------- + */ + +/** + Initializes the receiver with major, minor and maintenance version. + @param major The major version number + @param minor The minor version number + @param maintenance The maintainance/hotfix version number + @returns The initialized `DTVersion` + */ +- (DTVersion *)initWithMajor:(NSUInteger)major minor:(NSUInteger)minor maintenance:(NSUInteger)maintenance; + +/** + Initializes the receiver with major, minor and maintenance version. + @param major The major version number + @param minor The minor version number + @param maintenance The maintainance/hotfix version number + @param build The build number + @returns The initialized `DTVersion` + */ +- (DTVersion *)initWithMajor:(NSUInteger)major minor:(NSUInteger)minor maintenance:(NSUInteger)maintenance build:(NSUInteger)build; + +/** + creates and returns a DTVersion object initialized using the provided string + @param versionString The `NSString` to create a `DTVersion` from + @returns A DTVersion object or nil if the string is not a valid version number + */ ++ (DTVersion *)versionWithString:(NSString *)versionString; + +/** + creates and retuns a DTVersion object initialized with the version information of the current application + @returns A DTVersion object or nil if the string of the current application is not a valid version number + */ ++ (DTVersion *)appBundleVersion; + +/** + creates and retuns a DTVersion object initialized with the version information of the operating system + @returns A DTVersion object or nil if the string of the current application is not a valid version number + */ ++ (DTVersion *)osVersion; + +/**------------------------------------------------------------------------------------- + @name Comparing Versions + --------------------------------------------------------------------------------------- + */ + +/** + @param versionString The OS version as `NSString` to compare the receiver to + @returns true if the given version string is valid and less then the osVersion +*/ ++ (BOOL)osVersionIsLessThen:(NSString *)versionString; + +/** + @param versionString The OS version as `NSString` to compare the receiver to + @returns true if the given version string is valid and greater then the osVersion +*/ ++ (BOOL)osVersionIsGreaterThen:(NSString *)versionString; + +/** + @param version The `DTVersion` to compare the receiver to + @returns true if the give version is less then this version +*/ +- (BOOL)isLessThenVersion:(DTVersion *)version; + +/** + @param version The `DTVersion` to compare the receiver to + @returns true if the give version is greater then this version +*/ +- (BOOL)isGreaterThenVersion:(DTVersion *)version; + +/** + @param versionString The version as `NSString` to compare the receiver to + @returns true if the give version is less then this version string +*/ +- (BOOL)isLessThenVersionString:(NSString *)versionString; + +/** + @param versionString The version as `NSString` to compare the receiver to +* @returns true if the give version is greater then version string +*/ +- (BOOL)isGreaterThenVersionString:(NSString *)versionString; + +/** + Compares the receiver against a passed `DTVersion` instance + @param version The `DTVersion` to compare the receiver to + @returns `YES` is the versions are equal + */ +- (BOOL)isEqualToVersion:(DTVersion *)version; + +/** + Compares the receiver against a passed version as `NSString` + @param versionString The version as `NSString` to compare the receiver to + @returns `YES` is the versions are equal + */ +- (BOOL)isEqualToString:(NSString *)versionString; + +/** + Compares the receiver against a passed object + @param object An object of either `NSString` or `DTVersion` + @returns `YES` is the versions are equal + */ +- (BOOL)isEqual:(id)object; + +/** + Compares the receiver against a passed `DTVersion` instance + @param version The `DTVersion` to compare the receiver to + @returns The comparison result + */ +- (NSComparisonResult)compare:(DTVersion *)version; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTVersion.m b/Pods/DTFoundation/Core/Source/DTVersion.m new file mode 100644 index 0000000..9d63c76 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTVersion.m @@ -0,0 +1,265 @@ +// +// DTVersion.m +// DTFoundation +// +// Created by Oliver Drobnik +// Copyright 2012 Cocoanetics. All rights reserved. +// + +#import "DTVersion.h" + +@implementation DTVersion + +#pragma mark Creating Versions + +- (DTVersion *)initWithMajor:(NSUInteger)major minor:(NSUInteger)minor maintenance:(NSUInteger)maintenance build:(NSUInteger)build +{ + self = [super init]; + if (self) { + _major = major; + _minor = minor; + _maintenance = maintenance; + _build = build; + } + return self; +} + +- (DTVersion *)initWithMajor:(NSUInteger)major minor:(NSUInteger)minor maintenance:(NSUInteger)maintenance +{ + self = [self initWithMajor:major minor:minor maintenance:maintenance build:0]; + if (self) + { + } + return self; +} + ++ (DTVersion *)versionWithString:(NSString*)versionString +{ + if (!versionString) + { + return nil; + } + + NSInteger major = 0; + NSInteger minor = 0; + NSInteger maintenance = 0; + NSInteger build = 0; + + NSError *error; + NSString *pattern = @"^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:\\.(\\d+))?(?:$|\\s)"; + + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + + NSTextCheckingResult *match = [regex firstMatchInString:versionString + options:0 + range:NSMakeRange(0, [versionString length])]; + + if (!match) + { + return nil; + } + for (NSUInteger i = 1; i < match.numberOfRanges; i++) + { + NSRange range = [match rangeAtIndex:i]; + if (range.location == NSNotFound) + { + break; + } + NSUInteger value = [[versionString substringWithRange:range] integerValue]; + + switch (i) + { + case 1: + major = value; + break; + case 2: + minor = value; + break; + case 3: + maintenance = value; + break; + case 4: + build = value; + break; + default: + break; + } + } + + if (major >= 0 && + minor >= 0 && + maintenance >= 0 && + build >= 0) + { + return [[DTVersion alloc] initWithMajor:major minor:minor maintenance:maintenance build:build]; + } + + return nil; +} + ++ (DTVersion*)appBundleVersion +{ + NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; + DTVersion* retVersion = [DTVersion versionWithString:version]; + + return retVersion; +} + ++ (DTVersion *)osVersion +{ + static dispatch_once_t onceToken; + static DTVersion *version = nil; + + dispatch_once(&onceToken, ^{ +#if TARGET_OS_IPHONE + NSString *versionStr = [[UIDevice currentDevice] systemVersion]; + version = [DTVersion versionWithString:versionStr]; +#else + NSString *versionStr = [[NSProcessInfo processInfo] operatingSystemVersionString]; + versionStr = [versionStr stringByReplacingOccurrencesOfString:@"Version" withString:@""]; + versionStr = [versionStr stringByReplacingOccurrencesOfString:@"Build" withString:@""]; + versionStr = [versionStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + version = [DTVersion versionWithString:versionStr]; +#endif + }); + + return version; +} + +#pragma mark Comparing Versions + ++ (BOOL)osVersionIsLessThen:(NSString *)versionString +{ + return [[DTVersion osVersion] isLessThenVersionString:versionString]; +} + ++ (BOOL)osVersionIsGreaterThen:(NSString *)versionString +{ + return [[DTVersion osVersion] isGreaterThenVersionString:versionString]; +} + + +- (BOOL)isLessThenVersion:(DTVersion *)version +{ + int result = [self compare:version] == NSOrderedAscending; + //DDLogVerbose(@"%@ < %@? %d = %@", self, version, result, result ? @"YES" : @"NO"); + return result; +} + +- (BOOL)isGreaterThenVersion:(DTVersion *)version +{ + return [self compare:version] == NSOrderedDescending; +} + +- (BOOL)isLessThenVersionString:(NSString *)versionString +{ + return [self isLessThenVersion:[DTVersion versionWithString:versionString]]; +} + +- (BOOL)isGreaterThenVersionString:(NSString *)versionString +{ + return [self isGreaterThenVersion:[DTVersion versionWithString:versionString]]; +} + + + +- (BOOL) isEqualToVersion:(DTVersion *)version +{ + return (self.major == version.major) && (self.minor == version.minor) && (self.maintenance == version.maintenance); +} + +- (BOOL) isEqualToString:(NSString *)versionString +{ + DTVersion *versionToTest = [DTVersion versionWithString:versionString]; + return [self isEqualToVersion:versionToTest]; +} + + +- (BOOL) isEqual:(id)object +{ + if ([object isKindOfClass:[DTVersion class]]) + { + return [self isEqualToVersion:(DTVersion*)object]; + } + if ([object isKindOfClass:[NSString class]]) + { + return [self isEqualToString:(NSString*)object]; + } + return NO; +} + +- (NSComparisonResult)compare:(DTVersion *)version +{ + if (version == nil) + { + return NSOrderedDescending; + } + + if (self.major < version.major) + { + return NSOrderedAscending; + } + if (self.major > version.major) + { + return NSOrderedDescending; + } + if (self.minor < version.minor) + { + return NSOrderedAscending; + } + if (self.minor > version.minor) + { + return NSOrderedDescending; + } + if (self.maintenance < version.maintenance) + { + return NSOrderedAscending; + } + if (self.maintenance > version.maintenance) + { + return NSOrderedDescending; + } + if (self.build < version.build) + { + return NSOrderedAscending; + } + if (self.build > version.build) + { + return NSOrderedDescending; + } + + + return NSOrderedSame; +} + + +- (NSString *)description +{ + if (_build > 0) + { + return [NSString stringWithFormat:@"%d.%d.%d.%d", (int)_major, (int)_minor, (int)_maintenance, (int)_build]; + } + if (_maintenance > 0) + { + return [NSString stringWithFormat:@"%d.%d.%d", (int)_major, (int)_minor, (int)_maintenance]; + } + return [NSString stringWithFormat:@"%d.%d", (int)_major, (int)_minor]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + return [[DTVersion allocWithZone:zone] initWithMajor:_major minor:_minor maintenance:_maintenance build:_build]; +} + +#pragma mark - Properties + +@synthesize major = _major; +@synthesize minor = _minor; +@synthesize maintenance = _maintenance; +@synthesize build = _build; + +@end diff --git a/Pods/DTFoundation/Core/Source/DTWeakSupport.h b/Pods/DTFoundation/Core/Source/DTWeakSupport.h new file mode 100644 index 0000000..b3631d2 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/DTWeakSupport.h @@ -0,0 +1,33 @@ +// +// DTWeakSupport.h +// DTFoundation +// +// Created by Oliver Drobnik on 6/3/13. +// Copyright (c) 2013 Cocoanetics. All rights reserved. +// + +/** + Useful defines for building code the compiles with zeroing weak references if the deployment target allows it. This is possible from minimum supported iOS 5.0 and OS X 10.7 and above. Note that on OS X 10.7 some AppKit classes do not support having a weak ref, e.g. NSWindowController or NSViewController. +*/ + +#import + +#if __has_feature(objc_arc_weak) + + // zeroing weak refs are supported for ivars and properties + #define DT_WEAK_VARIABLE __weak + #define DT_WEAK_PROPERTY weak + +#elif __has_feature(objc_arc) + + /// zeroing weak refs not supported, fall back to unsafe unretained and assigning + #define DT_WEAK_VARIABLE __unsafe_unretained + #define DT_WEAK_PROPERTY assign + +#else + + // define something, as this header might be included in a non-ARC project for using compiled code from an ARC static lib + #define DT_WEAK_VARIABLE + #define DT_WEAK_PROPERTY assign + +#endif \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/NSArray+DTError.h b/Pods/DTFoundation/Core/Source/NSArray+DTError.h new file mode 100644 index 0000000..9d6c76a --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSArray+DTError.h @@ -0,0 +1,49 @@ +// +// NSArray+error.h +// DTFoundation +// +// Created by Oliver Drobnik on 6/15/10. +// Copyright 2010 Drobnik.com. All rights reserved. +// + +/** + A collection of useful additions for `NSArray` to deal with Property Lists and also to get error handling for malformed data. + */ + +@interface NSArray (DTError) + + +/**------------------------------------------------------------------------------------- + @name Property List Error Handling + --------------------------------------------------------------------------------------- + */ + +/** + Creates and returns an array found in a file specified by a given URL. + + @param URL An `NSURL`. The file identified by URL must contain a string representation of a property list whose root object is an array. + @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, pass in `NULL`. + @return A new array that contains the array at path, or `nil` if there is a file error or if the contents of the file are an invalid representation of a array. + */ ++ (NSArray *)arrayWithContentsOfURL:(NSURL *)URL error:(NSError **)error; + + +/** + Creates and returns an array found in a file specified by a given path. + + @param path A full or relative pathname. The file identified by path must contain a string representation of a property list whose root object is a dictionary. + @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, pass in `NULL`. + @return A new dictionary that contains the dictionary at path, or `nil` if there is a file error or if the contents of the file are an invalid representation of a dictionary. + */ ++ (NSArray *)arrayWithContentsOfFile:(NSString *)path error:(NSError **)error; + + +/** + Creates and returns an array encoded in the given blob of data. + + @param data The data object identified by data must contain a string representation of a property list whose root object is an array. + @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, pass in `NULL`. + @return A new array that contains the decoded array, or `nil` if there is an error or if the contents of the file are an invalid representation of an array. + */ ++ (NSArray *)arrayWithContentsOfData:(NSData *)data error:(NSError **)error; +@end diff --git a/Pods/DTFoundation/Core/Source/NSArray+DTError.m b/Pods/DTFoundation/Core/Source/NSArray+DTError.m new file mode 100644 index 0000000..ea86189 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSArray+DTError.m @@ -0,0 +1,56 @@ +// +// NSArray+DTError.m +// DTFoundation +// +// Created by Oliver Drobnik on 6/15/10. +// Copyright 2010 Drobnik.com. All rights reserved. +// + +#import "NSArray+DTError.h" +#import "DTFoundationConstants.h" + +@implementation NSArray (DTError) + ++ (NSArray *)arrayWithContentsOfURL:(NSURL *)URL error:(NSError **)error +{ + NSData *readData = [NSData dataWithContentsOfURL:URL options:0 error:error]; + + if (!readData) + { + return nil; + } + + return [NSArray arrayWithContentsOfData:readData error:error]; +} + ++ (NSArray *)arrayWithContentsOfFile:(NSString *)path error:(NSError **)error +{ + NSURL *url = [NSURL fileURLWithPath:path]; + return [NSArray arrayWithContentsOfURL:url error:error]; +} + ++ (NSArray *)arrayWithContentsOfData:(NSData *)data error:(NSError **)error +{ + CFErrorRef parseError = NULL; + NSArray *array = (__bridge_transfer NSArray *)CFPropertyListCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)data, kCFPropertyListImmutable, NULL, (CFErrorRef *)&parseError); + + if ([array isKindOfClass:[NSArray class]]) + { + return array; + } + + if (parseError) + { + if (error) + { + *error = (__bridge NSError *)(parseError); + } + + CFRelease(parseError); + } + + return nil; +} + + +@end diff --git a/Pods/DTFoundation/Core/Source/NSData+DTCrypto.h b/Pods/DTFoundation/Core/Source/NSData+DTCrypto.h new file mode 100644 index 0000000..8b5b25d --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSData+DTCrypto.h @@ -0,0 +1,39 @@ +// +// NSData+DTCrypto.h +// DTFoundation +// +// Created by Stefan Gugarel on 10/3/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Useful cryptography methods. + */ + +@interface NSData (DTCrypto) + +/**------------------------------------------------------------------------------------- + @name Generating HMAC Hashes + --------------------------------------------------------------------------------------- + */ + +/** + Generates a HMAC from the receiver using the SHA1 algorithm + @param key The encryption key + @returns The encrypted hash + */ +- (NSData *)encryptedDataUsingSHA1WithKey:(NSData *)key; + + +/**------------------------------------------------------------------------------------- + @name Digest Hashes + --------------------------------------------------------------------------------------- + */ + +/** + Generate an md5 checksum from the receiver + @returns An `NSData` containing the md5 digest. + */ +-(NSData *)dataWithMD5Hash; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSData+DTCrypto.m b/Pods/DTFoundation/Core/Source/NSData+DTCrypto.m new file mode 100644 index 0000000..2682e8c --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSData+DTCrypto.m @@ -0,0 +1,37 @@ +// +// NSData+DTCrypto.m +// DTFoundation +// +// Created by Stefan Gugarel on 10/3/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSData+DTCrypto.h" +#include +#import + +/** + Common cryptography methods + */ +@implementation NSData (DTCrypto) + + +- (NSData *)encryptedDataUsingSHA1WithKey:(NSData *)key +{ + unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH]; + + CCHmac(kCCHmacAlgSHA1, [key bytes], [key length], [self bytes], [self length], cHMAC); + + return [NSData dataWithBytes:&cHMAC length:CC_SHA1_DIGEST_LENGTH]; +} + +- (NSData *)dataWithMD5Hash +{ + const char *cStr = [self bytes]; + unsigned char digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5( cStr, (CC_LONG)[self length], digest ); + + return [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH]; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSDictionary+DTError.h b/Pods/DTFoundation/Core/Source/NSDictionary+DTError.h new file mode 100644 index 0000000..c462dca --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSDictionary+DTError.h @@ -0,0 +1,47 @@ +// +// NSDictionary+DTError.h +// DTFoundation +// +// Created by Oliver Drobnik on 4/16/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + A collection of useful additions for `NSDictionary` to deal with Property Lists and also to get error handling for malformed data. + */ + +@interface NSDictionary (DTError) + +/**------------------------------------------------------------------------------------- + @name Property List Error Handling + --------------------------------------------------------------------------------------- + */ + +/** + Creates and returns a dictionary using the keys and values found in a file specified by a given URL. + + @param URL An URL. The file identified by URL must contain a string representation of a property list whose root object is a dictionary. + @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, pass in `NULL`. + @return A new dictionary that contains the dictionary at path, or `nil` if there is a file error or if the contents of the file are an invalid representation of a dictionary. + */ ++ (NSDictionary *)dictionaryWithContentsOfURL:(NSURL *)URL error:(NSError **)error; + +/** + Creates and returns a dictionary using the keys and values found in a file specified by a given path. + + @param path A full or relative pathname. The file identified by path must contain a string representation of a property list whose root object is a dictionary. + @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, pass in `NULL`. + @return A new dictionary that contains the dictionary at path, or `nil` if there is a file error or if the contents of the file are an invalid representation of a dictionary. + */ ++ (NSDictionary *)dictionaryWithContentsOfFile:(NSString *)path error:(NSError **)error; + +/** + Creates and returns a dictionary using the keys and values found in the given data. + + @param data The data object identified by data must contain a string representation of a property list whose root object is a dictionary. + @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, pass in `NULL`. + @return A new dictionary that contains the dictionary at path, or `nil` if there is a file error or if the contents of the file are an invalid representation of a dictionary. + */ ++ (NSDictionary *)dictionaryWithContentsOfData:(NSData *)data error:(NSError **)error; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSDictionary+DTError.m b/Pods/DTFoundation/Core/Source/NSDictionary+DTError.m new file mode 100644 index 0000000..73917f8 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSDictionary+DTError.m @@ -0,0 +1,58 @@ +// +// NSDictionary+DTError.m +// DTFoundation +// +// Created by Oliver Drobnik on 4/16/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSDictionary+DTError.h" +#import "DTFoundationConstants.h" + +@implementation NSDictionary (DTError) + ++ (NSDictionary *)dictionaryWithContentsOfURL:(NSURL *)URL error:(NSError **)error +{ + NSData *readData = [NSData dataWithContentsOfURL:URL options:0 error:error]; + + if (!readData) + { + return nil; + } + + return [NSDictionary dictionaryWithContentsOfData:readData error:error]; +} + ++ (NSDictionary *)dictionaryWithContentsOfFile:(NSString *)path error:(NSError **)error +{ + NSURL *url = [NSURL fileURLWithPath:path]; + return [NSDictionary dictionaryWithContentsOfURL:url error:error]; +} + ++ (NSDictionary *)dictionaryWithContentsOfData:(NSData *)data error:(NSError **)error +{ + CFErrorRef parseError = NULL; + NSDictionary *dictionary = (__bridge_transfer NSDictionary *)CFPropertyListCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)data, kCFPropertyListImmutable, NULL, (CFErrorRef *)&parseError); + + // we check if it is the correct type and only return it if it is + if ([dictionary isKindOfClass:[NSDictionary class]]) + { + return dictionary; + } + else + { + if (parseError) + { + if (error) + { + *error = (__bridge NSError *)parseError; + } + + CFRelease(parseError); + } + + return nil; + } +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSFileWrapper+DTCopying.h b/Pods/DTFoundation/Core/Source/NSFileWrapper+DTCopying.h new file mode 100644 index 0000000..685c19b --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSFileWrapper+DTCopying.h @@ -0,0 +1,19 @@ +// +// NSFileWrapper+DTCopying.h +// DTFoundation +// +// Created by Oliver Drobnik on 10/19/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Methods for copying file wrappers. + */ +@interface NSFileWrapper (DTCopying) + +/** + Creates a copy of the receiver by deep copying all contained sub filewrappers. + */ +- (NSFileWrapper *)fileWrapperByDeepCopying; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSFileWrapper+DTCopying.m b/Pods/DTFoundation/Core/Source/NSFileWrapper+DTCopying.m new file mode 100644 index 0000000..dbb3218 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSFileWrapper+DTCopying.m @@ -0,0 +1,33 @@ +// +// NSFileWrapper+DTCopying.m +// DTFoundation +// +// Created by Oliver Drobnik on 10/19/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSFileWrapper+DTCopying.h" + +@implementation NSFileWrapper (DTCopying) + +- (NSFileWrapper *)fileWrapperByDeepCopying +{ + if ([self isDirectory]) + { + NSMutableDictionary *subFileWrappers = [NSMutableDictionary dictionary]; + + [self.fileWrappers enumerateKeysAndObjectsUsingBlock:^(NSString *fileName, NSFileWrapper *fileWrapper, BOOL *stop) { + NSFileWrapper *copyWrapper = [fileWrapper fileWrapperByDeepCopying]; + [subFileWrappers setObject:copyWrapper forKey:fileName]; + }]; + + NSFileWrapper *fileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:subFileWrappers]; + + return fileWrapper; + } + + NSFileWrapper *fileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:self.regularFileContents]; + return fileWrapper; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSMutableArray+DTMoving.h b/Pods/DTFoundation/Core/Source/NSMutableArray+DTMoving.h new file mode 100644 index 0000000..e315247 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSMutableArray+DTMoving.h @@ -0,0 +1,23 @@ +// +// NSMutableArray+DTMoving.h +// DTFoundation +// +// Created by Oliver Drobnik on 9/27/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Methods that add convenient moving methods to `NSMutableArray`. + */ + +@interface NSMutableArray (DTMoving) + +/** + Moves the object at the specified indexes to the new location. + + @param indexes The indexes of the objects to move. + @param idx The index in the mutable array at which to insert the objects. + */ +- (void)moveObjectsAtIndexes:(NSIndexSet *)indexes toIndex:(NSUInteger)idx; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSMutableArray+DTMoving.m b/Pods/DTFoundation/Core/Source/NSMutableArray+DTMoving.m new file mode 100644 index 0000000..e64ec21 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSMutableArray+DTMoving.m @@ -0,0 +1,26 @@ +// +// NSMutableArray+DTMoving.m +// DTFoundation +// +// Created by Oliver Drobnik on 9/27/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSMutableArray+DTMoving.h" + +@implementation NSMutableArray (DTMoving) + +// credit: http://www.cocoabuilder.com/archive/cocoa/189484-nsarray-move-items-at-indexes.html + +- (void)moveObjectsAtIndexes:(NSIndexSet *)indexes toIndex:(NSUInteger)idx +{ + NSArray *objectsToMove = [self objectsAtIndexes:indexes]; + + // If any of the removed objects come before the index, we want to decrement the index appropriately + idx -= [indexes countOfIndexesInRange:(NSRange){0, idx}]; + + [self removeObjectsAtIndexes:indexes]; + [self replaceObjectsInRange:(NSRange){idx,0} withObjectsFromArray:objectsToMove]; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTFormatNumbers.h b/Pods/DTFoundation/Core/Source/NSString+DTFormatNumbers.h new file mode 100644 index 0000000..381feaa --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTFormatNumbers.h @@ -0,0 +1,32 @@ +// +// NSString+DTFormatNumbers.h +// DTFoundation +// +// Created by Oliver Drobnik on 11/25/11. +// Copyright (c) 2011 Cocoanetics. All rights reserved. +// + + +/** + A collection of category extensions for `NSString` dealing with the formatting of numbers in special contexts. + */ + +@interface NSString (DTFormatNumbers) + +/**------------------------------------------------------------------------------------- + @name Formatting File Sizes + --------------------------------------------------------------------------------------- + */ + + +/** Formats the passed number as a byte value in a form that is pleasing to the user when displayed next to a progress bar. + + Output numbers are rounded to one decimal place. Bytes are not abbreviated because most users might not be used to B for that. Higher units are kB, MB, GB and TB. + + @param bytes The value of the bytes to be formatted + @return Returns the formatted string. + + */ ++ (NSString *)stringByFormattingBytes:(long long)bytes; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTFormatNumbers.m b/Pods/DTFoundation/Core/Source/NSString+DTFormatNumbers.m new file mode 100644 index 0000000..2ad6775 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTFormatNumbers.m @@ -0,0 +1,32 @@ +// +// NSString+DTFormatNumbers.m +// DTFoundation +// +// Created by Oliver Drobnik on 11/25/11. +// Copyright (c) 2011 Cocoanetics. All rights reserved. +// + +#import "NSString+DTFormatNumbers.h" + +@implementation NSString (DTFormatNumbers) + ++ (NSString *)stringByFormattingBytes:(long long)bytes +{ + NSArray *units = [NSArray arrayWithObjects:@"%1.0f Bytes", @"%1.1f KB", @"%1.1f MB", @"%1.1f GB", @"%1.1f TB", nil]; + + long long value = bytes * 10; + for (NSUInteger i=0; i<[units count]; i++) + { + if (i > 0) + { + value = value/1024; + } + if (value < 10000) + { + return [NSString stringWithFormat:[units objectAtIndex:i], value/10.0]; + } + } + + return [NSString stringWithFormat:[units objectAtIndex:[units count]-1], value/10.0]; +} +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTPaths.h b/Pods/DTFoundation/Core/Source/NSString+DTPaths.h new file mode 100644 index 0000000..31ec940 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTPaths.h @@ -0,0 +1,86 @@ +// +// NSString+DTPaths.h +// DTFoundation +// +// Created by Oliver Drobnik on 2/15/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + + +/** + A collection of useful additions for `NSString` to deal with paths. + */ + +@interface NSString (DTPaths) + +/**------------------------------------------------------------------------------------- + @name Getting Standard Paths + --------------------------------------------------------------------------------------- + */ + +/** Determines the path to the Library/Caches folder in the current application's sandbox. + + The return value is cached on the first call. + + @return The path to the app's Caches folder. + */ ++ (NSString *)cachesPath; + + +/** Determines the path to the Documents folder in the current application's sandbox. + + The return value is cached on the first call. + + @return The path to the app's Documents folder. + */ ++ (NSString *)documentsPath; + +/**------------------------------------------------------------------------------------- + @name Getting Temporary Paths + --------------------------------------------------------------------------------------- + */ + +/** Determines the path for temporary files in the current application's sandbox. + + The return value is cached on the first call. This value is different in Simulator than on the actual device. In Simulator you get a reference to /tmp wheras on iOS devices it is a special folder inside the application folder. + + @return The path to the app's folder for temporary files. + */ ++ (NSString *)temporaryPath; + + +/** Creates a unique filename that can be used for one temporary file or folder. + + The returned string is different on every call. It is created by combining the result from temporaryPath with a unique UUID. + + @return The generated temporary path. + */ ++ (NSString *)pathForTemporaryFile; + + +/**------------------------------------------------------------------------------------- + @name Working with Paths + --------------------------------------------------------------------------------------- + */ + +/** Appends or Increments a sequence number in brackets + + If the receiver already has a number suffix then it is incremented. If not then (1) is added. + + @return The incremented path +*/ +- (NSString *)pathByIncrementingSequenceNumber; + + +/** Removes a sequence number in brackets + + If the receiver number suffix then it is removed. If not the receiver is returned. + + @return The modified path + */ +- (NSString *)pathByDeletingSequenceNumber; + + + + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTPaths.m b/Pods/DTFoundation/Core/Source/NSString+DTPaths.m new file mode 100644 index 0000000..1f1f590 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTPaths.m @@ -0,0 +1,115 @@ +// +// NSString+DTPaths.m +// DTFoundation +// +// Created by Oliver Drobnik on 2/15/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSString+DTPaths.h" + +@implementation NSString (DTPaths) + +#pragma mark Standard Paths + ++ (NSString *)cachesPath +{ + static dispatch_once_t onceToken; + static NSString *cachedPath; + + dispatch_once(&onceToken, ^{ + cachedPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; + }); + + return cachedPath; +} + ++ (NSString *)documentsPath +{ + static dispatch_once_t onceToken; + static NSString *cachedPath; + + dispatch_once(&onceToken, ^{ + cachedPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; + }); + + return cachedPath; +} + +#pragma mark Temporary Paths + ++ (NSString *)temporaryPath +{ + static dispatch_once_t onceToken; + static NSString *cachedPath; + + dispatch_once(&onceToken, ^{ + cachedPath = NSTemporaryDirectory(); + }); + + return cachedPath; +} + ++ (NSString *)pathForTemporaryFile +{ + CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef newUniqueIdString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueId); + NSString *tmpPath = [[NSString temporaryPath] stringByAppendingPathComponent:(__bridge NSString *)newUniqueIdString]; + CFRelease(newUniqueId); + CFRelease(newUniqueIdString); + + return tmpPath; +} + +#pragma mark Working with Paths + +- (NSString *)pathByIncrementingSequenceNumber +{ + NSString *baseName = [self stringByDeletingPathExtension]; + NSString *extension = [self pathExtension]; + + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\(([0-9]+)\\)$" options:0 error:NULL]; + __block NSInteger sequenceNumber = 0; + + [regex enumerateMatchesInString:baseName options:0 range:NSMakeRange(0, [baseName length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){ + + NSRange range = [match rangeAtIndex:1]; // first capture group + NSString *substring= [self substringWithRange:range]; + + sequenceNumber = [substring integerValue]; + *stop = YES; + }]; + + NSString *nakedName = [baseName pathByDeletingSequenceNumber]; + + if ([extension isEqualToString:@""]) + { + return [nakedName stringByAppendingFormat:@"(%d)", (int)sequenceNumber+1]; + } + + return [[nakedName stringByAppendingFormat:@"(%d)", (int)sequenceNumber+1] stringByAppendingPathExtension:extension]; +} + +- (NSString *)pathByDeletingSequenceNumber +{ + NSString *baseName = [self stringByDeletingPathExtension]; + + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([0-9]+\\)$" options:0 error:NULL]; + __block NSRange range = NSMakeRange(NSNotFound, 0); + + [regex enumerateMatchesInString:baseName options:0 range:NSMakeRange(0, [baseName length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) { + + range = [match range]; + + *stop = YES; + }]; + + if (range.location != NSNotFound) + { + return [self stringByReplacingCharactersInRange:range withString:@""]; + } + + return self; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTURLEncoding.h b/Pods/DTFoundation/Core/Source/NSString+DTURLEncoding.h new file mode 100644 index 0000000..ab2b1df --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTURLEncoding.h @@ -0,0 +1,29 @@ +// +// NSString+DTURLEncoding.h +// DTFoundation +// +// Created by Oliver Drobnik on 4/16/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + A collection of useful additions for `NSString` to deal with URL encoding. + */ + +@interface NSString (DTURLEncoding) + +/**------------------------------------------------------------------------------------- + @name Encoding Strings for URLs + --------------------------------------------------------------------------------------- + */ + + +/** Encoding suitable for use in URLs. + + stringByAddingPercentEscapes does not replace serveral characters which are problematics in URLs. + + @return The encoded version of the receiver. + */ +- (NSString *)stringByURLEncoding; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTURLEncoding.m b/Pods/DTFoundation/Core/Source/NSString+DTURLEncoding.m new file mode 100644 index 0000000..91404ca --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTURLEncoding.m @@ -0,0 +1,18 @@ +// +// NSString+DTURLEncoding.m +// DTFoundation +// +// Created by Oliver Drobnik on 4/16/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSString+DTURLEncoding.h" + +@implementation NSString (DTURLEncoding) + +- (NSString *)stringByURLEncoding +{ + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef)self, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8); +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTUtilities.h b/Pods/DTFoundation/Core/Source/NSString+DTUtilities.h new file mode 100644 index 0000000..392d66f --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTUtilities.h @@ -0,0 +1,41 @@ +// +// NSString+DTUtilities.h +// DTFoundation +// +// Created by Oliver Drobnik on 4/16/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + + +/** + A collection of utility additions for `NSString`. + */ + +@interface NSString (DTUtilities) + +/**------------------------------------------------------------------------------------- + @name Working with Identifiers + --------------------------------------------------------------------------------------- + */ + +/** Creates a new string that contains a generated UUID. + + @return The path to the app's Caches folder. + */ ++ (NSString *)stringWithUUID; + + +/**------------------------------------------------------------------------------------- + @name Working with Checksums + --------------------------------------------------------------------------------------- + */ + +/** creates an MD5 checksum + + @return returns an MD5 hash for the receiver. + */ +- (NSString *)md5Checksum; + + + +@end diff --git a/Pods/DTFoundation/Core/Source/NSString+DTUtilities.m b/Pods/DTFoundation/Core/Source/NSString+DTUtilities.m new file mode 100644 index 0000000..d266609 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSString+DTUtilities.m @@ -0,0 +1,44 @@ +// +// NSString+DTUtilities.m +// DTFoundation +// +// Created by Oliver Drobnik on 4/16/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSString+DTUtilities.h" + +#import + +@implementation NSString (DTUtilities) + ++ (NSString *)stringWithUUID +{ + CFUUIDRef uuidObj = CFUUIDCreate(nil);//create a new UUID + + //get the string representation of the UUID + NSString *uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(nil, uuidObj); + + CFRelease(uuidObj); + return uuidString; +} + +- (NSString *)md5Checksum +{ + const char *cStr = [self UTF8String]; + unsigned char result [CC_MD5_DIGEST_LENGTH]; + CC_MD5( cStr, (CC_LONG)strlen(cStr), result ); + + return [NSString stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + result[0], result[1], + result[2], result[3], + result[4], result[5], + result[6], result[7], + result[8], result[9], + result[10], result[11], + result[12], result[13], + result[14], result[15] + ]; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSURL+DTComparing.h b/Pods/DTFoundation/Core/Source/NSURL+DTComparing.h new file mode 100644 index 0000000..a5933d3 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSURL+DTComparing.h @@ -0,0 +1,24 @@ +// +// NSURL+DTComparing.h +// DTFoundation +// +// Created by Oliver Drobnik on 13.11.12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Category for comparing URLs. + + Contrary to what you might think isEqual: does not work properly in many cases. + */ + +@interface NSURL (DTComparing) + +/** + Compares the receiver with another URL + @param URL another URL + @returns `YES` if the receiver is equivalent with the passed URL + */ +- (BOOL)isEqualToURL:(NSURL *)URL; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSURL+DTComparing.m b/Pods/DTFoundation/Core/Source/NSURL+DTComparing.m new file mode 100644 index 0000000..61f55c9 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSURL+DTComparing.m @@ -0,0 +1,36 @@ +// +// NSURL+DTComparing.m +// DTFoundation +// +// Created by Oliver Drobnik on 13.11.12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSURL+DTComparing.h" + +@implementation NSURL (DTComparing) + +- (BOOL)isEqualToURL:(NSURL *)URL +{ + // scheme must be same + if (![[self scheme] isEqualToString:[URL scheme]]) + { + return NO; + } + + // host must be same + if (![[self host] isEqualToString:[URL host]]) + { + return NO; + } + + // path must be same + if (![[self path] isEqualToString:[URL path]]) + { + return NO; + } + + return YES; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/NSURL+DTUnshorten.h b/Pods/DTFoundation/Core/Source/NSURL+DTUnshorten.h new file mode 100644 index 0000000..16adf03 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSURL+DTUnshorten.h @@ -0,0 +1,33 @@ +// +// NSURL+DTUnshorten.h +// DTFoundation +// +// Created by Oliver Drobnik on 6/2/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** Method for getting the full length URL for a shortened one. + +For example: + + NSURL *url = [NSURL URLWithString:@"buff.ly/L4uGoza"]; + + [url unshortenWithCompletion:^(NSURL *url) { + NSLog(@"Unshortened: %@", url); + }]; + + */ + +typedef void (^NSURLUnshortenCompletionHandler)(NSURL *); + +@interface NSURL (DTUnshorten) + +/** + Unshortens the receiver and returns the long URL via the completion handler. + + Results are cached and therefore a subsequent call for the same receiver will return instantly if the result is still present in the cache. + @param completion The completion handler + */ +- (void)unshortenWithCompletion:(NSURLUnshortenCompletionHandler)completion; + +@end diff --git a/Pods/DTFoundation/Core/Source/NSURL+DTUnshorten.m b/Pods/DTFoundation/Core/Source/NSURL+DTUnshorten.m new file mode 100644 index 0000000..f5476aa --- /dev/null +++ b/Pods/DTFoundation/Core/Source/NSURL+DTUnshorten.m @@ -0,0 +1,64 @@ +// +// NSURL+DTUnshorten.m +// DTFoundation +// +// Created by Oliver Drobnik on 6/2/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "NSURL+DTUnshorten.h" + +@implementation NSURL (DTUnshorten) + +- (void)unshortenWithCompletion:(NSURLUnshortenCompletionHandler)completion +{ + static NSCache *unshortenCache = nil; + static dispatch_queue_t shortenQueue = NULL; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + unshortenCache = [[NSCache alloc] init]; + shortenQueue = dispatch_queue_create("DTUnshortenQueue", 0); + }); + + NSURL *shortURL = self; + + // assume HTTP if scheme is missing + if (![self scheme]) + { + NSString *str = [@"http://" stringByAppendingString:[self absoluteString]]; + shortURL = [NSURL URLWithString:str]; + } + + dispatch_async(shortenQueue, ^{ + // look into cache first + NSURL *longURL = [unshortenCache objectForKey:shortURL]; + + // nothing cached, load it + if (!longURL) + { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:shortURL]; + request.HTTPMethod = @"HEAD"; + + NSError *error = nil; + NSHTTPURLResponse *response = nil; + + [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; + + longURL = [response URL]; + + // cache result + if (longURL) + { + [unshortenCache setObject:longURL forKey:shortURL]; + } + } + + if (completion) + { + completion(longURL); + } + }); +} + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/DTActivityTitleView.h b/Pods/DTFoundation/Core/Source/iOS/DTActivityTitleView.h new file mode 100644 index 0000000..d3d6923 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTActivityTitleView.h @@ -0,0 +1,26 @@ +// +// DTActivityTitleView.h +// DTFoundation +// +// Created by Rene Pirringer on 12.09.12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Alternative view for showing titles with a configurable activity indicator + instead of default title view in navigationItem. + */ +@interface DTActivityTitleView : UIView + +/** + Title that is shown + */ +@property (nonatomic, copy) NSString *title; + +/** + When busy is set to YES the activity indicator starts spinning + When set to NO the activity indicator stops spinning + */ +@property (nonatomic, assign) BOOL busy; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/DTActivityTitleView.m b/Pods/DTFoundation/Core/Source/iOS/DTActivityTitleView.m new file mode 100644 index 0000000..d09dc62 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTActivityTitleView.m @@ -0,0 +1,112 @@ +// +// DTActivityTitleView.m +// DTFoundation +// +// Created by Rene Pirringer on 12.09.12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTActivityTitleView.h" + +@interface DTActivityTitleView () + +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; +@property (nonatomic, strong) UILabel *titleLabel; + +@end + + +@implementation DTActivityTitleView + +- (id)init +{ + self = [super init]; + + if (self) + { + self.titleLabel = [[UILabel alloc] init]; + self.titleLabel.backgroundColor = [UIColor clearColor]; + + self.activityIndicator.hidesWhenStopped = YES; + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + { + self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + self.titleLabel.textColor = [UIColor colorWithRed:113.0/255.0 green:120.0/255.0 blue:128.0/255.0 alpha:1.0]; + self.titleLabel.shadowOffset = CGSizeMake(0, 1); + self.titleLabel.shadowColor = [UIColor whiteColor]; + } + else + { + self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + self.titleLabel.textColor = [UIColor whiteColor]; + self.titleLabel.shadowOffset = CGSizeMake(0, -1); + self.titleLabel.shadowColor = [UIColor blackColor]; + } + + self.titleLabel.font = [UIFont boldSystemFontOfSize:20]; + [self addSubview:self.titleLabel]; + [self addSubview:self.activityIndicator]; + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; +} + +#pragma mark - Properties + +- (void)setBusy:(BOOL)busy +{ + if (busy) + { + [self.activityIndicator startAnimating]; + } + else + { + [self.activityIndicator stopAnimating]; + } + + [self setNeedsLayout]; +} + +- (BOOL)busy +{ + return self.activityIndicator.isAnimating; +} + +- (void)setTitle:(NSString *)title +{ + self.titleLabel.text = title; + CGFloat gap = 5.0; + CGFloat height = self.activityIndicator.frame.size.height; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_6_1 + // issue 60: sizeWithFont: is deprecated with deployment target >= iOS 7 + NSDictionary *attribs = @{NSFontAttributeName:self.titleLabel.font}; + CGSize neededSize = [self.titleLabel.text sizeWithAttributes:attribs]; +#else + CGSize neededSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font]; +#endif + + if (height < neededSize.height) + { + height = neededSize.height; + } + + CGRect titleRect = CGRectMake(self.activityIndicator.frame.size.width+gap, 0, neededSize.width, height); + self.titleLabel.frame = titleRect; + self.bounds = CGRectMake(0, 0, self.activityIndicator.frame.size.width+neededSize.width+gap, height); + [self setNeedsLayout]; +} + +- (NSString *)title +{ + return self.titleLabel.text; +} + +@synthesize activityIndicator = _activityIndicator; + +@end \ No newline at end of file diff --git a/Pods/DTFoundation/Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.h b/Pods/DTFoundation/Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.h new file mode 100644 index 0000000..a8ab1c8 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.h @@ -0,0 +1,18 @@ +// +// DTAnimatedGIF.h +// DTFoundation +// +// Created by Oliver Drobnik on 7/2/14. +// Copyright (c) 2014 Cocoanetics. All rights reserved. +// + + +/** + Loads an animated GIF from file, compatible with UIImageView + */ +UIImage *DTAnimatedGIFFromFile(NSString *path); + +/** + Loads an animated GIF from data, compatible with UIImageView + */ +UIImage *DTAnimatedGIFFromData(NSData *data); diff --git a/Pods/DTFoundation/Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.m b/Pods/DTFoundation/Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.m new file mode 100644 index 0000000..2e12211 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.m @@ -0,0 +1,128 @@ +// +// DTAnimatedGIF.m +// DTFoundation +// +// Created by Oliver Drobnik on 7/2/14. +// Copyright (c) 2014 Cocoanetics. All rights reserved. +// + +#import "DTAnimatedGIF.h" +#import + +// returns the frame duration for a given image in 1/100th seconds +// source: http://stackoverflow.com/questions/16964366/delaytime-or-unclampeddelaytime-for-gifs +static NSUInteger DTAnimatedGIFFrameDurationForImageAtIndex(CGImageSourceRef source, NSUInteger index) +{ + NSUInteger frameDuration = 10; + + NSDictionary *frameProperties = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source,index,nil)); + NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; + + NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; + + if(delayTimeUnclampedProp) + { + frameDuration = [delayTimeUnclampedProp floatValue]*100; + } + else + { + NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; + + if(delayTimeProp) + { + frameDuration = [delayTimeProp floatValue]*100; + } + } + + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify + // a duration of <= 10 ms. See and + // for more information. + + if (frameDuration < 1) + { + frameDuration = 10; + } + + return frameDuration; +} + +// returns the great common factor of two numbers +static NSUInteger DTAnimatedGIFGreatestCommonFactor(NSUInteger num1, NSUInteger num2) +{ + NSUInteger t, remainder; + + if (num1 < num2) + { + t = num1; + num1 = num2; + num2 = t; + } + + remainder = num1 % num2; + + if (!remainder) + { + return num2; + } + else + { + return DTAnimatedGIFGreatestCommonFactor(num2, remainder); + } +} + +static UIImage *DTAnimatedGIFFromImageSource(CGImageSourceRef source) +{ + size_t const numImages = CGImageSourceGetCount(source); + + NSMutableArray *frames = [NSMutableArray arrayWithCapacity:numImages]; + + // determine gretest common factor of all image durations + NSUInteger greatestCommonFactor = DTAnimatedGIFFrameDurationForImageAtIndex(source, 0); + + for (NSUInteger i=1; i 0.1f) + { + CGPoint center = CGPointMake(CGRectGetMidX(drawRect), CGRectGetMidY(drawRect)); + CGFloat radius = center.x - drawRect.origin.x; + CGFloat angle = CGFloat_(_progressPercent) * CGFloat_(2.0 * M_PI); + + CGContextMoveToPoint(ctx, center.x, center.y); + CGContextAddArc(ctx, center.x, center.y, radius, CGFloat_(-M_PI_2), angle-CGFloat_(M_PI_2), 0); + CGContextAddLineToPoint(ctx, center.x, center.y); + + CGContextFillPath(ctx); + } + + CGContextEndTransparencyLayer(ctx); +} + + +#pragma mark Properties + +- (void)setProgressPercent:(float)progressPercent +{ + if (_progressPercent != progressPercent) + { + _progressPercent = progressPercent; + + [self setNeedsDisplay]; + } +} + +- (void)setColor:(UIColor *)color +{ + if (_color != color) + { + _color = color; + + [self setNeedsDisplay]; + } +} + +@synthesize progressPercent = _progressPercent; +@synthesize color = _color; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/DTSmartPagingScrollView.h b/Pods/DTFoundation/Core/Source/iOS/DTSmartPagingScrollView.h new file mode 100644 index 0000000..d11bbdd --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTSmartPagingScrollView.h @@ -0,0 +1,84 @@ +// +// DTSmartPagingScrollView.h +// DTSmartPhotoView +// +// Created by Stefan Gugarel on 5/11/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTWeakSupport.h" + +@class DTSmartPagingScrollView; + + +/** + Protocol for providing pages to + */ +@protocol DTSmartPagingScrollViewDatasource + +/** + The number of pages for the + @param smartPagingScrollView The scroll view asking + @returns The number of pages + */ +- (NSUInteger)numberOfPagesInSmartPagingScrollView:(DTSmartPagingScrollView *)smartPagingScrollView; + +/** + Method to provide UIViews to be used for the pages + + The frame of the passed view will be adjusted to the page size of the scroll view + @param smartPagingScrollView The scroll view asking + @param index The index of the page to provide + @returns The view to use for the given page index. + */ +- (UIView *)smartPagingScrollView:(DTSmartPagingScrollView *)smartPagingScrollView viewForPageAtIndex:(NSUInteger)index; + +@optional +/** + The number of pages for the + @param smartPagingScrollView The scroll view asking + @param index The index of the page + */ +- (void)smartPagingScrollView:(DTSmartPagingScrollView *)smartPagingScrollView didScrollToPageAtIndex:(NSUInteger)index; + +@end + +/** + A scroll view that automatically manages a set of pages + */ +@interface DTSmartPagingScrollView : UIScrollView + +/** + The page data source for the receiver + */ +@property (nonatomic, DT_WEAK_PROPERTY) id pageDatasource; + +/** + The current page index visible on the receiver + */ +@property (nonatomic, assign) NSUInteger currentPageIndex; + +/** + Reloads the pages from the datasource + */ +- (void)reloadData; + +/** + The range of indexes of the currently visible pages + */ +- (NSRange)rangeOfVisiblePages; + +/** + Scroll the receiver to the given page index + @param page The index of the page to move to + @param animated Whether the move should be animated + */ +- (void)scrollToPage:(NSInteger)page animated:(BOOL)animated; + +/** + Get a view for a specified index + @param index The index of the view to retrieve + */ +- (UIView *)viewForIndex:(NSUInteger)index; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/DTSmartPagingScrollView.m b/Pods/DTFoundation/Core/Source/iOS/DTSmartPagingScrollView.m new file mode 100644 index 0000000..b0edb36 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTSmartPagingScrollView.m @@ -0,0 +1,257 @@ +// +// DTSmartPagingScrollView.m +// DTSmartPhotoView +// +// Created by Stefan Gugarel on 5/11/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "DTSmartPagingScrollView.h" +#import "DTCoreGraphicsUtils.h" +#import + +@interface DTSmartPagingScrollView () + +- (void)_setupVisiblePageViews; +- (CGRect)frameForPageViewAtIndex:(NSUInteger)index; +- (void)_updateCurrentPage; + +@end + + +@implementation DTSmartPagingScrollView +{ + DT_WEAK_VARIABLE id _pageDatasource; + + NSUInteger _numberOfPages; + NSMutableDictionary *_viewsByPage; + + NSMutableSet *_visiblePageViews; + + NSUInteger _currentPageIndex; + + BOOL _firstLayoutDone; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + self.pagingEnabled = YES; + + // no indicators because zooming subview has there + self.showsVerticalScrollIndicator = NO; + self.showsHorizontalScrollIndicator = NO; + + _viewsByPage = [[NSMutableDictionary alloc] init]; + _visiblePageViews = [[NSMutableSet alloc] init]; + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (!_numberOfPages) + { + return; + } + + if (self.dragging) + { + // perform update right away + [self _updateCurrentPage]; + } + + // Note: first content size plus page so that the visible view is properly shown + CGSize neededContentSize = CGSizeMake(self.bounds.size.width * _numberOfPages, self.bounds.size.height); + + if (!CGSizeEqualToSize(neededContentSize, self.contentSize)) + { + // frame changed + self.contentSize = neededContentSize; + + [self scrollToPage:_currentPageIndex animated:NO]; + } + + [self _setupVisiblePageViews]; +} + +- (void)_updateCurrentPage +{ + NSUInteger newPageIndex = round(self.contentOffset.x / self.frame.size.width); + + if (_currentPageIndex != newPageIndex) + { + [self willChangeValueForKey:@"currentPageIndex"]; + _currentPageIndex = newPageIndex; + + if ([_pageDatasource respondsToSelector:@selector(smartPagingScrollView:didScrollToPageAtIndex:)]) + { + [_pageDatasource smartPagingScrollView:self didScrollToPageAtIndex:_currentPageIndex]; + } + + [self didChangeValueForKey:@"currentPageIndex"]; + } +} + +- (NSRange)rangeOfVisiblePages +{ + CGFloat position = self.contentOffset.x / self.bounds.size.width; + + NSInteger firstVisibleIndex = MAX(CGFloat_(0), floor(position)); + NSInteger lastVisibleIndex = MIN(ceil(position), CGFloat_(_numberOfPages-1)); + + // check if right page is really visible + CGRect rightFrame = [self frameForPageViewAtIndex:lastVisibleIndex]; + + if (!CGRectIntersectsRect(rightFrame, self.bounds)) + { + lastVisibleIndex--; + } + + // check if left page is really visible + CGRect leftFrame = [self frameForPageViewAtIndex:firstVisibleIndex]; + + if (!CGRectIntersectsRect(leftFrame, self.bounds)) + { + firstVisibleIndex++; + } + + return NSMakeRange(firstVisibleIndex, lastVisibleIndex - firstVisibleIndex + 1); +} + +- (UIView *)viewForIndex:(NSUInteger)index +{ + NSNumber *cacheKey = [NSNumber numberWithUnsignedInteger:index]; + + UIView *view = [_viewsByPage objectForKey:cacheKey]; + + if (view) + { + // got cached view + return view; + } + + // get view from datasource + view = [_pageDatasource smartPagingScrollView:self viewForPageAtIndex:index]; + + // cache it + [_viewsByPage setObject:view forKey:cacheKey]; + + return view; +} + +- (void)_setupVisiblePageViews +{ + NSRange visibleRange = [self rangeOfVisiblePages]; + + [CATransaction begin]; + [CATransaction setDisableActions:NO]; + + NSMutableSet *newVisiblePageViews = [NSMutableSet set]; + + for (NSInteger idx = visibleRange.location; idx)pageDatasource +{ + if (_pageDatasource != pageDatasource) + { + _pageDatasource = pageDatasource; + + // refresh parameters + [self reloadData]; + } +} + +- (void)setCurrentPageIndex:(NSUInteger)currentPageIndex +{ + if (_currentPageIndex != currentPageIndex) + { + _currentPageIndex = currentPageIndex; + + [self scrollToPage:_currentPageIndex animated:NO]; + } +} + +@synthesize pageDatasource = _pageDatasource; +@synthesize currentPageIndex = _currentPageIndex; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/DTTiledLayerWithoutFade.h b/Pods/DTFoundation/Core/Source/iOS/DTTiledLayerWithoutFade.h new file mode 100644 index 0000000..6b63482 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTTiledLayerWithoutFade.h @@ -0,0 +1,17 @@ +// +// DTTiledLayerWithoutFade.h +// DTRichTextEditor +// +// Created by Oliver Drobnik on 8/24/11. +// Copyright 2011 Cocoanetics. All rights reserved. +// + +#import + +/** + Simple subclass of `CATiledLayer` that does not fade in drawn tiles. + */ + +@interface DTTiledLayerWithoutFade : CATiledLayer + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/DTTiledLayerWithoutFade.m b/Pods/DTFoundation/Core/Source/iOS/DTTiledLayerWithoutFade.m new file mode 100644 index 0000000..32573a6 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/DTTiledLayerWithoutFade.m @@ -0,0 +1,18 @@ +// +// DTTiledLayerWithoutFade.m +// DTRichTextEditor +// +// Created by Oliver Drobnik on 8/24/11. +// Copyright 2011 Cocoanetics. All rights reserved. +// + +#import "DTTiledLayerWithoutFade.h" + +@implementation DTTiledLayerWithoutFade + ++ (CFTimeInterval)fadeDuration +{ + return 0; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/NSURL+DTAppLinks.h b/Pods/DTFoundation/Core/Source/iOS/NSURL+DTAppLinks.h new file mode 100644 index 0000000..02da2ab --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/NSURL+DTAppLinks.h @@ -0,0 +1,45 @@ +// +// NSURL+DTAppLinks.h +// DTFoundation +// +// Created by Oliver Drobnik on 11/25/11. +// Copyright (c) 2011 Cocoanetics. All rights reserved. +// + + +/** A collection of category extensions for `NSURL` that provide direct access to built-in app capabilities. + + For example: Open the app store on the page for the app + + NSURL *appURL = [NSURL appStoreURLforApplicationIdentifier:@"463623298"]; + [[UIApplication sharedApplication] openURL:appURL]; + */ + +@interface NSURL (DTAppLinks) + +/**------------------------------------------------------------------------------------- + @name Mobile App Store Pages + --------------------------------------------------------------------------------------- + */ + +/** Returns the URL to open the mobile app store on the app's page. + + URL construction as described in [QA1629](https://developer.apple.com/library/ios/#qa/qa2008/qa1629.html). Test and found to be opening the app store app directly even without the itms: or itms-apps: scheme. This kind of URL can also be used to forward a link to the app to non-iOS devices. + + @param identifier The application identifier that gets assigned to a new app when you add it to iTunes Connect. + @return Returns the URL to the direct app store link + */ ++ (NSURL *)appStoreURLforApplicationIdentifier:(NSString *)identifier; + + +/** Returns the URL to open the mobile app store on the app's review page. + + The reviews page is a sub-page of the normal app landing page you get with appStoreURLforApplicationIdentifier: + + @param identifier The application identifier that gets assigned to a new app when you add it to iTunes Connect. + @return Returns the URL to the direct app store link + */ ++ (NSURL *)appStoreReviewURLForApplicationIdentifier:(NSString *)identifier; + +@end + diff --git a/Pods/DTFoundation/Core/Source/iOS/NSURL+DTAppLinks.m b/Pods/DTFoundation/Core/Source/iOS/NSURL+DTAppLinks.m new file mode 100644 index 0000000..ab9552d --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/NSURL+DTAppLinks.m @@ -0,0 +1,26 @@ +// +// NSURL+DTAppLinks.m +// DTFoundation +// +// Created by Oliver Drobnik on 11/25/11. +// Copyright (c) 2011 Cocoanetics. All rights reserved. +// + +#import "NSURL+DTAppLinks.h" + +@implementation NSURL (DTAppLinks) + ++ (NSURL *)appStoreURLforApplicationIdentifier:(NSString *)identifier +{ + NSString *link = [NSString stringWithFormat:@"http://itunes.apple.com/us/app/id%@?mt=8", identifier]; + + return [NSURL URLWithString:link]; +} + ++ (NSURL *)appStoreReviewURLForApplicationIdentifier:(NSString *)identifier +{ + NSString *link = [NSString stringWithFormat:@"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@", identifier]; + return [NSURL URLWithString:link]; +} + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/UIApplication+DTNetworkActivity.h b/Pods/DTFoundation/Core/Source/iOS/UIApplication+DTNetworkActivity.h new file mode 100644 index 0000000..58024e9 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/UIApplication+DTNetworkActivity.h @@ -0,0 +1,24 @@ +// +// UIApplication+DTNetworkActivity.h +// DTFoundation +// +// Created by Oliver Drobnik on 5/21/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Enhancement for `UIApplication` to properly count active network sessions and show the network activity indicator whenever there are more than 0 active sessions. + */ +@interface UIApplication (DTNetworkActivity) + +/** + Increments the number of active network operations + */ +- (void)pushActiveNetworkOperation; + +/** + Decrements the number of active network operations + */ +- (void)popActiveNetworkOperation; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/UIApplication+DTNetworkActivity.m b/Pods/DTFoundation/Core/Source/iOS/UIApplication+DTNetworkActivity.m new file mode 100644 index 0000000..e7835d4 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/UIApplication+DTNetworkActivity.m @@ -0,0 +1,51 @@ +// +// UIApplication+DTNetworkActivity.m +// DTFoundation +// +// Created by Oliver Drobnik on 5/21/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "UIApplication+DTNetworkActivity.h" + +static NSUInteger __internalOperationCount = 0; + +@implementation UIApplication (DTNetworkActivity) + +- (void)pushActiveNetworkOperation +{ + @synchronized(self) + { + __internalOperationCount++; + + dispatch_async(dispatch_get_main_queue(), ^{ + if (!self.isNetworkActivityIndicatorVisible && __internalOperationCount) + { + self.networkActivityIndicatorVisible = YES; + } + }); + } +} + +- (void)popActiveNetworkOperation +{ + @synchronized(self) + { + if (__internalOperationCount==0) + { + // nothing to do + return; + } + + __internalOperationCount--; + + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.isNetworkActivityIndicatorVisible && !__internalOperationCount) + { + self.networkActivityIndicatorVisible = NO; + } + }); + } +} + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/UIImage+DTFoundation.h b/Pods/DTFoundation/Core/Source/iOS/UIImage+DTFoundation.h new file mode 100644 index 0000000..e95a3e0 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/UIImage+DTFoundation.h @@ -0,0 +1,106 @@ +// +// UIImage+DTFoundation.h +// DTFoundation +// +// Created by Oliver Drobnik on 3/8/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Methods to help with working with images. + */ +@interface UIImage (DTFoundation) + +/** + @name Generating Images + */ + +/** + Creates an image filled with a solid color + @param color The solid color that fills the image + @param size The size of the image + @returns The image filled with given color and given size + */ ++ (UIImage *)imageWithSolidColor:(UIColor *)color size:(CGSize)size; + + +/** + Creates an image filled with a tint color using the receiver as image mask. The resulting image ignores the receiver's color values and instead uses the alpha values combined with the passed color. + @param color The color to use for tinting + @returns A new image + */ +- (UIImage *)imageMaskedAndTintedWithColor:(UIColor *)color; + +/** + @name Loading from RemoteURLs + */ + + +/** + Creates and returns an image object synchronously by loading the image data from the specified URL and optionally caching it. + + Useful values for cachePolicy are: + + - NSURLRequestUseProtocolCachePolicy (default) + - NSURLRequestReloadIgnoringLocalCacheData + - NSURLRequestReturnCacheDataElseLoad + - NSURLRequestReturnCacheDataDontLoad + + @param URL The URL to load the image from + @param cachePolicy The cache policy to apply. + @param error An optional output parameter to return an error if the loading fails + @returns The image object for the specified URL, or nil if the method could not load the specified image. + */ ++ (UIImage *)imageWithContentsOfURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy error:(NSError **)error; + + + /** + @name Drawing + */ + +/** + Mimicks the way images are drawn differently by UIImageView based on the set content mode. + @param rect The rectangle to drawn in + @param contentMode The content mode. Note that UIViewContentModeRedraw is treated the same as UIViewContentModeScaleToFill. + */ +- (void)drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode; + +/** + @name Working with Tiles + */ + +/** + Cuts out a tile at the given row and column + + @param column The index of the column + @param columns The total number of columns + @param row The index of the row + @param rows The total number of rows + @returns The resulting image + */ +- (UIImage *)tileImageAtColumn:(NSUInteger)column ofColumns:(NSUInteger)columns row:(NSUInteger)row ofRows:(NSUInteger)rows; + +/** + Cuts out a tile at the given clip rect relative to the bounds + + @param clipRect The clipping rect to extract + @param bounds The bounds to which the clipRect is relative to + @param scale The image scale + @returns The resulting image + */ +- (UIImage *)tileImageInClipRect:(CGRect)clipRect inBounds:(CGRect)bounds scale:(CGFloat)scale; + + +/** + @name Modifying Images + */ + +/** + Resizes the receiver to the given size. + + @param newSize The target image size + @returns The resulting image + */ +- (UIImage *)imageScaledToSize:(CGSize)newSize; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/UIImage+DTFoundation.m b/Pods/DTFoundation/Core/Source/iOS/UIImage+DTFoundation.m new file mode 100644 index 0000000..2e17721 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/UIImage+DTFoundation.m @@ -0,0 +1,331 @@ +// +// UIImage+DTFoundation.m +// DTFoundation +// +// Created by Oliver Drobnik on 3/8/12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "UIImage+DTFoundation.h" +#import "DTCoreGraphicsUtils.h" +#import "DTLog.h" + +@implementation UIImage (DTFoundation) + +#pragma mark - Generating Images + ++ (UIImage *)imageWithSolidColor:(UIColor *)color size:(CGSize)size +{ + NSParameterAssert(color); + NSAssert(!CGSizeEqualToSize(size, CGSizeZero), @"Size cannot be CGSizeZero"); + + CGRect rect = CGRectMake(0, 0, size.width, size.height); + + // Create a context depending on given size + UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0); + + // Fill it with your color + [color setFill]; + UIRectFill(rect); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + +- (UIImage *)imageMaskedAndTintedWithColor:(UIColor *)color +{ + NSParameterAssert(color); + + UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + CGRect bounds = (CGRect){CGPointZero, self.size}; + + // do a vertical flip so that image is correct + CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, bounds.size.height); + CGContextConcatCTM(ctx, flipVertical); + + // create mask of image + CGContextClipToMask(ctx, bounds, self.CGImage); + + // fill with given color + [color setFill]; + CGContextFillRect(ctx, bounds); + + // get back new image + UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return retImage; +} + +#pragma mark - Loading + ++ (UIImage *)imageWithContentsOfURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy error:(NSError **)error +{ + NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:cachePolicy timeoutInterval:10.0]; + + NSCachedURLResponse *cacheResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request]; + + NSData *data; + + if (cacheResponse) + { + DTLogDebug(@"cache hit for %@", [URL absoluteString]); + } + else + { + DTLogDebug(@"cache fail for %@", [URL absoluteString]); + } + + NSURLResponse *response; + data = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:error]; + + if (!data) + { + DTLogError(@"Error loading image at %@", URL); + return nil; + } + + UIImage *image = [UIImage imageWithData:data]; + return image; +} + + +#pragma mark Drawing + +- (void)drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode +{ + CGRect drawRect; + CGSize size = self.size; + + switch (contentMode) + { + case UIViewContentModeRedraw: + case UIViewContentModeScaleToFill: + { + // nothing to do + [self drawInRect:rect]; + return; + } + + case UIViewContentModeScaleAspectFit: + { + CGFloat factor; + + if (size.width + +NSString *shadowContext = @"Shadow"; + +@implementation UIView (DTFoundation) + +- (UIImage *)snapshotImage +{ + UIGraphicsBeginImageContext(self.bounds.size); + [self.layer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + +- (void)setRoundedCornersWithRadius:(CGFloat)radius width:(CGFloat)width color:(UIColor *)color +{ + self.clipsToBounds = YES; + self.layer.cornerRadius = radius; + self.layer.borderWidth = width; + + if (color) + { + self.layer.borderColor = color.CGColor; + } +} + +- (void)addShadowWithColor:(UIColor *)color alpha:(CGFloat)alpha radius:(CGFloat)radius offset:(CGSize)offset +{ + self.layer.shadowOpacity = alpha; + self.layer.shadowRadius = radius; + self.layer.shadowOffset = offset; + + if (color) + { + self.layer.shadowColor = [color CGColor]; + } + + // cannot have masking + self.layer.masksToBounds = NO; +} + +- (void)updateShadowPathToBounds:(CGRect)bounds withDuration:(NSTimeInterval)duration +{ + CGPathRef oldPath = self.layer.shadowPath; + CGPathRef newPath = CGPathCreateWithRect(bounds, NULL); + + if (oldPath && duration>0) + { + CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:@"shadowPath"]; + theAnimation.duration = duration; + theAnimation.fromValue = (__bridge id)oldPath; + theAnimation.toValue = (__bridge id)newPath; + theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [self.layer addAnimation:theAnimation forKey:@"shadowPath"]; + } + + self.layer.shadowPath = newPath; + + CGPathRelease(newPath); +} + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/UIWebView+DTFoundation.h b/Pods/DTFoundation/Core/Source/iOS/UIWebView+DTFoundation.h new file mode 100644 index 0000000..9efd194 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/UIWebView+DTFoundation.h @@ -0,0 +1,21 @@ +// +// UIWebView+DTFoundation.h +// DTFoundation +// +// Created by Oliver Drobnik on 25.05.12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +/** + Some convenient functions that can be also achieved if you know JavaScript, but are way more easy to remember like this. + */ + +@interface UIWebView (DTFoundation) + +/** + Getting the current document's title + @returns A string with the document title + */ +- (NSString *)documentTitle; + +@end diff --git a/Pods/DTFoundation/Core/Source/iOS/UIWebView+DTFoundation.m b/Pods/DTFoundation/Core/Source/iOS/UIWebView+DTFoundation.m new file mode 100644 index 0000000..b29d263 --- /dev/null +++ b/Pods/DTFoundation/Core/Source/iOS/UIWebView+DTFoundation.m @@ -0,0 +1,18 @@ +// +// UIWebView+DTFoundation.m +// DTFoundation +// +// Created by Oliver Drobnik on 25.05.12. +// Copyright (c) 2012 Cocoanetics. All rights reserved. +// + +#import "UIWebView+DTFoundation.h" + +@implementation UIWebView (DTFoundation) + +- (NSString *)documentTitle +{ + return [self stringByEvaluatingJavaScriptFromString:@"document.title"]; +} + +@end diff --git a/Pods/EGOCache/EGOCache.h b/Pods/EGOCache/EGOCache.h new file mode 100755 index 0000000..51f18de --- /dev/null +++ b/Pods/EGOCache/EGOCache.h @@ -0,0 +1,88 @@ +// +// EGOCache.h +// enormego +// +// Created by Shaun Harrison. +// Copyright (c) 2009-2015 enormego. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +#if TARGET_OS_IPHONE +#import +#endif + +#if !__has_feature(nullability) +# define nullable +# define nonnull +# define __nullable +# define __nonnull +#endif + +@interface EGOCache : NSObject + ++ (nonnull instancetype)currentCache __deprecated_msg("Renamed to globalCache"); + +// Global cache for easy use ++ (nonnull instancetype)globalCache; + +// Opitionally create a different EGOCache instance with it's own cache directory +- (nonnull instancetype)initWithCacheDirectory:(NSString __nonnull*)cacheDirectory; + +- (void)clearCache; +- (void)removeCacheForKey:(NSString __nonnull*)key; + +- (BOOL)hasCacheForKey:(NSString __nonnull*)key; + +- (NSData* __nullable)dataForKey:(NSString __nonnull*)key; +- (void)setData:(NSData __nonnull*)data forKey:(NSString __nonnull*)key; +- (void)setData:(NSData __nonnull*)data forKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; + +- (NSString* __nullable)stringForKey:(NSString __nonnull*)key; +- (void)setString:(NSString __nonnull*)aString forKey:(NSString __nonnull*)key; +- (void)setString:(NSString __nonnull*)aString forKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; + +- (NSDate* __nullable)dateForKey:(NSString __nonnull*)key; +- (NSArray __nonnull*)allKeys; + +#if TARGET_OS_IPHONE +- (UIImage* __nullable)imageForKey:(NSString __nonnull*)key; +- (void)setImage:(UIImage __nonnull*)anImage forKey:(NSString __nonnull*)key; +- (void)setImage:(UIImage __nonnull*)anImage forKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; +#else +- (NSImage* __nullable)imageForKey:(NSString __nonnull*)key; +- (void)setImage:(NSImage __nonnull*)anImage forKey:(NSString __nonnull*)key; +- (void)setImage:(NSImage __nonnull*)anImage forKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; +#endif + +- (NSData* __nullable)plistForKey:(NSString __nonnull*)key; +- (void)setPlist:(nonnull id)plistObject forKey:(NSString __nonnull*)key; +- (void)setPlist:(nonnull id)plistObject forKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; + +- (void)copyFilePath:(NSString __nonnull*)filePath asKey:(NSString __nonnull*)key; +- (void)copyFilePath:(NSString __nonnull*)filePath asKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; + +- (nullable id)objectForKey:(NSString __nonnull*)key; +- (void)setObject:(nonnull id)anObject forKey:(NSString __nonnull*)key; +- (void)setObject:(nonnull id)anObject forKey:(NSString __nonnull*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; + +@property(nonatomic) NSTimeInterval defaultTimeoutInterval; // Default is 1 day +@end \ No newline at end of file diff --git a/Pods/EGOCache/EGOCache.m b/Pods/EGOCache/EGOCache.m new file mode 100755 index 0000000..fb4f602 --- /dev/null +++ b/Pods/EGOCache/EGOCache.m @@ -0,0 +1,373 @@ +// +// EGOCache.m +// enormego +// +// Created by Shaun Harrison. +// Copyright (c) 2009-2015 enormego. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "EGOCache.h" + +#if DEBUG +# define CHECK_FOR_EGOCACHE_PLIST() if([key isEqualToString:@"EGOCache.plist"]) { \ + NSLog(@"EGOCache.plist is a reserved key and can not be modified."); \ + return; } +#else +# define CHECK_FOR_EGOCACHE_PLIST() if([key isEqualToString:@"EGOCache.plist"]) return; +#endif + +static inline NSString* cachePathForKey(NSString* directory, NSString* key) { + key = [key stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; + return [directory stringByAppendingPathComponent:key]; +} + +#pragma mark - + +@interface EGOCache () { + dispatch_queue_t _cacheInfoQueue; + dispatch_queue_t _frozenCacheInfoQueue; + dispatch_queue_t _diskQueue; + NSMutableDictionary* _cacheInfo; + NSString* _directory; + BOOL _needsSave; +} + +@property(nonatomic,copy) NSDictionary* frozenCacheInfo; +@end + +@implementation EGOCache + ++ (instancetype)currentCache { + return [self globalCache]; +} + ++ (instancetype)globalCache { + static id instance; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[[self class] alloc] init]; + }); + + return instance; +} + +- (instancetype)init { + NSString* cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; + NSString* oldCachesDirectory = [[[cachesDirectory stringByAppendingPathComponent:[[NSProcessInfo processInfo] processName]] stringByAppendingPathComponent:@"EGOCache"] copy]; + + if([[NSFileManager defaultManager] fileExistsAtPath:oldCachesDirectory]) { + [[NSFileManager defaultManager] removeItemAtPath:oldCachesDirectory error:NULL]; + } + + cachesDirectory = [[[cachesDirectory stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]] stringByAppendingPathComponent:@"EGOCache"] copy]; + return [self initWithCacheDirectory:cachesDirectory]; +} + +- (instancetype)initWithCacheDirectory:(NSString*)cacheDirectory { + if((self = [super init])) { + _cacheInfoQueue = dispatch_queue_create("com.enormego.egocache.info", DISPATCH_QUEUE_SERIAL); + dispatch_queue_t priority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dispatch_set_target_queue(priority, _cacheInfoQueue); + + _frozenCacheInfoQueue = dispatch_queue_create("com.enormego.egocache.info.frozen", DISPATCH_QUEUE_SERIAL); + priority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dispatch_set_target_queue(priority, _frozenCacheInfoQueue); + + _diskQueue = dispatch_queue_create("com.enormego.egocache.disk", DISPATCH_QUEUE_CONCURRENT); + priority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); + dispatch_set_target_queue(priority, _diskQueue); + + + _directory = cacheDirectory; + + _cacheInfo = [[NSDictionary dictionaryWithContentsOfFile:cachePathForKey(_directory, @"EGOCache.plist")] mutableCopy]; + + if(!_cacheInfo) { + _cacheInfo = [[NSMutableDictionary alloc] init]; + } + + [[NSFileManager defaultManager] createDirectoryAtPath:_directory withIntermediateDirectories:YES attributes:nil error:NULL]; + + NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate]; + NSMutableArray* removedKeys = [[NSMutableArray alloc] init]; + + for(NSString* key in _cacheInfo) { + if([_cacheInfo[key] timeIntervalSinceReferenceDate] <= now) { + [[NSFileManager defaultManager] removeItemAtPath:cachePathForKey(_directory, key) error:NULL]; + [removedKeys addObject:key]; + } + } + + [_cacheInfo removeObjectsForKeys:removedKeys]; + self.frozenCacheInfo = _cacheInfo; + [self setDefaultTimeoutInterval:86400]; + } + + return self; +} + +- (void)clearCache { + dispatch_sync(_cacheInfoQueue, ^{ + for(NSString* key in _cacheInfo) { + [[NSFileManager defaultManager] removeItemAtPath:cachePathForKey(_directory, key) error:NULL]; + } + + [_cacheInfo removeAllObjects]; + + dispatch_sync(_frozenCacheInfoQueue, ^{ + self.frozenCacheInfo = [_cacheInfo copy]; + }); + + [self setNeedsSave]; + }); +} + +- (void)removeCacheForKey:(NSString*)key { + CHECK_FOR_EGOCACHE_PLIST(); + + dispatch_async(_diskQueue, ^{ + [[NSFileManager defaultManager] removeItemAtPath:cachePathForKey(_directory, key) error:NULL]; + }); + + [self setCacheTimeoutInterval:0 forKey:key]; +} + +- (BOOL)hasCacheForKey:(NSString*)key { + NSDate* date = [self dateForKey:key]; + if(date == nil) return NO; + if([date timeIntervalSinceReferenceDate] < CFAbsoluteTimeGetCurrent()) return NO; + + return [[NSFileManager defaultManager] fileExistsAtPath:cachePathForKey(_directory, key)]; +} + +- (NSDate*)dateForKey:(NSString*)key { + __block NSDate* date = nil; + + dispatch_sync(_frozenCacheInfoQueue, ^{ + date = (self.frozenCacheInfo)[key]; + }); + + return date; +} + +- (NSArray*)allKeys { + __block NSArray* keys = nil; + + dispatch_sync(_frozenCacheInfoQueue, ^{ + keys = [self.frozenCacheInfo allKeys]; + }); + + return keys; +} + +- (void)setCacheTimeoutInterval:(NSTimeInterval)timeoutInterval forKey:(NSString*)key { + NSDate* date = timeoutInterval > 0 ? [NSDate dateWithTimeIntervalSinceNow:timeoutInterval] : nil; + + // Temporarily store in the frozen state for quick reads + dispatch_sync(_frozenCacheInfoQueue, ^{ + NSMutableDictionary* info = [self.frozenCacheInfo mutableCopy]; + + if(date) { + info[key] = date; + } else { + [info removeObjectForKey:key]; + } + + self.frozenCacheInfo = info; + }); + + // Save the final copy (this may be blocked by other operations) + dispatch_async(_cacheInfoQueue, ^{ + if(date) { + _cacheInfo[key] = date; + } else { + [_cacheInfo removeObjectForKey:key]; + } + + dispatch_sync(_frozenCacheInfoQueue, ^{ + self.frozenCacheInfo = [_cacheInfo copy]; + }); + + [self setNeedsSave]; + }); +} + +#pragma mark - +#pragma mark Copy file methods + +- (void)copyFilePath:(NSString*)filePath asKey:(NSString*)key { + [self copyFilePath:filePath asKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)copyFilePath:(NSString*)filePath asKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval { + dispatch_async(_diskQueue, ^{ + [[NSFileManager defaultManager] copyItemAtPath:filePath toPath:cachePathForKey(_directory, key) error:NULL]; + }); + + [self setCacheTimeoutInterval:timeoutInterval forKey:key]; +} + +#pragma mark - +#pragma mark Data methods + +- (void)setData:(NSData*)data forKey:(NSString*)key { + [self setData:data forKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)setData:(NSData*)data forKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval { + CHECK_FOR_EGOCACHE_PLIST(); + + NSString* cachePath = cachePathForKey(_directory, key); + + dispatch_async(_diskQueue, ^{ + [data writeToFile:cachePath atomically:YES]; + }); + + [self setCacheTimeoutInterval:timeoutInterval forKey:key]; +} + +- (void)setNeedsSave { + dispatch_async(_cacheInfoQueue, ^{ + if(_needsSave) return; + _needsSave = YES; + + double delayInSeconds = 0.5; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); + dispatch_after(popTime, _cacheInfoQueue, ^(void){ + if(!_needsSave) return; + [_cacheInfo writeToFile:cachePathForKey(_directory, @"EGOCache.plist") atomically:YES]; + _needsSave = NO; + }); + }); +} + +- (NSData*)dataForKey:(NSString*)key { + if([self hasCacheForKey:key]) { + return [NSData dataWithContentsOfFile:cachePathForKey(_directory, key) options:0 error:NULL]; + } else { + return nil; + } +} + +#pragma mark - +#pragma mark String methods + +- (NSString*)stringForKey:(NSString*)key { + return [[NSString alloc] initWithData:[self dataForKey:key] encoding:NSUTF8StringEncoding]; +} + +- (void)setString:(NSString*)aString forKey:(NSString*)key { + [self setString:aString forKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)setString:(NSString*)aString forKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self setData:[aString dataUsingEncoding:NSUTF8StringEncoding] forKey:key withTimeoutInterval:timeoutInterval]; +} + +#pragma mark - +#pragma mark Image methds + +#if TARGET_OS_IPHONE + +- (UIImage*)imageForKey:(NSString*)key { + UIImage* image = nil; + + @try { + image = [NSKeyedUnarchiver unarchiveObjectWithFile:cachePathForKey(_directory, key)]; + } @catch (NSException* e) { + // Surpress any unarchiving exceptions and continue with nil + } + + return image; +} + +- (void)setImage:(UIImage*)anImage forKey:(NSString*)key { + [self setImage:anImage forKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)setImage:(UIImage*)anImage forKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval { + @try { + // Using NSKeyedArchiver preserves all information such as scale, orientation, and the proper image format instead of saving everything as pngs + [self setData:[NSKeyedArchiver archivedDataWithRootObject:anImage] forKey:key withTimeoutInterval:timeoutInterval]; + } @catch (NSException* e) { + // Something went wrong, but we'll fail silently. + } +} + + +#else + +- (NSImage*)imageForKey:(NSString*)key { + return [[NSImage alloc] initWithData:[self dataForKey:key]]; +} + +- (void)setImage:(NSImage*)anImage forKey:(NSString*)key { + [self setImage:anImage forKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)setImage:(NSImage*)anImage forKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self setData:[[[anImage representations] objectAtIndex:0] representationUsingType:NSPNGFileType properties:nil] forKey:key withTimeoutInterval:timeoutInterval]; +} + +#endif + +#pragma mark - +#pragma mark Property List methods + +- (NSData*)plistForKey:(NSString*)key; { + NSData* plistData = [self dataForKey:key]; + return [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:nil error:nil]; +} + +- (void)setPlist:(id)plistObject forKey:(NSString*)key; { + [self setPlist:plistObject forKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)setPlist:(id)plistObject forKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval; { + // Binary plists are used over XML for better performance + NSData* plistData = [NSPropertyListSerialization dataWithPropertyList:plistObject format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil]; + + if(plistData != nil) { + [self setData:plistData forKey:key withTimeoutInterval:timeoutInterval]; + } +} + +#pragma mark - +#pragma mark Object methods + +- (id)objectForKey:(NSString*)key { + if([self hasCacheForKey:key]) { + return [NSKeyedUnarchiver unarchiveObjectWithData:[self dataForKey:key]]; + } else { + return nil; + } +} + +- (void)setObject:(id)anObject forKey:(NSString*)key { + [self setObject:anObject forKey:key withTimeoutInterval:self.defaultTimeoutInterval]; +} + +- (void)setObject:(id)anObject forKey:(NSString*)key withTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self setData:[NSKeyedArchiver archivedDataWithRootObject:anObject] forKey:key withTimeoutInterval:timeoutInterval]; +} + +@end diff --git a/Pods/Godzippa/Godzippa/Godzippa.h b/Pods/Godzippa/Godzippa/Godzippa.h new file mode 100644 index 0000000..83e49a5 --- /dev/null +++ b/Pods/Godzippa/Godzippa/Godzippa.h @@ -0,0 +1,24 @@ +// Godzippa.h +// +// Copyright (c) 2012 Mattt Thompson (http://mattt.me/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "NSData+Godzippa.h" +#import "NSFileManager+Godzippa.h" diff --git a/Pods/Godzippa/Godzippa/NSData+Godzippa.h b/Pods/Godzippa/Godzippa/NSData+Godzippa.h new file mode 100644 index 0000000..6516cc1 --- /dev/null +++ b/Pods/Godzippa/Godzippa/NSData+Godzippa.h @@ -0,0 +1,98 @@ +// NSData+Godzippa.h +// +// Copyright (c) 2012 Mattt Thompson (http://mattt.me/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +/** + Godzippa provides a category on `NSData` to inflate and deflate data using gzip compression. + */ +@interface NSData (Godzippa) + +///------------------ +/// @name Compressing +///------------------ + +/** + Returns the deflated of the receiver using gzip compression. + + @param error The error that occurred while attempting to deflate the receiver. + + @return The compressed data. + */ +- (NSData *)dataByGZipCompressingWithError:(NSError * __autoreleasing *)error; + +/** + Returns the deflated of the receiver using gzip compression with the specified zlib values for compression level, window size, internal memory allocation, and strategy. + + @param level The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). + @param windowBits The base two logarithm of the window size (the size of the history buffer). Larger values of this parameter result in better compression at the expense of memory usage. + @param memLevel Specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. + @param strategy Used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. + @param error The error that occurred while attempting to deflate the receiver. + + @return The compressed data. + */ +- (NSData *)dataByGZipCompressingAtLevel:(int)level + windowSize:(int)windowBits + memoryLevel:(int)memLevel + strategy:(int)strategy + error:(NSError * __autoreleasing *)error; + +///-------------------- +/// @name Decompressing +///-------------------- + +/** + Returns the inflated of the receiver using gzip compression. + + @param error The error that occurred while attempting to inflate the receiver. + + @return The decompressed data. + */ +- (NSData *)dataByGZipDecompressingDataWithError:(NSError * __autoreleasing *)error; + +/** + Returns the inflated of the receiver using gzip compression with the specified zlib value for window size. + + @param windowBits The base two logarithm of the maximum window size (the size of the history buffer). Must be greater than or equal to the windowBits value provided to dataByGZipCompressingAtLevel:windowSize:memoryLevel:strategy:error: while compressing. + @param error The error that occurred while attempting to inflate the receiver. + + @return The decompressed data. + */ +- (NSData *)dataByGZipDecompressingDataWithWindowSize:(int)windowBits + error:(NSError * __autoreleasing *)error; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ### Constants + + `GodzippaZlibErrorDomain` + Godzippa errors. Error codes for `GodzippaZlibErrorDomain` correspond to status codes from zlib. + */ +extern NSString * const GodzippaZlibErrorDomain; diff --git a/Pods/Godzippa/Godzippa/NSData+Godzippa.m b/Pods/Godzippa/Godzippa/NSData+Godzippa.m new file mode 100644 index 0000000..b72d2cc --- /dev/null +++ b/Pods/Godzippa/Godzippa/NSData+Godzippa.m @@ -0,0 +1,157 @@ +// NSData+Godzippa.m +// +// Copyright (c) 2012 Mattt Thompson (http://mattt.me/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "NSData+Godzippa.h" + +static const int kGodzippaChunkSize = 1024; +static const int kGodzippaDefaultMemoryLevel = 8; +static const int kGodzippaDefaultWindowBits = 15; +static const int kGodzippaDefaultWindowBitsWithGZipHeader = 16 + kGodzippaDefaultWindowBits; + +NSString * const GodzippaZlibErrorDomain = @"com.godzippa.zlib.error"; + +@implementation NSData (Godzippa) + +- (NSData *)dataByGZipCompressingWithError:(NSError * __autoreleasing *)error { + return [self dataByGZipCompressingAtLevel:Z_DEFAULT_COMPRESSION windowSize:kGodzippaDefaultWindowBitsWithGZipHeader memoryLevel:kGodzippaDefaultMemoryLevel strategy:Z_DEFAULT_STRATEGY error:error]; +} + +- (NSData *)dataByGZipCompressingAtLevel:(int)level + windowSize:(int)windowBits + memoryLevel:(int)memLevel + strategy:(int)strategy + error:(NSError * __autoreleasing *)error +{ + if ([self length] == 0) { + return self; + } + + z_stream zStream; + bzero(&zStream, sizeof(z_stream)); + + zStream.zalloc = Z_NULL; + zStream.zfree = Z_NULL; + zStream.opaque = Z_NULL; + zStream.next_in = (Bytef *)[self bytes]; + zStream.avail_in = (unsigned int)[self length]; + zStream.total_out = 0; + + OSStatus status; + if ((status = deflateInit2(&zStream, level, Z_DEFLATED, windowBits, memLevel, strategy)) != Z_OK) { + if (error) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Failed deflateInit", nil) forKey:NSLocalizedDescriptionKey]; + *error = [[NSError alloc] initWithDomain:GodzippaZlibErrorDomain code:status userInfo:userInfo]; + } + + return nil; + } + + NSMutableData *compressedData = [NSMutableData dataWithLength:kGodzippaChunkSize]; + + do { + if ((status == Z_BUF_ERROR) || (zStream.total_out == [compressedData length])) { + [compressedData increaseLengthBy:kGodzippaChunkSize]; + } + + zStream.next_out = (Bytef*)[compressedData mutableBytes] + zStream.total_out; + zStream.avail_out = (unsigned int)([compressedData length] - zStream.total_out); + + status = deflate(&zStream, Z_FINISH); + } while ((status == Z_OK) || (status == Z_BUF_ERROR)); + + deflateEnd(&zStream); + + if ((status != Z_OK) && (status != Z_STREAM_END)) { + if (error) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Error deflating payload", nil) forKey:NSLocalizedDescriptionKey]; + *error = [[NSError alloc] initWithDomain:GodzippaZlibErrorDomain code:status userInfo:userInfo]; + } + + return nil; + } + + [compressedData setLength:zStream.total_out]; + + return compressedData; +} + +- (NSData *)dataByGZipDecompressingDataWithError:(NSError * __autoreleasing *)error { + return [self dataByGZipDecompressingDataWithWindowSize:kGodzippaDefaultWindowBitsWithGZipHeader error:error]; +} + +- (NSData *)dataByGZipDecompressingDataWithWindowSize:(int)windowBits + error:(NSError * __autoreleasing *)error +{ + if ([self length] == 0) { + return self; + } + + z_stream zStream; + bzero(&zStream, sizeof(z_stream)); + + zStream.zalloc = Z_NULL; + zStream.zfree = Z_NULL; + zStream.opaque = Z_NULL; + zStream.avail_in = (unsigned int)[self length]; + zStream.next_in = (Byte *)[self bytes]; + + OSStatus status; + if ((status = inflateInit2(&zStream, windowBits)) != Z_OK) { + if (error) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Failed inflateInit", nil) forKey:NSLocalizedDescriptionKey]; + *error = [[NSError alloc] initWithDomain:GodzippaZlibErrorDomain code:status userInfo:userInfo]; + } + + return nil; + } + + NSUInteger estimatedLength = (NSUInteger)((double)[self length] * 1.5); + NSMutableData *decompressedData = [NSMutableData dataWithLength:estimatedLength]; + + do { + if ((status == Z_BUF_ERROR) || (zStream.total_out == [decompressedData length])) { + [decompressedData increaseLengthBy:estimatedLength / 2]; + } + + zStream.next_out = (Bytef*)[decompressedData mutableBytes] + zStream.total_out; + zStream.avail_out = (unsigned int)([decompressedData length] - zStream.total_out); + + status = inflate(&zStream, Z_FINISH); + } while ((status == Z_OK) || (status == Z_BUF_ERROR)); + + inflateEnd(&zStream); + + if ((status != Z_OK) && (status != Z_STREAM_END)) { + if (error) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Error inflating payload", nil) forKey:NSLocalizedDescriptionKey]; + *error = [[NSError alloc] initWithDomain:GodzippaZlibErrorDomain code:status userInfo:userInfo]; + } + + return nil; + } + + [decompressedData setLength:zStream.total_out]; + + return decompressedData; +} + +@end diff --git a/Pods/Godzippa/Godzippa/NSFileManager+Godzippa.h b/Pods/Godzippa/Godzippa/NSFileManager+Godzippa.h new file mode 100644 index 0000000..d83ee96 --- /dev/null +++ b/Pods/Godzippa/Godzippa/NSFileManager+Godzippa.h @@ -0,0 +1,78 @@ +// NSFileManager+Godzippa.h +// +// Copyright (c) 2012–2014 Mattt Thompson (http://mattt.me/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +/** + Godzippa provides a category on `NSFileManager` to inflate and deflate files using gzip compression. + */ +@interface NSFileManager (Godzippa) + +///------------------ +/// @name Compressing +///------------------ + +/** + Compresses the specified file, writing data to a destination file. + + @param sourceFile The file to be compressed. + @param destinationFile The destination of the compressed file. + @param error The error that occurred while attempting to compress the source file, if any. + + @return Whether the compressed file contents were written successfully. + */ +- (BOOL)GZipCompressFile:(NSURL *)sourceFile + writingContentsToFile:(NSURL *)destinationFile + error:(NSError * __autoreleasing *)error; + +/** + Compresses the specified file at a particular level, writing data to a destination file. + + @param sourceFile The file to be compressed. + @param destinationFile The destination of the compressed file. + @param level The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). + @param error The error that occurred while attempting to compress the source file, if any. + + @return Whether the compressed file contents were written successfully. + */ +- (BOOL)GZipCompressFile:(NSURL *)sourceFile + writingContentsToFile:(NSURL *)destinationFile + atLevel:(int)level + error:(NSError *__autoreleasing *)error; + +///-------------------- +/// @name Decompressing +///-------------------- + +/** + Decompresses the specified Gzip-compressed file, writing data to a destination file. + + @param sourceFile The compressed file. + @param destinationFile The destination for the decompressed file. + */ +- (BOOL)GZipDecompressFile:(NSURL *)sourceFile + writingContentsToFile:(NSURL *)destinationFile + error:(NSError * __autoreleasing *)error; + +@end diff --git a/Pods/Godzippa/Godzippa/NSFileManager+Godzippa.m b/Pods/Godzippa/Godzippa/NSFileManager+Godzippa.m new file mode 100644 index 0000000..cbb081a --- /dev/null +++ b/Pods/Godzippa/Godzippa/NSFileManager+Godzippa.m @@ -0,0 +1,121 @@ +// NSFileManager+Godzippa.m +// +// Copyright (c) 2012–2014 Mattt Thompson (http://mattt.me/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "NSFileManager+Godzippa.h" + +static const int kGodzippaChunkSize = 4096; + +@implementation NSFileManager (Godzippa) + +#pragma mark - Compressing + +- (BOOL)GZipCompressFile:(NSURL *)sourceFile + writingContentsToFile:(NSURL *)destinationFile + error:(NSError * __autoreleasing *)error +{ + return [self GZipCompressFile:sourceFile writingContentsToFile:destinationFile atLevel:Z_DEFAULT_COMPRESSION error:error]; +} + +- (BOOL)GZipCompressFile:(NSURL *)sourceFile + writingContentsToFile:(NSURL *)destinationFile + atLevel:(int)level + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(sourceFile); + NSParameterAssert(destinationFile); + + NSDictionary *sourceAttributes = [self attributesOfItemAtPath:sourceFile.path error:error]; + if (!sourceAttributes || [sourceAttributes[NSFileSize] unsignedIntegerValue] == 0) { + return NO; + } + + const char *mode = NULL; + if (level == Z_DEFAULT_COMPRESSION) { + mode = "w"; + } else { + mode = [[NSString stringWithFormat:@"w%d", level] UTF8String]; + } + + NSFileHandle *sourceFileHandle = [NSFileHandle fileHandleForReadingFromURL:sourceFile error:error]; + { + gzFile output = gzopen([destinationFile.path UTF8String], mode); + { + int numberOfBytesWritten = 0; + + do { + @autoreleasepool { + NSData *data = [sourceFileHandle readDataOfLength:kGodzippaChunkSize]; + numberOfBytesWritten = gzwrite(output, data.bytes, (unsigned) data.length); + } + } while (numberOfBytesWritten == kGodzippaChunkSize); + } + gzclose(output); + } + [sourceFileHandle closeFile]; + + return YES; +} + + +#pragma mark - Decompressing + +- (BOOL)GZipDecompressFile:(NSURL *)sourceFile + writingContentsToFile:(NSURL *)destinationFile + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(sourceFile); + NSParameterAssert(destinationFile); + + NSDictionary *sourceAttributes = [self attributesOfItemAtPath:sourceFile.path error:error]; + if (!sourceAttributes || [sourceAttributes[NSFileSize] unsignedIntegerValue] == 0) { + return NO; + } + + if (![[NSFileManager defaultManager] fileExistsAtPath:destinationFile.path]) { + if (![[NSFileManager defaultManager] createFileAtPath:destinationFile.path contents:nil attributes:nil]) { + return NO; + } + } + + NSFileHandle *destinationFileHandle = [NSFileHandle fileHandleForWritingAtPath:destinationFile.path]; + { + gzFile input = gzopen([sourceFile.path UTF8String], "r"); + { + int numberOfBytesRead = 0; + + do { + @autoreleasepool { + NSMutableData *mutableData = [NSMutableData dataWithLength:kGodzippaChunkSize]; + numberOfBytesRead = gzread(input, mutableData.mutableBytes, kGodzippaChunkSize); + [destinationFileHandle writeData:[mutableData subdataWithRange:NSMakeRange(0, (NSUInteger)numberOfBytesRead)]]; + } + } while (numberOfBytesRead == kGodzippaChunkSize); + } + gzclose(input); + } + [destinationFileHandle synchronizeFile]; + [destinationFileHandle closeFile]; + + return YES; +} + +@end diff --git a/Pods/MBProgressHUD/MBProgressHUD.h b/Pods/MBProgressHUD/MBProgressHUD.h new file mode 100644 index 0000000..c535ea5 --- /dev/null +++ b/Pods/MBProgressHUD/MBProgressHUD.h @@ -0,0 +1,515 @@ +// +// MBProgressHUD.h +// Version 0.9.1 +// Created by Matej Bukovinski on 2.4.09. +// + +// This code is distributed under the terms and conditions of the MIT license. + +// Copyright (c) 2009-2015 Matej Bukovinski +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import +#import + +@protocol MBProgressHUDDelegate; + + +typedef NS_ENUM(NSInteger, MBProgressHUDMode) { + /** Progress is shown using an UIActivityIndicatorView. This is the default. */ + MBProgressHUDModeIndeterminate, + /** Progress is shown using a round, pie-chart like, progress view. */ + MBProgressHUDModeDeterminate, + /** Progress is shown using a horizontal progress bar */ + MBProgressHUDModeDeterminateHorizontalBar, + /** Progress is shown using a ring-shaped progress view. */ + MBProgressHUDModeAnnularDeterminate, + /** Shows a custom view */ + MBProgressHUDModeCustomView, + /** Shows only labels */ + MBProgressHUDModeText +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) { + /** Opacity animation */ + MBProgressHUDAnimationFade, + /** Opacity + scale animation */ + MBProgressHUDAnimationZoom, + MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom, + MBProgressHUDAnimationZoomIn +}; + + +#ifndef MB_INSTANCETYPE +#if __has_feature(objc_instancetype) + #define MB_INSTANCETYPE instancetype +#else + #define MB_INSTANCETYPE id +#endif +#endif + +#ifndef MB_STRONG +#if __has_feature(objc_arc) + #define MB_STRONG strong +#else + #define MB_STRONG retain +#endif +#endif + +#ifndef MB_WEAK +#if __has_feature(objc_arc_weak) + #define MB_WEAK weak +#elif __has_feature(objc_arc) + #define MB_WEAK unsafe_unretained +#else + #define MB_WEAK assign +#endif +#endif + +#if NS_BLOCKS_AVAILABLE +typedef void (^MBProgressHUDCompletionBlock)(); +#endif + + +/** + * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. + * + * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class. + * The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all + * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is + * drawn centered as a rounded semi-transparent view which resizes depending on the user specified content. + * + * This view supports four modes of operation: + * - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView + * - MBProgressHUDModeDeterminate - shows a custom round progress indicator + * - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator + * - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView) + * + * All three modes can have optional labels assigned: + * - If the labelText property is set and non-empty then a label containing the provided content is placed below the + * indicator view. + * - If also the detailsLabelText property is set then another label is placed below the first label. + */ +@interface MBProgressHUD : UIView + +/** + * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. + * + * @param view The view that the HUD will be added to + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * @return A reference to the created HUD. + * + * @see hideHUDForView:animated: + * @see animationType + */ ++ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; + +/** + * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:. + * + * @param view The view that is going to be searched for a HUD subview. + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @return YES if a HUD was found and removed, NO otherwise. + * + * @see showHUDAddedTo:animated: + * @see animationType + */ ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; + +/** + * Finds all the HUD subviews and hides them. + * + * @param view The view that is going to be searched for HUD subviews. + * @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use + * animations while disappearing. + * @return the number of HUDs found and removed. + * + * @see hideHUDForView:animated: + * @see animationType + */ ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated; + +/** + * Finds the top-most HUD subview and returns it. + * + * @param view The view that is going to be searched. + * @return A reference to the last HUD subview discovered. + */ ++ (MB_INSTANCETYPE)HUDForView:(UIView *)view; + +/** + * Finds all HUD subviews and returns them. + * + * @param view The view that is going to be searched. + * @return All found HUD views (array of MBProgressHUD objects). + */ ++ (NSArray *)allHUDsForView:(UIView *)view; + +/** + * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with + * window.bounds as the parameter. + * + * @param window The window instance that will provide the bounds for the HUD. Should be the same instance as + * the HUD's superview (i.e., the window that the HUD will be added to). + */ +- (id)initWithWindow:(UIWindow *)window; + +/** + * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with + * view.bounds as the parameter + * + * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as + * the HUD's superview (i.e., the view that the HUD will be added to). + */ +- (id)initWithView:(UIView *)view; + +/** + * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so + * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread + * (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest). + * + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * + * @see animationType + */ +- (void)show:(BOOL)animated; + +/** + * Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * + * @see animationType + */ +- (void)hide:(BOOL)animated; + +/** + * Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @param delay Delay in seconds until the HUD is hidden. + * + * @see animationType + */ +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay; + +/** + * Shows the HUD while a background task is executing in a new thread, then hides the HUD. + * + * This method also takes care of autorelease pools so your method does not have to be concerned with setting up a + * pool. + * + * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread. + * @param target The object that the target method belongs to. + * @param object An optional object to be passed to the method. + * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use + * animations while (dis)appearing. + */ +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated; + +#if NS_BLOCKS_AVAILABLE + +/** + * Shows the HUD while a block is executing on a background queue, then hides the HUD. + * + * @see showAnimated:whileExecutingBlock:onQueue:completionBlock: + */ +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block; + +/** + * Shows the HUD while a block is executing on a background queue, then hides the HUD. + * + * @see showAnimated:whileExecutingBlock:onQueue:completionBlock: + */ +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion; + +/** + * Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD. + * + * @see showAnimated:whileExecutingBlock:onQueue:completionBlock: + */ +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue; + +/** + * Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD. + * + * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will + * not use animations while (dis)appearing. + * @param block The block to be executed while the HUD is shown. + * @param queue The dispatch queue on which the block should be executed. + * @param completion The block to be executed on completion. + * + * @see completionBlock + */ +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue + completionBlock:(MBProgressHUDCompletionBlock)completion; + +/** + * A block that gets called after the HUD was completely hidden. + */ +@property (copy) MBProgressHUDCompletionBlock completionBlock; + +#endif + +/** + * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate. + * + * @see MBProgressHUDMode + */ +@property (assign) MBProgressHUDMode mode; + +/** + * The animation type that should be used when the HUD is shown and hidden. + * + * @see MBProgressHUDAnimation + */ +@property (assign) MBProgressHUDAnimation animationType; + +/** + * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView. + * For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds). + */ +@property (MB_STRONG) UIView *customView; + +/** + * The HUD delegate object. + * + * @see MBProgressHUDDelegate + */ +@property (MB_WEAK) id delegate; + +/** + * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit + * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or + * set to @"", then no message is displayed. + */ +@property (copy) NSString *labelText; + +/** + * An optional details message displayed below the labelText message. This message is displayed only if the labelText + * property is also set and is different from an empty string (@""). The details text can span multiple lines. + */ +@property (copy) NSString *detailsLabelText; + +/** + * The opacity of the HUD window. Defaults to 0.8 (80% opacity). + */ +@property (assign) float opacity; + +/** + * The color of the HUD window. Defaults to black. If this property is set, color is set using + * this UIColor and the opacity property is not used. using retain because performing copy on + * UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone. + */ +@property (MB_STRONG) UIColor *color; + +/** + * The x-axis offset of the HUD relative to the centre of the superview. + */ +@property (assign) float xOffset; + +/** + * The y-axis offset of the HUD relative to the centre of the superview. + */ +@property (assign) float yOffset; + +/** + * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views). + * Defaults to 20.0 + */ +@property (assign) float margin; + +/** + * The corner radius for the HUD + * Defaults to 10.0 + */ +@property (assign) float cornerRadius; + +/** + * Cover the HUD background view with a radial gradient. + */ +@property (assign) BOOL dimBackground; + +/* + * Grace period is the time (in seconds) that the invoked method may be run without + * showing the HUD. If the task finishes before the grace time runs out, the HUD will + * not be shown at all. + * This may be used to prevent HUD display for very short tasks. + * Defaults to 0 (no grace time). + * Grace time functionality is only supported when the task status is known! + * @see taskInProgress + */ +@property (assign) float graceTime; + +/** + * The minimum time (in seconds) that the HUD is shown. + * This avoids the problem of the HUD being shown and than instantly hidden. + * Defaults to 0 (no minimum show time). + */ +@property (assign) float minShowTime; + +/** + * Indicates that the executed operation is in progress. Needed for correct graceTime operation. + * If you don't set a graceTime (different than 0.0) this does nothing. + * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:. + * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly), + * you need to set this property when your task starts and completes in order to have normal graceTime + * functionality. + */ +@property (assign) BOOL taskInProgress; + +/** + * Removes the HUD from its parent view when hidden. + * Defaults to NO. + */ +@property (assign) BOOL removeFromSuperViewOnHide; + +/** + * Font to be used for the main label. Set this property if the default is not adequate. + */ +@property (MB_STRONG) UIFont* labelFont; + +/** + * Color to be used for the main label. Set this property if the default is not adequate. + */ +@property (MB_STRONG) UIColor* labelColor; + +/** + * Font to be used for the details label. Set this property if the default is not adequate. + */ +@property (MB_STRONG) UIFont* detailsLabelFont; + +/** + * Color to be used for the details label. Set this property if the default is not adequate. + */ +@property (MB_STRONG) UIColor* detailsLabelColor; + +/** + * The color of the activity indicator. Defaults to [UIColor whiteColor] + * Does nothing on pre iOS 5. + */ +@property (MB_STRONG) UIColor *activityIndicatorColor; + +/** + * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. + */ +@property (assign) float progress; + +/** + * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size). + */ +@property (assign) CGSize minSize; + + +/** + * The actual size of the HUD bezel. + * You can use this to limit touch handling on the bezel aria only. + * @see https://github.com/jdg/MBProgressHUD/pull/200 + */ +@property (atomic, assign, readonly) CGSize size; + + +/** + * Force the HUD dimensions to be equal if possible. + */ +@property (assign, getter = isSquare) BOOL square; + +@end + + +@protocol MBProgressHUDDelegate + +@optional + +/** + * Called after the HUD was fully hidden from the screen. + */ +- (void)hudWasHidden:(MBProgressHUD *)hud; + +@end + + +/** + * A progress view for showing definite progress by filling up a circle (pie chart). + */ +@interface MBRoundProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Indicator progress color. + * Defaults to white [UIColor whiteColor] + */ +@property (nonatomic, MB_STRONG) UIColor *progressTintColor; + +/** + * Indicator background (non-progress) color. + * Defaults to translucent white (alpha 0.1) + */ +@property (nonatomic, MB_STRONG) UIColor *backgroundTintColor; + +/* + * Display mode - NO = round or YES = annular. Defaults to round. + */ +@property (nonatomic, assign, getter = isAnnular) BOOL annular; + +@end + + +/** + * A flat bar progress view. + */ +@interface MBBarProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Bar border line color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, MB_STRONG) UIColor *lineColor; + +/** + * Bar background color. + * Defaults to clear [UIColor clearColor]; + */ +@property (nonatomic, MB_STRONG) UIColor *progressRemainingColor; + +/** + * Bar progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, MB_STRONG) UIColor *progressColor; + +@end diff --git a/Pods/MBProgressHUD/MBProgressHUD.m b/Pods/MBProgressHUD/MBProgressHUD.m new file mode 100644 index 0000000..ab086c6 --- /dev/null +++ b/Pods/MBProgressHUD/MBProgressHUD.m @@ -0,0 +1,1022 @@ +// +// MBProgressHUD.m +// Version 0.9.1 +// Created by Matej Bukovinski on 2.4.09. +// + +#import "MBProgressHUD.h" +#import + + +#if __has_feature(objc_arc) + #define MB_AUTORELEASE(exp) exp + #define MB_RELEASE(exp) exp + #define MB_RETAIN(exp) exp +#else + #define MB_AUTORELEASE(exp) [exp autorelease] + #define MB_RELEASE(exp) [exp release] + #define MB_RETAIN(exp) [exp retain] +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 + #define MBLabelAlignmentCenter NSTextAlignmentCenter +#else + #define MBLabelAlignmentCenter UITextAlignmentCenter +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + #define MB_TEXTSIZE(text, font) [text length] > 0 ? [text \ + sizeWithAttributes:@{NSFontAttributeName:font}] : CGSizeZero; +#else + #define MB_TEXTSIZE(text, font) [text length] > 0 ? [text sizeWithFont:font] : CGSizeZero; +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + #define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \ + boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin) \ + attributes:@{NSFontAttributeName:font} context:nil].size : CGSizeZero; +#else + #define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \ + sizeWithFont:font constrainedToSize:maxSize lineBreakMode:mode] : CGSizeZero; +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_7_0 + #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20 +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 + #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15 +#endif + + +static const CGFloat kPadding = 4.f; +static const CGFloat kLabelFontSize = 16.f; +static const CGFloat kDetailsLabelFontSize = 12.f; + + +@interface MBProgressHUD () { + BOOL useAnimation; + SEL methodForExecution; + id targetForExecution; + id objectForExecution; + UILabel *label; + UILabel *detailsLabel; + BOOL isFinished; + CGAffineTransform rotationTransform; +} + +@property (atomic, MB_STRONG) UIView *indicator; +@property (atomic, MB_STRONG) NSTimer *graceTimer; +@property (atomic, MB_STRONG) NSTimer *minShowTimer; +@property (atomic, MB_STRONG) NSDate *showStarted; + +@end + + +@implementation MBProgressHUD + +#pragma mark - Properties + +@synthesize animationType; +@synthesize delegate; +@synthesize opacity; +@synthesize color; +@synthesize labelFont; +@synthesize labelColor; +@synthesize detailsLabelFont; +@synthesize detailsLabelColor; +@synthesize indicator; +@synthesize xOffset; +@synthesize yOffset; +@synthesize minSize; +@synthesize square; +@synthesize margin; +@synthesize dimBackground; +@synthesize graceTime; +@synthesize minShowTime; +@synthesize graceTimer; +@synthesize minShowTimer; +@synthesize taskInProgress; +@synthesize removeFromSuperViewOnHide; +@synthesize customView; +@synthesize showStarted; +@synthesize mode; +@synthesize labelText; +@synthesize detailsLabelText; +@synthesize progress; +@synthesize size; +@synthesize activityIndicatorColor; +#if NS_BLOCKS_AVAILABLE +@synthesize completionBlock; +#endif + +#pragma mark - Class methods + ++ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [[self alloc] initWithView:view]; + hud.removeFromSuperViewOnHide = YES; + [view addSubview:hud]; + [hud show:animated]; + return MB_AUTORELEASE(hud); +} + ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [self HUDForView:view]; + if (hud != nil) { + hud.removeFromSuperViewOnHide = YES; + [hud hide:animated]; + return YES; + } + return NO; +} + ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated { + NSArray *huds = [MBProgressHUD allHUDsForView:view]; + for (MBProgressHUD *hud in huds) { + hud.removeFromSuperViewOnHide = YES; + [hud hide:animated]; + } + return [huds count]; +} + ++ (MB_INSTANCETYPE)HUDForView:(UIView *)view { + NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator]; + for (UIView *subview in subviewsEnum) { + if ([subview isKindOfClass:self]) { + return (MBProgressHUD *)subview; + } + } + return nil; +} + ++ (NSArray *)allHUDsForView:(UIView *)view { + NSMutableArray *huds = [NSMutableArray array]; + NSArray *subviews = view.subviews; + for (UIView *aView in subviews) { + if ([aView isKindOfClass:self]) { + [huds addObject:aView]; + } + } + return [NSArray arrayWithArray:huds]; +} + +#pragma mark - Lifecycle + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + // Set default values for properties + self.animationType = MBProgressHUDAnimationFade; + self.mode = MBProgressHUDModeIndeterminate; + self.labelText = nil; + self.detailsLabelText = nil; + self.opacity = 0.8f; + self.color = nil; + self.labelFont = [UIFont boldSystemFontOfSize:kLabelFontSize]; + self.labelColor = [UIColor whiteColor]; + self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize]; + self.detailsLabelColor = [UIColor whiteColor]; + self.activityIndicatorColor = [UIColor whiteColor]; + self.xOffset = 0.0f; + self.yOffset = 0.0f; + self.dimBackground = NO; + self.margin = 20.0f; + self.cornerRadius = 10.0f; + self.graceTime = 0.0f; + self.minShowTime = 0.0f; + self.removeFromSuperViewOnHide = NO; + self.minSize = CGSizeZero; + self.square = NO; + self.contentMode = UIViewContentModeCenter; + self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin + | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + + // Transparent background + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + // Make it invisible for now + self.alpha = 0.0f; + + taskInProgress = NO; + rotationTransform = CGAffineTransformIdentity; + + [self setupLabels]; + [self updateIndicators]; + [self registerForKVO]; + [self registerForNotifications]; + } + return self; +} + +- (id)initWithView:(UIView *)view { + NSAssert(view, @"View must not be nil."); + return [self initWithFrame:view.bounds]; +} + +- (id)initWithWindow:(UIWindow *)window { + return [self initWithView:window]; +} + +- (void)dealloc { + [self unregisterFromNotifications]; + [self unregisterFromKVO]; +#if !__has_feature(objc_arc) + [color release]; + [indicator release]; + [label release]; + [detailsLabel release]; + [labelText release]; + [detailsLabelText release]; + [graceTimer release]; + [minShowTimer release]; + [showStarted release]; + [customView release]; + [labelFont release]; + [labelColor release]; + [detailsLabelFont release]; + [detailsLabelColor release]; +#if NS_BLOCKS_AVAILABLE + [completionBlock release]; +#endif + [super dealloc]; +#endif +} + +#pragma mark - Show & hide + +- (void)show:(BOOL)animated { + useAnimation = animated; + // If the grace time is set postpone the HUD display + if (self.graceTime > 0.0) { + self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self + selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO]; + } + // ... otherwise show the HUD imediately + else { + [self showUsingAnimation:useAnimation]; + } +} + +- (void)hide:(BOOL)animated { + useAnimation = animated; + // If the minShow time is set, calculate how long the hud was shown, + // and pospone the hiding operation if necessary + if (self.minShowTime > 0.0 && showStarted) { + NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted]; + if (interv < self.minShowTime) { + self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self + selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO]; + return; + } + } + // ... otherwise hide the HUD immediately + [self hideUsingAnimation:useAnimation]; +} + +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay { + [self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay]; +} + +- (void)hideDelayed:(NSNumber *)animated { + [self hide:[animated boolValue]]; +} + +#pragma mark - Timer callbacks + +- (void)handleGraceTimer:(NSTimer *)theTimer { + // Show the HUD only if the task is still running + if (taskInProgress) { + [self showUsingAnimation:useAnimation]; + } +} + +- (void)handleMinShowTimer:(NSTimer *)theTimer { + [self hideUsingAnimation:useAnimation]; +} + +#pragma mark - View Hierrarchy + +- (void)didMoveToSuperview { + [self updateForCurrentOrientationAnimated:NO]; +} + +#pragma mark - Internal show & hide operations + +- (void)showUsingAnimation:(BOOL)animated { + // Cancel any scheduled hideDelayed: calls + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [self setNeedsDisplay]; + + if (animated && animationType == MBProgressHUDAnimationZoomIn) { + self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f)); + } else if (animated && animationType == MBProgressHUDAnimationZoomOut) { + self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f)); + } + self.showStarted = [NSDate date]; + // Fade in + if (animated) { + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.30]; + self.alpha = 1.0f; + if (animationType == MBProgressHUDAnimationZoomIn || animationType == MBProgressHUDAnimationZoomOut) { + self.transform = rotationTransform; + } + [UIView commitAnimations]; + } + else { + self.alpha = 1.0f; + } +} + +- (void)hideUsingAnimation:(BOOL)animated { + // Fade out + if (animated && showStarted) { + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.30]; + [UIView setAnimationDelegate:self]; + [UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)]; + // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden + // in the done method + if (animationType == MBProgressHUDAnimationZoomIn) { + self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f)); + } else if (animationType == MBProgressHUDAnimationZoomOut) { + self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f)); + } + + self.alpha = 0.02f; + [UIView commitAnimations]; + } + else { + self.alpha = 0.0f; + [self done]; + } + self.showStarted = nil; +} + +- (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context { + [self done]; +} + +- (void)done { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + isFinished = YES; + self.alpha = 0.0f; + if (removeFromSuperViewOnHide) { + [self removeFromSuperview]; + } +#if NS_BLOCKS_AVAILABLE + if (self.completionBlock) { + self.completionBlock(); + self.completionBlock = NULL; + } +#endif + if ([delegate respondsToSelector:@selector(hudWasHidden:)]) { + [delegate performSelector:@selector(hudWasHidden:) withObject:self]; + } +} + +#pragma mark - Threading + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { + methodForExecution = method; + targetForExecution = MB_RETAIN(target); + objectForExecution = MB_RETAIN(object); + // Launch execution in new thread + self.taskInProgress = YES; + [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil]; + // Show HUD view + [self show:animated]; +} + +#if NS_BLOCKS_AVAILABLE + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue { + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue + completionBlock:(MBProgressHUDCompletionBlock)completion { + self.taskInProgress = YES; + self.completionBlock = completion; + dispatch_async(queue, ^(void) { + block(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self cleanUp]; + }); + }); + [self show:animated]; +} + +#endif + +- (void)launchExecution { + @autoreleasepool { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + // Start executing the requested task + [targetForExecution performSelector:methodForExecution withObject:objectForExecution]; +#pragma clang diagnostic pop + // Task completed, update view in main thread (note: view operations should + // be done only in the main thread) + [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO]; + } +} + +- (void)cleanUp { + taskInProgress = NO; +#if !__has_feature(objc_arc) + [targetForExecution release]; + [objectForExecution release]; +#else + targetForExecution = nil; + objectForExecution = nil; +#endif + [self hide:useAnimation]; +} + +#pragma mark - UI + +- (void)setupLabels { + label = [[UILabel alloc] initWithFrame:self.bounds]; + label.adjustsFontSizeToFitWidth = NO; + label.textAlignment = MBLabelAlignmentCenter; + label.opaque = NO; + label.backgroundColor = [UIColor clearColor]; + label.textColor = self.labelColor; + label.font = self.labelFont; + label.text = self.labelText; + [self addSubview:label]; + + detailsLabel = [[UILabel alloc] initWithFrame:self.bounds]; + detailsLabel.font = self.detailsLabelFont; + detailsLabel.adjustsFontSizeToFitWidth = NO; + detailsLabel.textAlignment = MBLabelAlignmentCenter; + detailsLabel.opaque = NO; + detailsLabel.backgroundColor = [UIColor clearColor]; + detailsLabel.textColor = self.detailsLabelColor; + detailsLabel.numberOfLines = 0; + detailsLabel.font = self.detailsLabelFont; + detailsLabel.text = self.detailsLabelText; + [self addSubview:detailsLabel]; +} + +- (void)updateIndicators { + + BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; + BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]]; + + if (mode == MBProgressHUDModeIndeterminate) { + if (!isActivityIndicator) { + // Update to indeterminate indicator + [indicator removeFromSuperview]; + self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]); + [(UIActivityIndicatorView *)indicator startAnimating]; + [self addSubview:indicator]; + } +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000 + [(UIActivityIndicatorView *)indicator setColor:self.activityIndicatorColor]; +#endif + } + else if (mode == MBProgressHUDModeDeterminateHorizontalBar) { + // Update to bar determinate indicator + [indicator removeFromSuperview]; + self.indicator = MB_AUTORELEASE([[MBBarProgressView alloc] init]); + [self addSubview:indicator]; + } + else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) { + if (!isRoundIndicator) { + // Update to determinante indicator + [indicator removeFromSuperview]; + self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]); + [self addSubview:indicator]; + } + if (mode == MBProgressHUDModeAnnularDeterminate) { + [(MBRoundProgressView *)indicator setAnnular:YES]; + } + } + else if (mode == MBProgressHUDModeCustomView && customView != indicator) { + // Update custom view indicator + [indicator removeFromSuperview]; + self.indicator = customView; + [self addSubview:indicator]; + } else if (mode == MBProgressHUDModeText) { + [indicator removeFromSuperview]; + self.indicator = nil; + } +} + +#pragma mark - Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + + // Entirely cover the parent view + UIView *parent = self.superview; + if (parent) { + self.frame = parent.bounds; + } + CGRect bounds = self.bounds; + + // Determine the total widt and height needed + CGFloat maxWidth = bounds.size.width - 4 * margin; + CGSize totalSize = CGSizeZero; + + CGRect indicatorF = indicator.bounds; + indicatorF.size.width = MIN(indicatorF.size.width, maxWidth); + totalSize.width = MAX(totalSize.width, indicatorF.size.width); + totalSize.height += indicatorF.size.height; + + CGSize labelSize = MB_TEXTSIZE(label.text, label.font); + labelSize.width = MIN(labelSize.width, maxWidth); + totalSize.width = MAX(totalSize.width, labelSize.width); + totalSize.height += labelSize.height; + if (labelSize.height > 0.f && indicatorF.size.height > 0.f) { + totalSize.height += kPadding; + } + + CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin; + CGSize maxSize = CGSizeMake(maxWidth, remainingHeight); + CGSize detailsLabelSize = MB_MULTILINE_TEXTSIZE(detailsLabel.text, detailsLabel.font, maxSize, detailsLabel.lineBreakMode); + totalSize.width = MAX(totalSize.width, detailsLabelSize.width); + totalSize.height += detailsLabelSize.height; + if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) { + totalSize.height += kPadding; + } + + totalSize.width += 2 * margin; + totalSize.height += 2 * margin; + + // Position elements + CGFloat yPos = round(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset; + CGFloat xPos = xOffset; + indicatorF.origin.y = yPos; + indicatorF.origin.x = round((bounds.size.width - indicatorF.size.width) / 2) + xPos; + indicator.frame = indicatorF; + yPos += indicatorF.size.height; + + if (labelSize.height > 0.f && indicatorF.size.height > 0.f) { + yPos += kPadding; + } + CGRect labelF; + labelF.origin.y = yPos; + labelF.origin.x = round((bounds.size.width - labelSize.width) / 2) + xPos; + labelF.size = labelSize; + label.frame = labelF; + yPos += labelF.size.height; + + if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) { + yPos += kPadding; + } + CGRect detailsLabelF; + detailsLabelF.origin.y = yPos; + detailsLabelF.origin.x = round((bounds.size.width - detailsLabelSize.width) / 2) + xPos; + detailsLabelF.size = detailsLabelSize; + detailsLabel.frame = detailsLabelF; + + // Enforce minsize and quare rules + if (square) { + CGFloat max = MAX(totalSize.width, totalSize.height); + if (max <= bounds.size.width - 2 * margin) { + totalSize.width = max; + } + if (max <= bounds.size.height - 2 * margin) { + totalSize.height = max; + } + } + if (totalSize.width < minSize.width) { + totalSize.width = minSize.width; + } + if (totalSize.height < minSize.height) { + totalSize.height = minSize.height; + } + + size = totalSize; +} + +#pragma mark BG Drawing + +- (void)drawRect:(CGRect)rect { + + CGContextRef context = UIGraphicsGetCurrentContext(); + UIGraphicsPushContext(context); + + if (self.dimBackground) { + //Gradient colours + size_t gradLocationsNum = 2; + CGFloat gradLocations[2] = {0.0f, 1.0f}; + CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum); + CGColorSpaceRelease(colorSpace); + //Gradient center + CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); + //Gradient radius + float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ; + //Gradient draw + CGContextDrawRadialGradient (context, gradient, gradCenter, + 0, gradCenter, gradRadius, + kCGGradientDrawsAfterEndLocation); + CGGradientRelease(gradient); + } + + // Set background rect color + if (self.color) { + CGContextSetFillColorWithColor(context, self.color.CGColor); + } else { + CGContextSetGrayFillColor(context, 0.0f, self.opacity); + } + + + // Center HUD + CGRect allRect = self.bounds; + // Draw rounded HUD backgroud rect + CGRect boxRect = CGRectMake(round((allRect.size.width - size.width) / 2) + self.xOffset, + round((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height); + float radius = self.cornerRadius; + CGContextBeginPath(context); + CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect)); + CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0); + CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0); + CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0); + CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0); + CGContextClosePath(context); + CGContextFillPath(context); + + UIGraphicsPopContext(); +} + +#pragma mark - KVO + +- (void)registerForKVO { + for (NSString *keyPath in [self observableKeypaths]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; + } +} + +- (void)unregisterFromKVO { + for (NSString *keyPath in [self observableKeypaths]) { + [self removeObserver:self forKeyPath:keyPath]; + } +} + +- (NSArray *)observableKeypaths { + return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont", @"labelColor", + @"detailsLabelText", @"detailsLabelFont", @"detailsLabelColor", @"progress", @"activityIndicatorColor", nil]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if (![NSThread isMainThread]) { + [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO]; + } else { + [self updateUIForKeypath:keyPath]; + } +} + +- (void)updateUIForKeypath:(NSString *)keyPath { + if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"] || + [keyPath isEqualToString:@"activityIndicatorColor"]) { + [self updateIndicators]; + } else if ([keyPath isEqualToString:@"labelText"]) { + label.text = self.labelText; + } else if ([keyPath isEqualToString:@"labelFont"]) { + label.font = self.labelFont; + } else if ([keyPath isEqualToString:@"labelColor"]) { + label.textColor = self.labelColor; + } else if ([keyPath isEqualToString:@"detailsLabelText"]) { + detailsLabel.text = self.detailsLabelText; + } else if ([keyPath isEqualToString:@"detailsLabelFont"]) { + detailsLabel.font = self.detailsLabelFont; + } else if ([keyPath isEqualToString:@"detailsLabelColor"]) { + detailsLabel.textColor = self.detailsLabelColor; + } else if ([keyPath isEqualToString:@"progress"]) { + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(progress) forKey:@"progress"]; + } + return; + } + [self setNeedsLayout]; + [self setNeedsDisplay]; +} + +#pragma mark - Notifications + +- (void)registerForNotifications { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + [nc addObserver:self selector:@selector(statusBarOrientationDidChange:) + name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +} + +- (void)unregisterFromNotifications { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +} + +- (void)statusBarOrientationDidChange:(NSNotification *)notification { + UIView *superview = self.superview; + if (!superview) { + return; + } else { + [self updateForCurrentOrientationAnimated:YES]; + } +} + +- (void)updateForCurrentOrientationAnimated:(BOOL)animated { + // Stay in sync with the superview in any case + if (self.superview) { + self.bounds = self.superview.bounds; + [self setNeedsDisplay]; + } + + // Not needed on iOS 8+, compile out when the deployment target allows, + // to avoid sharedApplication problems on extension targets +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000 + // Only needed pre iOS 7 when added to a window + BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0; + if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return; + + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + CGFloat radians = 0; + if (UIInterfaceOrientationIsLandscape(orientation)) { + if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; } + else { radians = (CGFloat)M_PI_2; } + // Window coordinates differ! + self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width); + } else { + if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; } + else { radians = 0; } + } + rotationTransform = CGAffineTransformMakeRotation(radians); + + if (animated) { + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:0.3]; + } + [self setTransform:rotationTransform]; + if (animated) { + [UIView commitAnimations]; + } +#endif +} + +@end + + +@implementation MBRoundProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + _progress = 0.f; + _annular = NO; + _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f]; + _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f]; + [self registerForKVO]; + } + return self; +} + +- (void)dealloc { + [self unregisterFromKVO]; +#if !__has_feature(objc_arc) + [_progressTintColor release]; + [_backgroundTintColor release]; + [super dealloc]; +#endif +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + + CGRect allRect = self.bounds; + CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + if (_annular) { + // Draw background + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f; + UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath]; + processBackgroundPath.lineWidth = lineWidth; + processBackgroundPath.lineCapStyle = kCGLineCapButt; + CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); + CGFloat radius = (self.bounds.size.width - lineWidth)/2; + CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees + CGFloat endAngle = (2 * (float)M_PI) + startAngle; + [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_backgroundTintColor set]; + [processBackgroundPath stroke]; + // Draw progress + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare; + processPath.lineWidth = lineWidth; + endAngle = (self.progress * 2 * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_progressTintColor set]; + [processPath stroke]; + } else { + // Draw background + [_progressTintColor setStroke]; + [_backgroundTintColor setFill]; + CGContextSetLineWidth(context, 2.0f); + CGContextFillEllipseInRect(context, circleRect); + CGContextStrokeEllipseInRect(context, circleRect); + // Draw progress + CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2); + CGFloat radius = (allRect.size.width - 4) / 2; + CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees + CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle; + [_progressTintColor setFill]; + CGContextMoveToPoint(context, center.x, center.y); + CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0); + CGContextClosePath(context); + CGContextFillPath(context); + } +} + +#pragma mark - KVO + +- (void)registerForKVO { + for (NSString *keyPath in [self observableKeypaths]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; + } +} + +- (void)unregisterFromKVO { + for (NSString *keyPath in [self observableKeypaths]) { + [self removeObserver:self forKeyPath:keyPath]; + } +} + +- (NSArray *)observableKeypaths { + return [NSArray arrayWithObjects:@"progressTintColor", @"backgroundTintColor", @"progress", @"annular", nil]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + [self setNeedsDisplay]; +} + +@end + + +@implementation MBBarProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _progress = 0.f; + _lineColor = [UIColor whiteColor]; + _progressColor = [UIColor whiteColor]; + _progressRemainingColor = [UIColor clearColor]; + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + [self registerForKVO]; + } + return self; +} + +- (void)dealloc { + [self unregisterFromKVO]; +#if !__has_feature(objc_arc) + [_lineColor release]; + [_progressColor release]; + [_progressRemainingColor release]; + [super dealloc]; +#endif +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetLineWidth(context, 2); + CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]); + CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]); + + // Draw background + float radius = (rect.size.height / 2) - 2; + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextFillPath(context); + + // Draw border + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextStrokePath(context); + + CGContextSetFillColorWithColor(context, [_progressColor CGColor]); + radius = radius - 2; + float amount = self.progress * rect.size.width; + + // Progress in the middle area + if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, amount, 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, amount, rect.size.height - 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextFillPath(context); + } + + // Progress in the right arc + else if (amount > radius + 4) { + float x = amount - (rect.size.width - radius - 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4); + float angle = -acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4); + angle = acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextFillPath(context); + } + + // Progress is in the left arc + else if (amount < radius + 4 && amount > 0) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextFillPath(context); + } +} + +#pragma mark - KVO + +- (void)registerForKVO { + for (NSString *keyPath in [self observableKeypaths]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; + } +} + +- (void)unregisterFromKVO { + for (NSString *keyPath in [self observableKeypaths]) { + [self removeObserver:self forKeyPath:keyPath]; + } +} + +- (NSArray *)observableKeypaths { + return [NSArray arrayWithObjects:@"lineColor", @"progressRemainingColor", @"progressColor", @"progress", nil]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + [self setNeedsDisplay]; +} + +@end diff --git a/Pods/Mantle/Mantle/MTLJSONAdapter.h b/Pods/Mantle/Mantle/MTLJSONAdapter.h new file mode 100644 index 0000000..368c5ab --- /dev/null +++ b/Pods/Mantle/Mantle/MTLJSONAdapter.h @@ -0,0 +1,172 @@ +// +// MTLJSONAdapter.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@class MTLModel; + +// A MTLModel object that supports being parsed from and serialized to JSON. +@protocol MTLJSONSerializing +@required + +// Specifies how to map property keys to different key paths in JSON. +// +// Subclasses overriding this method should combine their values with those of +// `super`. +// +// Any property keys not present in the dictionary are assumed to match the JSON +// key that should be used. Any keys associated with NSNull will not participate +// in JSON serialization. +// +// Returns a dictionary mapping property keys to JSON key paths (as strings) or +// NSNull values. ++ (NSDictionary *)JSONKeyPathsByPropertyKey; + +@optional + +// Specifies how to convert a JSON value to the given property key. If +// reversible, the transformer will also be used to convert the property value +// back to JSON. +// +// If the receiver implements a `+JSONTransformer` method, MTLJSONAdapter +// will use the result of that method instead. +// +// Returns a value transformer, or nil if no transformation should be performed. ++ (NSValueTransformer *)JSONTransformerForKey:(NSString *)key; + +// Overridden to parse the receiver as a different class, based on information +// in the provided dictionary. +// +// This is mostly useful for class clusters, where the abstract base class would +// be passed into -[MTLJSONAdapter initWithJSONDictionary:modelClass:], but +// a subclass should be instantiated instead. +// +// JSONDictionary - The JSON dictionary that will be parsed. +// +// Returns the class that should be parsed (which may be the receiver), or nil +// to abort parsing (e.g., if the data is invalid). ++ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary; + +@end + +// The domain for errors originating from MTLJSONAdapter. +extern NSString * const MTLJSONAdapterErrorDomain; + +// +classForParsingJSONDictionary: returned nil for the given dictionary. +extern const NSInteger MTLJSONAdapterErrorNoClassFound; + +// The provided JSONDictionary is not valid. +extern const NSInteger MTLJSONAdapterErrorInvalidJSONDictionary; + +// The model's implementation of +JSONKeyPathsByPropertyKey included a key which +// does not actually exist in +propertyKeys. +extern const NSInteger MTLJSONAdapterErrorInvalidJSONMapping; + +// Converts a MTLModel object to and from a JSON dictionary. +@interface MTLJSONAdapter : NSObject + +// The model object that the receiver was initialized with, or that the receiver +// parsed from a JSON dictionary. +@property (nonatomic, strong, readonly) MTLModel *model; + +// Attempts to parse a JSON dictionary into a model object. +// +// modelClass - The MTLModel subclass to attempt to parse from the JSON. +// This class must conform to . This +// argument must not be nil. +// JSONDictionary - A dictionary representing JSON data. This should match the +// format returned by NSJSONSerialization. If this argument is +// nil, the method returns nil. +// error - If not NULL, this may be set to an error that occurs during +// parsing or initializing an instance of `modelClass`. +// +// Returns an instance of `modelClass` upon success, or nil if a parsing error +// occurred. ++ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error; + +// Attempts to parse an array of JSON dictionary objects into a model objects +// of a specific class. +// +// modelClass - The MTLModel subclass to attempt to parse from the JSON. This +// class must conform to . This argument must +// not be nil. +// JSONArray - A array of dictionaries representing JSON data. This should +// match the format returned by NSJSONSerialization. If this +// argument is nil, the method returns nil. +// error - If not NULL, this may be set to an error that occurs during +// parsing or initializing an any of the instances of +// `modelClass`. +// +// Returns an array of `modelClass` instances upon success, or nil if a parsing +// error occurred. ++ (NSArray *)modelsOfClass:(Class)modelClass fromJSONArray:(NSArray *)JSONArray error:(NSError **)error; + +// Converts a model into a JSON representation. +// +// model - The model to use for JSON serialization. This argument must not be +// nil. +// +// Returns a JSON dictionary, or nil if a serialization error occurred. ++ (NSDictionary *)JSONDictionaryFromModel:(MTLModel *)model; + +// Converts a array of models into a JSON representation. +// +// models - The array of models to use for JSON serialization. This argument +// must not be nil. +// +// Returns a JSON array, or nil if a serialization error occurred for any +// model. ++ (NSArray *)JSONArrayFromModels:(NSArray *)models; + +// Initializes the receiver by attempting to parse a JSON dictionary into +// a model object. +// +// JSONDictionary - A dictionary representing JSON data. This should match the +// format returned by NSJSONSerialization. If this argument is +// nil, the method returns nil and an error with code +// MTLJSONAdapterErrorInvalidJSONDictionary. +// modelClass - The MTLModel subclass to attempt to parse from the JSON. +// This class must conform to . This +// argument must not be nil. +// error - If not NULL, this may be set to an error that occurs during +// parsing or initializing an instance of `modelClass`. +// +// Returns an initialized adapter upon success, or nil if a parsing error +// occurred. +- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass error:(NSError **)error; + +// Initializes the receiver with an existing model. +// +// model - The model to use for JSON serialization. This argument must not be +// nil. +- (id)initWithModel:(MTLModel *)model; + +// Serializes the receiver's `model` into JSON. +// +// Returns a JSON dictionary, or nil if a serialization error occurred. +- (NSDictionary *)JSONDictionary; + +// Looks up the JSON key path in the model's +propertyKeys. +// +// Subclasses may override this method to customize the adapter's seralizing +// behavior. You should not call this method directly. +// +// key - The property key to retrieve the corresponding JSON key path for. This +// argument must not be nil. +// +// Returns a key path to use, or nil to omit the property from JSON. +- (NSString *)JSONKeyPathForPropertyKey:(NSString *)key; + +@end + +@interface MTLJSONAdapter (Deprecated) + ++ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary __attribute__((deprecated("Replaced by +modelOfClass:fromJSONDictionary:error:"))); +- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass __attribute__((deprecated("Replaced by -initWithJSONDictionary:modelClass:error:"))); + +@end diff --git a/Pods/Mantle/Mantle/MTLJSONAdapter.m b/Pods/Mantle/Mantle/MTLJSONAdapter.m new file mode 100644 index 0000000..9872921 --- /dev/null +++ b/Pods/Mantle/Mantle/MTLJSONAdapter.m @@ -0,0 +1,325 @@ +// +// MTLJSONAdapter.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLJSONAdapter.h" +#import "MTLModel.h" +#import "MTLReflection.h" + +NSString * const MTLJSONAdapterErrorDomain = @"MTLJSONAdapterErrorDomain"; +const NSInteger MTLJSONAdapterErrorNoClassFound = 2; +const NSInteger MTLJSONAdapterErrorInvalidJSONDictionary = 3; +const NSInteger MTLJSONAdapterErrorInvalidJSONMapping = 4; + +// An exception was thrown and caught. +const NSInteger MTLJSONAdapterErrorExceptionThrown = 1; + +// Associated with the NSException that was caught. +static NSString * const MTLJSONAdapterThrownExceptionErrorKey = @"MTLJSONAdapterThrownException"; + +@interface MTLJSONAdapter () + +// The MTLModel subclass being parsed, or the class of `model` if parsing has +// completed. +@property (nonatomic, strong, readonly) Class modelClass; + +// A cached copy of the return value of +JSONKeyPathsByPropertyKey. +@property (nonatomic, copy, readonly) NSDictionary *JSONKeyPathsByPropertyKey; + +// Looks up the NSValueTransformer that should be used for the given key. +// +// key - The property key to transform from or to. This argument must not be nil. +// +// Returns a transformer to use, or nil to not transform the property. +- (NSValueTransformer *)JSONTransformerForKey:(NSString *)key; + +@end + +@implementation MTLJSONAdapter + +#pragma mark Convenience methods + ++ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error { + MTLJSONAdapter *adapter = [[self alloc] initWithJSONDictionary:JSONDictionary modelClass:modelClass error:error]; + return adapter.model; +} + ++ (NSArray *)modelsOfClass:(Class)modelClass fromJSONArray:(NSArray *)JSONArray error:(NSError **)error { + if (JSONArray == nil || ![JSONArray isKindOfClass:NSArray.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Missing JSON array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%@ could not be created because an invalid JSON array was provided: %@", @""), NSStringFromClass(modelClass), JSONArray.class], + }; + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo]; + } + return nil; + } + + NSMutableArray *models = [NSMutableArray arrayWithCapacity:JSONArray.count]; + for (NSDictionary *JSONDictionary in JSONArray){ + MTLModel *model = [self modelOfClass:modelClass fromJSONDictionary:JSONDictionary error:error]; + + if (model == nil) return nil; + + [models addObject:model]; + } + + return models; +} + ++ (NSDictionary *)JSONDictionaryFromModel:(MTLModel *)model { + MTLJSONAdapter *adapter = [[self alloc] initWithModel:model]; + return adapter.JSONDictionary; +} + ++ (NSArray *)JSONArrayFromModels:(NSArray *)models { + NSParameterAssert(models != nil); + NSParameterAssert([models isKindOfClass:NSArray.class]); + + NSMutableArray *JSONArray = [NSMutableArray arrayWithCapacity:models.count]; + for (MTLModel *model in models) { + NSDictionary *JSONDictionary = [self JSONDictionaryFromModel:model]; + if (JSONDictionary == nil) return nil; + + [JSONArray addObject:JSONDictionary]; + } + + return JSONArray; +} + +#pragma mark Lifecycle + +- (id)init { + NSAssert(NO, @"%@ must be initialized with a JSON dictionary or model object", self.class); + return nil; +} + +- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass error:(NSError **)error { + NSParameterAssert(modelClass != nil); + NSParameterAssert([modelClass isSubclassOfClass:MTLModel.class]); + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)]); + + if (JSONDictionary == nil || ![JSONDictionary isKindOfClass:NSDictionary.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Missing JSON dictionary", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%@ could not be created because an invalid JSON dictionary was provided: %@", @""), NSStringFromClass(modelClass), JSONDictionary.class], + }; + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo]; + } + return nil; + } + + if ([modelClass respondsToSelector:@selector(classForParsingJSONDictionary:)]) { + modelClass = [modelClass classForParsingJSONDictionary:JSONDictionary]; + if (modelClass == nil) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not parse JSON", @""), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"No model class could be found to parse the JSON dictionary.", @"") + }; + + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorNoClassFound userInfo:userInfo]; + } + + return nil; + } + + NSAssert([modelClass isSubclassOfClass:MTLModel.class], @"Class %@ returned from +classForParsingJSONDictionary: is not a subclass of MTLModel", modelClass); + NSAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)], @"Class %@ returned from +classForParsingJSONDictionary: does not conform to ", modelClass); + } + + self = [super init]; + if (self == nil) return nil; + + _modelClass = modelClass; + _JSONKeyPathsByPropertyKey = [[modelClass JSONKeyPathsByPropertyKey] copy]; + + NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:JSONDictionary.count]; + + NSSet *propertyKeys = [self.modelClass propertyKeys]; + + for (NSString *mappedPropertyKey in self.JSONKeyPathsByPropertyKey) { + if (![propertyKeys containsObject:mappedPropertyKey]) { + NSAssert(NO, @"%@ is not a property of %@.", mappedPropertyKey, modelClass); + return nil; + } + + id value = self.JSONKeyPathsByPropertyKey[mappedPropertyKey]; + + if (![value isKindOfClass:NSString.class] && value != NSNull.null) { + NSAssert(NO, @"%@ must either map to a JSON key path or NSNull, got: %@.",mappedPropertyKey, value); + return nil; + } + } + + for (NSString *propertyKey in propertyKeys) { + NSString *JSONKeyPath = [self JSONKeyPathForPropertyKey:propertyKey]; + if (JSONKeyPath == nil) continue; + + id value; + @try { + value = [JSONDictionary valueForKeyPath:JSONKeyPath]; + } @catch (NSException *ex) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON dictionary", nil), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%1$@ could not be parsed because an invalid JSON dictionary was provided for key path \"%2$@\"", nil), modelClass, JSONKeyPath], + MTLJSONAdapterThrownExceptionErrorKey: ex + }; + + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo]; + } + + return nil; + } + + if (value == nil) continue; + + @try { + NSValueTransformer *transformer = [self JSONTransformerForKey:propertyKey]; + if (transformer != nil) { + // Map NSNull -> nil for the transformer, and then back for the + // dictionary we're going to insert into. + if ([value isEqual:NSNull.null]) value = nil; + value = [transformer transformedValue:value] ?: NSNull.null; + } + + dictionaryValue[propertyKey] = value; + } @catch (NSException *ex) { + NSLog(@"*** Caught exception %@ parsing JSON key path \"%@\" from: %@", ex, JSONKeyPath, JSONDictionary); + + // Fail fast in Debug builds. + #if DEBUG + @throw ex; + #else + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: ex.description, + NSLocalizedFailureReasonErrorKey: ex.reason, + MTLJSONAdapterThrownExceptionErrorKey: ex + }; + + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorExceptionThrown userInfo:userInfo]; + } + + return nil; + #endif + } + } + + _model = [self.modelClass modelWithDictionary:dictionaryValue error:error]; + if (_model == nil) return nil; + + return self; +} + +- (id)initWithModel:(MTLModel *)model { + NSParameterAssert(model != nil); + + self = [super init]; + if (self == nil) return nil; + + _model = model; + _modelClass = model.class; + _JSONKeyPathsByPropertyKey = [[model.class JSONKeyPathsByPropertyKey] copy]; + + return self; +} + +#pragma mark Serialization + +- (NSDictionary *)JSONDictionary { + NSDictionary *dictionaryValue = self.model.dictionaryValue; + NSMutableDictionary *JSONDictionary = [[NSMutableDictionary alloc] initWithCapacity:dictionaryValue.count]; + + [dictionaryValue enumerateKeysAndObjectsUsingBlock:^(NSString *propertyKey, id value, BOOL *stop) { + NSString *JSONKeyPath = [self JSONKeyPathForPropertyKey:propertyKey]; + if (JSONKeyPath == nil) return; + + NSValueTransformer *transformer = [self JSONTransformerForKey:propertyKey]; + if ([transformer.class allowsReverseTransformation]) { + // Map NSNull -> nil for the transformer, and then back for the + // dictionaryValue we're going to insert into. + if ([value isEqual:NSNull.null]) value = nil; + value = [transformer reverseTransformedValue:value] ?: NSNull.null; + } + + NSArray *keyPathComponents = [JSONKeyPath componentsSeparatedByString:@"."]; + + // Set up dictionaries at each step of the key path. + id obj = JSONDictionary; + for (NSString *component in keyPathComponents) { + if ([obj valueForKey:component] == nil) { + // Insert an empty mutable dictionary at this spot so that we + // can set the whole key path afterward. + [obj setValue:[NSMutableDictionary dictionary] forKey:component]; + } + + obj = [obj valueForKey:component]; + } + + [JSONDictionary setValue:value forKeyPath:JSONKeyPath]; + }]; + + return JSONDictionary; +} + +- (NSValueTransformer *)JSONTransformerForKey:(NSString *)key { + NSParameterAssert(key != nil); + + SEL selector = MTLSelectorWithKeyPattern(key, "JSONTransformer"); + if ([self.modelClass respondsToSelector:selector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self.modelClass methodSignatureForSelector:selector]]; + invocation.target = self.modelClass; + invocation.selector = selector; + [invocation invoke]; + + __unsafe_unretained id result = nil; + [invocation getReturnValue:&result]; + return result; + } + + if ([self.modelClass respondsToSelector:@selector(JSONTransformerForKey:)]) { + return [self.modelClass JSONTransformerForKey:key]; + } + + return nil; +} + +- (NSString *)JSONKeyPathForPropertyKey:(NSString *)key { + NSParameterAssert(key != nil); + + id JSONKeyPath = self.JSONKeyPathsByPropertyKey[key]; + if ([JSONKeyPath isEqual:NSNull.null]) return nil; + + if (JSONKeyPath == nil) { + return key; + } else { + return JSONKeyPath; + } +} + +@end + +@implementation MTLJSONAdapter (Deprecated) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + ++ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary { + return [self modelOfClass:modelClass fromJSONDictionary:JSONDictionary error:NULL]; +} + +- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass { + return [self initWithJSONDictionary:JSONDictionary modelClass:modelClass error:NULL]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Pods/Mantle/Mantle/MTLManagedObjectAdapter.h b/Pods/Mantle/Mantle/MTLManagedObjectAdapter.h new file mode 100644 index 0000000..11b2bcf --- /dev/null +++ b/Pods/Mantle/Mantle/MTLManagedObjectAdapter.h @@ -0,0 +1,215 @@ +// +// MTLManagedObjectAdapter.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-03-29. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@class MTLModel; + +// A MTLModel object that supports being serialized to and from Core Data as an +// NSManagedObject. +@protocol MTLManagedObjectSerializing +@required + +// The name of the Core Data entity that the receiver serializes to and +// deserializes from. +// +// This method must not return nil. ++ (NSString *)managedObjectEntityName; + +// Specifies how to map property keys to different keys on the receiver's +// +managedObjectEntity. +// +// Entity attributes will be mapped to and from the receiver's properties using +// +entityAttributeTransformerForKey:. Entity relationships will be mapped to +// and from MTLModel objects using +relationshipModelClassesByPropertyKey. +// Fetched properties are not supported. +// +// Subclasses overriding this method should combine their values with those of +// `super`. +// +// Any property keys not present in the dictionary are assumed to match the +// entity key that should be used. Any keys associated with NSNull will not +// participate in managed object serialization. +// +// Returns a dictionary mapping property keys to entity keys (as strings) or +// NSNull values. ++ (NSDictionary *)managedObjectKeysByPropertyKey; + +@optional + +// Specifies a set of property keys used by the adapter to check for an already +// existing managed object when converting the MTLModel to its related +// NSManagedObject. +// +// The adapter will first map any keys provided by this method to the correct +// keys in managedObjectKeysByPropertyKey. +// +// The adapter will then perform a fetch request in the provided context for a +// managed object that matches the MTLModel's managedObjectEntityName and has +// equal values set for the property keys on the MTLModel. +// +// The managed object returned by the fetch request will then be set with all +// values from the MTLModel that the managed object is being converted from. +// +// If a property value of our MTLModel is yet another MTLModel which needs to be +// converted to a managed object, the class for that MTLModel can also implement +// this method to perform its own uniqing. +// +// For example: +// 1. An MTLModel subclass has id_number = 10. +// 2. An NSManagedObject accessible to the adapter's context has idnumber = 10. +// 3. managedObjectKeysByPropertyKey returns @{@"id_number" : @"idnumber"} +// 4. propertyKeysForManagedObjectUniquing returns +// [NSSet setWithObject:@"id_number"]; +// 5. Then our fetch request may return this managed object (or another managed +// object with idnumber = 10). +// +// NOTE: If multiple managed objects follow the same uniquing criteria only one +// of them will be set with our MTLModel's values. ++ (NSSet *)propertyKeysForManagedObjectUniquing; + +// Specifies how to convert the given property key to a managed object +// attribute. If reversible, the transformer will also be used to convert the +// managed object attribute back to the property. +// +// If the receiver implements a `+EntityAttributeTransformer` method, +// MTLManagedObjectAdapter will use the result of that method instead. +// +// Returns a value transformer, or nil if no transformation should be performed. ++ (NSValueTransformer *)entityAttributeTransformerForKey:(NSString *)key; + +// Specifies the MTLModel subclasses that should be deserialized to the +// receiver's property keys when a property key corresponds to an entity +// relationship. +// +// In other words, the dictionary returned by this method is used to decode +// managed object relationships into MTLModels (or NSArrays or NSSets thereof) +// set on the receiver. +// +// If a property key is omitted from the returned dictionary, but present in +// +managedObjectKeysByPropertyKey, and the receiver's +managedObjectEntity has +// a relationship at the corresponding key, an exception will be thrown during +// deserialization. +// +// Subclasses overriding this method should combine their values with those of +// `super`. +// +// Returns a dictionary mapping property keys to the Class objects that should +// be used. ++ (NSDictionary *)relationshipModelClassesByPropertyKey; + +// Overridden to deserialize a different class instead of the receiver, based on +// information in the provided object. +// +// This is mostly useful for class clusters, where the abstract base class would +// be passed into +[MTLManagedObjectAdapter +// modelOfClass:fromManagedObject:error:], but a subclass should be instantiated +// instead. +// +// managedObject - The object that will be deserialized. +// +// Returns the class that should be instantiated (which may be the receiver), or +// nil to abort parsing (e.g., if the data is invalid). ++ (Class)classForDeserializingManagedObject:(NSManagedObject *)managedObject; + +// Overriden when merging the value of the given key on the receiver with the +// value of the same key from the given `NSManagedObject` requires custom +// handling. +// +// By default, this method is not implemented, and precedence will be given to +// the value of the receiving model implicitly. +// +// When implemented, this method is called when an existing `NSManagedObject` +// is found for the receiving model, before updating the `NSManagedObject`'s +// properties. +// +// When implementing, you should use `+managedObjectKeysByPropertyKey` to map +// the given `key` to the appropriate `NSManagedObject` property. +- (void)mergeValueForKey:(NSString *)key fromManagedObject:(NSManagedObject *)managedObject; + +// Overriden when merging values on the receiver with the given +// `NSManagedObject` requires custom handling. +// +// By default, this method is not implemented, and precedence will be given to +// the values of the receiving model implicitly. +// +// When implemented, this method is called when an existing `NSManagedObject` +// is found for the receiving model, before updating the `NSManagedObject`'s +// properties. +// +// When implementing, you should use `+managedObjectKeysByPropertyKey` to map +// the given `key` to the appropriate `NSManagedObject` property. +// +// If you have also implemented `mergeValueForKey:fromManagedObject:` you have +// to make sure to call `mergeValueForKey:fromManagedObject:` from this method +// when appropriate. +- (void)mergeValuesForKeysFromManagedObject:(NSManagedObject *)managedObject; + +@end + +// The domain for errors originating from MTLManagedObjectAdapter. +extern NSString * const MTLManagedObjectAdapterErrorDomain; + +// +classForDeserializingManagedObject: returned nil for the given object. +extern const NSInteger MTLManagedObjectAdapterErrorNoClassFound; + +// An NSManagedObject failed to initialize. +extern const NSInteger MTLManagedObjectAdapterErrorInitializationFailed; + +// The managed object key specified by +managedObjectKeysByPropertyKey does not +// exist in the NSEntityDescription. +extern const NSInteger MTLManagedObjectAdapterErrorInvalidManagedObjectKey; + +// The managed object property specified has a type that isn't supported by +// MTLManagedObjectAdapter. +extern const NSInteger MTLManagedObjectAdapterErrorUnsupportedManagedObjectPropertyType; + +// The fetch request to find an existing managed object based on +// `+propertyKeysForManagedObjectUniquing` failed. +extern const NSInteger MTLManagedObjectAdapterErrorUniqueFetchRequestFailed; + +// A MTLModel property cannot be serialized to or deserialized from an +// NSManagedObject relationship. +// +// For a to-one relationship, this means that the property does not contain +// a MTLModel, or the MTLModel does not conform to . +// +// For a to-many relationship, this means that the property does not contain an +// NSArray or NSSet of MTLModel instances. +extern const NSInteger MTLManagedObjectAdapterErrorUnsupportedRelationshipClass; + +// The model's implementation of +managedObjectKeysByPropertyKey included a key +// which does not actually exist in +propertyKeys. +extern const NSInteger MTLManagedObjectAdapterErrorInvalidManagedObjectMapping; + +// Converts a MTLModel object to and from an NSManagedObject. +@interface MTLManagedObjectAdapter : NSObject + +// Attempts to deserialize an NSManagedObject into a MTLModel object. +// +// modelClass - The MTLModel subclass to return. This class must conform to +// . This argument must not be nil. +// managedObject - The managed object to deserialize. If this argument is nil, +// the method returns nil. +// error - If not NULL, this may be set to an error that occurs during +// deserialization or initializing an instance of `modelClass`. +// +// Returns an instance of `modelClass` upon success, or nil if an error +// occurred. ++ (id)modelOfClass:(Class)modelClass fromManagedObject:(NSManagedObject *)managedObject error:(NSError **)error; + +// Serializes a MTLModel into an NSManagedObject. +// +// model - The model object to serialize. This argument must not be nil. +// context - The context into which to insert the created managed object. This +// argument must not be nil. +// error - If not NULL, this may be set to an error that occurs during +// serialization or insertion. ++ (id)managedObjectFromModel:(MTLModel *)model insertingIntoContext:(NSManagedObjectContext *)context error:(NSError **)error; + +@end diff --git a/Pods/Mantle/Mantle/MTLManagedObjectAdapter.m b/Pods/Mantle/Mantle/MTLManagedObjectAdapter.m new file mode 100644 index 0000000..84235c1 --- /dev/null +++ b/Pods/Mantle/Mantle/MTLManagedObjectAdapter.m @@ -0,0 +1,645 @@ +// +// MTLManagedObjectAdapter.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-03-29. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLManagedObjectAdapter.h" +#import "EXTScope.h" +#import "MTLModel.h" +#import "MTLReflection.h" +#import "NSArray+MTLManipulationAdditions.h" + +NSString * const MTLManagedObjectAdapterErrorDomain = @"MTLManagedObjectAdapterErrorDomain"; +const NSInteger MTLManagedObjectAdapterErrorNoClassFound = 2; +const NSInteger MTLManagedObjectAdapterErrorInitializationFailed = 3; +const NSInteger MTLManagedObjectAdapterErrorInvalidManagedObjectKey = 4; +const NSInteger MTLManagedObjectAdapterErrorUnsupportedManagedObjectPropertyType = 5; +const NSInteger MTLManagedObjectAdapterErrorUnsupportedRelationshipClass = 6; +const NSInteger MTLManagedObjectAdapterErrorUniqueFetchRequestFailed = 7; +const NSInteger MTLManagedObjectAdapterErrorInvalidManagedObjectMapping = 8; + +// Performs the given block in the context's queue, if it has one. +static id performInContext(NSManagedObjectContext *context, id (^block)(void)) { + if (context.concurrencyType == NSConfinementConcurrencyType) { + return block(); + } + + __block id result = nil; + [context performBlockAndWait:^{ + result = block(); + }]; + + return result; +} + +@interface MTLManagedObjectAdapter () + +// The MTLModel subclass being serialized or deserialized. +@property (nonatomic, strong, readonly) Class modelClass; + +// A cached copy of the return value of +managedObjectKeysByPropertyKey. +@property (nonatomic, copy, readonly) NSDictionary *managedObjectKeysByPropertyKey; + +// A cached copy of the return value of +relationshipModelClassesByPropertyKey. +@property (nonatomic, copy, readonly) NSDictionary *relationshipModelClassesByPropertyKey; + +// Initializes the receiver to serialize or deserialize a MTLModel of the given +// class. +- (id)initWithModelClass:(Class)modelClass; + +// Invoked from +modelOfClass:fromManagedObject:processedObjects:error: after +// the receiver's properties have been initialized. +- (id)modelFromManagedObject:(NSManagedObject *)managedObject processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error; + +// Performs the actual work of deserialization. This method is also invoked when +// processing relationships, to create a new adapter (if needed) to handle them. +// +// `processedObjects` is a dictionary mapping NSManagedObjects to the MTLModels +// that have been created so far. It should remain alive for the full process +// of deserializing the top-level managed object. ++ (id)modelOfClass:(Class)modelClass fromManagedObject:(NSManagedObject *)managedObject processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error; + +// Invoked from +// +managedObjectFromModel:insertingIntoContext:processedObjects:error: after +// the receiver's properties have been initialized. +- (id)managedObjectFromModel:(MTLModel *)model insertingIntoContext:(NSManagedObjectContext *)context processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error; + +// Performs the actual work of serialization. This method is also invoked when +// processing relationships, to create a new adapter (if needed) to handle them. +// +// `processedObjects` is a dictionary mapping MTLModels to the NSManagedObjects +// that have been created so far. It should remain alive for the full process +// of serializing the top-level MTLModel. ++ (id)managedObjectFromModel:(MTLModel *)model insertingIntoContext:(NSManagedObjectContext *)context processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error; + +// Looks up the NSValueTransformer that should be used for any attribute that +// corresponds the given property key. +// +// key - The property key to transform from or to. This argument must not be nil. +// +// Returns a transformer to use, or nil to not transform the property. +- (NSValueTransformer *)entityAttributeTransformerForKey:(NSString *)key; + +// Looks up the managed object key that corresponds to the given key. +// +// key - The property key to retrieve the corresponding managed object key for. +// This argument must not be nil. +// +// Returns a key to use, or nil to omit the property from managed object +// serialization. +- (NSString *)managedObjectKeyForKey:(NSString *)key; + +// Looks at propertyKeysForManagedObjectUniquing and forms an NSPredicate +// using the uniquing keys and the provided model. +- (NSPredicate *)uniquingPredicateForModel:(MTLModel *)model; + +@end + +@implementation MTLManagedObjectAdapter + +#pragma mark Lifecycle + +- (id)init { + NSAssert(NO, @"%@ should not be initialized using -init", self.class); + return nil; +} + +- (id)initWithModelClass:(Class)modelClass { + NSParameterAssert(modelClass != nil); + + self = [super init]; + if (self == nil) return nil; + + _modelClass = modelClass; + _managedObjectKeysByPropertyKey = [[modelClass managedObjectKeysByPropertyKey] copy]; + + if ([modelClass respondsToSelector:@selector(relationshipModelClassesByPropertyKey)]) { + _relationshipModelClassesByPropertyKey = [[modelClass relationshipModelClassesByPropertyKey] copy]; + } + + return self; +} + +#pragma mark Serialization + +- (id)modelFromManagedObject:(NSManagedObject *)managedObject processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error { + NSParameterAssert(managedObject != nil); + NSParameterAssert(processedObjects != nil); + + NSEntityDescription *entity = managedObject.entity; + NSAssert(entity != nil, @"%@ returned a nil +entity", managedObject); + + NSManagedObjectContext *context = managedObject.managedObjectContext; + + NSDictionary *managedObjectProperties = entity.propertiesByName; + MTLModel *model = [[self.modelClass alloc] init]; + + // Pre-emptively consider this object processed, so that we don't get into + // any cycles when processing its relationships. + CFDictionaryAddValue(processedObjects, (__bridge void *)managedObject, (__bridge void *)model); + + BOOL (^setValueForKey)(NSString *, id) = ^(NSString *key, id value) { + // Mark this as being autoreleased, because validateValue may return + // a new object to be stored in this variable (and we don't want ARC to + // double-free or leak the old or new values). + __autoreleasing id replaceableValue = value; + if (![model validateValue:&replaceableValue forKey:key error:error]) return NO; + + [model setValue:replaceableValue forKey:key]; + return YES; + }; + + for (NSString *propertyKey in [self.modelClass propertyKeys]) { + NSString *managedObjectKey = [self managedObjectKeyForKey:propertyKey]; + if (managedObjectKey == nil) continue; + + BOOL (^deserializeAttribute)(NSAttributeDescription *) = ^(NSAttributeDescription *attributeDescription) { + id value = performInContext(context, ^{ + return [managedObject valueForKey:managedObjectKey]; + }); + + NSValueTransformer *attributeTransformer = [self entityAttributeTransformerForKey:propertyKey]; + if (attributeTransformer != nil) value = [attributeTransformer reverseTransformedValue:value]; + + return setValueForKey(propertyKey, value); + }; + + BOOL (^deserializeRelationship)(NSRelationshipDescription *) = ^(NSRelationshipDescription *relationshipDescription) { + Class nestedClass = self.relationshipModelClassesByPropertyKey[propertyKey]; + if (nestedClass == nil) { + [NSException raise:NSInvalidArgumentException format:@"No class specified for decoding relationship at key \"%@\" in managed object %@", managedObjectKey, managedObject]; + } + + if ([relationshipDescription isToMany]) { + id models = performInContext(context, ^ id { + id relationshipCollection = [managedObject valueForKey:managedObjectKey]; + NSMutableArray *models = [NSMutableArray arrayWithCapacity:[relationshipCollection count]]; + + for (NSManagedObject *nestedObject in relationshipCollection) { + MTLModel *model = [self.class modelOfClass:nestedClass fromManagedObject:nestedObject processedObjects:processedObjects error:error]; + if (model == nil) return nil; + + [models addObject:model]; + } + + return models; + }); + + if (models == nil) return NO; + if (![relationshipDescription isOrdered]) models = [NSSet setWithArray:models]; + + return setValueForKey(propertyKey, models); + } else { + NSManagedObject *nestedObject = performInContext(context, ^{ + return [managedObject valueForKey:managedObjectKey]; + }); + + if (nestedObject == nil) return YES; + + MTLModel *model = [self.class modelOfClass:nestedClass fromManagedObject:nestedObject processedObjects:processedObjects error:error]; + if (model == nil) return NO; + + return setValueForKey(propertyKey, model); + } + }; + + BOOL (^deserializeProperty)(NSPropertyDescription *) = ^(NSPropertyDescription *propertyDescription) { + if (propertyDescription == nil) { + if (error != NULL) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"No property by name \"%@\" exists on the entity.", @""), managedObjectKey]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not deserialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason, + }; + + *error = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorInvalidManagedObjectKey userInfo:userInfo]; + } + + return NO; + } + + // Jump through some hoops to avoid referencing classes directly. + NSString *propertyClassName = NSStringFromClass(propertyDescription.class); + if ([propertyClassName isEqual:@"NSAttributeDescription"]) { + return deserializeAttribute((id)propertyDescription); + } else if ([propertyClassName isEqual:@"NSRelationshipDescription"]) { + return deserializeRelationship((id)propertyDescription); + } else { + if (error != NULL) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Property descriptions of class %@ are unsupported.", @""), propertyClassName]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not deserialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason, + }; + + *error = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorUnsupportedManagedObjectPropertyType userInfo:userInfo]; + } + + return NO; + } + }; + + if (!deserializeProperty(managedObjectProperties[managedObjectKey])) return nil; + } + + return model; +} + ++ (id)modelOfClass:(Class)modelClass fromManagedObject:(NSManagedObject *)managedObject error:(NSError **)error { + NSSet *propertyKeys = [modelClass propertyKeys]; + + for (NSString *mappedPropertyKey in [modelClass managedObjectKeysByPropertyKey]) { + if ([propertyKeys containsObject:mappedPropertyKey]) continue; + + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid entity attribute mapping", nil), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%1$@ could not be parsed because its entity attribute mapping contains illegal property keys.", nil), modelClass] + }; + + *error = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorInvalidManagedObjectMapping userInfo:userInfo]; + } + + return nil; + } + + CFMutableDictionaryRef processedObjects = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (processedObjects == NULL) return nil; + + @onExit { + CFRelease(processedObjects); + }; + + return [self modelOfClass:modelClass fromManagedObject:managedObject processedObjects:processedObjects error:error]; +} + ++ (id)modelOfClass:(Class)modelClass fromManagedObject:(NSManagedObject *)managedObject processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error { + NSParameterAssert(modelClass != nil); + NSParameterAssert(processedObjects != nil); + + if (managedObject == nil) return nil; + + const void *existingModel = CFDictionaryGetValue(processedObjects, (__bridge void *)managedObject); + if (existingModel != NULL) { + return (__bridge id)existingModel; + } + + if ([modelClass respondsToSelector:@selector(classForDeserializingManagedObject:)]) { + modelClass = [modelClass classForDeserializingManagedObject:managedObject]; + if (modelClass == nil) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not deserialize managed object", @""), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"No model class could be found to deserialize the object.", @"") + }; + + *error = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorNoClassFound userInfo:userInfo]; + } + + return nil; + } + } + + MTLManagedObjectAdapter *adapter = [[self alloc] initWithModelClass:modelClass]; + return [adapter modelFromManagedObject:managedObject processedObjects:processedObjects error:error]; +} + +- (id)managedObjectFromModel:(MTLModel *)model insertingIntoContext:(NSManagedObjectContext *)context processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error { + NSParameterAssert(model != nil); + NSParameterAssert(context != nil); + NSParameterAssert(processedObjects != nil); + + NSString *entityName = [model.class managedObjectEntityName]; + NSAssert(entityName != nil, @"%@ returned a nil +managedObjectEntityName", model.class); + + Class entityDescriptionClass = NSClassFromString(@"NSEntityDescription"); + NSAssert(entityDescriptionClass != nil, @"CoreData.framework must be linked to use MTLManagedObjectAdapter"); + + Class fetchRequestClass = NSClassFromString(@"NSFetchRequest"); + NSAssert(fetchRequestClass != nil, @"CoreData.framework must be linked to use MTLManagedObjectAdapter"); + + // If a uniquing predicate is provided, perform a fetch request to guarantee a unique managed object. + __block NSManagedObject *managedObject = nil; + NSPredicate *uniquingPredicate = [self uniquingPredicateForModel:model]; + + if (uniquingPredicate != nil) { + __block NSError *fetchRequestError = nil; + __block BOOL encountedError = NO; + managedObject = performInContext(context, ^ id { + NSFetchRequest *fetchRequest = [[fetchRequestClass alloc] init]; + fetchRequest.entity = [entityDescriptionClass entityForName:entityName inManagedObjectContext:context]; + fetchRequest.predicate = uniquingPredicate; + fetchRequest.returnsObjectsAsFaults = NO; + fetchRequest.fetchLimit = 1; + + NSArray *results = [context executeFetchRequest:fetchRequest error:&fetchRequestError]; + + if (results == nil) { + encountedError = YES; + if (error != NULL) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Failed to fetch a managed object for uniqing predicate \"%@\".", @""), uniquingPredicate]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not serialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason, + }; + + fetchRequestError = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorUniqueFetchRequestFailed userInfo:userInfo]; + } + + return nil; + } + + return results.mtl_firstObject; + }); + + if (encountedError && error != NULL) { + *error = fetchRequestError; + return nil; + } + } + + if (managedObject == nil) { + managedObject = [entityDescriptionClass insertNewObjectForEntityForName:entityName inManagedObjectContext:context]; + } else { + // Our CoreData store already has data for this model, we need to merge + [self mergeValuesOfModel:model forKeysFromManagedObject:managedObject]; + } + + if (managedObject == nil) { + if (error != NULL) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Failed to initialize a managed object from entity named \"%@\".", @""), entityName]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not serialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason, + }; + + *error = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorInitializationFailed userInfo:userInfo]; + } + + return nil; + } + + // Assign all errors to this variable to work around a memory problem. + // + // See https://github.com/github/Mantle/pull/120 for more context. + __block NSError *tmpError; + + // Pre-emptively consider this object processed, so that we don't get into + // any cycles when processing its relationships. + CFDictionaryAddValue(processedObjects, (__bridge void *)model, (__bridge void *)managedObject); + + NSDictionary *dictionaryValue = model.dictionaryValue; + NSDictionary *managedObjectProperties = managedObject.entity.propertiesByName; + + [dictionaryValue enumerateKeysAndObjectsUsingBlock:^(NSString *propertyKey, id value, BOOL *stop) { + NSString *managedObjectKey = [self managedObjectKeyForKey:propertyKey]; + if (managedObjectKey == nil) return; + if ([value isEqual:NSNull.null]) value = nil; + + BOOL (^serializeAttribute)(NSAttributeDescription *) = ^(NSAttributeDescription *attributeDescription) { + // Mark this as being autoreleased, because validateValue may return + // a new object to be stored in this variable (and we don't want ARC to + // double-free or leak the old or new values). + __autoreleasing id transformedValue = value; + + NSValueTransformer *attributeTransformer = [self entityAttributeTransformerForKey:propertyKey]; + if (attributeTransformer != nil) transformedValue = [attributeTransformer transformedValue:transformedValue]; + + if (![managedObject validateValue:&transformedValue forKey:managedObjectKey error:&tmpError]) return NO; + [managedObject setValue:transformedValue forKey:managedObjectKey]; + + return YES; + }; + + NSManagedObject * (^objectForRelationshipFromModel)(id) = ^ id (id model) { + if (![model isKindOfClass:MTLModel.class] || ![model conformsToProtocol:@protocol(MTLManagedObjectSerializing)]) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Property of class %@ cannot be encoded into an NSManagedObject.", @""), [model class]]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not serialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason + }; + + tmpError = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorUnsupportedRelationshipClass userInfo:userInfo]; + + return nil; + } + + return [self.class managedObjectFromModel:model insertingIntoContext:context processedObjects:processedObjects error:&tmpError]; + }; + + BOOL (^serializeRelationship)(NSRelationshipDescription *) = ^(NSRelationshipDescription *relationshipDescription) { + if (value == nil) return YES; + + if ([relationshipDescription isToMany]) { + if (![value conformsToProtocol:@protocol(NSFastEnumeration)]) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Property of class %@ cannot be encoded into a to-many relationship.", @""), [value class]]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not serialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason + }; + + tmpError = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorUnsupportedRelationshipClass userInfo:userInfo]; + + return NO; + } + + id relationshipCollection; + if ([relationshipDescription isOrdered]) { + relationshipCollection = [NSMutableOrderedSet orderedSet]; + } else { + relationshipCollection = [NSMutableSet set]; + } + + for (MTLModel *model in value) { + NSManagedObject *nestedObject = objectForRelationshipFromModel(model); + if (nestedObject == nil) return NO; + + [relationshipCollection addObject:nestedObject]; + } + + [managedObject setValue:relationshipCollection forKey:managedObjectKey]; + } else { + NSManagedObject *nestedObject = objectForRelationshipFromModel(value); + if (nestedObject == nil) return NO; + + [managedObject setValue:nestedObject forKey:managedObjectKey]; + } + + return YES; + }; + + BOOL (^serializeProperty)(NSPropertyDescription *) = ^(NSPropertyDescription *propertyDescription) { + if (propertyDescription == nil) { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"No property by name \"%@\" exists on the entity.", @""), managedObjectKey]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not serialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason + }; + + tmpError = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorInvalidManagedObjectKey userInfo:userInfo]; + + return NO; + } + + // Jump through some hoops to avoid referencing classes directly. + NSString *propertyClassName = NSStringFromClass(propertyDescription.class); + if ([propertyClassName isEqual:@"NSAttributeDescription"]) { + return serializeAttribute((id)propertyDescription); + } else if ([propertyClassName isEqual:@"NSRelationshipDescription"]) { + return serializeRelationship((id)propertyDescription); + } else { + NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Property descriptions of class %@ are unsupported.", @""), propertyClassName]; + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not serialize managed object", @""), + NSLocalizedFailureReasonErrorKey: failureReason + }; + + tmpError = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorUnsupportedManagedObjectPropertyType userInfo:userInfo]; + + return NO; + } + }; + + if (!serializeProperty(managedObjectProperties[managedObjectKey])) { + performInContext(context, ^ id { + [context deleteObject:managedObject]; + return nil; + }); + + managedObject = nil; + *stop = YES; + } + }]; + + if (managedObject != nil && ![managedObject validateForInsert:&tmpError]) { + managedObject = performInContext(context, ^ id { + [context deleteObject:managedObject]; + return nil; + }); + } + + if (error != NULL) { + *error = tmpError; + } + + return managedObject; +} + ++ (id)managedObjectFromModel:(MTLModel *)model insertingIntoContext:(NSManagedObjectContext *)context error:(NSError **)error { + CFDictionaryKeyCallBacks keyCallbacks = kCFTypeDictionaryKeyCallBacks; + + // Compare MTLModel keys using pointer equality, not -isEqual:. + keyCallbacks.equal = NULL; + + CFMutableDictionaryRef processedObjects = CFDictionaryCreateMutable(NULL, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks); + if (processedObjects == NULL) return nil; + + @onExit { + CFRelease(processedObjects); + }; + + return [self managedObjectFromModel:model insertingIntoContext:context processedObjects:processedObjects error:error]; +} + ++ (id)managedObjectFromModel:(MTLModel *)model insertingIntoContext:(NSManagedObjectContext *)context processedObjects:(CFMutableDictionaryRef)processedObjects error:(NSError **)error { + NSParameterAssert(model != nil); + NSParameterAssert(context != nil); + NSParameterAssert(processedObjects != nil); + + const void *existingManagedObject = CFDictionaryGetValue(processedObjects, (__bridge void *)model); + if (existingManagedObject != NULL) { + return (__bridge id)existingManagedObject; + } + + MTLManagedObjectAdapter *adapter = [[self alloc] initWithModelClass:model.class]; + return [adapter managedObjectFromModel:model insertingIntoContext:context processedObjects:processedObjects error:error]; +} + +- (NSValueTransformer *)entityAttributeTransformerForKey:(NSString *)key { + NSParameterAssert(key != nil); + + SEL selector = MTLSelectorWithKeyPattern(key, "EntityAttributeTransformer"); + if ([self.modelClass respondsToSelector:selector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self.modelClass methodSignatureForSelector:selector]]; + invocation.target = self.modelClass; + invocation.selector = selector; + [invocation invoke]; + + __unsafe_unretained id result = nil; + [invocation getReturnValue:&result]; + return result; + } + + if ([self.modelClass respondsToSelector:@selector(entityAttributeTransformerForKey:)]) { + return [self.modelClass entityAttributeTransformerForKey:key]; + } + + return nil; +} + +- (NSString *)managedObjectKeyForKey:(NSString *)key { + NSParameterAssert(key != nil); + + id managedObjectKey = self.managedObjectKeysByPropertyKey[key]; + if ([managedObjectKey isEqual:NSNull.null]) return nil; + + if (managedObjectKey == nil) { + return key; + } else { + return managedObjectKey; + } +} + +- (void)mergeValueOfModel:(MTLModel *)model forKey:(NSString *)key fromManagedObject:(NSManagedObject *)managedObject { + [model mergeValueForKey:key fromManagedObject:managedObject]; +} + +- (void)mergeValuesOfModel:(MTLModel *)model forKeysFromManagedObject:(NSManagedObject *)managedObject { + if ([model respondsToSelector:@selector(mergeValuesForKeysFromManagedObject:)]) { + [model mergeValuesForKeysFromManagedObject:managedObject]; + } else if ([model respondsToSelector:@selector(mergeValueForKey:fromManagedObject:)]) { + [[model.class managedObjectKeysByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *managedObjectKey, BOOL *stop) { + [self mergeValueOfModel:model forKey:key fromManagedObject:managedObject]; + }]; + } +} + +- (NSPredicate *)uniquingPredicateForModel:(MTLModel *)model { + if (![self.modelClass respondsToSelector:@selector(propertyKeysForManagedObjectUniquing)]) return nil; + + NSSet *propertyKeys = [self.modelClass propertyKeysForManagedObjectUniquing]; + + if (propertyKeys == nil) return nil; + + NSAssert(propertyKeys.count > 0, @"+propertyKeysForManagedObjectUniquing must not be empty."); + + NSMutableArray *subpredicates = [NSMutableArray array]; + for (NSString *propertyKey in propertyKeys) { + NSString *managedObjectKey = [self managedObjectKeyForKey:propertyKey]; + + NSAssert(managedObjectKey != nil, @"%@ must map to a managed object key.", propertyKey); + + id transformedValue = [model valueForKeyPath:propertyKey]; + + NSValueTransformer *attributeTransformer = [self entityAttributeTransformerForKey:propertyKey]; + if (attributeTransformer != nil) transformedValue = [attributeTransformer transformedValue:transformedValue]; + + NSPredicate *subpredicate = [NSPredicate predicateWithFormat:@"%K == %@", managedObjectKey, transformedValue]; + [subpredicates addObject:subpredicate]; + } + + return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; +} + +@end diff --git a/Pods/Mantle/Mantle/MTLModel+NSCoding.h b/Pods/Mantle/Mantle/MTLModel+NSCoding.h new file mode 100644 index 0000000..94b8f7b --- /dev/null +++ b/Pods/Mantle/Mantle/MTLModel+NSCoding.h @@ -0,0 +1,128 @@ +// +// MTLModel+NSCoding.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLModel.h" + +// Defines how a MTLModel property key should be encoded into an archive. +// +// MTLModelEncodingBehaviorExcluded - The property should never be encoded. +// MTLModelEncodingBehaviorUnconditional - The property should always be +// encoded. +// MTLModelEncodingBehaviorConditional - The object should be encoded only +// if unconditionally encoded elsewhere. +// This should only be used for object +// properties. +typedef enum : NSUInteger { + MTLModelEncodingBehaviorExcluded = 0, + MTLModelEncodingBehaviorUnconditional, + MTLModelEncodingBehaviorConditional, +} MTLModelEncodingBehavior; + +// Implements default archiving and unarchiving behaviors for MTLModel. +@interface MTLModel (NSCoding) + +// Initializes the receiver from an archive. +// +// This will decode the original +modelVersion of the archived object, then +// invoke -decodeValueForKey:withCoder:modelVersion: for each of the receiver's +// +propertyKeys. +// +// Returns an initialized model object, or nil if a decoding error occurred. +- (id)initWithCoder:(NSCoder *)coder; + +// Archives the receiver using the given coder. +// +// This will encode the receiver's +modelVersion, then the receiver's properties +// according to the behaviors specified in +encodingBehaviorsByPropertyKey. +- (void)encodeWithCoder:(NSCoder *)coder; + +// Determines how the +propertyKeys of the class are encoded into an archive. +// The values of this dictionary should be boxed MTLModelEncodingBehavior +// values. +// +// Any keys not present in the dictionary will be excluded from the archive. +// +// Subclasses overriding this method should combine their values with those of +// `super`. +// +// Returns a dictionary mapping the receiver's +propertyKeys to default encoding +// behaviors. If a property is an object with `weak` semantics, the default +// behavior is MTLModelEncodingBehaviorConditional; otherwise, the default is +// MTLModelEncodingBehaviorUnconditional. ++ (NSDictionary *)encodingBehaviorsByPropertyKey; + +// Determines the classes that are allowed to be decoded for each of the +// receiver's properties when using . The values of this +// dictionary should be NSArrays of Class objects. +// +// If any encodable keys (as determined by +encodingBehaviorsByPropertyKey) are +// not present in the dictionary, an exception will be thrown during secure +// encoding or decoding. +// +// Subclasses overriding this method should combine their values with those of +// `super`. +// +// Returns a dictionary mapping the receiver's encodable keys (as determined by +// +encodingBehaviorsByPropertyKey) to default allowed classes, based on the +// type that each property is declared as. If type of an encodable property +// cannot be determined (e.g., it is declared as `id`), it will be omitted from +// the dictionary, and subclasses must provide a valid value to prevent an +// exception being thrown during encoding/decoding. ++ (NSDictionary *)allowedSecureCodingClassesByPropertyKey; + +// Decodes the value of the given property key from an archive. +// +// By default, this method looks for a `-decodeWithCoder:modelVersion:` +// method on the receiver, and invokes it if found. +// +// If the custom method is not implemented and `coder` does not require secure +// coding, `-[NSCoder decodeObjectForKey:]` will be invoked with the given +// `key`. +// +// If the custom method is not implemented and `coder` requires secure coding, +// `-[NSCoder decodeObjectOfClasses:forKey:]` will be invoked with the +// information from +allowedSecureCodingClassesByPropertyKey and the given `key`. The +// receiver must conform to for this to work correctly. +// +// key - The property key to decode the value for. This argument cannot +// be nil. +// coder - The NSCoder representing the archive being decoded. This +// argument cannot be nil. +// modelVersion - The version of the original model object that was encoded. +// +// Returns the decoded and boxed value, or nil if the key was not present. +- (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:(NSUInteger)modelVersion; + +// The version of this MTLModel subclass. +// +// This version number is saved in archives so that later model changes can be +// made backwards-compatible with old versions. +// +// Subclasses should override this method to return a higher version number +// whenever a breaking change is made to the model. +// +// Returns 0. ++ (NSUInteger)modelVersion; + +@end + +// This method must be overridden to support archives created by older versions +// of Mantle (before the `MTLModel+NSCoding` interface existed). +@interface MTLModel (OldArchiveSupport) + +// Converts an archived external representation to a dictionary suitable for +// passing to -initWithDictionary:. +// +// externalRepresentation - The decoded external representation of the receiver. +// fromVersion - The model version at the time the external +// representation was encoded. +// +// Returns nil by default, indicating that conversion failed. ++ (NSDictionary *)dictionaryValueFromArchivedExternalRepresentation:(NSDictionary *)externalRepresentation version:(NSUInteger)fromVersion; + +@end diff --git a/Pods/Mantle/Mantle/MTLModel+NSCoding.m b/Pods/Mantle/Mantle/MTLModel+NSCoding.m new file mode 100644 index 0000000..852f5ae --- /dev/null +++ b/Pods/Mantle/Mantle/MTLModel+NSCoding.m @@ -0,0 +1,267 @@ +// +// MTLModel+NSCoding.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLModel+NSCoding.h" +#import "EXTRuntimeExtensions.h" +#import "EXTScope.h" +#import "MTLReflection.h" +#import + +// Used in archives to store the modelVersion of the archived instance. +static NSString * const MTLModelVersionKey = @"MTLModelVersion"; + +// Used to cache the reflection performed in +allowedSecureCodingClassesByPropertyKey. +static void *MTLModelCachedAllowedClassesKey = &MTLModelCachedAllowedClassesKey; + +// Returns whether the given NSCoder requires secure coding. +static BOOL coderRequiresSecureCoding(NSCoder *coder) { + SEL requiresSecureCodingSelector = @selector(requiresSecureCoding); + + // Only invoke the method if it's implemented (i.e., only on OS X 10.8+ and + // iOS 6+). + if (![coder respondsToSelector:requiresSecureCodingSelector]) return NO; + + BOOL (*requiresSecureCodingIMP)(NSCoder *, SEL) = (__typeof__(requiresSecureCodingIMP))[coder methodForSelector:requiresSecureCodingSelector]; + if (requiresSecureCodingIMP == NULL) return NO; + + return requiresSecureCodingIMP(coder, requiresSecureCodingSelector); +} + +// Returns all of the given class' encodable property keys (those that will not +// be excluded from archives). +static NSSet *encodablePropertyKeysForClass(Class modelClass) { + return [[modelClass encodingBehaviorsByPropertyKey] keysOfEntriesPassingTest:^ BOOL (NSString *propertyKey, NSNumber *behavior, BOOL *stop) { + return behavior.unsignedIntegerValue != MTLModelEncodingBehaviorExcluded; + }]; +} + +// Verifies that all of the specified class' encodable property keys are present +// in +allowedSecureCodingClassesByPropertyKey, and throws an exception if not. +static void verifyAllowedClassesByPropertyKey(Class modelClass) { + NSDictionary *allowedClasses = [modelClass allowedSecureCodingClassesByPropertyKey]; + + NSMutableSet *specifiedPropertyKeys = [[NSMutableSet alloc] initWithArray:allowedClasses.allKeys]; + [specifiedPropertyKeys minusSet:encodablePropertyKeysForClass(modelClass)]; + + if (specifiedPropertyKeys.count > 0) { + [NSException raise:NSInvalidArgumentException format:@"Cannot encode %@ securely, because keys are missing from +allowedSecureCodingClassesByPropertyKey: %@", modelClass, specifiedPropertyKeys]; + } +} + +@implementation MTLModel (NSCoding) + +#pragma mark Versioning + ++ (NSUInteger)modelVersion { + return 0; +} + +#pragma mark Encoding Behaviors + ++ (NSDictionary *)encodingBehaviorsByPropertyKey { + NSSet *propertyKeys = self.propertyKeys; + NSMutableDictionary *behaviors = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count]; + + for (NSString *key in propertyKeys) { + objc_property_t property = class_getProperty(self, key.UTF8String); + NSAssert(property != NULL, @"Could not find property \"%@\" on %@", key, self); + + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + MTLModelEncodingBehavior behavior = (attributes->weak ? MTLModelEncodingBehaviorConditional : MTLModelEncodingBehaviorUnconditional); + behaviors[key] = @(behavior); + } + + return behaviors; +} + ++ (NSDictionary *)allowedSecureCodingClassesByPropertyKey { + NSDictionary *cachedClasses = objc_getAssociatedObject(self, MTLModelCachedAllowedClassesKey); + if (cachedClasses != nil) return cachedClasses; + + // Get all property keys that could potentially be encoded. + NSSet *propertyKeys = [self.encodingBehaviorsByPropertyKey keysOfEntriesPassingTest:^ BOOL (NSString *propertyKey, NSNumber *behavior, BOOL *stop) { + return behavior.unsignedIntegerValue != MTLModelEncodingBehaviorExcluded; + }]; + + NSMutableDictionary *allowedClasses = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count]; + + for (NSString *key in propertyKeys) { + objc_property_t property = class_getProperty(self, key.UTF8String); + NSAssert(property != NULL, @"Could not find property \"%@\" on %@", key, self); + + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + // If the property is not of object or class type, assume that it's + // a primitive which would be boxed into an NSValue. + if (attributes->type[0] != '@' && attributes->type[0] != '#') { + allowedClasses[key] = @[ NSValue.class ]; + continue; + } + + // Omit this property from the dictionary if its class isn't known. + if (attributes->objectClass != nil) { + allowedClasses[key] = @[ attributes->objectClass ]; + } + } + + // It doesn't really matter if we replace another thread's work, since we do + // it atomically and the result should be the same. + objc_setAssociatedObject(self, MTLModelCachedAllowedClassesKey, allowedClasses, OBJC_ASSOCIATION_COPY); + + return allowedClasses; +} + +- (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:(NSUInteger)modelVersion { + NSParameterAssert(key != nil); + NSParameterAssert(coder != nil); + + SEL selector = MTLSelectorWithCapitalizedKeyPattern("decode", key, "WithCoder:modelVersion:"); + if ([self respondsToSelector:selector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]]; + invocation.target = self; + invocation.selector = selector; + [invocation setArgument:&coder atIndex:2]; + [invocation setArgument:&modelVersion atIndex:3]; + [invocation invoke]; + + __unsafe_unretained id result = nil; + [invocation getReturnValue:&result]; + return result; + } + + @try { + if (coderRequiresSecureCoding(coder)) { + NSArray *allowedClasses = self.class.allowedSecureCodingClassesByPropertyKey[key]; + NSAssert(allowedClasses != nil, @"No allowed classes specified for securely decoding key \"%@\" on %@", key, self.class); + + return [coder decodeObjectOfClasses:[NSSet setWithArray:allowedClasses] forKey:key]; + } else { + return [coder decodeObjectForKey:key]; + } + } @catch (NSException *ex) { + NSLog(@"*** Caught exception decoding value for key \"%@\" on class %@: %@", key, self.class, ex); + @throw ex; + } +} + +#pragma mark NSCoding + +- (instancetype)initWithCoder:(NSCoder *)coder { + BOOL requiresSecureCoding = coderRequiresSecureCoding(coder); + NSNumber *version = nil; + if (requiresSecureCoding) { + version = [coder decodeObjectOfClass:NSNumber.class forKey:MTLModelVersionKey]; + } else { + version = [coder decodeObjectForKey:MTLModelVersionKey]; + } + + if (version == nil) { + NSLog(@"Warning: decoding an archive of %@ without a version, assuming 0", self.class); + } else if (version.unsignedIntegerValue > self.class.modelVersion) { + // Don't try to decode newer versions. + return nil; + } + + if (requiresSecureCoding) { + verifyAllowedClassesByPropertyKey(self.class); + } else { + // Handle the old archive format. + NSDictionary *externalRepresentation = [coder decodeObjectForKey:@"externalRepresentation"]; + if (externalRepresentation != nil) { + NSAssert([self.class methodForSelector:@selector(dictionaryValueFromArchivedExternalRepresentation:version:)] != [MTLModel methodForSelector:@selector(dictionaryValueFromArchivedExternalRepresentation:version:)], @"Decoded an old archive of %@ that contains an externalRepresentation, but +dictionaryValueFromArchivedExternalRepresentation:version: is not overridden to handle it", self.class); + + NSDictionary *dictionaryValue = [self.class dictionaryValueFromArchivedExternalRepresentation:externalRepresentation version:version.unsignedIntegerValue]; + if (dictionaryValue == nil) return nil; + + NSError *error = nil; + self = [self initWithDictionary:dictionaryValue error:&error]; + if (self == nil) NSLog(@"*** Could not decode old %@ archive: %@", self.class, error); + + return self; + } + } + + NSSet *propertyKeys = self.class.propertyKeys; + NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count]; + + for (NSString *key in propertyKeys) { + id value = [self decodeValueForKey:key withCoder:coder modelVersion:version.unsignedIntegerValue]; + if (value == nil) continue; + + dictionaryValue[key] = value; + } + + NSError *error = nil; + self = [self initWithDictionary:dictionaryValue error:&error]; + if (self == nil) NSLog(@"*** Could not unarchive %@: %@", self.class, error); + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + if (coderRequiresSecureCoding(coder)) verifyAllowedClassesByPropertyKey(self.class); + + [coder encodeObject:@(self.class.modelVersion) forKey:MTLModelVersionKey]; + + NSDictionary *encodingBehaviors = self.class.encodingBehaviorsByPropertyKey; + [self.dictionaryValue enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + @try { + // Skip nil values. + if ([value isEqual:NSNull.null]) return; + + switch ([encodingBehaviors[key] unsignedIntegerValue]) { + // This will also match a nil behavior. + case MTLModelEncodingBehaviorExcluded: + break; + + case MTLModelEncodingBehaviorUnconditional: + [coder encodeObject:value forKey:key]; + break; + + case MTLModelEncodingBehaviorConditional: + [coder encodeConditionalObject:value forKey:key]; + break; + + default: + NSAssert(NO, @"Unrecognized encoding behavior %@ on class %@ for key \"%@\"", self.class, encodingBehaviors[key], key); + } + } @catch (NSException *ex) { + NSLog(@"*** Caught exception encoding value for key \"%@\" on class %@: %@", key, self.class, ex); + @throw ex; + } + }]; +} + +#pragma mark NSSecureCoding + ++ (BOOL)supportsSecureCoding { + // Disable secure coding support by default, so subclasses are forced to + // opt-in by conforming to the protocol and overriding this method. + // + // We only implement this method because XPC complains if a subclass tries + // to implement it but does not override -initWithCoder:. See + // https://github.com/github/Mantle/issues/74. + return NO; +} + +@end + +@implementation MTLModel (OldArchiveSupport) + ++ (NSDictionary *)dictionaryValueFromArchivedExternalRepresentation:(NSDictionary *)externalRepresentation version:(NSUInteger)fromVersion { + return nil; +} + +@end diff --git a/Pods/Mantle/Mantle/MTLModel.h b/Pods/Mantle/Mantle/MTLModel.h new file mode 100644 index 0000000..65da40d --- /dev/null +++ b/Pods/Mantle/Mantle/MTLModel.h @@ -0,0 +1,125 @@ +// +// MTLModel.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +// An abstract base class for model objects, using reflection to provide +// sensible default behaviors. +// +// The default implementations of , -hash, and -isEqual: make use of +// the +propertyKeys method. +@interface MTLModel : NSObject + +// Returns a new instance of the receiver initialized using +// -initWithDictionary:error:. ++ (instancetype)modelWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error; + +// Initializes the receiver with default values. +// +// This is the designated initializer for this class. +- (instancetype)init; + +// Initializes the receiver using key-value coding, setting the keys and values +// in the given dictionary. +// +// Subclass implementations may override this method, calling the super +// implementation, in order to perform further processing and initialization +// after deserialization. +// +// dictionaryValue - Property keys and values to set on the receiver. Any NSNull +// values will be converted to nil before being used. KVC +// validation methods will automatically be invoked for all of +// the properties given. If nil, this method is equivalent to +// -init. +// error - If not NULL, this may be set to any error that occurs +// (like a KVC validation error). +// +// Returns an initialized model object, or nil if validation failed. +- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error; + +// Returns the keys for all @property declarations, except for `readonly` +// properties without ivars, or properties on MTLModel itself. ++ (NSSet *)propertyKeys; + +// A dictionary representing the properties of the receiver. +// +// The default implementation combines the values corresponding to all +// +propertyKeys into a dictionary, with any nil values represented by NSNull. +// +// This property must never be nil. +@property (nonatomic, copy, readonly) NSDictionary *dictionaryValue; + +// Merges the value of the given key on the receiver with the value of the same +// key from the given model object, giving precedence to the other model object. +// +// By default, this method looks for a `-mergeFromModel:` method on the +// receiver, and invokes it if found. If not found, and `model` is not nil, the +// value for the given key is taken from `model`. +- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model; + +// Merges the values of the given model object into the receiver, using +// -mergeValueForKey:fromModel: for each key in +propertyKeys. +// +// `model` must be an instance of the receiver's class or a subclass thereof. +- (void)mergeValuesForKeysFromModel:(MTLModel *)model; + +// Compares the receiver with another object for equality. +// +// The default implementation is equivalent to comparing both models' +// -dictionaryValue. +// +// Note that this may lead to infinite loops if the receiver holds a circular +// reference to another MTLModel and both use the default behavior. +// It is recommended to override -isEqual: in this scenario. +- (BOOL)isEqual:(id)object; + +// A string that describes the contents of the receiver. +// +// The default implementation is based on the receiver's class and its +// -dictionaryValue. +// +// Note that this may lead to infinite loops if the receiver holds a circular +// reference to another MTLModel and both use the default behavior. +// It is recommended to override -description in this scenario. +- (NSString *)description; + +@end + +// Implements validation logic for MTLModel. +@interface MTLModel (Validation) + +// Validates the model. +// +// The default implementation simply invokes -validateValue:forKey:error: with +// all +propertyKeys and their current value. If -validateValue:forKey:error: +// returns a new value, the property is set to that new value. +// +// error - If not NULL, this may be set to any error that occurs during +// validation +// +// Returns YES if the model is valid, or NO if the validation failed. +- (BOOL)validate:(NSError **)error; + +@end + +@interface MTLModel (Unavailable) + ++ (instancetype)modelWithDictionary:(NSDictionary *)dictionaryValue __attribute__((deprecated("Replaced by +modelWithDictionary:error:"))); +- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue __attribute__((deprecated("Replaced by -initWithDictionary:error:"))); + ++ (instancetype)modelWithExternalRepresentation:(NSDictionary *)externalRepresentation __attribute__((deprecated("Replaced by -[MTLJSONAdapter initWithJSONDictionary:modelClass:]"))); +- (instancetype)initWithExternalRepresentation:(NSDictionary *)externalRepresentation __attribute__((deprecated("Replaced by -[MTLJSONAdapter initWithJSONDictionary:modelClass:]"))); + +@property (nonatomic, copy, readonly) NSDictionary *externalRepresentation __attribute__((deprecated("Replaced by MTLJSONAdapter.JSONDictionary"))); + ++ (NSDictionary *)externalRepresentationKeyPathsByPropertyKey __attribute__((deprecated("Replaced by +JSONKeyPathsByPropertyKey in "))); ++ (NSValueTransformer *)transformerForKey:(NSString *)key __attribute__((deprecated("Replaced by +JSONTransformerForKey: in "))); + ++ (NSDictionary *)migrateExternalRepresentation:(NSDictionary *)externalRepresentation fromVersion:(NSUInteger)fromVersion __attribute__((deprecated("Replaced by -decodeValueForKey:withCoder:modelVersion:"))); + +@end diff --git a/Pods/Mantle/Mantle/MTLModel.m b/Pods/Mantle/Mantle/MTLModel.m new file mode 100644 index 0000000..2ef60fe --- /dev/null +++ b/Pods/Mantle/Mantle/MTLModel.m @@ -0,0 +1,245 @@ +// +// MTLModel.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSError+MTLModelException.h" +#import "MTLModel.h" +#import "EXTRuntimeExtensions.h" +#import "EXTScope.h" +#import "MTLReflection.h" +#import + +// This coupling is needed for backwards compatibility in MTLModel's deprecated +// methods. +#import "MTLJSONAdapter.h" +#import "MTLModel+NSCoding.h" + +// Used to cache the reflection performed in +propertyKeys. +static void *MTLModelCachedPropertyKeysKey = &MTLModelCachedPropertyKeysKey; + +// Validates a value for an object and sets it if necessary. +// +// obj - The object for which the value is being validated. This value +// must not be nil. +// key - The name of one of `obj`s properties. This value must not be +// nil. +// value - The new value for the property identified by `key`. +// forceUpdate - If set to `YES`, the value is being updated even if validating +// it did not change it. +// error - If not NULL, this may be set to any error that occurs during +// validation +// +// Returns YES if `value` could be validated and set, or NO if an error +// occurred. +static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUpdate, NSError **error) { + // Mark this as being autoreleased, because validateValue may return + // a new object to be stored in this variable (and we don't want ARC to + // double-free or leak the old or new values). + __autoreleasing id validatedValue = value; + + @try { + if (![obj validateValue:&validatedValue forKey:key error:error]) return NO; + + if (forceUpdate || value != validatedValue) { + [obj setValue:validatedValue forKey:key]; + } + + return YES; + } @catch (NSException *ex) { + NSLog(@"*** Caught exception setting key \"%@\" : %@", key, ex); + + // Fail fast in Debug builds. + #if DEBUG + @throw ex; + #else + if (error != NULL) { + *error = [NSError mtl_modelErrorWithException:ex]; + } + + return NO; + #endif + } +} + +@interface MTLModel () + +// Enumerates all properties of the receiver's class hierarchy, starting at the +// receiver, and continuing up until (but not including) MTLModel. +// +// The given block will be invoked multiple times for any properties declared on +// multiple classes in the hierarchy. ++ (void)enumeratePropertiesUsingBlock:(void (^)(objc_property_t property, BOOL *stop))block; + +@end + +@implementation MTLModel + +#pragma mark Lifecycle + ++ (instancetype)modelWithDictionary:(NSDictionary *)dictionary error:(NSError **)error { + return [[self alloc] initWithDictionary:dictionary error:error]; +} + +- (instancetype)init { + // Nothing special by default, but we have a declaration in the header. + return [super init]; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error { + self = [self init]; + if (self == nil) return nil; + + for (NSString *key in dictionary) { + // Mark this as being autoreleased, because validateValue may return + // a new object to be stored in this variable (and we don't want ARC to + // double-free or leak the old or new values). + __autoreleasing id value = [dictionary objectForKey:key]; + + if ([value isEqual:NSNull.null]) value = nil; + + BOOL success = MTLValidateAndSetValue(self, key, value, YES, error); + if (!success) return nil; + } + + return self; +} + +#pragma mark Reflection + ++ (void)enumeratePropertiesUsingBlock:(void (^)(objc_property_t property, BOOL *stop))block { + Class cls = self; + BOOL stop = NO; + + while (!stop && ![cls isEqual:MTLModel.class]) { + unsigned count = 0; + objc_property_t *properties = class_copyPropertyList(cls, &count); + + cls = cls.superclass; + if (properties == NULL) continue; + + @onExit { + free(properties); + }; + + for (unsigned i = 0; i < count; i++) { + block(properties[i], &stop); + if (stop) break; + } + } +} + ++ (NSSet *)propertyKeys { + NSSet *cachedKeys = objc_getAssociatedObject(self, MTLModelCachedPropertyKeysKey); + if (cachedKeys != nil) return cachedKeys; + + NSMutableSet *keys = [NSMutableSet set]; + + [self enumeratePropertiesUsingBlock:^(objc_property_t property, BOOL *stop) { + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + if (attributes->readonly && attributes->ivar == NULL) return; + + NSString *key = @(property_getName(property)); + [keys addObject:key]; + }]; + + // It doesn't really matter if we replace another thread's work, since we do + // it atomically and the result should be the same. + objc_setAssociatedObject(self, MTLModelCachedPropertyKeysKey, keys, OBJC_ASSOCIATION_COPY); + + return keys; +} + +- (NSDictionary *)dictionaryValue { + return [self dictionaryWithValuesForKeys:self.class.propertyKeys.allObjects]; +} + +#pragma mark Merging + +- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model { + NSParameterAssert(key != nil); + + SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:"); + if (![self respondsToSelector:selector]) { + if (model != nil) { + [self setValue:[model valueForKey:key] forKey:key]; + } + + return; + } + + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]]; + invocation.target = self; + invocation.selector = selector; + + [invocation setArgument:&model atIndex:2]; + [invocation invoke]; +} + +- (void)mergeValuesForKeysFromModel:(MTLModel *)model { + NSSet *propertyKeys = model.class.propertyKeys; + for (NSString *key in self.class.propertyKeys) { + if (![propertyKeys containsObject:key]) continue; + + [self mergeValueForKey:key fromModel:model]; + } +} + +#pragma mark Validation + +- (BOOL)validate:(NSError **)error { + for (NSString *key in self.class.propertyKeys) { + id value = [self valueForKey:key]; + + BOOL success = MTLValidateAndSetValue(self, key, value, NO, error); + if (!success) return NO; + } + + return YES; +} + +#pragma mark NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[self.class allocWithZone:zone] initWithDictionary:self.dictionaryValue error:NULL]; +} + +#pragma mark NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.dictionaryValue]; +} + +- (NSUInteger)hash { + NSUInteger value = 0; + + for (NSString *key in self.class.propertyKeys) { + value ^= [[self valueForKey:key] hash]; + } + + return value; +} + +- (BOOL)isEqual:(MTLModel *)model { + if (self == model) return YES; + if (![model isMemberOfClass:self.class]) return NO; + + for (NSString *key in self.class.propertyKeys) { + id selfValue = [self valueForKey:key]; + id modelValue = [model valueForKey:key]; + + BOOL valuesEqual = ((selfValue == nil && modelValue == nil) || [selfValue isEqual:modelValue]); + if (!valuesEqual) return NO; + } + + return YES; +} + +@end diff --git a/Pods/Mantle/Mantle/MTLReflection.h b/Pods/Mantle/Mantle/MTLReflection.h new file mode 100644 index 0000000..f4c100e --- /dev/null +++ b/Pods/Mantle/Mantle/MTLReflection.h @@ -0,0 +1,31 @@ +// +// MTLReflection.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-03-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +// Creates a selector from a key and a constant string. +// +// key - The key to insert into the generated selector. This key should be in +// its natural case. +// suffix - A string to append to the key as part of the selector. +// +// Returns a selector, or NULL if the input strings cannot form a valid +// selector. +SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2))); + +// Creates a selector from a key and a constant prefix and suffix. +// +// prefix - A string to prepend to the key as part of the selector. +// key - The key to insert into the generated selector. This key should be in +// its natural case, and will have its first letter capitalized when +// inserted. +// suffix - A string to append to the key as part of the selector. +// +// Returns a selector, or NULL if the input strings cannot form a valid +// selector. +SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2, 3))); diff --git a/Pods/Mantle/Mantle/MTLReflection.m b/Pods/Mantle/Mantle/MTLReflection.m new file mode 100644 index 0000000..923e9e2 --- /dev/null +++ b/Pods/Mantle/Mantle/MTLReflection.m @@ -0,0 +1,50 @@ +// +// MTLReflection.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-03-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLReflection.h" +#import + +SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) { + NSUInteger keyLength = [key maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSUInteger suffixLength = strlen(suffix); + + char selector[keyLength + suffixLength + 1]; + + BOOL success = [key getBytes:selector maxLength:keyLength usedLength:&keyLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, key.length) remainingRange:NULL]; + if (!success) return NULL; + + memcpy(selector + keyLength, suffix, suffixLength); + selector[keyLength + suffixLength] = '\0'; + + return sel_registerName(selector); +} + +SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) { + NSUInteger prefixLength = strlen(prefix); + NSUInteger suffixLength = strlen(suffix); + + NSString *initial = [key substringToIndex:1].uppercaseString; + NSUInteger initialLength = [initial maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + NSString *rest = [key substringFromIndex:1]; + NSUInteger restLength = [rest maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + char selector[prefixLength + initialLength + restLength + suffixLength + 1]; + memcpy(selector, prefix, prefixLength); + + BOOL success = [initial getBytes:selector + prefixLength maxLength:initialLength usedLength:&initialLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, initial.length) remainingRange:NULL]; + if (!success) return NULL; + + success = [rest getBytes:selector + prefixLength + initialLength maxLength:restLength usedLength:&restLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, rest.length) remainingRange:NULL]; + if (!success) return NULL; + + memcpy(selector + prefixLength + initialLength + restLength, suffix, suffixLength); + selector[prefixLength + initialLength + restLength + suffixLength] = '\0'; + + return sel_registerName(selector); +} diff --git a/Pods/Mantle/Mantle/MTLValueTransformer.h b/Pods/Mantle/Mantle/MTLValueTransformer.h new file mode 100644 index 0000000..231b59f --- /dev/null +++ b/Pods/Mantle/Mantle/MTLValueTransformer.h @@ -0,0 +1,29 @@ +// +// MTLValueTransformer.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +typedef id (^MTLValueTransformerBlock)(id); + +// +// A value transformer supporting block-based transformation. +// +@interface MTLValueTransformer : NSValueTransformer + +// Returns a transformer which transforms values using the given block. Reverse +// transformations will not be allowed. ++ (instancetype)transformerWithBlock:(MTLValueTransformerBlock)transformationBlock; + +// Returns a transformer which transforms values using the given block, for +// forward or reverse transformations. ++ (instancetype)reversibleTransformerWithBlock:(MTLValueTransformerBlock)transformationBlock; + +// Returns a transformer which transforms values using the given blocks. ++ (instancetype)reversibleTransformerWithForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock; + +@end diff --git a/Pods/Mantle/Mantle/MTLValueTransformer.m b/Pods/Mantle/Mantle/MTLValueTransformer.m new file mode 100644 index 0000000..5400d45 --- /dev/null +++ b/Pods/Mantle/Mantle/MTLValueTransformer.m @@ -0,0 +1,88 @@ +// +// MTLValueTransformer.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "MTLValueTransformer.h" + +// +// Any MTLValueTransformer supporting reverse transformation. Necessary because +// +allowsReverseTransformation is a class method. +// +@interface MTLReversibleValueTransformer : MTLValueTransformer +@end + +@interface MTLValueTransformer () + +@property (nonatomic, copy, readonly) MTLValueTransformerBlock forwardBlock; +@property (nonatomic, copy, readonly) MTLValueTransformerBlock reverseBlock; + +@end + +@implementation MTLValueTransformer + +#pragma mark Lifecycle + ++ (instancetype)transformerWithBlock:(MTLValueTransformerBlock)transformationBlock { + return [[self alloc] initWithForwardBlock:transformationBlock reverseBlock:nil]; +} + ++ (instancetype)reversibleTransformerWithBlock:(MTLValueTransformerBlock)transformationBlock { + return [self reversibleTransformerWithForwardBlock:transformationBlock reverseBlock:transformationBlock]; +} + ++ (instancetype)reversibleTransformerWithForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock { + return [[MTLReversibleValueTransformer alloc] initWithForwardBlock:forwardBlock reverseBlock:reverseBlock]; +} + +- (id)initWithForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock { + NSParameterAssert(forwardBlock != nil); + + self = [super init]; + if (self == nil) return nil; + + _forwardBlock = [forwardBlock copy]; + _reverseBlock = [reverseBlock copy]; + + return self; +} + +#pragma mark NSValueTransformer + ++ (BOOL)allowsReverseTransformation { + return NO; +} + ++ (Class)transformedValueClass { + return [NSObject class]; +} + +- (id)transformedValue:(id)value { + return self.forwardBlock(value); +} + +@end + +@implementation MTLReversibleValueTransformer + +#pragma mark Lifecycle + +- (id)initWithForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock { + NSParameterAssert(reverseBlock != nil); + return [super initWithForwardBlock:forwardBlock reverseBlock:reverseBlock]; +} + +#pragma mark NSValueTransformer + ++ (BOOL)allowsReverseTransformation { + return YES; +} + +- (id)reverseTransformedValue:(id)value { + return self.reverseBlock(value); +} + +@end diff --git a/Pods/Mantle/Mantle/Mantle.h b/Pods/Mantle/Mantle/Mantle.h new file mode 100644 index 0000000..ebd74e7 --- /dev/null +++ b/Pods/Mantle/Mantle/Mantle.h @@ -0,0 +1,26 @@ +// +// Mantle.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-04. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +//! Project version number for Mantle. +FOUNDATION_EXPORT double MantleVersionNumber; + +//! Project version string for Mantle. +FOUNDATION_EXPORT const unsigned char MantleVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/Pods/Mantle/Mantle/NSArray+MTLManipulationAdditions.h b/Pods/Mantle/Mantle/NSArray+MTLManipulationAdditions.h new file mode 100644 index 0000000..fd7347c --- /dev/null +++ b/Pods/Mantle/Mantle/NSArray+MTLManipulationAdditions.h @@ -0,0 +1,28 @@ +// +// NSArray+MTLManipulationAdditions.h +// Mantle +// +// Created by Josh Abernathy on 9/19/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +@interface NSArray (MTLManipulationAdditions) + +// The first object in the array or nil if the array is empty. +// Forwards to `firstObject` which has been first declared in iOS7, but works with iOS4/10.6. +@property (nonatomic, readonly, strong) id mtl_firstObject; + +// Returns a new array without all instances of the given object. +- (NSArray *)mtl_arrayByRemovingObject:(id)object; + +// Returns a new array without the first object. If the array is empty, it +// returns the empty array. +- (NSArray *)mtl_arrayByRemovingFirstObject; + +// Returns a new array without the last object. If the array is empty, it +// returns the empty array. +- (NSArray *)mtl_arrayByRemovingLastObject; + +@end diff --git a/Pods/Mantle/Mantle/NSArray+MTLManipulationAdditions.m b/Pods/Mantle/Mantle/NSArray+MTLManipulationAdditions.m new file mode 100644 index 0000000..e6932c9 --- /dev/null +++ b/Pods/Mantle/Mantle/NSArray+MTLManipulationAdditions.m @@ -0,0 +1,42 @@ +// +// NSArray+MTLManipulationAdditions.m +// Mantle +// +// Created by Josh Abernathy on 9/19/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSArray+MTLManipulationAdditions.h" + +@interface NSArray (MTLDeclarations) + +// This declaration is needed so Mantle can be compiled with SDK 6 / 10.8. +- (id)firstObject; + +@end + +@implementation NSArray (MTLManipulationAdditions) + +- (id)mtl_firstObject { + return self.firstObject; +} + +- (instancetype)mtl_arrayByRemovingObject:(id)object { + NSMutableArray *result = [self mutableCopy]; + [result removeObject:object]; + return result; +} + +- (instancetype)mtl_arrayByRemovingFirstObject { + if (self.count == 0) return self; + + return [self subarrayWithRange:NSMakeRange(1, self.count - 1)]; +} + +- (instancetype)mtl_arrayByRemovingLastObject { + if (self.count == 0) return self; + + return [self subarrayWithRange:NSMakeRange(0, self.count - 1)]; +} + +@end diff --git a/Pods/Mantle/Mantle/NSDictionary+MTLManipulationAdditions.h b/Pods/Mantle/Mantle/NSDictionary+MTLManipulationAdditions.h new file mode 100644 index 0000000..83254d3 --- /dev/null +++ b/Pods/Mantle/Mantle/NSDictionary+MTLManipulationAdditions.h @@ -0,0 +1,25 @@ +// +// NSDictionary+MTLManipulationAdditions.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-24. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +@interface NSDictionary (MTLManipulationAdditions) + +// Merges the keys and values from the given dictionary into the receiver. If +// both the receiver and `dictionary` have a given key, the value from +// `dictionary` is used. +// +// Returns a new dictionary containing the entries of the receiver combined with +// those of `dictionary`. +- (NSDictionary *)mtl_dictionaryByAddingEntriesFromDictionary:(NSDictionary *)dictionary; + +// Creates a new dictionary with all the entries for the given keys removed from +// the receiver. +- (NSDictionary *)mtl_dictionaryByRemovingEntriesWithKeys:(NSSet *)keys; + +@end diff --git a/Pods/Mantle/Mantle/NSDictionary+MTLManipulationAdditions.m b/Pods/Mantle/Mantle/NSDictionary+MTLManipulationAdditions.m new file mode 100644 index 0000000..0ed5746 --- /dev/null +++ b/Pods/Mantle/Mantle/NSDictionary+MTLManipulationAdditions.m @@ -0,0 +1,25 @@ +// +// NSDictionary+MTLManipulationAdditions.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-24. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSDictionary+MTLManipulationAdditions.h" + +@implementation NSDictionary (MTLManipulationAdditions) + +- (NSDictionary *)mtl_dictionaryByAddingEntriesFromDictionary:(NSDictionary *)dictionary { + NSMutableDictionary *result = [self mutableCopy]; + [result addEntriesFromDictionary:dictionary]; + return result; +} + +- (NSDictionary *)mtl_dictionaryByRemovingEntriesWithKeys:(NSSet *)keys { + NSMutableDictionary *result = [self mutableCopy]; + [result removeObjectsForKeys:keys.allObjects]; + return result; +} + +@end diff --git a/Pods/Mantle/Mantle/NSError+MTLModelException.h b/Pods/Mantle/Mantle/NSError+MTLModelException.h new file mode 100644 index 0000000..f7c1e9e --- /dev/null +++ b/Pods/Mantle/Mantle/NSError+MTLModelException.h @@ -0,0 +1,23 @@ +// +// NSError+MTLModelException.h +// Mantle +// +// Created by Robert Böhnke on 7/6/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@interface NSError (MTLModelException) + +// Creates a new error for an exception that occured during updating an +// MTLModel. +// +// exception - The exception that was thrown while updating the model. +// This argument must not be nil. +// +// Returns an error that takes its localized description and failure reason +// from the exception. ++ (instancetype)mtl_modelErrorWithException:(NSException *)exception; + +@end diff --git a/Pods/Mantle/Mantle/NSError+MTLModelException.m b/Pods/Mantle/Mantle/NSError+MTLModelException.m new file mode 100644 index 0000000..8b71e06 --- /dev/null +++ b/Pods/Mantle/Mantle/NSError+MTLModelException.m @@ -0,0 +1,36 @@ +// +// NSError+MTLModelException.m +// Mantle +// +// Created by Robert Böhnke on 7/6/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLModel.h" + +#import "NSError+MTLModelException.h" + +// The domain for errors originating from MTLModel. +static NSString * const MTLModelErrorDomain = @"MTLModelErrorDomain"; + +// An exception was thrown and caught. +static const NSInteger MTLModelErrorExceptionThrown = 1; + +// Associated with the NSException that was caught. +static NSString * const MTLModelThrownExceptionErrorKey = @"MTLModelThrownException"; + +@implementation NSError (MTLModelException) + ++ (instancetype)mtl_modelErrorWithException:(NSException *)exception { + NSParameterAssert(exception != nil); + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: exception.description, + NSLocalizedFailureReasonErrorKey: exception.reason, + MTLModelThrownExceptionErrorKey: exception + }; + + return [NSError errorWithDomain:MTLModelErrorDomain code:MTLModelErrorExceptionThrown userInfo:userInfo]; +} + +@end diff --git a/Pods/Mantle/Mantle/NSObject+MTLComparisonAdditions.h b/Pods/Mantle/Mantle/NSObject+MTLComparisonAdditions.h new file mode 100644 index 0000000..4f7c03e --- /dev/null +++ b/Pods/Mantle/Mantle/NSObject+MTLComparisonAdditions.h @@ -0,0 +1,15 @@ +// +// NSObject+MTLComparisonAdditions.h +// Mantle +// +// Created by Josh Vera on 10/26/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// +// Portions copyright (c) 2011 Bitswift. All rights reserved. +// See the LICENSE file for more information. +// + +#import + +// Returns whether both objects are identical or equal via -isEqual: +BOOL MTLEqualObjects(id obj1, id obj2); diff --git a/Pods/Mantle/Mantle/NSObject+MTLComparisonAdditions.m b/Pods/Mantle/Mantle/NSObject+MTLComparisonAdditions.m new file mode 100644 index 0000000..3b77b35 --- /dev/null +++ b/Pods/Mantle/Mantle/NSObject+MTLComparisonAdditions.m @@ -0,0 +1,16 @@ +// +// NSObject+MTLComparisonAdditions.m +// Mantle +// +// Created by Josh Vera on 10/26/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// +// Portions copyright (c) 2011 Bitswift. All rights reserved. +// See the LICENSE file for more information. +// + +#import "NSObject+MTLComparisonAdditions.h" + +BOOL MTLEqualObjects(id obj1, id obj2) { + return (obj1 == obj2 || [obj1 isEqual:obj2]); +} diff --git a/Pods/Mantle/Mantle/NSValueTransformer+MTLInversionAdditions.h b/Pods/Mantle/Mantle/NSValueTransformer+MTLInversionAdditions.h new file mode 100644 index 0000000..eefceec --- /dev/null +++ b/Pods/Mantle/Mantle/NSValueTransformer+MTLInversionAdditions.h @@ -0,0 +1,21 @@ +// +// NSValueTransformer+MTLInversionAdditions.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-05-18. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@interface NSValueTransformer (MTLInversionAdditions) + +// Flips the direction of the receiver's transformation, such that +// -transformedValue: will become -reverseTransformedValue:, and vice-versa. +// +// The receiver must allow reverse transformation. +// +// Returns an inverted transformer. +- (NSValueTransformer *)mtl_invertedTransformer; + +@end diff --git a/Pods/Mantle/Mantle/NSValueTransformer+MTLInversionAdditions.m b/Pods/Mantle/Mantle/NSValueTransformer+MTLInversionAdditions.m new file mode 100644 index 0000000..71fe4b0 --- /dev/null +++ b/Pods/Mantle/Mantle/NSValueTransformer+MTLInversionAdditions.m @@ -0,0 +1,24 @@ +// +// NSValueTransformer+MTLInversionAdditions.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-05-18. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "NSValueTransformer+MTLInversionAdditions.h" +#import "MTLValueTransformer.h" + +@implementation NSValueTransformer (MTLInversionAdditions) + +- (NSValueTransformer *)mtl_invertedTransformer { + NSParameterAssert(self.class.allowsReverseTransformation); + + return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(id value) { + return [self reverseTransformedValue:value]; + } reverseBlock:^(id value) { + return [self transformedValue:value]; + }]; +} + +@end diff --git a/Pods/Mantle/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.h b/Pods/Mantle/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.h new file mode 100644 index 0000000..78a6b19 --- /dev/null +++ b/Pods/Mantle/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.h @@ -0,0 +1,84 @@ +// +// NSValueTransformer+MTLPredefinedTransformerAdditions.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-27. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +// The name for a value transformer that converts strings into URLs and back. +extern NSString * const MTLURLValueTransformerName; + +// Ensure an NSNumber is backed by __NSCFBoolean/CFBooleanRef +// +// NSJSONSerialization, and likely other serialization libraries, ordinarily +// serialize NSNumbers as numbers, and thus booleans would be serialized as +// 0/1. The exception is when the NSNumber is backed by __NSCFBoolean, which, +// though very much an implementation detail, is detected and serialized as a +// proper boolean. +extern NSString * const MTLBooleanValueTransformerName; + +@interface NSValueTransformer (MTLPredefinedTransformerAdditions) + +// Creates a reversible transformer to convert a JSON dictionary into a MTLModel +// object, and vice-versa. +// +// modelClass - The MTLModel subclass to attempt to parse from the JSON. This +// class must conform to . This argument must +// not be nil. +// +// Returns a reversible transformer which uses MTLJSONAdapter for transforming +// values back and forth. ++ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass; + +// Creates a reversible transformer to convert an array of JSON dictionaries +// into an array of MTLModel objects, and vice-versa. +// +// modelClass - The MTLModel subclass to attempt to parse from each JSON +// dictionary. This class must conform to . +// This argument must not be nil. +// +// Returns a reversible transformer which uses MTLJSONAdapter for transforming +// array elements back and forth. ++ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass; + +// A reversible value transformer to transform between the keys and objects of a +// dictionary. +// +// dictionary - The dictionary whose keys and values should be +// transformed between. This argument must not be nil. +// defaultValue - The result to fall back to, in case no key matching the +// input value was found during a forward transformation. +// reverseDefaultValue - The result to fall back to, in case no value matching +// the input value was found during a reverse +// transformation. +// +// Can for example be used for transforming between enum values and their string +// representation. +// +// NSValueTransformer *valueTransformer = [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{ +// @"foo": @(EnumDataTypeFoo), +// @"bar": @(EnumDataTypeBar), +// } defaultValue: @(EnumDataTypeUndefined) reverseDefaultValue: @"undefined"]; +// +// Returns a transformer that will map from keys to values in dictionary +// for forward transformation, and from values to keys for reverse +// transformations. If no matching key or value can be found, the respective +// default value is returned. ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary defaultValue:(id)defaultValue reverseDefaultValue:(id)reverseDefaultValue; + +// Returns a value transformer created by calling +// `+mtl_valueMappingTransformerWithDictionary:defaultValue:reverseDefaultValue:` +// with a default value of `nil` and a reverse default value of `nil`. ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary; + +@end + +@interface NSValueTransformer (UnavailableMTLPredefinedTransformerAdditions) + ++ (NSValueTransformer *)mtl_externalRepresentationTransformerWithModelClass:(Class)modelClass __attribute__((deprecated("Replaced by +mtl_JSONDictionaryTransformerWithModelClass:"))); ++ (NSValueTransformer *)mtl_externalRepresentationArrayTransformerWithModelClass:(Class)modelClass __attribute__((deprecated("Replaced by +mtl_JSONArrayTransformerWithModelClass:"))); + +@end diff --git a/Pods/Mantle/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m b/Pods/Mantle/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m new file mode 100644 index 0000000..10622a0 --- /dev/null +++ b/Pods/Mantle/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m @@ -0,0 +1,141 @@ +// +// NSValueTransformer+MTLPredefinedTransformerAdditions.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-27. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSValueTransformer+MTLPredefinedTransformerAdditions.h" +#import "MTLJSONAdapter.h" +#import "MTLModel.h" +#import "MTLValueTransformer.h" + +NSString * const MTLURLValueTransformerName = @"MTLURLValueTransformerName"; +NSString * const MTLBooleanValueTransformerName = @"MTLBooleanValueTransformerName"; + +@implementation NSValueTransformer (MTLPredefinedTransformerAdditions) + +#pragma mark Category Loading + ++ (void)load { + @autoreleasepool { + MTLValueTransformer *URLValueTransformer = [MTLValueTransformer + reversibleTransformerWithForwardBlock:^ id (NSString *str) { + if (![str isKindOfClass:NSString.class]) return nil; + return [NSURL URLWithString:str]; + } + reverseBlock:^ id (NSURL *URL) { + if (![URL isKindOfClass:NSURL.class]) return nil; + return URL.absoluteString; + }]; + + [NSValueTransformer setValueTransformer:URLValueTransformer forName:MTLURLValueTransformerName]; + + MTLValueTransformer *booleanValueTransformer = [MTLValueTransformer + reversibleTransformerWithBlock:^ id (NSNumber *boolean) { + if (![boolean isKindOfClass:NSNumber.class]) return nil; + return (NSNumber *)(boolean.boolValue ? kCFBooleanTrue : kCFBooleanFalse); + }]; + + [NSValueTransformer setValueTransformer:booleanValueTransformer forName:MTLBooleanValueTransformerName]; + } +} + +#pragma mark Customizable Transformers + ++ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass { + NSParameterAssert([modelClass isSubclassOfClass:MTLModel.class]); + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)]); + + return [MTLValueTransformer + reversibleTransformerWithForwardBlock:^ id (id JSONDictionary) { + if (JSONDictionary == nil) return nil; + + NSAssert([JSONDictionary isKindOfClass:NSDictionary.class], @"Expected a dictionary, got: %@", JSONDictionary); + + return [MTLJSONAdapter modelOfClass:modelClass fromJSONDictionary:JSONDictionary error:NULL]; + } + reverseBlock:^ id (id model) { + if (model == nil) return nil; + + NSAssert([model isKindOfClass:MTLModel.class], @"Expected a MTLModel object, got %@", model); + NSAssert([model conformsToProtocol:@protocol(MTLJSONSerializing)], @"Expected a model object conforming to , got %@", model); + + return [MTLJSONAdapter JSONDictionaryFromModel:model]; + }]; +} + ++ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass { + NSValueTransformer *dictionaryTransformer = [self mtl_JSONDictionaryTransformerWithModelClass:modelClass]; + + return [MTLValueTransformer + reversibleTransformerWithForwardBlock:^ id (NSArray *dictionaries) { + if (dictionaries == nil) return nil; + + NSAssert([dictionaries isKindOfClass:NSArray.class], @"Expected an array of dictionaries, got: %@", dictionaries); + + NSMutableArray *models = [NSMutableArray arrayWithCapacity:dictionaries.count]; + for (id JSONDictionary in dictionaries) { + if (JSONDictionary == NSNull.null) { + [models addObject:NSNull.null]; + continue; + } + + NSAssert([JSONDictionary isKindOfClass:NSDictionary.class], @"Expected a dictionary or an NSNull, got: %@", JSONDictionary); + + id model = [dictionaryTransformer transformedValue:JSONDictionary]; + if (model == nil) continue; + + [models addObject:model]; + } + + return models; + } + reverseBlock:^ id (NSArray *models) { + if (models == nil) return nil; + + NSAssert([models isKindOfClass:NSArray.class], @"Expected an array of MTLModels, got: %@", models); + + NSMutableArray *dictionaries = [NSMutableArray arrayWithCapacity:models.count]; + for (id model in models) { + if (model == NSNull.null) { + [dictionaries addObject:NSNull.null]; + continue; + } + + NSAssert([model isKindOfClass:MTLModel.class], @"Expected an MTLModel or an NSNull, got: %@", model); + + NSDictionary *dict = [dictionaryTransformer reverseTransformedValue:model]; + if (dict == nil) continue; + + [dictionaries addObject:dict]; + } + + return dictionaries; + }]; +} + ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary { + return [self mtl_valueMappingTransformerWithDictionary:dictionary defaultValue:nil reverseDefaultValue:nil]; +} + ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary defaultValue:(id)defaultValue reverseDefaultValue:(id)reverseDefaultValue { + NSParameterAssert(dictionary != nil); + NSParameterAssert(dictionary.count == [[NSSet setWithArray:dictionary.allValues] count]); + + return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(id key) { + return dictionary[key ?: NSNull.null] ?: defaultValue; + } reverseBlock:^(id object) { + __block id result = nil; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id anObject, BOOL *stop) { + if ([object isEqual:anObject]) { + result = key; + *stop = YES; + } + }]; + return result ?: reverseDefaultValue; + }]; +} + +@end diff --git a/Pods/Mantle/Mantle/extobjc/EXTKeyPathCoding.h b/Pods/Mantle/Mantle/extobjc/EXTKeyPathCoding.h new file mode 100644 index 0000000..f34dc4a --- /dev/null +++ b/Pods/Mantle/Mantle/extobjc/EXTKeyPathCoding.h @@ -0,0 +1,68 @@ +// +// EXTKeyPathCoding.h +// extobjc +// +// Created by Justin Spahr-Summers on 19.06.12. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import +#import "metamacros.h" + +/** + * \@keypath allows compile-time verification of key paths. Given a real object + * receiver and key path: + * + * @code + +NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String); +// => @"lowercaseString.UTF8String" + +NSString *versionPath = @keypath(NSObject, version); +// => @"version" + +NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString); +// => @"lowercaseString" + + * @endcode + * + * ... the macro returns an \c NSString containing all but the first path + * component or argument (e.g., @"lowercaseString.UTF8String", @"version"). + * + * In addition to simply creating a key path, this macro ensures that the key + * path is valid at compile-time (causing a syntax error if not), and supports + * refactoring, such that changing the name of the property will also update any + * uses of \@keypath. + */ +#define keypath(...) \ + metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__)) + +#define keypath1(PATH) \ + (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1)) + +#define keypath2(OBJ, PATH) \ + (((void)(NO && ((void)OBJ.PATH, NO)), # PATH)) + +/** + * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object + * receiver, collection object receiver and related keypaths: + * + * @code + + NSString *employessFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName) + // => @"employees.firstName" + + NSString *employessFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName) + // => @"employees.firstName" + + * @endcode + * + */ +#define collectionKeypath(...) \ + metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__)) + +#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) + +#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(OBJ, PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) + diff --git a/Pods/Mantle/Mantle/extobjc/EXTRuntimeExtensions.h b/Pods/Mantle/Mantle/extobjc/EXTRuntimeExtensions.h new file mode 100644 index 0000000..191d749 --- /dev/null +++ b/Pods/Mantle/Mantle/extobjc/EXTRuntimeExtensions.h @@ -0,0 +1,113 @@ +// +// EXTRuntimeExtensions.h +// extobjc +// +// Created by Justin Spahr-Summers on 2011-03-05. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import + +/** + * Describes the memory management policy of a property. + */ +typedef enum { + /** + * The value is assigned. + */ + mtl_propertyMemoryManagementPolicyAssign = 0, + + /** + * The value is retained. + */ + mtl_propertyMemoryManagementPolicyRetain, + + /** + * The value is copied. + */ + mtl_propertyMemoryManagementPolicyCopy +} mtl_propertyMemoryManagementPolicy; + +/** + * Describes the attributes and type information of a property. + */ +typedef struct { + /** + * Whether this property was declared with the \c readonly attribute. + */ + BOOL readonly; + + /** + * Whether this property was declared with the \c nonatomic attribute. + */ + BOOL nonatomic; + + /** + * Whether the property is a weak reference. + */ + BOOL weak; + + /** + * Whether the property is eligible for garbage collection. + */ + BOOL canBeCollected; + + /** + * Whether this property is defined with \c \@dynamic. + */ + BOOL dynamic; + + /** + * The memory management policy for this property. This will always be + * #mtl_propertyMemoryManagementPolicyAssign if #readonly is \c YES. + */ + mtl_propertyMemoryManagementPolicy memoryManagementPolicy; + + /** + * The selector for the getter of this property. This will reflect any + * custom \c getter= attribute provided in the property declaration, or the + * inferred getter name otherwise. + */ + SEL getter; + + /** + * The selector for the setter of this property. This will reflect any + * custom \c setter= attribute provided in the property declaration, or the + * inferred setter name otherwise. + * + * @note If #readonly is \c YES, this value will represent what the setter + * \e would be, if the property were writable. + */ + SEL setter; + + /** + * The backing instance variable for this property, or \c NULL if \c + * \c @synthesize was not used, and therefore no instance variable exists. This + * would also be the case if the property is implemented dynamically. + */ + const char *ivar; + + /** + * If this property is defined as being an instance of a specific class, + * this will be the class object representing it. + * + * This will be \c nil if the property was defined as type \c id, if the + * property is not of an object type, or if the class could not be found at + * runtime. + */ + Class objectClass; + + /** + * The type encoding for the value of this property. This is the type as it + * would be returned by the \c \@encode() directive. + */ + char type[]; +} mtl_propertyAttributes; + +/** + * Returns a pointer to a structure containing information about \a property. + * You must \c free() the returned pointer. Returns \c NULL if there is an error + * obtaining information from \a property. + */ +mtl_propertyAttributes *mtl_copyPropertyAttributes (objc_property_t property); diff --git a/Pods/Mantle/Mantle/extobjc/EXTRuntimeExtensions.m b/Pods/Mantle/Mantle/extobjc/EXTRuntimeExtensions.m new file mode 100644 index 0000000..6a034b0 --- /dev/null +++ b/Pods/Mantle/Mantle/extobjc/EXTRuntimeExtensions.m @@ -0,0 +1,211 @@ +// +// EXTRuntimeExtensions.m +// extobjc +// +// Created by Justin Spahr-Summers on 2011-03-05. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "EXTRuntimeExtensions.h" + +#import + +mtl_propertyAttributes *mtl_copyPropertyAttributes (objc_property_t property) { + const char * const attrString = property_getAttributes(property); + if (!attrString) { + fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property)); + return NULL; + } + + if (attrString[0] != 'T') { + fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property)); + return NULL; + } + + const char *typeString = attrString + 1; + const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL); + if (!next) { + fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + size_t typeLength = next - typeString; + if (!typeLength) { + fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + // allocate enough space for the structure and the type string (plus a NUL) + mtl_propertyAttributes *attributes = calloc(1, sizeof(mtl_propertyAttributes) + typeLength + 1); + if (!attributes) { + fprintf(stderr, "ERROR: Could not allocate mtl_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + // copy the type string + strncpy(attributes->type, typeString, typeLength); + attributes->type[typeLength] = '\0'; + + // if this is an object type, and immediately followed by a quoted string... + if (typeString[0] == *(@encode(id)) && typeString[1] == '"') { + // we should be able to extract a class name + const char *className = typeString + 2; + next = strchr(className, '"'); + + if (!next) { + fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + if (className != next) { + size_t classNameLength = next - className; + char trimmedName[classNameLength + 1]; + + strncpy(trimmedName, className, classNameLength); + trimmedName[classNameLength] = '\0'; + + // attempt to look up the class in the runtime + attributes->objectClass = objc_getClass(trimmedName); + } + } + + if (*next != '\0') { + // skip past any junk before the first flag + next = strchr(next, ','); + } + + while (next && *next == ',') { + char flag = next[1]; + next += 2; + + switch (flag) { + case '\0': + break; + + case 'R': + attributes->readonly = YES; + break; + + case 'C': + attributes->memoryManagementPolicy = mtl_propertyMemoryManagementPolicyCopy; + break; + + case '&': + attributes->memoryManagementPolicy = mtl_propertyMemoryManagementPolicyRetain; + break; + + case 'N': + attributes->nonatomic = YES; + break; + + case 'G': + case 'S': + { + const char *nextFlag = strchr(next, ','); + SEL name = NULL; + + if (!nextFlag) { + // assume that the rest of the string is the selector + const char *selectorString = next; + next = ""; + + name = sel_registerName(selectorString); + } else { + size_t selectorLength = nextFlag - next; + if (!selectorLength) { + fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + goto errorOut; + } + + char selectorString[selectorLength + 1]; + + strncpy(selectorString, next, selectorLength); + selectorString[selectorLength] = '\0'; + + name = sel_registerName(selectorString); + next = nextFlag; + } + + if (flag == 'G') + attributes->getter = name; + else + attributes->setter = name; + } + + break; + + case 'D': + attributes->dynamic = YES; + attributes->ivar = NULL; + break; + + case 'V': + // assume that the rest of the string (if present) is the ivar name + if (*next == '\0') { + // if there's nothing there, let's assume this is dynamic + attributes->ivar = NULL; + } else { + attributes->ivar = next; + next = ""; + } + + break; + + case 'W': + attributes->weak = YES; + break; + + case 'P': + attributes->canBeCollected = YES; + break; + + case 't': + fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + + // skip over this type encoding + while (*next != ',' && *next != '\0') + ++next; + + break; + + default: + fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property)); + } + } + + if (next && *next != '\0') { + fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property)); + } + + if (!attributes->getter) { + // use the property name as the getter by default + attributes->getter = sel_registerName(property_getName(property)); + } + + if (!attributes->setter) { + const char *propertyName = property_getName(property); + size_t propertyNameLength = strlen(propertyName); + + // we want to transform the name to setProperty: style + size_t setterLength = propertyNameLength + 4; + + char setterName[setterLength + 1]; + strncpy(setterName, "set", 3); + strncpy(setterName + 3, propertyName, propertyNameLength); + + // capitalize property name for the setter + setterName[3] = (char)toupper(setterName[3]); + + setterName[setterLength - 1] = ':'; + setterName[setterLength] = '\0'; + + attributes->setter = sel_registerName(setterName); + } + + return attributes; + +errorOut: + free(attributes); + return NULL; +} diff --git a/Pods/Mantle/Mantle/extobjc/EXTScope.h b/Pods/Mantle/Mantle/extobjc/EXTScope.h new file mode 100644 index 0000000..7548b81 --- /dev/null +++ b/Pods/Mantle/Mantle/extobjc/EXTScope.h @@ -0,0 +1,99 @@ +// +// EXTScope.h +// extobjc +// +// Created by Justin Spahr-Summers on 2011-05-04. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "metamacros.h" + +/** + * \@onExit defines some code to be executed when the current scope exits. The + * code must be enclosed in braces and terminated with a semicolon, and will be + * executed regardless of how the scope is exited, including from exceptions, + * \c goto, \c return, \c break, and \c continue. + * + * Provided code will go into a block to be executed later. Keep this in mind as + * it pertains to memory management, restrictions on assignment, etc. Because + * the code is used within a block, \c return is a legal (though perhaps + * confusing) way to exit the cleanup block early. + * + * Multiple \@onExit statements in the same scope are executed in reverse + * lexical order. This helps when pairing resource acquisition with \@onExit + * statements, as it guarantees teardown in the opposite order of acquisition. + * + * @note This statement cannot be used within scopes defined without braces + * (like a one line \c if). In practice, this is not an issue, since \@onExit is + * a useless construct in such a case anyways. + */ +#define onExit \ + try {} @finally {} \ + __strong mtl_cleanupBlock_t metamacro_concat(mtl_exitBlock_, __LINE__) __attribute__((cleanup(mtl_executeCleanupBlock), unused)) = ^ + +/** + * Creates \c __weak shadow variables for each of the variables provided as + * arguments, which can later be made strong again with #strongify. + * + * This is typically used to weakly reference variables in a block, but then + * ensure that the variables stay alive during the actual execution of the block + * (if they were live upon entry). + * + * See #strongify for an example of usage. + */ +#define weakify(...) \ + try {} @finally {} \ + metamacro_foreach_cxt(mtl_weakify_,, __weak, __VA_ARGS__) + +/** + * Like #weakify, but uses \c __unsafe_unretained instead, for targets or + * classes that do not support weak references. + */ +#define unsafeify(...) \ + try {} @finally {} \ + metamacro_foreach_cxt(mtl_weakify_,, __unsafe_unretained, __VA_ARGS__) + +/** + * Strongly references each of the variables provided as arguments, which must + * have previously been passed to #weakify. + * + * The strong references created will shadow the original variable names, such + * that the original names can be used without issue (and a significantly + * reduced risk of retain cycles) in the current scope. + * + * @code + + id foo = [[NSObject alloc] init]; + id bar = [[NSObject alloc] init]; + + @weakify(foo, bar); + + // this block will not keep 'foo' or 'bar' alive + BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){ + // but now, upon entry, 'foo' and 'bar' will stay alive until the block has + // finished executing + @strongify(foo, bar); + + return [foo isEqual:obj] || [bar isEqual:obj]; + }; + + * @endcode + */ +#define strongify(...) \ + try {} @finally {} \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wshadow\"") \ + metamacro_foreach(mtl_strongify_,, __VA_ARGS__) \ + _Pragma("clang diagnostic pop") + +/*** implementation details follow ***/ +typedef void (^mtl_cleanupBlock_t)(); + +void mtl_executeCleanupBlock (__strong mtl_cleanupBlock_t *block); + +#define mtl_weakify_(INDEX, CONTEXT, VAR) \ + CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR); + +#define mtl_strongify_(INDEX, VAR) \ + __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_); diff --git a/Pods/Mantle/Mantle/extobjc/EXTScope.m b/Pods/Mantle/Mantle/extobjc/EXTScope.m new file mode 100644 index 0000000..b34c1c6 --- /dev/null +++ b/Pods/Mantle/Mantle/extobjc/EXTScope.m @@ -0,0 +1,15 @@ +// +// EXTScope.m +// extobjc +// +// Created by Justin Spahr-Summers on 2011-05-04. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "EXTScope.h" + +void mtl_executeCleanupBlock (__strong mtl_cleanupBlock_t *block) { + (*block)(); +} + diff --git a/Pods/Mantle/Mantle/extobjc/metamacros.h b/Pods/Mantle/Mantle/extobjc/metamacros.h new file mode 100644 index 0000000..77a77b5 --- /dev/null +++ b/Pods/Mantle/Mantle/extobjc/metamacros.h @@ -0,0 +1,666 @@ +/** + * Macros for metaprogramming + * ExtendedC + * + * Copyright (C) 2012 Justin Spahr-Summers + * Released under the MIT license + */ + +#ifndef EXTC_METAMACROS_H +#define EXTC_METAMACROS_H + +/** + * Executes one or more expressions (which may have a void type, such as a call + * to a function that returns no value) and always returns true. + */ +#define metamacro_exprify(...) \ + ((__VA_ARGS__), true) + +/** + * Returns a string representation of VALUE after full macro expansion. + */ +#define metamacro_stringify(VALUE) \ + metamacro_stringify_(VALUE) + +/** + * Returns A and B concatenated after full macro expansion. + */ +#define metamacro_concat(A, B) \ + metamacro_concat_(A, B) + +/** + * Returns the Nth variadic argument (starting from zero). At least + * N + 1 variadic arguments must be given. N must be between zero and twenty, + * inclusive. + */ +#define metamacro_at(N, ...) \ + metamacro_concat(metamacro_at, N)(__VA_ARGS__) + +/** + * Returns the number of arguments (up to twenty) provided to the macro. At + * least one argument must be provided. + * + * Inspired by P99: http://p99.gforge.inria.fr + */ +#define metamacro_argcount(...) \ + metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) + +/** + * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is + * given. Only the index and current argument will thus be passed to MACRO. + */ +#define metamacro_foreach(MACRO, SEP, ...) \ + metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) + +/** + * For each consecutive variadic argument (up to twenty), MACRO is passed the + * zero-based index of the current argument, CONTEXT, and then the argument + * itself. The results of adjoining invocations of MACRO are then separated by + * SEP. + * + * Inspired by P99: http://p99.gforge.inria.fr + */ +#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ + metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) + +/** + * Identical to #metamacro_foreach_cxt. This can be used when the former would + * fail due to recursive macro expansion. + */ +#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ + metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) + +/** + * In consecutive order, appends each variadic argument (up to twenty) onto + * BASE. The resulting concatenations are then separated by SEP. + * + * This is primarily useful to manipulate a list of macro invocations into instead + * invoking a different, possibly related macro. + */ +#define metamacro_foreach_concat(BASE, SEP, ...) \ + metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) + +/** + * Iterates COUNT times, each time invoking MACRO with the current index + * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO + * are then separated by SEP. + * + * COUNT must be an integer between zero and twenty, inclusive. + */ +#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ + metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) + +/** + * Returns the first argument given. At least one argument must be provided. + * + * This is useful when implementing a variadic macro, where you may have only + * one variadic argument, but no way to retrieve it (for example, because \c ... + * always needs to match at least one argument). + * + * @code + +#define varmacro(...) \ + metamacro_head(__VA_ARGS__) + + * @endcode + */ +#define metamacro_head(...) \ + metamacro_head_(__VA_ARGS__, 0) + +/** + * Returns every argument except the first. At least two arguments must be + * provided. + */ +#define metamacro_tail(...) \ + metamacro_tail_(__VA_ARGS__) + +/** + * Returns the first N (up to twenty) variadic arguments as a new argument list. + * At least N variadic arguments must be provided. + */ +#define metamacro_take(N, ...) \ + metamacro_concat(metamacro_take, N)(__VA_ARGS__) + +/** + * Removes the first N (up to twenty) variadic arguments from the given argument + * list. At least N variadic arguments must be provided. + */ +#define metamacro_drop(N, ...) \ + metamacro_concat(metamacro_drop, N)(__VA_ARGS__) + +/** + * Decrements VAL, which must be a number between zero and twenty, inclusive. + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_dec(VAL) \ + metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) + +/** + * Increments VAL, which must be a number between zero and twenty, inclusive. + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_inc(VAL) \ + metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) + +/** + * If A is equal to B, the next argument list is expanded; otherwise, the + * argument list after that is expanded. A and B must be numbers between zero + * and twenty, inclusive. Additionally, B must be greater than or equal to A. + * + * @code + +// expands to true +metamacro_if_eq(0, 0)(true)(false) + +// expands to false +metamacro_if_eq(0, 1)(true)(false) + + * @endcode + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_if_eq(A, B) \ + metamacro_concat(metamacro_if_eq, A)(B) + +/** + * Identical to #metamacro_if_eq. This can be used when the former would fail + * due to recursive macro expansion. + */ +#define metamacro_if_eq_recursive(A, B) \ + metamacro_concat(metamacro_if_eq_recursive, A)(B) + +/** + * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and + * twenty, inclusive. + * + * For the purposes of this test, zero is considered even. + */ +#define metamacro_is_even(N) \ + metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) + +/** + * Returns the logical NOT of B, which must be the number zero or one. + */ +#define metamacro_not(B) \ + metamacro_at(B, 1, 0) + +// IMPLEMENTATION DETAILS FOLLOW! +// Do not write code that depends on anything below this line. +#define metamacro_stringify_(VALUE) # VALUE +#define metamacro_concat_(A, B) A ## B +#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) +#define metamacro_head_(FIRST, ...) FIRST +#define metamacro_tail_(FIRST, ...) __VA_ARGS__ +#define metamacro_consume_(...) +#define metamacro_expand_(...) __VA_ARGS__ + +// implemented from scratch so that metamacro_concat() doesn't end up nesting +#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) +#define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG + +// metamacro_at expansions +#define metamacro_at0(...) metamacro_head(__VA_ARGS__) +#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) + +// metamacro_foreach_cxt expansions +#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) +#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) + +#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ + metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ + SEP \ + MACRO(1, CONTEXT, _1) + +#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ + SEP \ + MACRO(2, CONTEXT, _2) + +#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + SEP \ + MACRO(3, CONTEXT, _3) + +#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + SEP \ + MACRO(4, CONTEXT, _4) + +#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + SEP \ + MACRO(5, CONTEXT, _5) + +#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + SEP \ + MACRO(6, CONTEXT, _6) + +#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + SEP \ + MACRO(7, CONTEXT, _7) + +#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + SEP \ + MACRO(8, CONTEXT, _8) + +#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + SEP \ + MACRO(9, CONTEXT, _9) + +#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + SEP \ + MACRO(10, CONTEXT, _10) + +#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + SEP \ + MACRO(11, CONTEXT, _11) + +#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + SEP \ + MACRO(12, CONTEXT, _12) + +#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + SEP \ + MACRO(13, CONTEXT, _13) + +#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + SEP \ + MACRO(14, CONTEXT, _14) + +#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + SEP \ + MACRO(15, CONTEXT, _15) + +#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + SEP \ + MACRO(16, CONTEXT, _16) + +#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + SEP \ + MACRO(17, CONTEXT, _17) + +#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + SEP \ + MACRO(18, CONTEXT, _18) + +#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ + metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + SEP \ + MACRO(19, CONTEXT, _19) + +// metamacro_foreach_cxt_recursive expansions +#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) +#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) + +#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ + metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ + SEP \ + MACRO(1, CONTEXT, _1) + +#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ + SEP \ + MACRO(2, CONTEXT, _2) + +#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + SEP \ + MACRO(3, CONTEXT, _3) + +#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + SEP \ + MACRO(4, CONTEXT, _4) + +#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + SEP \ + MACRO(5, CONTEXT, _5) + +#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + SEP \ + MACRO(6, CONTEXT, _6) + +#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + SEP \ + MACRO(7, CONTEXT, _7) + +#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + SEP \ + MACRO(8, CONTEXT, _8) + +#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + SEP \ + MACRO(9, CONTEXT, _9) + +#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + SEP \ + MACRO(10, CONTEXT, _10) + +#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + SEP \ + MACRO(11, CONTEXT, _11) + +#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + SEP \ + MACRO(12, CONTEXT, _12) + +#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + SEP \ + MACRO(13, CONTEXT, _13) + +#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + SEP \ + MACRO(14, CONTEXT, _14) + +#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + SEP \ + MACRO(15, CONTEXT, _15) + +#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + SEP \ + MACRO(16, CONTEXT, _16) + +#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + SEP \ + MACRO(17, CONTEXT, _17) + +#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + SEP \ + MACRO(18, CONTEXT, _18) + +#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ + metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + SEP \ + MACRO(19, CONTEXT, _19) + +// metamacro_for_cxt expansions +#define metamacro_for_cxt0(MACRO, SEP, CONTEXT) +#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) + +#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(1, CONTEXT) + +#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(2, CONTEXT) + +#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(3, CONTEXT) + +#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(4, CONTEXT) + +#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(5, CONTEXT) + +#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(6, CONTEXT) + +#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(7, CONTEXT) + +#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(8, CONTEXT) + +#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(9, CONTEXT) + +#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(10, CONTEXT) + +#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(11, CONTEXT) + +#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(12, CONTEXT) + +#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(13, CONTEXT) + +#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(14, CONTEXT) + +#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(15, CONTEXT) + +#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(16, CONTEXT) + +#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(17, CONTEXT) + +#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(18, CONTEXT) + +#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(19, CONTEXT) + +// metamacro_if_eq expansions +#define metamacro_if_eq0(VALUE) \ + metamacro_concat(metamacro_if_eq0_, VALUE) + +#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ +#define metamacro_if_eq0_1(...) metamacro_expand_ +#define metamacro_if_eq0_2(...) metamacro_expand_ +#define metamacro_if_eq0_3(...) metamacro_expand_ +#define metamacro_if_eq0_4(...) metamacro_expand_ +#define metamacro_if_eq0_5(...) metamacro_expand_ +#define metamacro_if_eq0_6(...) metamacro_expand_ +#define metamacro_if_eq0_7(...) metamacro_expand_ +#define metamacro_if_eq0_8(...) metamacro_expand_ +#define metamacro_if_eq0_9(...) metamacro_expand_ +#define metamacro_if_eq0_10(...) metamacro_expand_ +#define metamacro_if_eq0_11(...) metamacro_expand_ +#define metamacro_if_eq0_12(...) metamacro_expand_ +#define metamacro_if_eq0_13(...) metamacro_expand_ +#define metamacro_if_eq0_14(...) metamacro_expand_ +#define metamacro_if_eq0_15(...) metamacro_expand_ +#define metamacro_if_eq0_16(...) metamacro_expand_ +#define metamacro_if_eq0_17(...) metamacro_expand_ +#define metamacro_if_eq0_18(...) metamacro_expand_ +#define metamacro_if_eq0_19(...) metamacro_expand_ +#define metamacro_if_eq0_20(...) metamacro_expand_ + +#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) +#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) +#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) +#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) +#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) +#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) +#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) +#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) +#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) +#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) +#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) +#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) +#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) +#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) +#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) +#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) +#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) +#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) +#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) +#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) + +// metamacro_if_eq_recursive expansions +#define metamacro_if_eq_recursive0(VALUE) \ + metamacro_concat(metamacro_if_eq_recursive0_, VALUE) + +#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ +#define metamacro_if_eq_recursive0_1(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_2(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_3(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_4(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_5(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_6(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_7(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_8(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_9(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_10(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_11(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_12(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_13(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_14(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_15(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_16(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_17(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_18(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_19(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_20(...) metamacro_expand_ + +#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) + +// metamacro_take expansions +#define metamacro_take0(...) +#define metamacro_take1(...) metamacro_head(__VA_ARGS__) +#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) +#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) +#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) +#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) +#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) +#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) +#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) +#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) +#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) +#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) +#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) +#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) +#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) +#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) +#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) +#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) +#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) +#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) +#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) + +// metamacro_drop expansions +#define metamacro_drop0(...) __VA_ARGS__ +#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) +#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) + +#endif diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..963d476 --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,3212 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 006E8A5DDB48AD0E82306788 /* MTLModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BE128E57C6D864A051CC8A5 /* MTLModel.h */; }; + 0086DDE85A22673FC7AD6859 /* UIImage+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 09D74DAE23C038E1A14CE924 /* UIImage+DTFoundation.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 00D46518F2022BD4A57A8436 /* DTHTMLWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9097904A80D8A7F24ADC07DB /* DTHTMLWriter.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0177A3E3A5951C0FC1BE8A73 /* DTExtendedFileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 221F95CBD014F9E63281BEE7 /* DTExtendedFileAttributes.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0252B09D1C770C2D67C8690D /* default.css.c in Sources */ = {isa = PBXBuildFile; fileRef = ACB76F8380C3F1D5515F2A4B /* default.css.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 025B8F23517C2E2D6C8E5B0A /* DTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AFC81561555A203DCA80986 /* DTVersion.h */; }; + 03FEB05066DFF6AF53B7D39C /* UIView+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 69E9E1C87B98BF60ED6B0429 /* UIView+DTFoundation.h */; }; + 04848A85C8363BB27A420E1D /* DTTextAttachmentHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 66C6218CF6E13E047F8CDD16 /* DTTextAttachmentHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0521F2C2D800AF01FCC436BC /* DTCoreTextGlyphRun.m in Sources */ = {isa = PBXBuildFile; fileRef = E4228BBDE97A358CCB899B7B /* DTCoreTextGlyphRun.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 06A39D2C0BE4E5C0C58F925E /* SDWebImageDownloaderOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = EA1F63E998324A933B864BF1 /* SDWebImageDownloaderOperation.h */; }; + 08D2FAAEB4028205D841FF4B /* NSString+CSS.h in Headers */ = {isa = PBXBuildFile; fileRef = 749B8332FF564663E57BFCE9 /* NSString+CSS.h */; }; + 09665611DC56E1BB0AD3A8EF /* UIKit+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D7B9F66B9415561599C8879 /* UIKit+AFNetworking.h */; }; + 0C3907DCC2D10CA43C1FFC09 /* DTDictationPlaceholderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E2BE295E0CA7EBC4570237D /* DTDictationPlaceholderView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0C648546F9A6A55795BD788F /* NSString+Paragraphs.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CAEDBC718BBC91DC5AB3CD6 /* NSString+Paragraphs.h */; }; + 0C9BD490C9DE484D0B24B639 /* NSAttributedString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = DFA1010AA36B765E499B1FDD /* NSAttributedString+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0CD65DFA4E39BB6E139BDB51 /* EGOCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A7FA25460BD0A14CE76D3DF /* EGOCache.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0D74B76307D3FFBA9CFD144E /* DTAccessibilityViewProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 84513A2E6747DF1945EB57E6 /* DTAccessibilityViewProxy.h */; }; + 0DD2CB104A575EC16ED45F0C /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = F36DFDC1D28DDE52EC3796E4 /* NSString+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 0F0A2C6A2BB724885704612F /* NSFileManager+Godzippa.h in Headers */ = {isa = PBXBuildFile; fileRef = 648F316B0C15B0C42B9F483D /* NSFileManager+Godzippa.h */; }; + 0FEBF5E40C70242ADEC557E0 /* NSArray+MTLManipulationAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D0427BB77844F551A044D37 /* NSArray+MTLManipulationAdditions.h */; }; + 1142A10869BD826C4718A17C /* DTCSSStylesheet.h in Headers */ = {isa = PBXBuildFile; fileRef = 499B36A4F41B73059E29F3E0 /* DTCSSStylesheet.h */; }; + 13EAAAEE13C61A26A470DE1D /* NSAttributedString+DTDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 8EE25AF5774365C08845F9C4 /* NSAttributedString+DTDebug.h */; }; + 140F91F5E0A09CDBE4A6BA3D /* UIActivityIndicatorView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EB56E074E9C09F2406F744A /* UIActivityIndicatorView+AFNetworking.m */; }; + 1486AC8C3A7297133A92AFAA /* DTCoreTextLayoutLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F3AA2982830D803D19E4E40 /* DTCoreTextLayoutLine.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 158B46AE25066CEEF2355EB5 /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 349161E009EB67623A7A576E /* SDWebImageDownloader.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 15F3EB3135EA5C3BC80BCBC6 /* NSDictionary+MTLManipulationAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 124E98330C7C6C750FC9E2C1 /* NSDictionary+MTLManipulationAdditions.h */; }; + 1667D80BB4AAC91719824B33 /* NSURL+DTComparing.m in Sources */ = {isa = PBXBuildFile; fileRef = AF8A9093D0B137CE9B22C7A2 /* NSURL+DTComparing.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 183D801DD22D9B6FC571203E /* DTCoreTextFontDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C4CF9E12F9F5431B1DB7424 /* DTCoreTextFontDescriptor.h */; }; + 185D5357505F0F39C5D04631 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 18E0D08DDB96160495E07051 /* DTCSSListStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 5048BF1FE7D776CF0E99C53D /* DTCSSListStyle.h */; }; + 1A5EA1AC22A38461089595F3 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 25F5B5E271E33029DFD03BEF /* EXTRuntimeExtensions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 1BA8386868B95662C6607E0E /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = C4B18DCB03C46935261F01FB /* EXTScope.h */; }; + 1D6661D2BAE2603741406D09 /* DTBlockFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7524B99AB4298B2349FD1128 /* DTBlockFunctions.h */; }; + 1DF1511AED1ED3D7D108B1B4 /* DTFolderMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = DF597B5C373F01B275417E33 /* DTFolderMonitor.h */; }; + 1E6B778FC5E9F2F75547761F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 1FCAD3D7DB8504F29AE83E5A /* NSFileWrapper+DTCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = C80E4986A69A3380A13EDA4F /* NSFileWrapper+DTCopying.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 205B3EB176711FFF8C702899 /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F032A391FA9974FEB16D8424 /* AFHTTPSessionManager.m */; }; + 2095AB02626F82C3BFECE0BF /* DTCoreGraphicsUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C05ECAAEFDD379130CEC69 /* DTCoreGraphicsUtils.h */; }; + 20A5C2FAB1C3E995D9FD1E78 /* DTCoreTextLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = D4429C837BD4425D786F352A /* DTCoreTextLayouter.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 20EBED8E298504927CB38D69 /* AFNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C98317A2D9BC73A065CD84 /* AFNetworkReachabilityManager.h */; }; + 228F668BDA5E4D151B7B5191 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 95E8E93955BD84AB7D61B561 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 24FA2C0C0F51B355ED36931E /* NSFileManager+Godzippa.m in Sources */ = {isa = PBXBuildFile; fileRef = 12DE9529ACCA6BA9429BA38E /* NSFileManager+Godzippa.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 257AAAAD2AA19C108A49975A /* Pods-DTFoundation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 313D9BC92774DAE5CC6D7A8F /* Pods-DTFoundation-dummy.m */; }; + 2607530297B5305CE76E6E73 /* DTIframeTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F7CF4F74439B9D4D12F935C /* DTIframeTextAttachment.h */; }; + 268D6909B71C79CC18B6B959 /* NSValueTransformer+MTLInversionAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = E495FC07286B9FA610C35F1F /* NSValueTransformer+MTLInversionAdditions.h */; }; + 27CAF58D597A80E539024F2D /* DTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 7847982D5FED405E21CA5041 /* DTLog.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 295FDF855B4E2B3F8F73EC1F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 297D0CA90179129CC1C33C49 /* DTColor+Compatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = E59081C0EB06143AD057BAD3 /* DTColor+Compatibility.h */; }; + 299178842E6970CB24FBB92D /* NSURL+DTUnshorten.m in Sources */ = {isa = PBXBuildFile; fileRef = 121D4C2CE488ECFF04E7CC4A /* NSURL+DTUnshorten.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 29F902685D1DADF99157C686 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12CFFC0AAEB2049E3D38839B /* MediaPlayer.framework */; }; + 2AF1A3D27062859B92E89187 /* DTStylesheetHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = D3B28D24ACD99923AE54B04E /* DTStylesheetHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 2D8D387E9FD546E2B03ABB39 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = ABEB20229B4440B279466FB6 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h */; }; + 2DDB042B5FCC8BBB36942239 /* NSMutableString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = AC5A316ABA995034D8C3B6C8 /* NSMutableString+HTML.h */; }; + 3027E6DD7D492B53F8E19335 /* SBJson4StreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 94D59B3E6671961F9FC0D3B9 /* SBJson4StreamWriter.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 30E0C85B295BB7080C1D130D /* NSAttributedStringRunDelegates.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B21AE8AAA30A1308C14335 /* NSAttributedStringRunDelegates.h */; }; + 3169F1A2BB798470827CA2AC /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3CB55DAB2A2950F0DAF0C6 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h */; }; + 318D92F094A58B116E355A29 /* UIImageView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 31C53FE22C24600E279C2A81 /* UIImageView+WebCache.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 31A0AFF6C96292F12D3B9712 /* AFSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = C336AC714C253351DB4D3E1D /* AFSecurityPolicy.h */; }; + 31A1CFEAF50D4CA62F837EB0 /* NSString+DTFormatNumbers.m in Sources */ = {isa = PBXBuildFile; fileRef = 753131FB3B0B281B823061DB /* NSString+DTFormatNumbers.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 321F2323FA8CFA062B53B31B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 32C531ED8DE3B177D1A8ACF0 /* SBJson4StreamTokeniser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8631416B11CCD525941D9201 /* SBJson4StreamTokeniser.h */; }; + 32C63F178EBD39CA7AA9F05A /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F69301F38456253353C2FB3 /* UIImage+GIF.h */; }; + 3350FB460047A05968BEFC4A /* DTImage+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 13305323CFB19F9B7DBBF7B7 /* DTImage+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 339A5219261849C8589C1F2C /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 6763C598E5DC3B3A5A0655F9 /* SDWebImageDecoder.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3422822F28C19EC7D762CFB6 /* EXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = D24263A900677EE623D3679C /* EXTScope.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 349632871DB5BDD0380803AB /* DTCoreTextParagraphStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = A758F0825180829D7CE75B87 /* DTCoreTextParagraphStyle.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3593973A551BBBE8377F98F0 /* DTHorizontalRuleHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = A3473269AF9E0EF0A56CF9B2 /* DTHorizontalRuleHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 35E2FD9B211B81E86C4D6FF8 /* DTHTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A1F1C6F924DBF4917B97A1B /* DTHTMLParser.h */; }; + 37569F8A96C035D32EE5E467 /* DTDictationPlaceholderView.h in Headers */ = {isa = PBXBuildFile; fileRef = B35D826DD7A9C3AED97F787C /* DTDictationPlaceholderView.h */; }; + 3799521D45A82B28D35246C3 /* UIProgressView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C459247AAAEF8F0357FF79 /* UIProgressView+AFNetworking.m */; }; + 3869F2B3ABEA4E809754E34C /* DTAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CCD9AC3DC59EFD8A7760B6BC /* DTAttributedLabel.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 38FC274AEEB6A7995723AA84 /* UIImageView+HighlightedWebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CF3B0023ABA3D5362C4C9EB /* UIImageView+HighlightedWebCache.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 39970542050FEB1D39277097 /* UIButton+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = F3C15A09965A76FB14357315 /* UIButton+AFNetworking.m */; }; + 39BF0CA8E5545D8D7E3A11B3 /* DTHTMLParserNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F808E117E65DD98C903DACD /* DTHTMLParserNode.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 39D83E6EA467FEED0EFE1990 /* DTLinkButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D5BD60D8CE96A58045E1EEF /* DTLinkButton.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 39F1BC63303BA6AE31E077AC /* MTLReflection.m in Sources */ = {isa = PBXBuildFile; fileRef = 08648726B4DF14197FFE2669 /* MTLReflection.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3A3F04458C7BE8BE7DF0B851 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 62C8556BD6C33126297EACEC /* AFURLConnectionOperation.m */; }; + 3C450D4A15AC3879D088DA0A /* NSCoder+DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = DDF6C7DE85237BEFA9C0EAD3 /* NSCoder+DTCompatibility.h */; }; + 3C49D84D9495797298A68717 /* DTHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 034681345CE339CC0D7F4BC7 /* DTHTMLElement.h */; }; + 3CDB315891882709CD80A8FA /* NSAttributedString+SmallCaps.m in Sources */ = {isa = PBXBuildFile; fileRef = FBE40E3E904127BB450B3FD4 /* NSAttributedString+SmallCaps.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3E47CAC5074E18E77550A384 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CF1A5A1EE415567B64AE71 /* EXTKeyPathCoding.h */; }; + 3E5A91A4DC637944A6AFCF49 /* Pods-Godzippa-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 436327479F3B7896DCBE3876 /* Pods-Godzippa-dummy.m */; }; + 3E917B3F443312CB957CEFE7 /* UIApplication+DTNetworkActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F09EFAA166D58583C1180D7 /* UIApplication+DTNetworkActivity.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3F1C961363E12D2AFDAB8699 /* SBJson4.h in Headers */ = {isa = PBXBuildFile; fileRef = CA5F091D0B1700AFE4DF24AC /* SBJson4.h */; }; + 3F2BD7C9310F9FAA075BCAD0 /* NSData+Godzippa.m in Sources */ = {isa = PBXBuildFile; fileRef = FCA578486B8C46E809FE8BC6 /* NSData+Godzippa.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3F7EAF8D6277AC4CF9361105 /* DTPieProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 980E5243E51E9C323AA25021 /* DTPieProgressIndicator.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 40439EB75949CCC27C74EEB2 /* NSString+DTURLEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = DCD7926ADFF42331088E92EB /* NSString+DTURLEncoding.h */; }; + 405FD5CA64325AF2CFCE489B /* UIFont+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F0F4805C5D575AADE2BB87D /* UIFont+DTCoreText.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 40AE0EECD87647C61827B8CD /* DTAccessibilityElement.m in Sources */ = {isa = PBXBuildFile; fileRef = EC57F2F80C580BA1D339FECE /* DTAccessibilityElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 40C653B8C9F9F84A45FB434C /* Pods-EGOCache-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D24C02FBF4C29B7C9BD5965 /* Pods-EGOCache-dummy.m */; }; + 421D4B741F5A803ED2C70860 /* AFURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD36549EF3A54A1AD6CC794E /* AFURLSessionManager.h */; }; + 42D9CD0A8BCD3401BE27344D /* SBJson4StreamParser.m in Sources */ = {isa = PBXBuildFile; fileRef = CAB06CD8D214A93380CA359F /* SBJson4StreamParser.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 44AA3CEC5B596ED5F4E51890 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 4536A105F3E7CA56003ED6D7 /* UIImage+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = CDA3C0D0DBA5CA06E7D5F29C /* UIImage+DTFoundation.h */; }; + 45E1B3508DA65ABAE6B6CCFD /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DC97954CF46773DA323661B4 /* UIImageView+WebCache.h */; }; + 461BBBA11D7AC5A56F57B76A /* UIView+WebCacheOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CC250296BA77530564C8631 /* UIView+WebCacheOperation.h */; }; + 46A90EF74ACA421D5E3D8715 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B431B9F8EEBC811ECBF6A5D4 /* MobileCoreServices.framework */; }; + 47689BD55E998A4443FEC356 /* NSData+DTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CFF339A8E0F272B95A05400 /* NSData+DTCrypto.h */; }; + 47863598A02CBE4276EE356B /* NSArray+MTLManipulationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B413A933260FB0F4B93158CC /* NSArray+MTLManipulationAdditions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 47E5693168FE0FD41B08E4D5 /* DTAsyncFileDeleter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C09C605E4DF2F9240812865 /* DTAsyncFileDeleter.h */; }; + 496E00F823F9F24A147EE3A8 /* UIWebView+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = A8A9067C9B073EC236A3E05D /* UIWebView+DTFoundation.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 4A41EC290E6E4993B5A8BEEF /* UIWebView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AEDC475AE181182905CC7C9 /* UIWebView+AFNetworking.h */; }; + 4A4492948E213192F36CFDF5 /* NSMutableAttributedString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BF40ECE7E4E0682190A71C4 /* NSMutableAttributedString+HTML.h */; }; + 4AE6DE6A90D76A5E825443E6 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9085028F0F1956A346E6C1FB /* EXTRuntimeExtensions.h */; }; + 4B669FF737A17B91306D9691 /* DTTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = BAEAE04DC9D6049ECD98EADA /* DTTextAttachment.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 4C9258CC9F2171809204D548 /* NSValueTransformer+MTLInversionAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AB1625C41A6A0C8B22D379 /* NSValueTransformer+MTLInversionAdditions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 4D9E5C5DC3C7255D0ABFDA68 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 4E83DB75860E8D40A5926BFC /* AFHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 181B343ACA3D419E8FF324D6 /* AFHTTPSessionManager.h */; }; + 4F07009C76CA7FC9A4668CF2 /* SBJson4StreamTokeniser.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6EDE8ED4679A5632162A1F /* SBJson4StreamTokeniser.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 4F1A4CC0BBEBD17C92FDA0D0 /* DTAttributedTextContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = ACED3FB738D11F8A10B394A3 /* DTAttributedTextContentView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 519487F4FAF706EEE0B46BDA /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = B89B359C774D0BEEADCF72FE /* Reachability.m */; }; + 523BE366D987FD575975D4B8 /* Pods-Reachability-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EB112F511BE632013ED5CEA0 /* Pods-Reachability-dummy.m */; }; + 53C2CC0D9AC616DC5A1FD145 /* DTCoreTextLayoutLine.h in Headers */ = {isa = PBXBuildFile; fileRef = A72BEDBD2D2DB007DDBDC61A /* DTCoreTextLayoutLine.h */; }; + 541EF05C376C64CA1DDF426C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 573F80B0149DC5B099187EC4 /* NSMutableArray+DTMoving.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFC006BEF1B085DB3C57659 /* NSMutableArray+DTMoving.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 57553B499FA92F48C4E4CC2F /* NSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D228843B5620E10036CEF16 /* NSString+HTML.h */; }; + 58B501E02AD95E7A9DAEE42C /* DTImageTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 03CDA45689F3CBAC4D698D25 /* DTImageTextAttachment.h */; }; + 58C73BE476258C1B13B8908B /* MTLManagedObjectAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = D2E5C56114175812BBC678AC /* MTLManagedObjectAdapter.h */; }; + 5A11C4C2E8900CBA9F87F867 /* NSData+ImageContentType.h in Headers */ = {isa = PBXBuildFile; fileRef = EB703E7EA6C7395F91F232CC /* NSData+ImageContentType.h */; }; + 5BC2EC66463E842B0B956ADC /* NSURL+DTAppLinks.h in Headers */ = {isa = PBXBuildFile; fileRef = 8301F7F2078CE377A3C33B23 /* NSURL+DTAppLinks.h */; }; + 5F147E54FD8BEC7DBD17DB3E /* SBJson4StreamParserState.m in Sources */ = {isa = PBXBuildFile; fileRef = 08BFFA49C32FC84AC3484233 /* SBJson4StreamParserState.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 5F889348A787181D3DEB35DC /* NSDictionary+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = B439B8C90661AC8FFC66E04D /* NSDictionary+DTCoreText.h */; }; + 632DCF9C1FB9B535111A658F /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FFB2500E4380D67EF8A7E41 /* CoreText.framework */; }; + 635CEB8A826BA00DEC25F107 /* DTHTMLAttributedStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = D2B82CCEE3DEBD49E4E63DCB /* DTHTMLAttributedStringBuilder.h */; }; + 64460E5AD866F07D47465E5F /* Pods-SDWebImage-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A9C4C308E5654ADF4D23B3B4 /* Pods-SDWebImage-dummy.m */; }; + 65085786CA31DD9D3CB703AA /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 868740E807D0BE8A311DD536 /* Reachability.h */; }; + 65169BB7EEE5F582D93E1532 /* DTLazyImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = B10FE77E0C21FFBB51B6DE3A /* DTLazyImageView.h */; }; + 66AEAB7C4A3AF87F5B13E119 /* DTAnimatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = 451382DBA924128C00A949F8 /* DTAnimatedGIF.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 66B33DB1CFD813A00D2A76BF /* DTTiledLayerWithoutFade.m in Sources */ = {isa = PBXBuildFile; fileRef = 631A5E7C34AF78A47633C5B9 /* DTTiledLayerWithoutFade.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 67FB538314D2551EEE5BA225 /* DTCoreTextFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1593656BA44FF46A80E0DECD /* DTCoreTextFunctions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 69351A995F035FC3C63A1B7B /* NSData+DTCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E3DD3C14D91284AB605925 /* NSData+DTCrypto.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 6964DAF676F34653C54A7F08 /* DTColor+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DF67C46304BCAB7A718C6A0 /* DTColor+Compatibility.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 6AA0C5FD4313213097AC8517 /* UIWebView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ED322C23FFB410FA8D01C3A /* UIWebView+AFNetworking.m */; }; + 6BB3B14636CBE7F37DA0FDD8 /* DTCoreGraphicsUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C3357CEED63E5037D30890 /* DTCoreGraphicsUtils.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 6BE18395789E145663DD8AAC /* MTLJSONAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EA8F67176A1B0327A19827A /* MTLJSONAdapter.h */; }; + 6CF3735F2787AAB0F30F79F9 /* UIImageView+HighlightedWebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 60D2BD28B2F0D25CE81FC512 /* UIImageView+HighlightedWebCache.h */; }; + 6D3120FC189517685446E4DB /* DTFolderMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A23B07692829993061C93B8 /* DTFolderMonitor.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 6DB67A3F39F51BCB5722BD1F /* AFHTTPRequestOperationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BFAF6715DE24498F152ADAAC /* AFHTTPRequestOperationManager.h */; }; + 6E41C1B1DCFD1734ED45E655 /* DTHTMLWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DDC4EE3C0AEEB5D12C523DC /* DTHTMLWriter.h */; }; + 6FC98B5B70DB1AFD81BE5ECB /* DTAttributedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 9021B480CC3318D413444498 /* DTAttributedTextView.h */; }; + 71DE93520654BF2FADC152CD /* DTCoreTextConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 78085540FD8FB9B1E5B4B884 /* DTCoreTextConstants.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 729CDAD5F8AC0D4024B5A469 /* NSString+DTPaths.m in Sources */ = {isa = PBXBuildFile; fileRef = 429293BD4B1AA8A0171C4214 /* NSString+DTPaths.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 7352114F0969A1090E887BA2 /* AFNetworkActivityIndicatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 26880A2C45EBEF24D2C3AA1D /* AFNetworkActivityIndicatorManager.h */; }; + 743213F71753153707379338 /* DTAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 29C23375359F548C0F64C164 /* DTAccessibilityElement.h */; }; + 7440F7C02AF2CF5670806875 /* DTDictationPlaceholderTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DABDE8AEC54A0F7DAE9ACED /* DTDictationPlaceholderTextAttachment.h */; }; + 749BE2234A38066B3F34009F /* DTObjectTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 229A3A64BEB4B6DD9D5AB3CD /* DTObjectTextAttachment.h */; }; + 750A9EF27A2AAB925679C558 /* Pods-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 02856BB35E43267D13DC3C28 /* Pods-dummy.m */; }; + 756B0CD301D58061B2B0DD04 /* DTIframeTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 862F1FEB67B7CEFE9074BE6E /* DTIframeTextAttachment.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 771DE92C7116683778C06935 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D9DA8599F9A65C67763F7C8 /* QuartzCore.framework */; }; + 776A4C0A07984C3E3EC1455B /* NSMutableAttributedString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = B249319A47C18D3B6B91AC78 /* NSMutableAttributedString+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 7830A690D9A0ACB28817A52F /* DTPieProgressIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 600F177FEBAD10AE1CB576B9 /* DTPieProgressIndicator.h */; }; + 783986CDC5980EBFFF2E3F07 /* SDWebImageCompat.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A4E3976555492FF46129D87 /* SDWebImageCompat.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 78AD68EDB757EE08807BFE10 /* DTCoreTextLayoutFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = A6384E42A1880883FE59B266 /* DTCoreTextLayoutFrame.h */; }; + 7A26E1BCAE7003001459B520 /* STableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E787096E33E2E0215B8203E /* STableViewController.h */; }; + 7B660EAB3E3D508DEDE4938B /* NSAttributedString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B33B1CFDC6A466868B6EE09 /* NSAttributedString+HTML.h */; }; + 7C2BDE6C40775A8227268816 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = BC7EB9B8C3DBE8D6402CCCF4 /* SDWebImageDecoder.h */; }; + 7D596AC35EA03B3FB79E63C8 /* AFURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = C5DE72E147F2C83C77BFD809 /* AFURLResponseSerialization.h */; }; + 814E1D72F788E010C724643E /* EGOCache.h in Headers */ = {isa = PBXBuildFile; fileRef = F2EBA251105B96547A07DCEA /* EGOCache.h */; }; + 8227B1DEDFDCF4F404706997 /* DTCoreTextFontCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = EA0B8816C7DA1DA71122BE90 /* DTCoreTextFontCollection.h */; }; + 829E3CCF98B84434C7F2BEAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 84C0C37012DC64A199A376C9 /* UIButton+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = BE8902F9D667F3CC87EB82C4 /* UIButton+WebCache.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 84ED666A37AFB365A95D324D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 85B122A0B790FBB766B5D6B5 /* NSString+DTURLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C19578BFB4FFB0FB793EAC /* NSString+DTURLEncoding.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 8615AFA7EACA0C898BAFB936 /* Godzippa.h in Headers */ = {isa = PBXBuildFile; fileRef = AC40CBC3BCEB21F15A6DB3A7 /* Godzippa.h */; }; + 8676051929B4CB63D317DDEA /* DTColorFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E14665E1CB4D66432BD3126 /* DTColorFunctions.h */; }; + 8677D9D80E290ECC0B5F17CC /* NSError+MTLModelException.m in Sources */ = {isa = PBXBuildFile; fileRef = DA95D3A59A8B4392A30955E3 /* NSError+MTLModelException.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 869651B749404A2D1A6D390A /* DTHTMLParserTextNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A91454224704755E8B78C0F /* DTHTMLParserTextNode.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 86F76E1C9198F1C6747CAEA0 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CE3DB4CDED08038EE99FAD6 /* ImageIO.framework */; }; + 873EF7F0BC972FB913ECC928 /* UIApplication+DTNetworkActivity.h in Headers */ = {isa = PBXBuildFile; fileRef = E7837043A9EB881C27D148A7 /* UIApplication+DTNetworkActivity.h */; }; + 87FC8EA1F74F2A658826586C /* UIRefreshControl+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 70029B1D27A0D1E81984D23A /* UIRefreshControl+AFNetworking.m */; }; + 8977B3AE81A03812AC22B958 /* NSMutableString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 83D2D6D8C8AFB62D63AAB400 /* NSMutableString+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 89B3A1365C6FFA518DC2EE22 /* NSString+CSS.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C11C7730CECE62713E638B8 /* NSString+CSS.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 89E8E51F68B762A5E498E955 /* DTCoreTextLayouter.h in Headers */ = {isa = PBXBuildFile; fileRef = 17023854963F0DB77E8B6E1D /* DTCoreTextLayouter.h */; }; + 8ACA5FF363531E8533E14B3B /* DTBase64Coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 13478FB8906355D0CFF5F748 /* DTBase64Coding.h */; }; + 8B3331673A14B3E3F18D78BE /* MTLReflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A1D41BE1C3FD8398AF8BAD1 /* MTLReflection.h */; }; + 8CB422996C4C2C2E6F215DFA /* DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 8FB81A3F3CC5C8EEE0D0A053 /* DTCoreText.h */; }; + 8EC1828E3A868C0F540ABB7B /* UIFont+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 031D447444B32B34E8E8ABA3 /* UIFont+DTCoreText.h */; }; + 8F3EF58DA81526ABDB2B7879 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + 8FF49F70E6E95B8563020000 /* SBJson4StreamParser.h in Headers */ = {isa = PBXBuildFile; fileRef = ED46C1D27547182DEDF1398C /* SBJson4StreamParser.h */; }; + 902EE13D641DB030842ED966 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 463D2C1499B05E24A3C9466E /* SDWebImageDownloader.h */; }; + 9072D69F7FE72C168B9AC69D /* DTCoreTextFontCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = EA66A1341B5A8EB596C03A0C /* DTCoreTextFontCollection.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 90B220E0FBD8BFC9AFA1F6C0 /* SBJson4Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E6483BA82724E795E88521B /* SBJson4Parser.h */; }; + 914119B2F1C42A9394654CD1 /* UIProgressView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = FB630DF11D1945A734238961 /* UIProgressView+AFNetworking.h */; }; + 91A4B793968B4E69F46A53DB /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = DC969E8DEEA062BF9FCB0635 /* metamacros.h */; }; + 921424517EF3A81C2FAECFAD /* NSCharacterSet+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 3586666B2105EBE27406E900 /* NSCharacterSet+HTML.h */; }; + 92C46811EF0CAC7736025CA7 /* NSAttributedString+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = C138EF04D1765832AB4C5AEC /* NSAttributedString+DTCoreText.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 937BC16FD45E5333DDB02A99 /* AFHTTPRequestOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 59479D57BEE566952C5882C7 /* AFHTTPRequestOperation.h */; }; + 938052F386CA31E805C89C2C /* DTHTMLParserNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E65128E94D4FDD10661ED58 /* DTHTMLParserNode.h */; }; + 93F806636B85BA09EC55E61D /* DTActivityTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = AD7766A1EDACC24F6BBDFD7C /* DTActivityTitleView.h */; }; + 945D831328EEE14295EDA3CC /* DTBlockFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = C1ABA98F064631211B3CC4A1 /* DTBlockFunctions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 9585F212B94D2A484959443D /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C650430B30114D4A8D3A459 /* SystemConfiguration.framework */; }; + 96DAFB592CA5953A2538932C /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = B4F7C13BA2FCD171CDD56CDC /* SDWebImageDownloaderOperation.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 97796C608B74E393FDC9632D /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 56E447DB384683DAEBFFA224 /* AFNetworkReachabilityManager.m */; }; + 98142A5EB29A45F1B7A42E5A /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CE3DB4CDED08038EE99FAD6 /* ImageIO.framework */; }; + 98B992D2F710F64D9811DDF6 /* DTCoreTextConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 21F213E272C360C6D6AA4EA1 /* DTCoreTextConstants.h */; }; + 9914CB02EC65EDDF8B3BA4A5 /* DTCoreTextLayoutFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E6DD371D30AD5BE472DB12F /* DTCoreTextLayoutFrame.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 99FB40845A706C3EFC2A87D8 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F70AC862BA746865DA102F47 /* CoreGraphics.framework */; }; + 9B67C67D8BA21DF1AD5DD48D /* NSArray+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 858693B43C8CC8542AEA5B41 /* NSArray+DTError.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 9B882F43716A56DFADA5566F /* UIAlertView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = C2B5049DC7669D3A6F803BDE /* UIAlertView+AFNetworking.h */; }; + 9C81821B6B5AEDC7D35C05FB /* DTCoreTextFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F92EAE13C59713EAC676DF /* DTCoreTextFunctions.h */; }; + 9D360FA230D9012E699CBA92 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CE3DB4CDED08038EE99FAD6 /* ImageIO.framework */; }; + 9DC59B5AB2F4BEED7AABFDE1 /* NSString+DTFormatNumbers.h in Headers */ = {isa = PBXBuildFile; fileRef = D15510793C867C9BDD98F461 /* NSString+DTFormatNumbers.h */; }; + 9E948B93DD7F42453584A543 /* DTListItemHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = B03C715F5A8AEA36DC55688C /* DTListItemHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A08CB28DABFD69124C599A34 /* DTColorFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AAD4CF32D8A346312E37D3A /* DTColorFunctions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A0BCBD0CC7ADDACED9F31FF7 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EE6A2C5E812DB3AA45E0BB /* UIImageView+AFNetworking.m */; }; + A0DE573AD455495418B70689 /* DTCoreTextGlyphRun.h in Headers */ = {isa = PBXBuildFile; fileRef = DBEF3AD8AF418F5D0DB9FA36 /* DTCoreTextGlyphRun.h */; }; + A1BF468F622C373AF92FD1E2 /* Pods-AFNetworking-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 02F6C05E609F4CB08029EBD2 /* Pods-AFNetworking-dummy.m */; }; + A231ABD93492364A84B5A471 /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = FAA41802E83005EB01C21826 /* MBProgressHUD.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A3C11D206C9C8E8544A93AE6 /* DTBreakHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D930E71CD2A5F2B17ACCF87C /* DTBreakHTMLElement.h */; }; + A3E4281B7B817B7973E8BF3A /* UIImageView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = A1CF066DE80C9B874E3E7DF4 /* UIImageView+AFNetworking.h */; }; + A402940FF1251FB6C56260C2 /* DTCSSStylesheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 58632D3FA9629CB1A72776FD /* DTCSSStylesheet.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A47FEBDC058AD86C254F94CB /* DTBase64Coding.m in Sources */ = {isa = PBXBuildFile; fileRef = AF6401F09777B31791FA7DCD /* DTBase64Coding.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A4C1E805180612ECBBE6B7B5 /* Pods-MBProgressHUD-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 210C12C212EB913E2531E21F /* Pods-MBProgressHUD-dummy.m */; }; + A5E557B33AA32F2059FB4E04 /* MBProgressHUD.h in Headers */ = {isa = PBXBuildFile; fileRef = E00A730FB3A0D7136C29BAC8 /* MBProgressHUD.h */; }; + A618F1AC97A49B6EFA20737A /* DTAttributedTextCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 198A05236D0FC24D7567E389 /* DTAttributedTextCell.h */; }; + A798D9B3034547C09E83F358 /* UIActivityIndicatorView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 91C83B61CE2EF0D23B05EFFE /* UIActivityIndicatorView+AFNetworking.h */; }; + A7BCABC62211F84CAE4BD062 /* NSDictionary+MTLManipulationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6467D845AE0A362D6D98BAAB /* NSDictionary+MTLManipulationAdditions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A90AF96FDCC3A5071BAC4144 /* DTActivityTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = AB4E35D99C28AD0A2FF788B0 /* DTActivityTitleView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A90FF25FDC1D30E19CA81C01 /* DTWebVideoView.m in Sources */ = {isa = PBXBuildFile; fileRef = E9A33E2EDAA2355F14CBDA34 /* DTWebVideoView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A9AA93D8F85244B1E17AF3DE /* DTHTMLAttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = D35A7D47AFBD0118F9B61044 /* DTHTMLAttributedStringBuilder.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + A9EE3CC09E2DB29E60D9FC33 /* AFURLConnectionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B8793FEA52392FA7191B99E /* AFURLConnectionOperation.h */; }; + AABA793C21EFA0E7816196A1 /* DTCustomColoredAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3C9B76C6CB2B8E8AD78A5D /* DTCustomColoredAccessory.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + AAEBCF5D301CCB8790619B66 /* UIImage+MultiFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DE1F401D6E30BE57ED5553E /* UIImage+MultiFormat.h */; }; + AC3456F15937764410B8CBC7 /* SecureUDID.h in Headers */ = {isa = PBXBuildFile; fileRef = 4047E9A4D159D0380E1B9036 /* SecureUDID.h */; }; + ACB7546D8938B32152D42820 /* NSDictionary+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = E8D0520AF1A770AB710D7265 /* NSDictionary+DTError.h */; }; + AD8E2541BFDAEE26D2751460 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F70AC862BA746865DA102F47 /* CoreGraphics.framework */; }; + AE9AFBA6F8C69255ABB8B5FF /* DTAnchorHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 21E3E130EB30C467D9C459DE /* DTAnchorHTMLElement.h */; }; + AEC9D1786E7FD34490B50328 /* DTAttributedTextContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = F3F21EEACD8507ED0252B482 /* DTAttributedTextContentView.h */; }; + AEF03B4A5B3944F2EF87C250 /* NSAttributedString+DTCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AB2BD7A7440128E0C379878 /* NSAttributedString+DTCoreText.h */; }; + AFB8BFB855C0C63967A251D3 /* MTLValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = F088E1D7101129C6B135F01F /* MTLValueTransformer.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B0940F71B5AC2A8EA130FCA3 /* DTWebVideoView.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ADE193BEB78F37154DF3071 /* DTWebVideoView.h */; }; + B15E483D877F94E784894E8E /* DTSmartPagingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = FA8B5BEEBC19FB385FCCD58B /* DTSmartPagingScrollView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B1B50550FF732C90C300955C /* MTLJSONAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 773EE21FF809BE6B99B9AC7A /* MTLJSONAdapter.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B1DC7B89D864F6B02EEDA74B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + B1E28880C0B75E49F11FCEA9 /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A7A256B6D33BA5BB7B04F363 /* AFURLSessionManager.m */; }; + B24342887751389B44819681 /* DTAnchorHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = BD038E1FE6A969A543DAD866 /* DTAnchorHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B2933834DD6328D056AD30C1 /* DTTextBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C699FE7FCD126A43DBC1131 /* DTTextBlock.h */; }; + B2E8C082F4A6487860419FD3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D9DA8599F9A65C67763F7C8 /* QuartzCore.framework */; }; + B30FFA4960DC18DB298B921B /* DTObjectTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FD09FA7EC7172A286E2375 /* DTObjectTextAttachment.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B3DB28AD0BACD96DCE448E8B /* DTHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 623DD018EBC66CF653AB4EB8 /* DTHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B48CC4E852D8EBC259F5E934 /* NSFileWrapper+DTCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = 7512364D89353CEF9E99172C /* NSFileWrapper+DTCopying.h */; }; + B5AC89CB2D9E01445724E2F8 /* SBJson4Writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 306A576CB103E0DD61B011B4 /* SBJson4Writer.h */; }; + B6E807D7D2B9FA5F38971684 /* DTAttributedTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3139D5BE25741C00C26B11D2 /* DTAttributedTextCell.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + B7B9AFCDB7D18A35AF42F963 /* DTHorizontalRuleHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 1321C721D6711CABE43A8E72 /* DTHorizontalRuleHTMLElement.h */; }; + B7F0DA5A42C46845F1929D92 /* DTLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 2566190B16B3CD1AF9910616 /* DTLog.h */; }; + B820EED8DE06567A092F4993 /* DTVideoTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B5CBFFC0BEA9B64BB37BE4D /* DTVideoTextAttachment.h */; }; + B8A779CD128129DFDF61A0CB /* UIButton+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B38A1D04122792A32F17DB1 /* UIButton+AFNetworking.h */; }; + B947160D514F27E2A92651DD /* DTStylesheetHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 2C0EA53A4DA9ED8E20C9A1FE /* DTStylesheetHTMLElement.h */; }; + BA6072EDB611D4165FEBAF63 /* NSDictionary+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = 977A96477E21AFE119F2C818 /* NSDictionary+DTCoreText.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + BAAE263C13C07C2C764DEBEA /* DTTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = 982D461DCD6CCFF9DE4D96AE /* DTTextAttachment.h */; }; + BC30233499EEA41D1B79C352 /* AFURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 72E37C355B02AC874A58D9DC /* AFURLRequestSerialization.h */; }; + BC415F6750A9B46CA6F31025 /* DTBreakHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = C634EA81A406E4A8BFEE29DA /* DTBreakHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + BCC1E43F38919F8458618D1A /* DTLinkButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 977BBA287FC7987841DABAD4 /* DTLinkButton.h */; }; + BCFEF4BD15F36298C36D1C6D /* DTCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 31EC48DC08E838E541102AD5 /* DTCompatibility.h */; }; + BFD497A40209BCD4B74DC3F3 /* NSDictionary+DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = F2053B64B2DB2943F0B5188E /* NSDictionary+DTError.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C13995FB16410EC19B591697 /* NSData+Godzippa.h in Headers */ = {isa = PBXBuildFile; fileRef = 5551BF11DCD892AAD12F8198 /* NSData+Godzippa.h */; }; + C1E4CD9FF999577AC919EC62 /* SBJson4Writer.m in Sources */ = {isa = PBXBuildFile; fileRef = E8F7E1C043613475AE028FB4 /* SBJson4Writer.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C1F85729F5760C1810839FD1 /* MTLModel+NSCoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D24DCCEF60B5A02011747B8 /* MTLModel+NSCoding.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C24A4C0C3BCF7D268BF709BD /* DTTextBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 9678D7E8E9FB8406B0A8FBF9 /* DTTextBlock.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C2E336F8768AAF053C034E9E /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = D75D8DD36B2DF3A94A8D8BDD /* SDWebImageCompat.h */; }; + C2FB69FAC5583253FD49B122 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F70AC862BA746865DA102F47 /* CoreGraphics.framework */; }; + C34763940F077F6025B86F02 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + C34FA0F6289DE254F12B1A6C /* NSArray+DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = F54F6D9A0488B12179371EF5 /* NSArray+DTError.h */; }; + C4066C9CB0204E7C5607F1E3 /* NSScanner+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 718C8CE67FFF7CAE824B278D /* NSScanner+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C41C9D347427FED569814048 /* NSCharacterSet+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E746C3CFC80A6AD3E9C140E /* NSCharacterSet+HTML.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C4D544EEA622A81517444651 /* SDWebImageOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C095AE7D2B4E1C122FE2C0C /* SDWebImageOperation.h */; }; + C59757460B5B2A66111DB1A3 /* NSCoder+DTCompatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = BD775CFA71EB688731C92C0A /* NSCoder+DTCompatibility.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C598401584ECF2DBA9BDBB89 /* DTAttributedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D323E71416F20743DEBB7A7 /* DTAttributedTextView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C618D33D98ADF42A90174FF5 /* MTLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = FD901850C8BB4946D4508DC2 /* MTLModel.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C66A11537EB5FB169072545E /* DTImageTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD2416791FED90FF21E02F8 /* DTImageTextAttachment.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C6B0B8DAFD83D2C685C49397 /* DTTiledLayerWithoutFade.h in Headers */ = {isa = PBXBuildFile; fileRef = E7375484F885B52DF346730C /* DTTiledLayerWithoutFade.h */; }; + C7AFD84573BC801CDF859A41 /* SBJson4StreamWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = A4189FA23F95F690EA94D56C /* SBJson4StreamWriter.h */; }; + C7D3E28327D690FAF0C1D8CC /* STableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 711CF44F1F86192C72867546 /* STableViewController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + C86A49C44B96DB1A46E8A3AA /* UIAlertView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F60AFB8DB89C64EB54A4343 /* UIAlertView+AFNetworking.m */; }; + C8B318E55390767643C2EF6A /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 70D0115758D95D3FC9BEA163 /* AFHTTPRequestOperation.m */; }; + C8F315C06587A14B844B27EE /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C650430B30114D4A8D3A459 /* SystemConfiguration.framework */; }; + C8FA434179CEEDC1152F1C07 /* SBJson4StreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = F371ABD4531A53010DFF1B0A /* SBJson4StreamWriterState.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + C9485CA9D0A2F491FE3C75A0 /* UIImage+MultiFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 98E8971B9A319D428BC49638 /* UIImage+MultiFormat.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + CB19B20ADC234425401FD225 /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EAA2847D3C5E00C6F7FB80B /* AFNetworkActivityIndicatorManager.m */; }; + CB90B296E1832BCB538E84CD /* UIWebView+DTFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A4F7B232DD157C6696C84091 /* UIWebView+DTFoundation.h */; }; + CBA4D8AC17AE1B1B33AC6698 /* DTAttributedLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = 765380382A09CD38070F811C /* DTAttributedLabel.h */; }; + CBF38583A945F44960FED8C8 /* Mantle.h in Headers */ = {isa = PBXBuildFile; fileRef = 80A2C8039197A10FCF713E70 /* Mantle.h */; }; + CC47185CD83952BBD8AD4F29 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA99AFD1949A958427E26882 /* Foundation.framework */; }; + CCEE6FC7D3410A79A752031C /* SBJson4StreamParserState.h in Headers */ = {isa = PBXBuildFile; fileRef = 99BDEC117EA48AB5F7091E99 /* SBJson4StreamParserState.h */; }; + CCF9CB47C58FDFE6B85F788A /* DTCoreTextParagraphStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 39928E568AFCA854CA213D64 /* DTCoreTextParagraphStyle.h */; }; + CD42A2421AD4E769BD5D5A6B /* NSURL+DTComparing.h in Headers */ = {isa = PBXBuildFile; fileRef = EC308D3EB0A448BF90595176 /* NSURL+DTComparing.h */; }; + CDCFE565F410F93E1B36A8F6 /* DTVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = BE965E64C9C461F7C6E8DBDE /* DTVersion.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + CE7EE0058A71A4D222CAA4E9 /* DTWeakSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = F3C2266FC0CEEEED5742B433 /* DTWeakSupport.h */; }; + CEFB7FCC5635A278E307FE4F /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9135155F192247F273C4BFF1 /* SDImageCache.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + CFA7AE793E4E4AF99A9397BA /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AB780C1815C17A0D187A5D9 /* AFSecurityPolicy.m */; }; + D121C44563D59F112EE63D69 /* DTCoreTextFontDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = DF6A723334932A205E74EF3A /* DTCoreTextFontDescriptor.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D1F8E53B1F4E073935314C10 /* SBJson4Parser.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F22809819A01C39B5C9566 /* SBJson4Parser.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D28BE1F81FC1BB8E03643C33 /* MTLModel+NSCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 94D42913953EF40E145B5E1E /* MTLModel+NSCoding.h */; }; + D3C3EFC6280843D121CE4715 /* NSAttributedStringRunDelegates.m in Sources */ = {isa = PBXBuildFile; fileRef = 02DC7955EDC01BBD2A005D13 /* NSAttributedStringRunDelegates.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D49E76E28CA995A0C500A2EA /* NSString+DTUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D06E48BDE9CA5867AEA039A /* NSString+DTUtilities.h */; }; + D5B0BB5D8753C8C35DCE88D1 /* UIView+DTFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = AD5E1EDE9FFA20FF03F5BCA6 /* UIView+DTFoundation.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D6112D0DA51751CCACA8BEAF /* NSObject+MTLComparisonAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B1012FEDA5057A6C62506D76 /* NSObject+MTLComparisonAdditions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D64996D2F94443EA203C14FF /* NSAttributedString+SmallCaps.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F2EFD81D171B1FCF5D860C8 /* NSAttributedString+SmallCaps.h */; }; + D6E2BC2513B64A350CB868E7 /* SBJson4StreamWriterState.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A059B8990583D788A1583F5 /* SBJson4StreamWriterState.h */; }; + D81CCA3819928CECB8631372 /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B8EEF53333924DE92410F18 /* UIImage+GIF.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D84657DAD8F43A566863C534 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E62EA74C8AE7E7D9633E921 /* UIButton+WebCache.h */; }; + D8A8525D5C07AA0BE9255726 /* NSURL+DTUnshorten.h in Headers */ = {isa = PBXBuildFile; fileRef = E49765B4A8AB643850A47E16 /* NSURL+DTUnshorten.h */; }; + D95B20BCEF61A201C12BE3E4 /* UIRefreshControl+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FCA4151E6647F1CFEFCC278 /* UIRefreshControl+AFNetworking.h */; }; + D99EF764ECEFCB2A341D0B55 /* NSURL+DTAppLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = BE42064DDDEC23B7164ACBAA /* NSURL+DTAppLinks.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + D9E7AD43EFC13F34505C7E6E /* NSAttributedString+DTDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = F6B15107CDB30F016A3287B7 /* NSAttributedString+DTDebug.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + DAC028C6327B39D76993C57C /* DTLazyImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CC40769EDB0F6EE0D5ED72 /* DTLazyImageView.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + DADB597E48335C0E9DDBD93E /* Pods-DTCoreText-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EFC70E70FA1FB546F52DC58E /* Pods-DTCoreText-dummy.m */; }; + DB4BFC9A2071B0B162BBEE6D /* DTHTMLParserTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 98B78BA7BD331BD145EF9497 /* DTHTMLParserTextNode.h */; }; + DCB6A670A920D76D13AAED32 /* SDWebImagePrefetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E104C4243AE675E793B5BAB /* SDWebImagePrefetcher.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + DCE8615C91B2D5C1173FD4D1 /* DTFoundationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = CC13076D657AA25084358E58 /* DTFoundationConstants.h */; }; + DD8DF24D1E04B745E9B4416A /* SDWebImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F037412F1FDD7AE4A9587 /* SDWebImageManager.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + DF230E5C2429185B6739CF5A /* DTAccessibilityViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE2E34F5BA4FA31632E183E /* DTAccessibilityViewProxy.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + DF5D0D70F433DAD7BCA70FC3 /* DTCoreTextLayoutFrame+Cursor.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CBCF8EDBCBA565559D7FD04 /* DTCoreTextLayoutFrame+Cursor.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + E139CE6CC1EE4617174263DD /* DTCustomColoredAccessory.h in Headers */ = {isa = PBXBuildFile; fileRef = 085E58814B278EF843F3DA88 /* DTCustomColoredAccessory.h */; }; + E1F604BF295C4183599F1B86 /* AFHTTPRequestOperationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EFB97EEC7222C01E64AA0AFA /* AFHTTPRequestOperationManager.m */; }; + E28716DF7951966EE3057A30 /* DTCSSListStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 80B0262BBEA47B83C25F3008 /* DTCSSListStyle.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + E2A651C267AF90FC069AF713 /* DTImage+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 2688EDDC3B016EC4E28E8C38 /* DTImage+HTML.h */; }; + E50DD5C9A1D62DAF34719CB9 /* DTAnimatedGIF.h in Headers */ = {isa = PBXBuildFile; fileRef = 0100264285411761FBAEF5AB /* DTAnimatedGIF.h */; }; + E57FD66AC32DE0F8C7FBB973 /* DTSmartPagingScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = D9177A65BD60036FE28650C3 /* DTSmartPagingScrollView.h */; }; + E5818A5906AB34360097C00F /* SecureUDID.m in Sources */ = {isa = PBXBuildFile; fileRef = AF8355CDB07E5B6CF7F8EB08 /* SecureUDID.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + E58BE4878AC0529F7B16B51F /* DTExtendedFileAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = FC9A7467DFDD854EF3C94812 /* DTExtendedFileAttributes.h */; }; + E68D051B6DE12611D7D4B224 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FDA44BD8987326E6506A677A /* NSValueTransformer+MTLPredefinedTransformerAdditions.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + E6E67253E3A018A5258D5449 /* DTListItemHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E28C861356A22F849E1F8A2E /* DTListItemHTMLElement.h */; }; + E711807D347FF56B48AAB48A /* Pods-SecureUDID-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 03941FD41B7FBA5E343D1349 /* Pods-SecureUDID-dummy.m */; }; + E77BB0E36975923F3B64D625 /* DTCoreTextLayoutFrame+Cursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 511326CEB4AAD81D048FC1CC /* DTCoreTextLayoutFrame+Cursor.h */; }; + E81F3DA70F2DE25048968CD3 /* NSObject+MTLComparisonAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = F682CD207E26545FC21C23E4 /* NSObject+MTLComparisonAdditions.h */; }; + EA403E002B3297A7BF4C7EA1 /* Pods-SBJson-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B98FCB2622BDB2878F1AD1F6 /* Pods-SBJson-dummy.m */; }; + EAE11E3D0A11CF929F2D8028 /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D407039B1428945391E15138 /* AFURLResponseSerialization.m */; }; + EC5A18F75163F5870C53FBD4 /* Pods-STableViewController-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B6CBABA8BF5C20E72993CAB8 /* Pods-STableViewController-dummy.m */; }; + EEFA03C86F230EE8BBCE1E02 /* DTAsyncFileDeleter.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D9D642C6BD33E1FF820543 /* DTAsyncFileDeleter.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + EF7B5F9C0E24CDA067CEADD5 /* MTLManagedObjectAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D90CEF4D748B98F5C4F9223 /* MTLManagedObjectAdapter.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F031A055A9D138B59D13EB53 /* NSScanner+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E8C90E8027986DF576B89C4 /* NSScanner+HTML.h */; }; + F05C84F2C7A451756D3E2C3D /* AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B664423A7FD62262F9F2B35 /* AFNetworking.h */; }; + F087F348D1C60D4FABFC4F97 /* NSMutableArray+DTMoving.h in Headers */ = {isa = PBXBuildFile; fileRef = FA072650B95846F21456B71D /* NSMutableArray+DTMoving.h */; }; + F205AFE36DAF280D295AB44A /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = AA3E1D39F56369F6EDA19850 /* SDImageCache.h */; }; + F372C5BEAE9180262BE0EE19 /* NSString+DTUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E9CD7B761C440D9F616B334 /* NSString+DTUtilities.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F4852FA60E2EC97814726C55 /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 40835E80F4E2B55E419BA88E /* SDWebImageManager.h */; }; + F4A4C10FA290EFC508E68005 /* NSString+Paragraphs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0000202764C2F63046B6D1CF /* NSString+Paragraphs.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F6332302421A5A6DFCAA267F /* DTDictationPlaceholderTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FF95BB286D9C557B742A904 /* DTDictationPlaceholderTextAttachment.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F722B92F1822D49C0F28FA9E /* Pods-Mantle-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A8619D6089A77D57DF8CED95 /* Pods-Mantle-dummy.m */; }; + F76CB54C7DCB204DE88A6A1D /* DTHTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = E198021BFFF9A2FE7C037F22 /* DTHTMLParser.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F86C8BDF958E95140B09C440 /* DTCoreTextMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 3141E70A8914DBA8EF4C8BF4 /* DTCoreTextMacros.h */; }; + F88FE806D71817CC2994F060 /* DTVideoTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 330A7FB9F02C86B2A3D1FB27 /* DTVideoTextAttachment.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F8F1996A698C08E96BFADDAA /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDBACD80680B57A390CCA5F0 /* Security.framework */; }; + FA049A0D048A88922969E495 /* MTLValueTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 524815F3F479FF6AA50B15DD /* MTLValueTransformer.h */; }; + FA45F4D479B94C4B1B0D599F /* DTTextHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = C58EAF174D65FF96676BC746 /* DTTextHTMLElement.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + FC4E8B8BB82D31ACE287990B /* UIView+WebCacheOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0263AD274F91008318446903 /* UIView+WebCacheOperation.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + FC550C9621953C2570FAFC11 /* DTTextHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = CED305D27339CE40E6A4451C /* DTTextHTMLElement.h */; }; + FD0AD22134E622A6ADB06198 /* DTFoundationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 531F610A6F31FEA918264187 /* DTFoundationConstants.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + FD77C063EEA3207EA01EB550 /* NSError+MTLModelException.h in Headers */ = {isa = PBXBuildFile; fileRef = 675C7BA63827ACB61FF3B9BB /* NSError+MTLModelException.h */; }; + FD8842DB63CE74002BC66191 /* DTTextAttachmentHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 08480DC911580F657E539083 /* DTTextAttachmentHTMLElement.h */; }; + FEEDC23525EBE5BA766FF715 /* NSString+DTPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = 37ED3CAD19ADFD28C18D3D5A /* NSString+DTPaths.h */; }; + FEF2324967A284F786FA8862 /* NSData+ImageContentType.m in Sources */ = {isa = PBXBuildFile; fileRef = B4440BBB972015707D689FF1 /* NSData+ImageContentType.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + FF1C5E0EA5C2147FAF01EC79 /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = FC77E089388C8856323DA3BB /* AFURLRequestSerialization.m */; }; + FF5D332BB1254B7808BCB323 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 07197356167DAEDE87AD03C3 /* SDWebImagePrefetcher.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0AF119C304C7E6B97C8EAEDE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9F5764BE6373D3C604DC32DB; + remoteInfo = "Pods-MBProgressHUD"; + }; + 1AE2B067A1CA3D72AFABE6C0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 687D88D56283FDAD6B3057E4; + remoteInfo = "Pods-SecureUDID"; + }; + 1E1A2277E8E1325DC879D4EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3381CB8EE23A95628CC6C71F; + remoteInfo = "Pods-Mantle"; + }; + 1FB9CA3998814F21ECB25FA7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 081D18D023E185DE5065872B; + remoteInfo = "Pods-DTFoundation"; + }; + 28C31C0BA09D5AE8533E10EF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7863761EC80891B08770A0B7; + remoteInfo = "Pods-EGOCache"; + }; + 2C73F227D15ECF4C66684945 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 081D18D023E185DE5065872B; + remoteInfo = "Pods-DTFoundation"; + }; + 3790C3B2100E0426DD94ADD3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1E660322F08C69E13AF91D0F; + remoteInfo = "Pods-SDWebImage"; + }; + 4785B5502507F907A4D71B72 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 55917C107FF891B225E9EF02; + remoteInfo = "Pods-Godzippa"; + }; + 58BFE040792B0926A4925ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7EACA4AE3AF64CCDA69E50CB; + remoteInfo = "Pods-STableViewController"; + }; + 6D57ED432EE98E625C3B5900 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E0126A5C2A3D1F035604EE6E; + remoteInfo = "Pods-Reachability"; + }; + 72EF303CC913D1118C6AE72F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2231DE2C5936A2D8C4DE7FBF; + remoteInfo = "Pods-DTCoreText"; + }; + 982F2A730A4CD84826FDF3A2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A592E8303475C5A51F058D5D; + remoteInfo = "Pods-SBJson"; + }; + A626E2F1CFCE96457A3245AE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F4FC1885C0BD8C197F91B8F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7D2693D881B07E422984E1FA; + remoteInfo = "Pods-AFNetworking"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0000202764C2F63046B6D1CF /* NSString+Paragraphs.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+Paragraphs.m"; path = "Core/Source/NSString+Paragraphs.m"; sourceTree = ""; }; + 00B21AE8AAA30A1308C14335 /* NSAttributedStringRunDelegates.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = NSAttributedStringRunDelegates.h; path = Core/Source/NSAttributedStringRunDelegates.h; sourceTree = ""; }; + 0100264285411761FBAEF5AB /* DTAnimatedGIF.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAnimatedGIF.h; path = Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.h; sourceTree = ""; }; + 0263AD274F91008318446903 /* UIView+WebCacheOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+WebCacheOperation.m"; path = "SDWebImage/UIView+WebCacheOperation.m"; sourceTree = ""; }; + 02856BB35E43267D13DC3C28 /* Pods-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-dummy.m"; sourceTree = ""; }; + 02DC7955EDC01BBD2A005D13 /* NSAttributedStringRunDelegates.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = NSAttributedStringRunDelegates.m; path = Core/Source/NSAttributedStringRunDelegates.m; sourceTree = ""; }; + 02F6C05E609F4CB08029EBD2 /* Pods-AFNetworking-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-AFNetworking-dummy.m"; sourceTree = ""; }; + 031D447444B32B34E8E8ABA3 /* UIFont+DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIFont+DTCoreText.h"; path = "Core/Source/UIFont+DTCoreText.h"; sourceTree = ""; }; + 034681345CE339CC0D7F4BC7 /* DTHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLElement.h; path = Core/Source/DTHTMLElement.h; sourceTree = ""; }; + 03941FD41B7FBA5E343D1349 /* Pods-SecureUDID-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SecureUDID-dummy.m"; sourceTree = ""; }; + 03CDA45689F3CBAC4D698D25 /* DTImageTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTImageTextAttachment.h; path = Core/Source/DTImageTextAttachment.h; sourceTree = ""; }; + 07197356167DAEDE87AD03C3 /* SDWebImagePrefetcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImagePrefetcher.h; path = SDWebImage/SDWebImagePrefetcher.h; sourceTree = ""; }; + 07FA956C464AF62249F42893 /* Pods-Godzippa.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Godzippa.xcconfig"; sourceTree = ""; }; + 08480DC911580F657E539083 /* DTTextAttachmentHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextAttachmentHTMLElement.h; path = Core/Source/DTTextAttachmentHTMLElement.h; sourceTree = ""; }; + 085E58814B278EF843F3DA88 /* DTCustomColoredAccessory.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCustomColoredAccessory.h; path = Core/Source/iOS/DTCustomColoredAccessory.h; sourceTree = ""; }; + 08648726B4DF14197FFE2669 /* MTLReflection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MTLReflection.m; path = Mantle/MTLReflection.m; sourceTree = ""; }; + 08BFFA49C32FC84AC3484233 /* SBJson4StreamParserState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4StreamParserState.m; path = src/main/objc/SBJson4StreamParserState.m; sourceTree = ""; }; + 09D74DAE23C038E1A14CE924 /* UIImage+DTFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+DTFoundation.m"; path = "Core/Source/iOS/UIImage+DTFoundation.m"; sourceTree = ""; }; + 09E7556AF9D2BB4E65D06658 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.debug.xcconfig; sourceTree = ""; }; + 0A23B07692829993061C93B8 /* DTFolderMonitor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTFolderMonitor.m; path = Core/Source/DTFolderMonitor.m; sourceTree = ""; }; + 0A4E3976555492FF46129D87 /* SDWebImageCompat.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageCompat.m; path = SDWebImage/SDWebImageCompat.m; sourceTree = ""; }; + 0B33B1CFDC6A466868B6EE09 /* NSAttributedString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+HTML.h"; path = "Core/Source/NSAttributedString+HTML.h"; sourceTree = ""; }; + 0BF40ECE7E4E0682190A71C4 /* NSMutableAttributedString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMutableAttributedString+HTML.h"; path = "Core/Source/NSMutableAttributedString+HTML.h"; sourceTree = ""; }; + 0CAA48134B8A56EEB761E70F /* Pods-Reachability-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Reachability-Private.xcconfig"; sourceTree = ""; }; + 0D7B9F66B9415561599C8879 /* UIKit+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIKit+AFNetworking.h"; path = "UIKit+AFNetworking/UIKit+AFNetworking.h"; sourceTree = ""; }; + 0E14665E1CB4D66432BD3126 /* DTColorFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTColorFunctions.h; path = Core/Source/DTColorFunctions.h; sourceTree = ""; }; + 0E2BE295E0CA7EBC4570237D /* DTDictationPlaceholderView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTDictationPlaceholderView.m; path = Core/Source/DTDictationPlaceholderView.m; sourceTree = ""; }; + 0E6DD371D30AD5BE472DB12F /* DTCoreTextLayoutFrame.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayoutFrame.m; path = Core/Source/DTCoreTextLayoutFrame.m; sourceTree = ""; }; + 0F09EFAA166D58583C1180D7 /* UIApplication+DTNetworkActivity.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+DTNetworkActivity.m"; path = "Core/Source/iOS/UIApplication+DTNetworkActivity.m"; sourceTree = ""; }; + 121D4C2CE488ECFF04E7CC4A /* NSURL+DTUnshorten.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURL+DTUnshorten.m"; path = "Core/Source/NSURL+DTUnshorten.m"; sourceTree = ""; }; + 124E98330C7C6C750FC9E2C1 /* NSDictionary+MTLManipulationAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+MTLManipulationAdditions.h"; path = "Mantle/NSDictionary+MTLManipulationAdditions.h"; sourceTree = ""; }; + 12CFFC0AAEB2049E3D38839B /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/MediaPlayer.framework; sourceTree = DEVELOPER_DIR; }; + 12DE9529ACCA6BA9429BA38E /* NSFileManager+Godzippa.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSFileManager+Godzippa.m"; path = "Godzippa/NSFileManager+Godzippa.m"; sourceTree = ""; }; + 1321C721D6711CABE43A8E72 /* DTHorizontalRuleHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHorizontalRuleHTMLElement.h; path = Core/Source/DTHorizontalRuleHTMLElement.h; sourceTree = ""; }; + 13305323CFB19F9B7DBBF7B7 /* DTImage+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DTImage+HTML.m"; path = "Core/Source/DTImage+HTML.m"; sourceTree = ""; }; + 13478FB8906355D0CFF5F748 /* DTBase64Coding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTBase64Coding.h; path = Core/Source/DTBase64Coding.h; sourceTree = ""; }; + 13C19578BFB4FFB0FB793EAC /* NSString+DTURLEncoding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTURLEncoding.m"; path = "Core/Source/NSString+DTURLEncoding.m"; sourceTree = ""; }; + 1429FB4555EBB97DE560F5EB /* libPods-SDWebImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SDWebImage.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1593656BA44FF46A80E0DECD /* DTCoreTextFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFunctions.m; path = Core/Source/DTCoreTextFunctions.m; sourceTree = ""; }; + 17023854963F0DB77E8B6E1D /* DTCoreTextLayouter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayouter.h; path = Core/Source/DTCoreTextLayouter.h; sourceTree = ""; }; + 17C459247AAAEF8F0357FF79 /* UIProgressView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIProgressView+AFNetworking.m"; path = "UIKit+AFNetworking/UIProgressView+AFNetworking.m"; sourceTree = ""; }; + 181B343ACA3D419E8FF324D6 /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPSessionManager.h; path = AFNetworking/AFHTTPSessionManager.h; sourceTree = ""; }; + 18FD09FA7EC7172A286E2375 /* DTObjectTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTObjectTextAttachment.m; path = Core/Source/DTObjectTextAttachment.m; sourceTree = ""; }; + 198A05236D0FC24D7567E389 /* DTAttributedTextCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedTextCell.h; path = Core/Source/DTAttributedTextCell.h; sourceTree = ""; }; + 1AAD4CF32D8A346312E37D3A /* DTColorFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTColorFunctions.m; path = Core/Source/DTColorFunctions.m; sourceTree = ""; }; + 1AB2BD7A7440128E0C379878 /* NSAttributedString+DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+DTCoreText.h"; path = "Core/Source/NSAttributedString+DTCoreText.h"; sourceTree = ""; }; + 1B664423A7FD62262F9F2B35 /* AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworking.h; path = AFNetworking/AFNetworking.h; sourceTree = ""; }; + 1B8793FEA52392FA7191B99E /* AFURLConnectionOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLConnectionOperation.h; path = AFNetworking/AFURLConnectionOperation.h; sourceTree = ""; }; + 1C09C605E4DF2F9240812865 /* DTAsyncFileDeleter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAsyncFileDeleter.h; path = Core/Source/DTAsyncFileDeleter.h; sourceTree = ""; }; + 1E6483BA82724E795E88521B /* SBJson4Parser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4Parser.h; path = src/main/objc/SBJson4Parser.h; sourceTree = ""; }; + 1E65128E94D4FDD10661ED58 /* DTHTMLParserNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLParserNode.h; path = Core/Source/DTHTMLParserNode.h; sourceTree = ""; }; + 1F0F4805C5D575AADE2BB87D /* UIFont+DTCoreText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIFont+DTCoreText.m"; path = "Core/Source/UIFont+DTCoreText.m"; sourceTree = ""; }; + 210C12C212EB913E2531E21F /* Pods-MBProgressHUD-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-MBProgressHUD-dummy.m"; sourceTree = ""; }; + 21E3E130EB30C467D9C459DE /* DTAnchorHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAnchorHTMLElement.h; path = Core/Source/DTAnchorHTMLElement.h; sourceTree = ""; }; + 21F213E272C360C6D6AA4EA1 /* DTCoreTextConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextConstants.h; path = Core/Source/DTCoreTextConstants.h; sourceTree = ""; }; + 221F95CBD014F9E63281BEE7 /* DTExtendedFileAttributes.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTExtendedFileAttributes.m; path = Core/Source/DTExtendedFileAttributes.m; sourceTree = ""; }; + 229A3A64BEB4B6DD9D5AB3CD /* DTObjectTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTObjectTextAttachment.h; path = Core/Source/DTObjectTextAttachment.h; sourceTree = ""; }; + 25273CFE86FF9C58AE4AC127 /* Pods-Reachability.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Reachability.xcconfig"; sourceTree = ""; }; + 2566190B16B3CD1AF9910616 /* DTLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLog.h; path = Core/Source/DTLog.h; sourceTree = ""; }; + 25F5B5E271E33029DFD03BEF /* EXTRuntimeExtensions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = EXTRuntimeExtensions.m; path = Mantle/extobjc/EXTRuntimeExtensions.m; sourceTree = ""; }; + 26880A2C45EBEF24D2C3AA1D /* AFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworkActivityIndicatorManager.h; path = "UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h"; sourceTree = ""; }; + 2688EDDC3B016EC4E28E8C38 /* DTImage+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTImage+HTML.h"; path = "Core/Source/DTImage+HTML.h"; sourceTree = ""; }; + 275C328BCE7F67B37082744B /* libPods-STableViewController.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-STableViewController.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 27E3DD3C14D91284AB605925 /* NSData+DTCrypto.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+DTCrypto.m"; path = "Core/Source/NSData+DTCrypto.m"; sourceTree = ""; }; + 29C23375359F548C0F64C164 /* DTAccessibilityElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAccessibilityElement.h; path = Core/Source/DTAccessibilityElement.h; sourceTree = ""; }; + 2A145653E1D487150FE0CBA9 /* Pods-STableViewController.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-STableViewController.xcconfig"; sourceTree = ""; }; + 2A1F1C6F924DBF4917B97A1B /* DTHTMLParser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLParser.h; path = Core/Source/DTHTMLParser/DTHTMLParser.h; sourceTree = ""; }; + 2A874FAFF81C4D3E50640FB5 /* Pods-DTFoundation-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DTFoundation-Private.xcconfig"; sourceTree = ""; }; + 2ADE193BEB78F37154DF3071 /* DTWebVideoView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTWebVideoView.h; path = Core/Source/DTWebVideoView.h; sourceTree = ""; }; + 2C0EA53A4DA9ED8E20C9A1FE /* DTStylesheetHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTStylesheetHTMLElement.h; path = Core/Source/DTStylesheetHTMLElement.h; sourceTree = ""; }; + 2C650430B30114D4A8D3A459 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; + 2CF3B0023ABA3D5362C4C9EB /* UIImageView+HighlightedWebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+HighlightedWebCache.m"; path = "SDWebImage/UIImageView+HighlightedWebCache.m"; sourceTree = ""; }; + 2D24DCCEF60B5A02011747B8 /* MTLModel+NSCoding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "MTLModel+NSCoding.m"; path = "Mantle/MTLModel+NSCoding.m"; sourceTree = ""; }; + 2DDC4EE3C0AEEB5D12C523DC /* DTHTMLWriter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLWriter.h; path = Core/Source/DTHTMLWriter.h; sourceTree = ""; }; + 2DF67C46304BCAB7A718C6A0 /* DTColor+Compatibility.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DTColor+Compatibility.m"; path = "Core/Source/DTColor+Compatibility.m"; sourceTree = ""; }; + 2EAA2847D3C5E00C6F7FB80B /* AFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFNetworkActivityIndicatorManager.m; path = "UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m"; sourceTree = ""; }; + 2F60AFB8DB89C64EB54A4343 /* UIAlertView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIAlertView+AFNetworking.m"; path = "UIKit+AFNetworking/UIAlertView+AFNetworking.m"; sourceTree = ""; }; + 2F808E117E65DD98C903DACD /* DTHTMLParserNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParserNode.m; path = Core/Source/DTHTMLParserNode.m; sourceTree = ""; }; + 301AB1AD7F62B5AE55C9823C /* Pods-EGOCache-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-EGOCache-Private.xcconfig"; sourceTree = ""; }; + 306A576CB103E0DD61B011B4 /* SBJson4Writer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4Writer.h; path = src/main/objc/SBJson4Writer.h; sourceTree = ""; }; + 3139D5BE25741C00C26B11D2 /* DTAttributedTextCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedTextCell.m; path = Core/Source/DTAttributedTextCell.m; sourceTree = ""; }; + 313D9BC92774DAE5CC6D7A8F /* Pods-DTFoundation-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-DTFoundation-dummy.m"; sourceTree = ""; }; + 3141E70A8914DBA8EF4C8BF4 /* DTCoreTextMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextMacros.h; path = Core/Source/DTCoreTextMacros.h; sourceTree = ""; }; + 31C53FE22C24600E279C2A81 /* UIImageView+WebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+WebCache.m"; path = "SDWebImage/UIImageView+WebCache.m"; sourceTree = ""; }; + 31EC48DC08E838E541102AD5 /* DTCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCompatibility.h; path = Core/Source/DTCompatibility.h; sourceTree = ""; }; + 32C11BFEBFEC9CE3AF6EBE87 /* libPods-SecureUDID.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SecureUDID.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 32F92EAE13C59713EAC676DF /* DTCoreTextFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFunctions.h; path = Core/Source/DTCoreTextFunctions.h; sourceTree = ""; }; + 330A7FB9F02C86B2A3D1FB27 /* DTVideoTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTVideoTextAttachment.m; path = Core/Source/DTVideoTextAttachment.m; sourceTree = ""; }; + 349161E009EB67623A7A576E /* SDWebImageDownloader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloader.m; path = SDWebImage/SDWebImageDownloader.m; sourceTree = ""; }; + 3586666B2105EBE27406E900 /* NSCharacterSet+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+HTML.h"; path = "Core/Source/NSCharacterSet+HTML.h"; sourceTree = ""; }; + 37ED3CAD19ADFD28C18D3D5A /* NSString+DTPaths.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTPaths.h"; path = "Core/Source/NSString+DTPaths.h"; sourceTree = ""; }; + 39928E568AFCA854CA213D64 /* DTCoreTextParagraphStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextParagraphStyle.h; path = Core/Source/DTCoreTextParagraphStyle.h; sourceTree = ""; }; + 3C095AE7D2B4E1C122FE2C0C /* SDWebImageOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageOperation.h; path = SDWebImage/SDWebImageOperation.h; sourceTree = ""; }; + 3CC250296BA77530564C8631 /* UIView+WebCacheOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+WebCacheOperation.h"; path = "SDWebImage/UIView+WebCacheOperation.h"; sourceTree = ""; }; + 3E104C4243AE675E793B5BAB /* SDWebImagePrefetcher.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImagePrefetcher.m; path = SDWebImage/SDWebImagePrefetcher.m; sourceTree = ""; }; + 3E9CD7B761C440D9F616B334 /* NSString+DTUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTUtilities.m"; path = "Core/Source/NSString+DTUtilities.m"; sourceTree = ""; }; + 4047E9A4D159D0380E1B9036 /* SecureUDID.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = SecureUDID.h; sourceTree = ""; }; + 40835E80F4E2B55E419BA88E /* SDWebImageManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageManager.h; path = SDWebImage/SDWebImageManager.h; sourceTree = ""; }; + 415E34057EF4F3E375C35F62 /* Pods-SecureUDID.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SecureUDID.xcconfig"; sourceTree = ""; }; + 4222F268674ABE22FB012CE8 /* libPods-EGOCache.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-EGOCache.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 429293BD4B1AA8A0171C4214 /* NSString+DTPaths.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTPaths.m"; path = "Core/Source/NSString+DTPaths.m"; sourceTree = ""; }; + 436327479F3B7896DCBE3876 /* Pods-Godzippa-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Godzippa-dummy.m"; sourceTree = ""; }; + 439B2C42A0B6956DB02EBC70 /* libPods-Mantle.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Mantle.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 43C3357CEED63E5037D30890 /* DTCoreGraphicsUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreGraphicsUtils.m; path = Core/Source/DTCoreGraphicsUtils.m; sourceTree = ""; }; + 451382DBA924128C00A949F8 /* DTAnimatedGIF.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAnimatedGIF.m; path = Core/Source/iOS/DTAnimatedGIF/DTAnimatedGIF.m; sourceTree = ""; }; + 462B84C37B10724E69874AC2 /* Pods-Mantle-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Mantle-Private.xcconfig"; sourceTree = ""; }; + 463D2C1499B05E24A3C9466E /* SDWebImageDownloader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloader.h; path = SDWebImage/SDWebImageDownloader.h; sourceTree = ""; }; + 471A3ED3891038D7D9EBB38F /* Pods-environment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-environment.h"; sourceTree = ""; }; + 4881CF962E85111BB932C11C /* Pods-AFNetworking-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AFNetworking-Private.xcconfig"; sourceTree = ""; }; + 496866DDF2FA8C8B8DE160E1 /* Pods-DTFoundation-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-DTFoundation-prefix.pch"; sourceTree = ""; }; + 49708DE5D2380D2CBA14932B /* Pods-Mantle-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Mantle-prefix.pch"; sourceTree = ""; }; + 499B36A4F41B73059E29F3E0 /* DTCSSStylesheet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCSSStylesheet.h; path = Core/Source/DTCSSStylesheet.h; sourceTree = ""; }; + 4A7FA25460BD0A14CE76D3DF /* EGOCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = EGOCache.m; sourceTree = ""; }; + 4AFC81561555A203DCA80986 /* DTVersion.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTVersion.h; path = Core/Source/DTVersion.h; sourceTree = ""; }; + 4CAEDBC718BBC91DC5AB3CD6 /* NSString+Paragraphs.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+Paragraphs.h"; path = "Core/Source/NSString+Paragraphs.h"; sourceTree = ""; }; + 4CE3DB4CDED08038EE99FAD6 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/ImageIO.framework; sourceTree = DEVELOPER_DIR; }; + 4D06E48BDE9CA5867AEA039A /* NSString+DTUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTUtilities.h"; path = "Core/Source/NSString+DTUtilities.h"; sourceTree = ""; }; + 4D24C02FBF4C29B7C9BD5965 /* Pods-EGOCache-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-EGOCache-dummy.m"; sourceTree = ""; }; + 4D5BD60D8CE96A58045E1EEF /* DTLinkButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTLinkButton.m; path = Core/Source/DTLinkButton.m; sourceTree = ""; }; + 4E8C90E8027986DF576B89C4 /* NSScanner+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSScanner+HTML.h"; path = "Core/Source/NSScanner+HTML.h"; sourceTree = ""; }; + 4F2EFD81D171B1FCF5D860C8 /* NSAttributedString+SmallCaps.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+SmallCaps.h"; path = "Core/Source/NSAttributedString+SmallCaps.h"; sourceTree = ""; }; + 5048BF1FE7D776CF0E99C53D /* DTCSSListStyle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCSSListStyle.h; path = Core/Source/DTCSSListStyle.h; sourceTree = ""; }; + 511326CEB4AAD81D048FC1CC /* DTCoreTextLayoutFrame+Cursor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTCoreTextLayoutFrame+Cursor.h"; path = "Core/Source/DTCoreTextLayoutFrame+Cursor.h"; sourceTree = ""; }; + 51B757136856DB70036EF6E5 /* Pods-SDWebImage-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SDWebImage-Private.xcconfig"; sourceTree = ""; }; + 524815F3F479FF6AA50B15DD /* MTLValueTransformer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MTLValueTransformer.h; path = Mantle/MTLValueTransformer.h; sourceTree = ""; }; + 531F610A6F31FEA918264187 /* DTFoundationConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTFoundationConstants.m; path = Core/Source/DTFoundationConstants.m; sourceTree = ""; }; + 53744589D3AD1C9BA3FC3A13 /* libPods-DTFoundation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DTFoundation.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 5551BF11DCD892AAD12F8198 /* NSData+Godzippa.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSData+Godzippa.h"; path = "Godzippa/NSData+Godzippa.h"; sourceTree = ""; }; + 56E447DB384683DAEBFFA224 /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFNetworkReachabilityManager.m; path = AFNetworking/AFNetworkReachabilityManager.m; sourceTree = ""; }; + 58632D3FA9629CB1A72776FD /* DTCSSStylesheet.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCSSStylesheet.m; path = Core/Source/DTCSSStylesheet.m; sourceTree = ""; }; + 59479D57BEE566952C5882C7 /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPRequestOperation.h; path = AFNetworking/AFHTTPRequestOperation.h; sourceTree = ""; }; + 59B4A447BE0C035D09F66B29 /* Pods-SBJson-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SBJson-Private.xcconfig"; sourceTree = ""; }; + 5A1D41BE1C3FD8398AF8BAD1 /* MTLReflection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MTLReflection.h; path = Mantle/MTLReflection.h; sourceTree = ""; }; + 5A91454224704755E8B78C0F /* DTHTMLParserTextNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParserTextNode.m; path = Core/Source/DTHTMLParserTextNode.m; sourceTree = ""; }; + 5B1086BD033B9EAA35C830F9 /* Pods-AFNetworking-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-AFNetworking-prefix.pch"; sourceTree = ""; }; + 5B7F037412F1FDD7AE4A9587 /* SDWebImageManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageManager.m; path = SDWebImage/SDWebImageManager.m; sourceTree = ""; }; + 5C699FE7FCD126A43DBC1131 /* DTTextBlock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextBlock.h; path = Core/Source/DTTextBlock.h; sourceTree = ""; }; + 5D0427BB77844F551A044D37 /* NSArray+MTLManipulationAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+MTLManipulationAdditions.h"; path = "Mantle/NSArray+MTLManipulationAdditions.h"; sourceTree = ""; }; + 5D323E71416F20743DEBB7A7 /* DTAttributedTextView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedTextView.m; path = Core/Source/DTAttributedTextView.m; sourceTree = ""; }; + 5ED322C23FFB410FA8D01C3A /* UIWebView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIWebView+AFNetworking.m"; path = "UIKit+AFNetworking/UIWebView+AFNetworking.m"; sourceTree = ""; }; + 5F69301F38456253353C2FB3 /* UIImage+GIF.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+GIF.h"; path = "SDWebImage/UIImage+GIF.h"; sourceTree = ""; }; + 5F7CF4F74439B9D4D12F935C /* DTIframeTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTIframeTextAttachment.h; path = Core/Source/DTIframeTextAttachment.h; sourceTree = ""; }; + 5F8D4344C9F779F3EC9A84CC /* Pods-MBProgressHUD-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MBProgressHUD-Private.xcconfig"; sourceTree = ""; }; + 5FCA4151E6647F1CFEFCC278 /* UIRefreshControl+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIRefreshControl+AFNetworking.h"; path = "UIKit+AFNetworking/UIRefreshControl+AFNetworking.h"; sourceTree = ""; }; + 600D123E65ED5A8F8C1E6220 /* Pods-DTCoreText-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-DTCoreText-prefix.pch"; sourceTree = ""; }; + 600F177FEBAD10AE1CB576B9 /* DTPieProgressIndicator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTPieProgressIndicator.h; path = Core/Source/iOS/DTPieProgressIndicator.h; sourceTree = ""; }; + 60D2BD28B2F0D25CE81FC512 /* UIImageView+HighlightedWebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+HighlightedWebCache.h"; path = "SDWebImage/UIImageView+HighlightedWebCache.h"; sourceTree = ""; }; + 6218027F5B4E249C3AD17D43 /* Pods-DTFoundation.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DTFoundation.xcconfig"; sourceTree = ""; }; + 623DD018EBC66CF653AB4EB8 /* DTHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLElement.m; path = Core/Source/DTHTMLElement.m; sourceTree = ""; }; + 62C8556BD6C33126297EACEC /* AFURLConnectionOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLConnectionOperation.m; path = AFNetworking/AFURLConnectionOperation.m; sourceTree = ""; }; + 631A5E7C34AF78A47633C5B9 /* DTTiledLayerWithoutFade.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTiledLayerWithoutFade.m; path = Core/Source/iOS/DTTiledLayerWithoutFade.m; sourceTree = ""; }; + 6467D845AE0A362D6D98BAAB /* NSDictionary+MTLManipulationAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+MTLManipulationAdditions.m"; path = "Mantle/NSDictionary+MTLManipulationAdditions.m"; sourceTree = ""; }; + 648F316B0C15B0C42B9F483D /* NSFileManager+Godzippa.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSFileManager+Godzippa.h"; path = "Godzippa/NSFileManager+Godzippa.h"; sourceTree = ""; }; + 66C6218CF6E13E047F8CDD16 /* DTTextAttachmentHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextAttachmentHTMLElement.m; path = Core/Source/DTTextAttachmentHTMLElement.m; sourceTree = ""; }; + 675C7BA63827ACB61FF3B9BB /* NSError+MTLModelException.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+MTLModelException.h"; path = "Mantle/NSError+MTLModelException.h"; sourceTree = ""; }; + 6763C598E5DC3B3A5A0655F9 /* SDWebImageDecoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDecoder.m; path = SDWebImage/SDWebImageDecoder.m; sourceTree = ""; }; + 69E9E1C87B98BF60ED6B0429 /* UIView+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+DTFoundation.h"; path = "Core/Source/iOS/UIView+DTFoundation.h"; sourceTree = ""; }; + 6B8EEF53333924DE92410F18 /* UIImage+GIF.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+GIF.m"; path = "SDWebImage/UIImage+GIF.m"; sourceTree = ""; }; + 6C4CF9E12F9F5431B1DB7424 /* DTCoreTextFontDescriptor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFontDescriptor.h; path = Core/Source/DTCoreTextFontDescriptor.h; sourceTree = ""; }; + 6D228843B5620E10036CEF16 /* NSString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+HTML.h"; path = "Core/Source/NSString+HTML.h"; sourceTree = ""; }; + 6E6EDE8ED4679A5632162A1F /* SBJson4StreamTokeniser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4StreamTokeniser.m; path = src/main/objc/SBJson4StreamTokeniser.m; sourceTree = ""; }; + 6E746C3CFC80A6AD3E9C140E /* NSCharacterSet+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSCharacterSet+HTML.m"; path = "Core/Source/NSCharacterSet+HTML.m"; sourceTree = ""; }; + 6EA8F67176A1B0327A19827A /* MTLJSONAdapter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MTLJSONAdapter.h; path = Mantle/MTLJSONAdapter.h; sourceTree = ""; }; + 6EE2E34F5BA4FA31632E183E /* DTAccessibilityViewProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAccessibilityViewProxy.m; path = Core/Source/DTAccessibilityViewProxy.m; sourceTree = ""; }; + 6FF95BB286D9C557B742A904 /* DTDictationPlaceholderTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTDictationPlaceholderTextAttachment.m; path = Core/Source/DTDictationPlaceholderTextAttachment.m; sourceTree = ""; }; + 70029B1D27A0D1E81984D23A /* UIRefreshControl+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIRefreshControl+AFNetworking.m"; path = "UIKit+AFNetworking/UIRefreshControl+AFNetworking.m"; sourceTree = ""; }; + 705873D7F9EFE10352328291 /* Pods-Godzippa-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Godzippa-prefix.pch"; sourceTree = ""; }; + 70D0115758D95D3FC9BEA163 /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPRequestOperation.m; path = AFNetworking/AFHTTPRequestOperation.m; sourceTree = ""; }; + 711CF44F1F86192C72867546 /* STableViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = STableViewController.m; path = STableViewController/STableViewController.m; sourceTree = ""; }; + 718C8CE67FFF7CAE824B278D /* NSScanner+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSScanner+HTML.m"; path = "Core/Source/NSScanner+HTML.m"; sourceTree = ""; }; + 729CC1C46AB6C6F9A5FF910A /* Pods-SDWebImage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SDWebImage.xcconfig"; sourceTree = ""; }; + 72E37C355B02AC874A58D9DC /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLRequestSerialization.h; path = AFNetworking/AFURLRequestSerialization.h; sourceTree = ""; }; + 749B8332FF564663E57BFCE9 /* NSString+CSS.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+CSS.h"; path = "Core/Source/NSString+CSS.h"; sourceTree = ""; }; + 74CF1A5A1EE415567B64AE71 /* EXTKeyPathCoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = EXTKeyPathCoding.h; path = Mantle/extobjc/EXTKeyPathCoding.h; sourceTree = ""; }; + 7512364D89353CEF9E99172C /* NSFileWrapper+DTCopying.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSFileWrapper+DTCopying.h"; path = "Core/Source/NSFileWrapper+DTCopying.h"; sourceTree = ""; }; + 7524B99AB4298B2349FD1128 /* DTBlockFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTBlockFunctions.h; path = Core/Source/DTBlockFunctions.h; sourceTree = ""; }; + 753131FB3B0B281B823061DB /* NSString+DTFormatNumbers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+DTFormatNumbers.m"; path = "Core/Source/NSString+DTFormatNumbers.m"; sourceTree = ""; }; + 765380382A09CD38070F811C /* DTAttributedLabel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedLabel.h; path = Core/Source/DTAttributedLabel.h; sourceTree = ""; }; + 773EE21FF809BE6B99B9AC7A /* MTLJSONAdapter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MTLJSONAdapter.m; path = Mantle/MTLJSONAdapter.m; sourceTree = ""; }; + 78085540FD8FB9B1E5B4B884 /* DTCoreTextConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextConstants.m; path = Core/Source/DTCoreTextConstants.m; sourceTree = ""; }; + 7847982D5FED405E21CA5041 /* DTLog.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTLog.m; path = Core/Source/DTLog.m; sourceTree = ""; }; + 7A059B8990583D788A1583F5 /* SBJson4StreamWriterState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4StreamWriterState.h; path = src/main/objc/SBJson4StreamWriterState.h; sourceTree = ""; }; + 7A63E6AC5C6B2A60F4A3EC9C /* libPods-DTCoreText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DTCoreText.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AB780C1815C17A0D187A5D9 /* AFSecurityPolicy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFSecurityPolicy.m; path = AFNetworking/AFSecurityPolicy.m; sourceTree = ""; }; + 7AEDC475AE181182905CC7C9 /* UIWebView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIWebView+AFNetworking.h"; path = "UIKit+AFNetworking/UIWebView+AFNetworking.h"; sourceTree = ""; }; + 7CBCF8EDBCBA565559D7FD04 /* DTCoreTextLayoutFrame+Cursor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DTCoreTextLayoutFrame+Cursor.m"; path = "Core/Source/DTCoreTextLayoutFrame+Cursor.m"; sourceTree = ""; }; + 7D9DA8599F9A65C67763F7C8 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + 7FFB2500E4380D67EF8A7E41 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/CoreText.framework; sourceTree = DEVELOPER_DIR; }; + 80A2C8039197A10FCF713E70 /* Mantle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Mantle.h; path = Mantle/Mantle.h; sourceTree = ""; }; + 80B0262BBEA47B83C25F3008 /* DTCSSListStyle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCSSListStyle.m; path = Core/Source/DTCSSListStyle.m; sourceTree = ""; }; + 81D9D642C6BD33E1FF820543 /* DTAsyncFileDeleter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAsyncFileDeleter.m; path = Core/Source/DTAsyncFileDeleter.m; sourceTree = ""; }; + 81F000D344438B5DE5B50F83 /* Pods-MBProgressHUD.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MBProgressHUD.xcconfig"; sourceTree = ""; }; + 8301F7F2078CE377A3C33B23 /* NSURL+DTAppLinks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURL+DTAppLinks.h"; path = "Core/Source/iOS/NSURL+DTAppLinks.h"; sourceTree = ""; }; + 83AB1625C41A6A0C8B22D379 /* NSValueTransformer+MTLInversionAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSValueTransformer+MTLInversionAdditions.m"; path = "Mantle/NSValueTransformer+MTLInversionAdditions.m"; sourceTree = ""; }; + 83D2D6D8C8AFB62D63AAB400 /* NSMutableString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMutableString+HTML.m"; path = "Core/Source/NSMutableString+HTML.m"; sourceTree = ""; }; + 84513A2E6747DF1945EB57E6 /* DTAccessibilityViewProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAccessibilityViewProxy.h; path = Core/Source/DTAccessibilityViewProxy.h; sourceTree = ""; }; + 857FA51AF993242E771D051A /* libPods-Reachability.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Reachability.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 858693B43C8CC8542AEA5B41 /* NSArray+DTError.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+DTError.m"; path = "Core/Source/NSArray+DTError.m"; sourceTree = ""; }; + 862F1FEB67B7CEFE9074BE6E /* DTIframeTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTIframeTextAttachment.m; path = Core/Source/DTIframeTextAttachment.m; sourceTree = ""; }; + 8631416B11CCD525941D9201 /* SBJson4StreamTokeniser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4StreamTokeniser.h; path = src/main/objc/SBJson4StreamTokeniser.h; sourceTree = ""; }; + 868740E807D0BE8A311DD536 /* Reachability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; + 88EE6A2C5E812DB3AA45E0BB /* UIImageView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+AFNetworking.m"; path = "UIKit+AFNetworking/UIImageView+AFNetworking.m"; sourceTree = ""; }; + 8B5CBFFC0BEA9B64BB37BE4D /* DTVideoTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTVideoTextAttachment.h; path = Core/Source/DTVideoTextAttachment.h; sourceTree = ""; }; + 8BE128E57C6D864A051CC8A5 /* MTLModel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MTLModel.h; path = Mantle/MTLModel.h; sourceTree = ""; }; + 8CFF339A8E0F272B95A05400 /* NSData+DTCrypto.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSData+DTCrypto.h"; path = "Core/Source/NSData+DTCrypto.h"; sourceTree = ""; }; + 8D90CEF4D748B98F5C4F9223 /* MTLManagedObjectAdapter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MTLManagedObjectAdapter.m; path = Mantle/MTLManagedObjectAdapter.m; sourceTree = ""; }; + 8DABDE8AEC54A0F7DAE9ACED /* DTDictationPlaceholderTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTDictationPlaceholderTextAttachment.h; path = Core/Source/DTDictationPlaceholderTextAttachment.h; sourceTree = ""; }; + 8E4B16797709C7881194F23F /* Pods-Reachability-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Reachability-prefix.pch"; sourceTree = ""; }; + 8EB56E074E9C09F2406F744A /* UIActivityIndicatorView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIActivityIndicatorView+AFNetworking.m"; path = "UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m"; sourceTree = ""; }; + 8EE25AF5774365C08845F9C4 /* NSAttributedString+DTDebug.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+DTDebug.h"; path = "Core/Source/NSAttributedString+DTDebug.h"; sourceTree = ""; }; + 8EFC006BEF1B085DB3C57659 /* NSMutableArray+DTMoving.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+DTMoving.m"; path = "Core/Source/NSMutableArray+DTMoving.m"; sourceTree = ""; }; + 8FB81A3F3CC5C8EEE0D0A053 /* DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreText.h; path = Core/Source/DTCoreText.h; sourceTree = ""; }; + 9021B480CC3318D413444498 /* DTAttributedTextView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedTextView.h; path = Core/Source/DTAttributedTextView.h; sourceTree = ""; }; + 9085028F0F1956A346E6C1FB /* EXTRuntimeExtensions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = EXTRuntimeExtensions.h; path = Mantle/extobjc/EXTRuntimeExtensions.h; sourceTree = ""; }; + 9097904A80D8A7F24ADC07DB /* DTHTMLWriter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLWriter.m; path = Core/Source/DTHTMLWriter.m; sourceTree = ""; }; + 9135155F192247F273C4BFF1 /* SDImageCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDImageCache.m; path = SDWebImage/SDImageCache.m; sourceTree = ""; }; + 91C83B61CE2EF0D23B05EFFE /* UIActivityIndicatorView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIActivityIndicatorView+AFNetworking.h"; path = "UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h"; sourceTree = ""; }; + 93CC40769EDB0F6EE0D5ED72 /* DTLazyImageView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTLazyImageView.m; path = Core/Source/DTLazyImageView.m; sourceTree = ""; }; + 94D42913953EF40E145B5E1E /* MTLModel+NSCoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "MTLModel+NSCoding.h"; path = "Mantle/MTLModel+NSCoding.h"; sourceTree = ""; }; + 94D59B3E6671961F9FC0D3B9 /* SBJson4StreamWriter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4StreamWriter.m; path = src/main/objc/SBJson4StreamWriter.m; sourceTree = ""; }; + 95D8CA211E8F70F133431035 /* Podfile */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 95E8E93955BD84AB7D61B561 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayoutFrameAccessibilityElementGenerator.m; path = Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m; sourceTree = ""; }; + 9678D7E8E9FB8406B0A8FBF9 /* DTTextBlock.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextBlock.m; path = Core/Source/DTTextBlock.m; sourceTree = ""; }; + 977A96477E21AFE119F2C818 /* NSDictionary+DTCoreText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+DTCoreText.m"; path = "Core/Source/NSDictionary+DTCoreText.m"; sourceTree = ""; }; + 977BBA287FC7987841DABAD4 /* DTLinkButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLinkButton.h; path = Core/Source/DTLinkButton.h; sourceTree = ""; }; + 980E5243E51E9C323AA25021 /* DTPieProgressIndicator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTPieProgressIndicator.m; path = Core/Source/iOS/DTPieProgressIndicator.m; sourceTree = ""; }; + 982D461DCD6CCFF9DE4D96AE /* DTTextAttachment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextAttachment.h; path = Core/Source/DTTextAttachment.h; sourceTree = ""; }; + 98B78BA7BD331BD145EF9497 /* DTHTMLParserTextNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLParserTextNode.h; path = Core/Source/DTHTMLParserTextNode.h; sourceTree = ""; }; + 98E8971B9A319D428BC49638 /* UIImage+MultiFormat.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+MultiFormat.m"; path = "SDWebImage/UIImage+MultiFormat.m"; sourceTree = ""; }; + 992C5817448ED305FA3AD445 /* Pods-SecureUDID-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-SecureUDID-prefix.pch"; sourceTree = ""; }; + 994CD0C8E00BBA950FDC15C5 /* Pods-STableViewController-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-STableViewController-Private.xcconfig"; sourceTree = ""; }; + 99BDEC117EA48AB5F7091E99 /* SBJson4StreamParserState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4StreamParserState.h; path = src/main/objc/SBJson4StreamParserState.h; sourceTree = ""; }; + 9B38A1D04122792A32F17DB1 /* UIButton+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIButton+AFNetworking.h"; path = "UIKit+AFNetworking/UIButton+AFNetworking.h"; sourceTree = ""; }; + 9B9A846D33E7580CAF1F9228 /* Pods-Mantle.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Mantle.xcconfig"; sourceTree = ""; }; + 9C11C7730CECE62713E638B8 /* NSString+CSS.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+CSS.m"; path = "Core/Source/NSString+CSS.m"; sourceTree = ""; }; + 9DE1F401D6E30BE57ED5553E /* UIImage+MultiFormat.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+MultiFormat.h"; path = "SDWebImage/UIImage+MultiFormat.h"; sourceTree = ""; }; + 9E62EA74C8AE7E7D9633E921 /* UIButton+WebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIButton+WebCache.h"; path = "SDWebImage/UIButton+WebCache.h"; sourceTree = ""; }; + 9E787096E33E2E0215B8203E /* STableViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = STableViewController.h; path = STableViewController/STableViewController.h; sourceTree = ""; }; + 9EE1E9C074198D7DBA970623 /* libPods-MBProgressHUD.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MBProgressHUD.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9F2FC1F559C3E93CF482DDF4 /* Pods-DTCoreText-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DTCoreText-Private.xcconfig"; sourceTree = ""; }; + 9F3AA2982830D803D19E4E40 /* DTCoreTextLayoutLine.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayoutLine.m; path = Core/Source/DTCoreTextLayoutLine.m; sourceTree = ""; }; + A012184FCB2A91D1B3FD1F88 /* Pods-DTCoreText.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DTCoreText.xcconfig"; sourceTree = ""; }; + A1A4ECC1525DC43647B6285C /* Pods-SDWebImage-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-SDWebImage-prefix.pch"; sourceTree = ""; }; + A1CF066DE80C9B874E3E7DF4 /* UIImageView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+AFNetworking.h"; path = "UIKit+AFNetworking/UIImageView+AFNetworking.h"; sourceTree = ""; }; + A1CF7E7A550A22B59D81840F /* Pods-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-resources.sh"; sourceTree = ""; }; + A3473269AF9E0EF0A56CF9B2 /* DTHorizontalRuleHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHorizontalRuleHTMLElement.m; path = Core/Source/DTHorizontalRuleHTMLElement.m; sourceTree = ""; }; + A4189FA23F95F690EA94D56C /* SBJson4StreamWriter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4StreamWriter.h; path = src/main/objc/SBJson4StreamWriter.h; sourceTree = ""; }; + A4F7B232DD157C6696C84091 /* UIWebView+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIWebView+DTFoundation.h"; path = "Core/Source/iOS/UIWebView+DTFoundation.h"; sourceTree = ""; }; + A6384E42A1880883FE59B266 /* DTCoreTextLayoutFrame.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayoutFrame.h; path = Core/Source/DTCoreTextLayoutFrame.h; sourceTree = ""; }; + A72BEDBD2D2DB007DDBDC61A /* DTCoreTextLayoutLine.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayoutLine.h; path = Core/Source/DTCoreTextLayoutLine.h; sourceTree = ""; }; + A758F0825180829D7CE75B87 /* DTCoreTextParagraphStyle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextParagraphStyle.m; path = Core/Source/DTCoreTextParagraphStyle.m; sourceTree = ""; }; + A79E2E040FCDDB7E3B284DB8 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.release.xcconfig; sourceTree = ""; }; + A7A256B6D33BA5BB7B04F363 /* AFURLSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLSessionManager.m; path = AFNetworking/AFURLSessionManager.m; sourceTree = ""; }; + A8619D6089A77D57DF8CED95 /* Pods-Mantle-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Mantle-dummy.m"; sourceTree = ""; }; + A8A9067C9B073EC236A3E05D /* UIWebView+DTFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIWebView+DTFoundation.m"; path = "Core/Source/iOS/UIWebView+DTFoundation.m"; sourceTree = ""; }; + A9C4C308E5654ADF4D23B3B4 /* Pods-SDWebImage-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SDWebImage-dummy.m"; sourceTree = ""; }; + AA3E1D39F56369F6EDA19850 /* SDImageCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDImageCache.h; path = SDWebImage/SDImageCache.h; sourceTree = ""; }; + AB4E35D99C28AD0A2FF788B0 /* DTActivityTitleView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTActivityTitleView.m; path = Core/Source/iOS/DTActivityTitleView.m; sourceTree = ""; }; + ABEB20229B4440B279466FB6 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSValueTransformer+MTLPredefinedTransformerAdditions.h"; path = "Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.h"; sourceTree = ""; }; + AC40CBC3BCEB21F15A6DB3A7 /* Godzippa.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Godzippa.h; path = Godzippa/Godzippa.h; sourceTree = ""; }; + AC5A316ABA995034D8C3B6C8 /* NSMutableString+HTML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMutableString+HTML.h"; path = "Core/Source/NSMutableString+HTML.h"; sourceTree = ""; }; + ACB76F8380C3F1D5515F2A4B /* default.css.c */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.c; name = default.css.c; path = Core/Source/default.css.c; sourceTree = ""; }; + ACED3FB738D11F8A10B394A3 /* DTAttributedTextContentView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedTextContentView.m; path = Core/Source/DTAttributedTextContentView.m; sourceTree = ""; }; + AD36549EF3A54A1AD6CC794E /* AFURLSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLSessionManager.h; path = AFNetworking/AFURLSessionManager.h; sourceTree = ""; }; + AD5E1EDE9FFA20FF03F5BCA6 /* UIView+DTFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+DTFoundation.m"; path = "Core/Source/iOS/UIView+DTFoundation.m"; sourceTree = ""; }; + AD7766A1EDACC24F6BBDFD7C /* DTActivityTitleView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTActivityTitleView.h; path = Core/Source/iOS/DTActivityTitleView.h; sourceTree = ""; }; + AF6401F09777B31791FA7DCD /* DTBase64Coding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTBase64Coding.m; path = Core/Source/DTBase64Coding.m; sourceTree = ""; }; + AF8355CDB07E5B6CF7F8EB08 /* SecureUDID.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = SecureUDID.m; sourceTree = ""; }; + AF8A9093D0B137CE9B22C7A2 /* NSURL+DTComparing.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURL+DTComparing.m"; path = "Core/Source/NSURL+DTComparing.m"; sourceTree = ""; }; + B03C715F5A8AEA36DC55688C /* DTListItemHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTListItemHTMLElement.m; path = Core/Source/DTListItemHTMLElement.m; sourceTree = ""; }; + B1012FEDA5057A6C62506D76 /* NSObject+MTLComparisonAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+MTLComparisonAdditions.m"; path = "Mantle/NSObject+MTLComparisonAdditions.m"; sourceTree = ""; }; + B10FE77E0C21FFBB51B6DE3A /* DTLazyImageView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTLazyImageView.h; path = Core/Source/DTLazyImageView.h; sourceTree = ""; }; + B1C23B5198890B521A4DF725 /* libPods-Godzippa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Godzippa.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B249319A47C18D3B6B91AC78 /* NSMutableAttributedString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMutableAttributedString+HTML.m"; path = "Core/Source/NSMutableAttributedString+HTML.m"; sourceTree = ""; }; + B35D826DD7A9C3AED97F787C /* DTDictationPlaceholderView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTDictationPlaceholderView.h; path = Core/Source/DTDictationPlaceholderView.h; sourceTree = ""; }; + B413A933260FB0F4B93158CC /* NSArray+MTLManipulationAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+MTLManipulationAdditions.m"; path = "Mantle/NSArray+MTLManipulationAdditions.m"; sourceTree = ""; }; + B431B9F8EEBC811ECBF6A5D4 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; }; + B439B8C90661AC8FFC66E04D /* NSDictionary+DTCoreText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+DTCoreText.h"; path = "Core/Source/NSDictionary+DTCoreText.h"; sourceTree = ""; }; + B4440BBB972015707D689FF1 /* NSData+ImageContentType.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+ImageContentType.m"; path = "SDWebImage/NSData+ImageContentType.m"; sourceTree = ""; }; + B4F7C13BA2FCD171CDD56CDC /* SDWebImageDownloaderOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloaderOperation.m; path = SDWebImage/SDWebImageDownloaderOperation.m; sourceTree = ""; }; + B6CBABA8BF5C20E72993CAB8 /* Pods-STableViewController-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-STableViewController-dummy.m"; sourceTree = ""; }; + B89B359C774D0BEEADCF72FE /* Reachability.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; + B98FCB2622BDB2878F1AD1F6 /* Pods-SBJson-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SBJson-dummy.m"; sourceTree = ""; }; + BA5F17CD2A18CA002DA9B0EA /* Pods-AFNetworking.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AFNetworking.xcconfig"; sourceTree = ""; }; + BAEAE04DC9D6049ECD98EADA /* DTTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextAttachment.m; path = Core/Source/DTTextAttachment.m; sourceTree = ""; }; + BC7EB9B8C3DBE8D6402CCCF4 /* SDWebImageDecoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageDecoder.h; path = SDWebImage/SDWebImageDecoder.h; sourceTree = ""; }; + BD038E1FE6A969A543DAD866 /* DTAnchorHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAnchorHTMLElement.m; path = Core/Source/DTAnchorHTMLElement.m; sourceTree = ""; }; + BD775CFA71EB688731C92C0A /* NSCoder+DTCompatibility.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSCoder+DTCompatibility.m"; path = "Core/Source/NSCoder+DTCompatibility.m"; sourceTree = ""; }; + BE42064DDDEC23B7164ACBAA /* NSURL+DTAppLinks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURL+DTAppLinks.m"; path = "Core/Source/iOS/NSURL+DTAppLinks.m"; sourceTree = ""; }; + BE8902F9D667F3CC87EB82C4 /* UIButton+WebCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIButton+WebCache.m"; path = "SDWebImage/UIButton+WebCache.m"; sourceTree = ""; }; + BE965E64C9C461F7C6E8DBDE /* DTVersion.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTVersion.m; path = Core/Source/DTVersion.m; sourceTree = ""; }; + BFAF6715DE24498F152ADAAC /* AFHTTPRequestOperationManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPRequestOperationManager.h; path = AFNetworking/AFHTTPRequestOperationManager.h; sourceTree = ""; }; + C138EF04D1765832AB4C5AEC /* NSAttributedString+DTCoreText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+DTCoreText.m"; path = "Core/Source/NSAttributedString+DTCoreText.m"; sourceTree = ""; }; + C1ABA98F064631211B3CC4A1 /* DTBlockFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTBlockFunctions.m; path = Core/Source/DTBlockFunctions.m; sourceTree = ""; }; + C2B5049DC7669D3A6F803BDE /* UIAlertView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIAlertView+AFNetworking.h"; path = "UIKit+AFNetworking/UIAlertView+AFNetworking.h"; sourceTree = ""; }; + C2C05ECAAEFDD379130CEC69 /* DTCoreGraphicsUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreGraphicsUtils.h; path = Core/Source/DTCoreGraphicsUtils.h; sourceTree = ""; }; + C336AC714C253351DB4D3E1D /* AFSecurityPolicy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFSecurityPolicy.h; path = AFNetworking/AFSecurityPolicy.h; sourceTree = ""; }; + C4B18DCB03C46935261F01FB /* EXTScope.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = EXTScope.h; path = Mantle/extobjc/EXTScope.h; sourceTree = ""; }; + C58EAF174D65FF96676BC746 /* DTTextHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTTextHTMLElement.m; path = Core/Source/DTTextHTMLElement.m; sourceTree = ""; }; + C5DE72E147F2C83C77BFD809 /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLResponseSerialization.h; path = AFNetworking/AFURLResponseSerialization.h; sourceTree = ""; }; + C634EA81A406E4A8BFEE29DA /* DTBreakHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTBreakHTMLElement.m; path = Core/Source/DTBreakHTMLElement.m; sourceTree = ""; }; + C80E4986A69A3380A13EDA4F /* NSFileWrapper+DTCopying.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSFileWrapper+DTCopying.m"; path = "Core/Source/NSFileWrapper+DTCopying.m"; sourceTree = ""; }; + C91562ADBA84A66B5A686B5B /* Pods-SBJson-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-SBJson-prefix.pch"; sourceTree = ""; }; + CA4C181DEEE4F965BC1B8B3C /* Pods-EGOCache-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-EGOCache-prefix.pch"; sourceTree = ""; }; + CA5F091D0B1700AFE4DF24AC /* SBJson4.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4.h; path = src/main/objc/SBJson4.h; sourceTree = ""; }; + CAB06CD8D214A93380CA359F /* SBJson4StreamParser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4StreamParser.m; path = src/main/objc/SBJson4StreamParser.m; sourceTree = ""; }; + CC13076D657AA25084358E58 /* DTFoundationConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTFoundationConstants.h; path = Core/Source/DTFoundationConstants.h; sourceTree = ""; }; + CCD9AC3DC59EFD8A7760B6BC /* DTAttributedLabel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAttributedLabel.m; path = Core/Source/DTAttributedLabel.m; sourceTree = ""; }; + CDA3C0D0DBA5CA06E7D5F29C /* UIImage+DTFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+DTFoundation.h"; path = "Core/Source/iOS/UIImage+DTFoundation.h"; sourceTree = ""; }; + CED305D27339CE40E6A4451C /* DTTextHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTextHTMLElement.h; path = Core/Source/DTTextHTMLElement.h; sourceTree = ""; }; + D0C98317A2D9BC73A065CD84 /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworkReachabilityManager.h; path = AFNetworking/AFNetworkReachabilityManager.h; sourceTree = ""; }; + D15510793C867C9BDD98F461 /* NSString+DTFormatNumbers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTFormatNumbers.h"; path = "Core/Source/NSString+DTFormatNumbers.h"; sourceTree = ""; }; + D24263A900677EE623D3679C /* EXTScope.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = EXTScope.m; path = Mantle/extobjc/EXTScope.m; sourceTree = ""; }; + D2B82CCEE3DEBD49E4E63DCB /* DTHTMLAttributedStringBuilder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTHTMLAttributedStringBuilder.h; path = Core/Source/DTHTMLAttributedStringBuilder.h; sourceTree = ""; }; + D2E5C56114175812BBC678AC /* MTLManagedObjectAdapter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MTLManagedObjectAdapter.h; path = Mantle/MTLManagedObjectAdapter.h; sourceTree = ""; }; + D35A7D47AFBD0118F9B61044 /* DTHTMLAttributedStringBuilder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLAttributedStringBuilder.m; path = Core/Source/DTHTMLAttributedStringBuilder.m; sourceTree = ""; }; + D3B28D24ACD99923AE54B04E /* DTStylesheetHTMLElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTStylesheetHTMLElement.m; path = Core/Source/DTStylesheetHTMLElement.m; sourceTree = ""; }; + D407039B1428945391E15138 /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLResponseSerialization.m; path = AFNetworking/AFURLResponseSerialization.m; sourceTree = ""; }; + D4429C837BD4425D786F352A /* DTCoreTextLayouter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextLayouter.m; path = Core/Source/DTCoreTextLayouter.m; sourceTree = ""; }; + D75D8DD36B2DF3A94A8D8BDD /* SDWebImageCompat.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageCompat.h; path = SDWebImage/SDWebImageCompat.h; sourceTree = ""; }; + D7749F1AF8D031564B1684A0 /* Pods-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-acknowledgements.plist"; sourceTree = ""; }; + D845CF0C240B6ED9C52C6423 /* Pods-SecureUDID-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SecureUDID-Private.xcconfig"; sourceTree = ""; }; + D9177A65BD60036FE28650C3 /* DTSmartPagingScrollView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTSmartPagingScrollView.h; path = Core/Source/iOS/DTSmartPagingScrollView.h; sourceTree = ""; }; + D930E71CD2A5F2B17ACCF87C /* DTBreakHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTBreakHTMLElement.h; path = Core/Source/DTBreakHTMLElement.h; sourceTree = ""; }; + D9B387604713395C983D6059 /* Pods-Godzippa-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Godzippa-Private.xcconfig"; sourceTree = ""; }; + DA95D3A59A8B4392A30955E3 /* NSError+MTLModelException.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+MTLModelException.m"; path = "Mantle/NSError+MTLModelException.m"; sourceTree = ""; }; + DBEF3AD8AF418F5D0DB9FA36 /* DTCoreTextGlyphRun.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextGlyphRun.h; path = Core/Source/DTCoreTextGlyphRun.h; sourceTree = ""; }; + DBFD3B43AEFF5EF15DD36197 /* Pods-STableViewController-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-STableViewController-prefix.pch"; sourceTree = ""; }; + DC969E8DEEA062BF9FCB0635 /* metamacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = metamacros.h; path = Mantle/extobjc/metamacros.h; sourceTree = ""; }; + DC97954CF46773DA323661B4 /* UIImageView+WebCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+WebCache.h"; path = "SDWebImage/UIImageView+WebCache.h"; sourceTree = ""; }; + DCD7926ADFF42331088E92EB /* NSString+DTURLEncoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+DTURLEncoding.h"; path = "Core/Source/NSString+DTURLEncoding.h"; sourceTree = ""; }; + DDCCD2A03D4D4F3FFB0556C9 /* libPods-SBJson.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SBJson.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DDD2416791FED90FF21E02F8 /* DTImageTextAttachment.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTImageTextAttachment.m; path = Core/Source/DTImageTextAttachment.m; sourceTree = ""; }; + DDF6C7DE85237BEFA9C0EAD3 /* NSCoder+DTCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSCoder+DTCompatibility.h"; path = "Core/Source/NSCoder+DTCompatibility.h"; sourceTree = ""; }; + DF597B5C373F01B275417E33 /* DTFolderMonitor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTFolderMonitor.h; path = Core/Source/DTFolderMonitor.h; sourceTree = ""; }; + DF6A723334932A205E74EF3A /* DTCoreTextFontDescriptor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFontDescriptor.m; path = Core/Source/DTCoreTextFontDescriptor.m; sourceTree = ""; }; + DFA1010AA36B765E499B1FDD /* NSAttributedString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+HTML.m"; path = "Core/Source/NSAttributedString+HTML.m"; sourceTree = ""; }; + E00A730FB3A0D7136C29BAC8 /* MBProgressHUD.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = MBProgressHUD.h; sourceTree = ""; }; + E08190E89688C788EAF7D3AB /* Pods-MBProgressHUD-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-MBProgressHUD-prefix.pch"; sourceTree = ""; }; + E198021BFFF9A2FE7C037F22 /* DTHTMLParser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTHTMLParser.m; path = Core/Source/DTHTMLParser/DTHTMLParser.m; sourceTree = ""; }; + E28C861356A22F849E1F8A2E /* DTListItemHTMLElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTListItemHTMLElement.h; path = Core/Source/DTListItemHTMLElement.h; sourceTree = ""; }; + E4228BBDE97A358CCB899B7B /* DTCoreTextGlyphRun.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextGlyphRun.m; path = Core/Source/DTCoreTextGlyphRun.m; sourceTree = ""; }; + E495FC07286B9FA610C35F1F /* NSValueTransformer+MTLInversionAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSValueTransformer+MTLInversionAdditions.h"; path = "Mantle/NSValueTransformer+MTLInversionAdditions.h"; sourceTree = ""; }; + E49765B4A8AB643850A47E16 /* NSURL+DTUnshorten.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURL+DTUnshorten.h"; path = "Core/Source/NSURL+DTUnshorten.h"; sourceTree = ""; }; + E517749B614413A4BA9C4598 /* Pods-SBJson.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SBJson.xcconfig"; sourceTree = ""; }; + E59081C0EB06143AD057BAD3 /* DTColor+Compatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DTColor+Compatibility.h"; path = "Core/Source/DTColor+Compatibility.h"; sourceTree = ""; }; + E7375484F885B52DF346730C /* DTTiledLayerWithoutFade.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTTiledLayerWithoutFade.h; path = Core/Source/iOS/DTTiledLayerWithoutFade.h; sourceTree = ""; }; + E7837043A9EB881C27D148A7 /* UIApplication+DTNetworkActivity.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIApplication+DTNetworkActivity.h"; path = "Core/Source/iOS/UIApplication+DTNetworkActivity.h"; sourceTree = ""; }; + E8D0520AF1A770AB710D7265 /* NSDictionary+DTError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+DTError.h"; path = "Core/Source/NSDictionary+DTError.h"; sourceTree = ""; }; + E8F7E1C043613475AE028FB4 /* SBJson4Writer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4Writer.m; path = src/main/objc/SBJson4Writer.m; sourceTree = ""; }; + E9A33E2EDAA2355F14CBDA34 /* DTWebVideoView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTWebVideoView.m; path = Core/Source/DTWebVideoView.m; sourceTree = ""; }; + EA0B8816C7DA1DA71122BE90 /* DTCoreTextFontCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFontCollection.h; path = Core/Source/DTCoreTextFontCollection.h; sourceTree = ""; }; + EA1F63E998324A933B864BF1 /* SDWebImageDownloaderOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloaderOperation.h; path = SDWebImage/SDWebImageDownloaderOperation.h; sourceTree = ""; }; + EA66A1341B5A8EB596C03A0C /* DTCoreTextFontCollection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCoreTextFontCollection.m; path = Core/Source/DTCoreTextFontCollection.m; sourceTree = ""; }; + EA99AFD1949A958427E26882 /* 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; }; + EAAF615D779625D88C3803CC /* libPods-AFNetworking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + EB112F511BE632013ED5CEA0 /* Pods-Reachability-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Reachability-dummy.m"; sourceTree = ""; }; + EB703E7EA6C7395F91F232CC /* NSData+ImageContentType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSData+ImageContentType.h"; path = "SDWebImage/NSData+ImageContentType.h"; sourceTree = ""; }; + EC308D3EB0A448BF90595176 /* NSURL+DTComparing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURL+DTComparing.h"; path = "Core/Source/NSURL+DTComparing.h"; sourceTree = ""; }; + EC57F2F80C580BA1D339FECE /* DTAccessibilityElement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTAccessibilityElement.m; path = Core/Source/DTAccessibilityElement.m; sourceTree = ""; }; + ED46C1D27547182DEDF1398C /* SBJson4StreamParser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SBJson4StreamParser.h; path = src/main/objc/SBJson4StreamParser.h; sourceTree = ""; }; + EDBACD80680B57A390CCA5F0 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; + EEE61E9153BBEDA3C78E3373 /* Pods-EGOCache.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-EGOCache.xcconfig"; sourceTree = ""; }; + EFB97EEC7222C01E64AA0AFA /* AFHTTPRequestOperationManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPRequestOperationManager.m; path = AFNetworking/AFHTTPRequestOperationManager.m; sourceTree = ""; }; + EFC70E70FA1FB546F52DC58E /* Pods-DTCoreText-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-DTCoreText-dummy.m"; sourceTree = ""; }; + F032A391FA9974FEB16D8424 /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPSessionManager.m; path = AFNetworking/AFHTTPSessionManager.m; sourceTree = ""; }; + F088E1D7101129C6B135F01F /* MTLValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MTLValueTransformer.m; path = Mantle/MTLValueTransformer.m; sourceTree = ""; }; + F2053B64B2DB2943F0B5188E /* NSDictionary+DTError.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+DTError.m"; path = "Core/Source/NSDictionary+DTError.m"; sourceTree = ""; }; + F2EBA251105B96547A07DCEA /* EGOCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = EGOCache.h; sourceTree = ""; }; + F36DFDC1D28DDE52EC3796E4 /* NSString+HTML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+HTML.m"; path = "Core/Source/NSString+HTML.m"; sourceTree = ""; }; + F371ABD4531A53010DFF1B0A /* SBJson4StreamWriterState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4StreamWriterState.m; path = src/main/objc/SBJson4StreamWriterState.m; sourceTree = ""; }; + F3C15A09965A76FB14357315 /* UIButton+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIButton+AFNetworking.m"; path = "UIKit+AFNetworking/UIButton+AFNetworking.m"; sourceTree = ""; }; + F3C2266FC0CEEEED5742B433 /* DTWeakSupport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTWeakSupport.h; path = Core/Source/DTWeakSupport.h; sourceTree = ""; }; + F3F21EEACD8507ED0252B482 /* DTAttributedTextContentView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTAttributedTextContentView.h; path = Core/Source/DTAttributedTextContentView.h; sourceTree = ""; }; + F54F6D9A0488B12179371EF5 /* NSArray+DTError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+DTError.h"; path = "Core/Source/NSArray+DTError.h"; sourceTree = ""; }; + F5F22809819A01C39B5C9566 /* SBJson4Parser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SBJson4Parser.m; path = src/main/objc/SBJson4Parser.m; sourceTree = ""; }; + F682CD207E26545FC21C23E4 /* NSObject+MTLComparisonAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+MTLComparisonAdditions.h"; path = "Mantle/NSObject+MTLComparisonAdditions.h"; sourceTree = ""; }; + F6B15107CDB30F016A3287B7 /* NSAttributedString+DTDebug.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+DTDebug.m"; path = "Core/Source/NSAttributedString+DTDebug.m"; sourceTree = ""; }; + F70AC862BA746865DA102F47 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + FA072650B95846F21456B71D /* NSMutableArray+DTMoving.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+DTMoving.h"; path = "Core/Source/NSMutableArray+DTMoving.h"; sourceTree = ""; }; + FA3C9B76C6CB2B8E8AD78A5D /* DTCustomColoredAccessory.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTCustomColoredAccessory.m; path = Core/Source/iOS/DTCustomColoredAccessory.m; sourceTree = ""; }; + FA3CB55DAB2A2950F0DAF0C6 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextLayoutFrameAccessibilityElementGenerator.h; path = Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.h; sourceTree = ""; }; + FA8B5BEEBC19FB385FCCD58B /* DTSmartPagingScrollView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DTSmartPagingScrollView.m; path = Core/Source/iOS/DTSmartPagingScrollView.m; sourceTree = ""; }; + FAA41802E83005EB01C21826 /* MBProgressHUD.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = MBProgressHUD.m; sourceTree = ""; }; + FB630DF11D1945A734238961 /* UIProgressView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIProgressView+AFNetworking.h"; path = "UIKit+AFNetworking/UIProgressView+AFNetworking.h"; sourceTree = ""; }; + FBE40E3E904127BB450B3FD4 /* NSAttributedString+SmallCaps.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+SmallCaps.m"; path = "Core/Source/NSAttributedString+SmallCaps.m"; sourceTree = ""; }; + FC77E089388C8856323DA3BB /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLRequestSerialization.m; path = AFNetworking/AFURLRequestSerialization.m; sourceTree = ""; }; + FC9A7467DFDD854EF3C94812 /* DTExtendedFileAttributes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTExtendedFileAttributes.h; path = Core/Source/DTExtendedFileAttributes.h; sourceTree = ""; }; + FCA578486B8C46E809FE8BC6 /* NSData+Godzippa.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+Godzippa.m"; path = "Godzippa/NSData+Godzippa.m"; sourceTree = ""; }; + FD901850C8BB4946D4508DC2 /* MTLModel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MTLModel.m; path = Mantle/MTLModel.m; sourceTree = ""; }; + FDA44BD8987326E6506A677A /* NSValueTransformer+MTLPredefinedTransformerAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSValueTransformer+MTLPredefinedTransformerAdditions.m"; path = "Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m"; sourceTree = ""; }; + FF6C581616FEC60EAF92818B /* Pods-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-acknowledgements.markdown"; sourceTree = ""; }; + FFCA0EA913B070BF15B0823D /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1744D52129F88E41AD4832B2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 185D5357505F0F39C5D04631 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1CC02958158B7F2940012F86 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AD8E2541BFDAEE26D2751460 /* CoreGraphics.framework in Frameworks */, + 632DCF9C1FB9B535111A658F /* CoreText.framework in Frameworks */, + 541EF05C376C64CA1DDF426C /* Foundation.framework in Frameworks */, + 86F76E1C9198F1C6747CAEA0 /* ImageIO.framework in Frameworks */, + 29F902685D1DADF99157C686 /* MediaPlayer.framework in Frameworks */, + B2E8C082F4A6487860419FD3 /* QuartzCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1CE4724CDDADB9F6AC694684 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 829E3CCF98B84434C7F2BEAE /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4692AD318E813A720D52710A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 295FDF855B4E2B3F8F73EC1F /* Foundation.framework in Frameworks */, + 98142A5EB29A45F1B7A42E5A /* ImageIO.framework in Frameworks */, + 771DE92C7116683778C06935 /* QuartzCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4EC37A1FA06F2D8BA89A4C82 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D9E5C5DC3C7255D0ABFDA68 /* Foundation.framework in Frameworks */, + 9D360FA230D9012E699CBA92 /* ImageIO.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 557A2EA9D5F29D69F9A17C26 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2FB69FAC5583253FD49B122 /* CoreGraphics.framework in Frameworks */, + 44AA3CEC5B596ED5F4E51890 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6323BD06B74E253065D1F5B5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C34763940F077F6025B86F02 /* Foundation.framework in Frameworks */, + 9585F212B94D2A484959443D /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 68FDA229229167E7F458CF3B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B1DC7B89D864F6B02EEDA74B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9001FC43EFDB0FA7573FE283 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 84ED666A37AFB365A95D324D /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AD1D32C760443A3AC8E26434 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 321F2323FA8CFA062B53B31B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BFFDEE9417D0FA49C4305055 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8F3EF58DA81526ABDB2B7879 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DBCFD7B737F1BA735B70A8DA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CC47185CD83952BBD8AD4F29 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F1B2C90A3E954D06E40E123C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 99FB40845A706C3EFC2A87D8 /* CoreGraphics.framework in Frameworks */, + 1E6B778FC5E9F2F75547761F /* Foundation.framework in Frameworks */, + 46A90EF74ACA421D5E3D8715 /* MobileCoreServices.framework in Frameworks */, + F8F1996A698C08E96BFADDAA /* Security.framework in Frameworks */, + C8F315C06587A14B844B27EE /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 13325DC3A19819C1243DCF54 /* Support Files */ = { + isa = PBXGroup; + children = ( + 729CC1C46AB6C6F9A5FF910A /* Pods-SDWebImage.xcconfig */, + 51B757136856DB70036EF6E5 /* Pods-SDWebImage-Private.xcconfig */, + A9C4C308E5654ADF4D23B3B4 /* Pods-SDWebImage-dummy.m */, + A1A4ECC1525DC43647B6285C /* Pods-SDWebImage-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-SDWebImage"; + sourceTree = ""; + }; + 14C0DB963C6A954C6AB6114C /* DTHTMLParser */ = { + isa = PBXGroup; + children = ( + 2A1F1C6F924DBF4917B97A1B /* DTHTMLParser.h */, + E198021BFFF9A2FE7C037F22 /* DTHTMLParser.m */, + ); + name = DTHTMLParser; + sourceTree = ""; + }; + 1AE8A7870B8007F8EC550D15 /* Support Files */ = { + isa = PBXGroup; + children = ( + 415E34057EF4F3E375C35F62 /* Pods-SecureUDID.xcconfig */, + D845CF0C240B6ED9C52C6423 /* Pods-SecureUDID-Private.xcconfig */, + 03941FD41B7FBA5E343D1349 /* Pods-SecureUDID-dummy.m */, + 992C5817448ED305FA3AD445 /* Pods-SecureUDID-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-SecureUDID"; + sourceTree = ""; + }; + 23EF747865BF7806B3A5F26A /* EGOCache */ = { + isa = PBXGroup; + children = ( + F2EBA251105B96547A07DCEA /* EGOCache.h */, + 4A7FA25460BD0A14CE76D3DF /* EGOCache.m */, + 7D4C769D221BB5114B92DA5C /* Support Files */, + ); + path = EGOCache; + sourceTree = ""; + }; + 27205F2897E814FC177176C4 /* NSURLConnection */ = { + isa = PBXGroup; + children = ( + 59479D57BEE566952C5882C7 /* AFHTTPRequestOperation.h */, + 70D0115758D95D3FC9BEA163 /* AFHTTPRequestOperation.m */, + BFAF6715DE24498F152ADAAC /* AFHTTPRequestOperationManager.h */, + EFB97EEC7222C01E64AA0AFA /* AFHTTPRequestOperationManager.m */, + 1B8793FEA52392FA7191B99E /* AFURLConnectionOperation.h */, + 62C8556BD6C33126297EACEC /* AFURLConnectionOperation.m */, + ); + name = NSURLConnection; + sourceTree = ""; + }; + 2F5B4C0801719E5E58C90585 /* AFNetworking */ = { + isa = PBXGroup; + children = ( + 1B664423A7FD62262F9F2B35 /* AFNetworking.h */, + 27205F2897E814FC177176C4 /* NSURLConnection */, + 852B78CACD4EA0EEF1DB8CD9 /* NSURLSession */, + 412B4745DF78CF65E50034E1 /* Reachability */, + FB6ED99F670A89E3F221FA8A /* Security */, + 94255EE38FFC7E12F240D096 /* Serialization */, + 6B62943745E9220B841B43C7 /* Support Files */, + 34453EEDB44C1CC46C8B13A8 /* UIKit */, + ); + path = AFNetworking; + sourceTree = ""; + }; + 32C81C9845EAA3BC0DFD8C1E /* Support Files */ = { + isa = PBXGroup; + children = ( + A012184FCB2A91D1B3FD1F88 /* Pods-DTCoreText.xcconfig */, + 9F2FC1F559C3E93CF482DDF4 /* Pods-DTCoreText-Private.xcconfig */, + EFC70E70FA1FB546F52DC58E /* Pods-DTCoreText-dummy.m */, + 600D123E65ED5A8F8C1E6220 /* Pods-DTCoreText-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-DTCoreText"; + sourceTree = ""; + }; + 34453EEDB44C1CC46C8B13A8 /* UIKit */ = { + isa = PBXGroup; + children = ( + 26880A2C45EBEF24D2C3AA1D /* AFNetworkActivityIndicatorManager.h */, + 2EAA2847D3C5E00C6F7FB80B /* AFNetworkActivityIndicatorManager.m */, + 91C83B61CE2EF0D23B05EFFE /* UIActivityIndicatorView+AFNetworking.h */, + 8EB56E074E9C09F2406F744A /* UIActivityIndicatorView+AFNetworking.m */, + C2B5049DC7669D3A6F803BDE /* UIAlertView+AFNetworking.h */, + 2F60AFB8DB89C64EB54A4343 /* UIAlertView+AFNetworking.m */, + 9B38A1D04122792A32F17DB1 /* UIButton+AFNetworking.h */, + F3C15A09965A76FB14357315 /* UIButton+AFNetworking.m */, + A1CF066DE80C9B874E3E7DF4 /* UIImageView+AFNetworking.h */, + 88EE6A2C5E812DB3AA45E0BB /* UIImageView+AFNetworking.m */, + 0D7B9F66B9415561599C8879 /* UIKit+AFNetworking.h */, + FB630DF11D1945A734238961 /* UIProgressView+AFNetworking.h */, + 17C459247AAAEF8F0357FF79 /* UIProgressView+AFNetworking.m */, + 5FCA4151E6647F1CFEFCC278 /* UIRefreshControl+AFNetworking.h */, + 70029B1D27A0D1E81984D23A /* UIRefreshControl+AFNetworking.m */, + 7AEDC475AE181182905CC7C9 /* UIWebView+AFNetworking.h */, + 5ED322C23FFB410FA8D01C3A /* UIWebView+AFNetworking.m */, + ); + name = UIKit; + sourceTree = ""; + }; + 412B4745DF78CF65E50034E1 /* Reachability */ = { + isa = PBXGroup; + children = ( + D0C98317A2D9BC73A065CD84 /* AFNetworkReachabilityManager.h */, + 56E447DB384683DAEBFFA224 /* AFNetworkReachabilityManager.m */, + ); + name = Reachability; + sourceTree = ""; + }; + 4DD9A328FB8954DC86E72FC3 /* Reachability */ = { + isa = PBXGroup; + children = ( + 868740E807D0BE8A311DD536 /* Reachability.h */, + B89B359C774D0BEEADCF72FE /* Reachability.m */, + 5AE9328AE5160F6F0171E034 /* Support Files */, + ); + path = Reachability; + sourceTree = ""; + }; + 5AE9328AE5160F6F0171E034 /* Support Files */ = { + isa = PBXGroup; + children = ( + 25273CFE86FF9C58AE4AC127 /* Pods-Reachability.xcconfig */, + 0CAA48134B8A56EEB761E70F /* Pods-Reachability-Private.xcconfig */, + EB112F511BE632013ED5CEA0 /* Pods-Reachability-dummy.m */, + 8E4B16797709C7881194F23F /* Pods-Reachability-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-Reachability"; + sourceTree = ""; + }; + 6B62943745E9220B841B43C7 /* Support Files */ = { + isa = PBXGroup; + children = ( + BA5F17CD2A18CA002DA9B0EA /* Pods-AFNetworking.xcconfig */, + 4881CF962E85111BB932C11C /* Pods-AFNetworking-Private.xcconfig */, + 02F6C05E609F4CB08029EBD2 /* Pods-AFNetworking-dummy.m */, + 5B1086BD033B9EAA35C830F9 /* Pods-AFNetworking-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-AFNetworking"; + sourceTree = ""; + }; + 79CBCB486D4F16EB61975800 /* extobjc */ = { + isa = PBXGroup; + children = ( + 74CF1A5A1EE415567B64AE71 /* EXTKeyPathCoding.h */, + 9085028F0F1956A346E6C1FB /* EXTRuntimeExtensions.h */, + 25F5B5E271E33029DFD03BEF /* EXTRuntimeExtensions.m */, + C4B18DCB03C46935261F01FB /* EXTScope.h */, + D24263A900677EE623D3679C /* EXTScope.m */, + DC969E8DEEA062BF9FCB0635 /* metamacros.h */, + ); + name = extobjc; + sourceTree = ""; + }; + 7CEDFFDD03FC7AF4A468CD36 /* Mantle */ = { + isa = PBXGroup; + children = ( + 6EA8F67176A1B0327A19827A /* MTLJSONAdapter.h */, + 773EE21FF809BE6B99B9AC7A /* MTLJSONAdapter.m */, + D2E5C56114175812BBC678AC /* MTLManagedObjectAdapter.h */, + 8D90CEF4D748B98F5C4F9223 /* MTLManagedObjectAdapter.m */, + 8BE128E57C6D864A051CC8A5 /* MTLModel.h */, + FD901850C8BB4946D4508DC2 /* MTLModel.m */, + 94D42913953EF40E145B5E1E /* MTLModel+NSCoding.h */, + 2D24DCCEF60B5A02011747B8 /* MTLModel+NSCoding.m */, + 5A1D41BE1C3FD8398AF8BAD1 /* MTLReflection.h */, + 08648726B4DF14197FFE2669 /* MTLReflection.m */, + 524815F3F479FF6AA50B15DD /* MTLValueTransformer.h */, + F088E1D7101129C6B135F01F /* MTLValueTransformer.m */, + 80A2C8039197A10FCF713E70 /* Mantle.h */, + 5D0427BB77844F551A044D37 /* NSArray+MTLManipulationAdditions.h */, + B413A933260FB0F4B93158CC /* NSArray+MTLManipulationAdditions.m */, + 124E98330C7C6C750FC9E2C1 /* NSDictionary+MTLManipulationAdditions.h */, + 6467D845AE0A362D6D98BAAB /* NSDictionary+MTLManipulationAdditions.m */, + 675C7BA63827ACB61FF3B9BB /* NSError+MTLModelException.h */, + DA95D3A59A8B4392A30955E3 /* NSError+MTLModelException.m */, + F682CD207E26545FC21C23E4 /* NSObject+MTLComparisonAdditions.h */, + B1012FEDA5057A6C62506D76 /* NSObject+MTLComparisonAdditions.m */, + E495FC07286B9FA610C35F1F /* NSValueTransformer+MTLInversionAdditions.h */, + 83AB1625C41A6A0C8B22D379 /* NSValueTransformer+MTLInversionAdditions.m */, + ABEB20229B4440B279466FB6 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h */, + FDA44BD8987326E6506A677A /* NSValueTransformer+MTLPredefinedTransformerAdditions.m */, + ED20B50E96609DB52E139FAC /* Support Files */, + 79CBCB486D4F16EB61975800 /* extobjc */, + ); + path = Mantle; + sourceTree = ""; + }; + 7D4C769D221BB5114B92DA5C /* Support Files */ = { + isa = PBXGroup; + children = ( + EEE61E9153BBEDA3C78E3373 /* Pods-EGOCache.xcconfig */, + 301AB1AD7F62B5AE55C9823C /* Pods-EGOCache-Private.xcconfig */, + 4D24C02FBF4C29B7C9BD5965 /* Pods-EGOCache-dummy.m */, + CA4C181DEEE4F965BC1B8B3C /* Pods-EGOCache-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-EGOCache"; + sourceTree = ""; + }; + 7FE7EEA1A0BF0A3CA62D2CA9 /* MBProgressHUD */ = { + isa = PBXGroup; + children = ( + E00A730FB3A0D7136C29BAC8 /* MBProgressHUD.h */, + FAA41802E83005EB01C21826 /* MBProgressHUD.m */, + 900B55A0BC771F17626A2BBC /* Support Files */, + ); + path = MBProgressHUD; + sourceTree = ""; + }; + 852B78CACD4EA0EEF1DB8CD9 /* NSURLSession */ = { + isa = PBXGroup; + children = ( + 181B343ACA3D419E8FF324D6 /* AFHTTPSessionManager.h */, + F032A391FA9974FEB16D8424 /* AFHTTPSessionManager.m */, + AD36549EF3A54A1AD6CC794E /* AFURLSessionManager.h */, + A7A256B6D33BA5BB7B04F363 /* AFURLSessionManager.m */, + ); + name = NSURLSession; + sourceTree = ""; + }; + 88788B8DCE6A42535E13EA21 /* STableViewController */ = { + isa = PBXGroup; + children = ( + 9E787096E33E2E0215B8203E /* STableViewController.h */, + 711CF44F1F86192C72867546 /* STableViewController.m */, + ED6B1E38B2C5525DE7EE7C84 /* Support Files */, + ); + path = STableViewController; + sourceTree = ""; + }; + 8BB14C39C30044A06E2DC9EA /* Godzippa */ = { + isa = PBXGroup; + children = ( + AC40CBC3BCEB21F15A6DB3A7 /* Godzippa.h */, + 5551BF11DCD892AAD12F8198 /* NSData+Godzippa.h */, + FCA578486B8C46E809FE8BC6 /* NSData+Godzippa.m */, + 648F316B0C15B0C42B9F483D /* NSFileManager+Godzippa.h */, + 12DE9529ACCA6BA9429BA38E /* NSFileManager+Godzippa.m */, + A8B9BA30596C971772588F5D /* Support Files */, + ); + path = Godzippa; + sourceTree = ""; + }; + 900B55A0BC771F17626A2BBC /* Support Files */ = { + isa = PBXGroup; + children = ( + 81F000D344438B5DE5B50F83 /* Pods-MBProgressHUD.xcconfig */, + 5F8D4344C9F779F3EC9A84CC /* Pods-MBProgressHUD-Private.xcconfig */, + 210C12C212EB913E2531E21F /* Pods-MBProgressHUD-dummy.m */, + E08190E89688C788EAF7D3AB /* Pods-MBProgressHUD-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-MBProgressHUD"; + sourceTree = ""; + }; + 9397C915BEAA0C8DB2D88FC8 = { + isa = PBXGroup; + children = ( + 95D8CA211E8F70F133431035 /* Podfile */, + A8E1143DD764E9C3C42DCE49 /* Frameworks */, + E10704731447FDE68CC79AC7 /* Pods */, + C8804537B6D6407F76DF362E /* Products */, + D25157F1C80836AC45A2C418 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 94255EE38FFC7E12F240D096 /* Serialization */ = { + isa = PBXGroup; + children = ( + 72E37C355B02AC874A58D9DC /* AFURLRequestSerialization.h */, + FC77E089388C8856323DA3BB /* AFURLRequestSerialization.m */, + C5DE72E147F2C83C77BFD809 /* AFURLResponseSerialization.h */, + D407039B1428945391E15138 /* AFURLResponseSerialization.m */, + ); + name = Serialization; + sourceTree = ""; + }; + 9F42795112BF6261F8230A7C /* Core */ = { + isa = PBXGroup; + children = ( + 1C09C605E4DF2F9240812865 /* DTAsyncFileDeleter.h */, + 81D9D642C6BD33E1FF820543 /* DTAsyncFileDeleter.m */, + 13478FB8906355D0CFF5F748 /* DTBase64Coding.h */, + AF6401F09777B31791FA7DCD /* DTBase64Coding.m */, + 7524B99AB4298B2349FD1128 /* DTBlockFunctions.h */, + C1ABA98F064631211B3CC4A1 /* DTBlockFunctions.m */, + C2C05ECAAEFDD379130CEC69 /* DTCoreGraphicsUtils.h */, + 43C3357CEED63E5037D30890 /* DTCoreGraphicsUtils.m */, + FC9A7467DFDD854EF3C94812 /* DTExtendedFileAttributes.h */, + 221F95CBD014F9E63281BEE7 /* DTExtendedFileAttributes.m */, + DF597B5C373F01B275417E33 /* DTFolderMonitor.h */, + 0A23B07692829993061C93B8 /* DTFolderMonitor.m */, + CC13076D657AA25084358E58 /* DTFoundationConstants.h */, + 531F610A6F31FEA918264187 /* DTFoundationConstants.m */, + 2566190B16B3CD1AF9910616 /* DTLog.h */, + 7847982D5FED405E21CA5041 /* DTLog.m */, + 4AFC81561555A203DCA80986 /* DTVersion.h */, + BE965E64C9C461F7C6E8DBDE /* DTVersion.m */, + F3C2266FC0CEEEED5742B433 /* DTWeakSupport.h */, + F54F6D9A0488B12179371EF5 /* NSArray+DTError.h */, + 858693B43C8CC8542AEA5B41 /* NSArray+DTError.m */, + 8CFF339A8E0F272B95A05400 /* NSData+DTCrypto.h */, + 27E3DD3C14D91284AB605925 /* NSData+DTCrypto.m */, + E8D0520AF1A770AB710D7265 /* NSDictionary+DTError.h */, + F2053B64B2DB2943F0B5188E /* NSDictionary+DTError.m */, + 7512364D89353CEF9E99172C /* NSFileWrapper+DTCopying.h */, + C80E4986A69A3380A13EDA4F /* NSFileWrapper+DTCopying.m */, + FA072650B95846F21456B71D /* NSMutableArray+DTMoving.h */, + 8EFC006BEF1B085DB3C57659 /* NSMutableArray+DTMoving.m */, + D15510793C867C9BDD98F461 /* NSString+DTFormatNumbers.h */, + 753131FB3B0B281B823061DB /* NSString+DTFormatNumbers.m */, + 37ED3CAD19ADFD28C18D3D5A /* NSString+DTPaths.h */, + 429293BD4B1AA8A0171C4214 /* NSString+DTPaths.m */, + DCD7926ADFF42331088E92EB /* NSString+DTURLEncoding.h */, + 13C19578BFB4FFB0FB793EAC /* NSString+DTURLEncoding.m */, + 4D06E48BDE9CA5867AEA039A /* NSString+DTUtilities.h */, + 3E9CD7B761C440D9F616B334 /* NSString+DTUtilities.m */, + EC308D3EB0A448BF90595176 /* NSURL+DTComparing.h */, + AF8A9093D0B137CE9B22C7A2 /* NSURL+DTComparing.m */, + E49765B4A8AB643850A47E16 /* NSURL+DTUnshorten.h */, + 121D4C2CE488ECFF04E7CC4A /* NSURL+DTUnshorten.m */, + ); + name = Core; + sourceTree = ""; + }; + A63D21D1E1894280189C387D /* Support Files */ = { + isa = PBXGroup; + children = ( + E517749B614413A4BA9C4598 /* Pods-SBJson.xcconfig */, + 59B4A447BE0C035D09F66B29 /* Pods-SBJson-Private.xcconfig */, + B98FCB2622BDB2878F1AD1F6 /* Pods-SBJson-dummy.m */, + C91562ADBA84A66B5A686B5B /* Pods-SBJson-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-SBJson"; + sourceTree = ""; + }; + A8B9BA30596C971772588F5D /* Support Files */ = { + isa = PBXGroup; + children = ( + 07FA956C464AF62249F42893 /* Pods-Godzippa.xcconfig */, + D9B387604713395C983D6059 /* Pods-Godzippa-Private.xcconfig */, + 436327479F3B7896DCBE3876 /* Pods-Godzippa-dummy.m */, + 705873D7F9EFE10352328291 /* Pods-Godzippa-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-Godzippa"; + sourceTree = ""; + }; + A8E1143DD764E9C3C42DCE49 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B26F0E34F4888B129D82BCD3 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + B26F0E34F4888B129D82BCD3 /* iOS */ = { + isa = PBXGroup; + children = ( + F70AC862BA746865DA102F47 /* CoreGraphics.framework */, + 7FFB2500E4380D67EF8A7E41 /* CoreText.framework */, + EA99AFD1949A958427E26882 /* Foundation.framework */, + 4CE3DB4CDED08038EE99FAD6 /* ImageIO.framework */, + 12CFFC0AAEB2049E3D38839B /* MediaPlayer.framework */, + B431B9F8EEBC811ECBF6A5D4 /* MobileCoreServices.framework */, + 7D9DA8599F9A65C67763F7C8 /* QuartzCore.framework */, + EDBACD80680B57A390CCA5F0 /* Security.framework */, + 2C650430B30114D4A8D3A459 /* SystemConfiguration.framework */, + ); + name = iOS; + sourceTree = ""; + }; + BFFB510FC728CB8ACE812006 /* DTAnimatedGIF */ = { + isa = PBXGroup; + children = ( + 0100264285411761FBAEF5AB /* DTAnimatedGIF.h */, + 451382DBA924128C00A949F8 /* DTAnimatedGIF.m */, + ); + name = DTAnimatedGIF; + sourceTree = ""; + }; + C10C14482AB1F06C50ACE846 /* Support Files */ = { + isa = PBXGroup; + children = ( + 6218027F5B4E249C3AD17D43 /* Pods-DTFoundation.xcconfig */, + 2A874FAFF81C4D3E50640FB5 /* Pods-DTFoundation-Private.xcconfig */, + 313D9BC92774DAE5CC6D7A8F /* Pods-DTFoundation-dummy.m */, + 496866DDF2FA8C8B8DE160E1 /* Pods-DTFoundation-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-DTFoundation"; + sourceTree = ""; + }; + C130C9914E5079B78B8AEBA7 /* UIKit */ = { + isa = PBXGroup; + children = ( + AD7766A1EDACC24F6BBDFD7C /* DTActivityTitleView.h */, + AB4E35D99C28AD0A2FF788B0 /* DTActivityTitleView.m */, + 085E58814B278EF843F3DA88 /* DTCustomColoredAccessory.h */, + FA3C9B76C6CB2B8E8AD78A5D /* DTCustomColoredAccessory.m */, + 600F177FEBAD10AE1CB576B9 /* DTPieProgressIndicator.h */, + 980E5243E51E9C323AA25021 /* DTPieProgressIndicator.m */, + D9177A65BD60036FE28650C3 /* DTSmartPagingScrollView.h */, + FA8B5BEEBC19FB385FCCD58B /* DTSmartPagingScrollView.m */, + E7375484F885B52DF346730C /* DTTiledLayerWithoutFade.h */, + 631A5E7C34AF78A47633C5B9 /* DTTiledLayerWithoutFade.m */, + 8301F7F2078CE377A3C33B23 /* NSURL+DTAppLinks.h */, + BE42064DDDEC23B7164ACBAA /* NSURL+DTAppLinks.m */, + E7837043A9EB881C27D148A7 /* UIApplication+DTNetworkActivity.h */, + 0F09EFAA166D58583C1180D7 /* UIApplication+DTNetworkActivity.m */, + CDA3C0D0DBA5CA06E7D5F29C /* UIImage+DTFoundation.h */, + 09D74DAE23C038E1A14CE924 /* UIImage+DTFoundation.m */, + 69E9E1C87B98BF60ED6B0429 /* UIView+DTFoundation.h */, + AD5E1EDE9FFA20FF03F5BCA6 /* UIView+DTFoundation.m */, + A4F7B232DD157C6696C84091 /* UIWebView+DTFoundation.h */, + A8A9067C9B073EC236A3E05D /* UIWebView+DTFoundation.m */, + ); + name = UIKit; + sourceTree = ""; + }; + C5742A04A93053B56B331A51 /* SecureUDID */ = { + isa = PBXGroup; + children = ( + 4047E9A4D159D0380E1B9036 /* SecureUDID.h */, + AF8355CDB07E5B6CF7F8EB08 /* SecureUDID.m */, + 1AE8A7870B8007F8EC550D15 /* Support Files */, + ); + path = SecureUDID; + sourceTree = ""; + }; + C8804537B6D6407F76DF362E /* Products */ = { + isa = PBXGroup; + children = ( + FFCA0EA913B070BF15B0823D /* libPods.a */, + EAAF615D779625D88C3803CC /* libPods-AFNetworking.a */, + 7A63E6AC5C6B2A60F4A3EC9C /* libPods-DTCoreText.a */, + 53744589D3AD1C9BA3FC3A13 /* libPods-DTFoundation.a */, + 4222F268674ABE22FB012CE8 /* libPods-EGOCache.a */, + B1C23B5198890B521A4DF725 /* libPods-Godzippa.a */, + 9EE1E9C074198D7DBA970623 /* libPods-MBProgressHUD.a */, + 439B2C42A0B6956DB02EBC70 /* libPods-Mantle.a */, + 857FA51AF993242E771D051A /* libPods-Reachability.a */, + DDCCD2A03D4D4F3FFB0556C9 /* libPods-SBJson.a */, + 1429FB4555EBB97DE560F5EB /* libPods-SDWebImage.a */, + 275C328BCE7F67B37082744B /* libPods-STableViewController.a */, + 32C11BFEBFEC9CE3AF6EBE87 /* libPods-SecureUDID.a */, + ); + name = Products; + sourceTree = ""; + }; + CBEA9D43235C1F1AF39F36F4 /* Pods */ = { + isa = PBXGroup; + children = ( + FF6C581616FEC60EAF92818B /* Pods-acknowledgements.markdown */, + D7749F1AF8D031564B1684A0 /* Pods-acknowledgements.plist */, + 02856BB35E43267D13DC3C28 /* Pods-dummy.m */, + 471A3ED3891038D7D9EBB38F /* Pods-environment.h */, + A1CF7E7A550A22B59D81840F /* Pods-resources.sh */, + 09E7556AF9D2BB4E65D06658 /* Pods.debug.xcconfig */, + A79E2E040FCDDB7E3B284DB8 /* Pods.release.xcconfig */, + ); + name = Pods; + path = "Target Support Files/Pods"; + sourceTree = ""; + }; + D25157F1C80836AC45A2C418 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + CBEA9D43235C1F1AF39F36F4 /* Pods */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + D8B676D68A0DDF3998D4F27D /* DTFoundation */ = { + isa = PBXGroup; + children = ( + 9F42795112BF6261F8230A7C /* Core */, + BFFB510FC728CB8ACE812006 /* DTAnimatedGIF */, + 14C0DB963C6A954C6AB6114C /* DTHTMLParser */, + C10C14482AB1F06C50ACE846 /* Support Files */, + C130C9914E5079B78B8AEBA7 /* UIKit */, + ); + path = DTFoundation; + sourceTree = ""; + }; + DCE7707371B3CDA8B8E8BEBC /* DTCoreText */ = { + isa = PBXGroup; + children = ( + 29C23375359F548C0F64C164 /* DTAccessibilityElement.h */, + EC57F2F80C580BA1D339FECE /* DTAccessibilityElement.m */, + 84513A2E6747DF1945EB57E6 /* DTAccessibilityViewProxy.h */, + 6EE2E34F5BA4FA31632E183E /* DTAccessibilityViewProxy.m */, + 21E3E130EB30C467D9C459DE /* DTAnchorHTMLElement.h */, + BD038E1FE6A969A543DAD866 /* DTAnchorHTMLElement.m */, + 765380382A09CD38070F811C /* DTAttributedLabel.h */, + CCD9AC3DC59EFD8A7760B6BC /* DTAttributedLabel.m */, + 198A05236D0FC24D7567E389 /* DTAttributedTextCell.h */, + 3139D5BE25741C00C26B11D2 /* DTAttributedTextCell.m */, + F3F21EEACD8507ED0252B482 /* DTAttributedTextContentView.h */, + ACED3FB738D11F8A10B394A3 /* DTAttributedTextContentView.m */, + 9021B480CC3318D413444498 /* DTAttributedTextView.h */, + 5D323E71416F20743DEBB7A7 /* DTAttributedTextView.m */, + D930E71CD2A5F2B17ACCF87C /* DTBreakHTMLElement.h */, + C634EA81A406E4A8BFEE29DA /* DTBreakHTMLElement.m */, + 5048BF1FE7D776CF0E99C53D /* DTCSSListStyle.h */, + 80B0262BBEA47B83C25F3008 /* DTCSSListStyle.m */, + 499B36A4F41B73059E29F3E0 /* DTCSSStylesheet.h */, + 58632D3FA9629CB1A72776FD /* DTCSSStylesheet.m */, + E59081C0EB06143AD057BAD3 /* DTColor+Compatibility.h */, + 2DF67C46304BCAB7A718C6A0 /* DTColor+Compatibility.m */, + 0E14665E1CB4D66432BD3126 /* DTColorFunctions.h */, + 1AAD4CF32D8A346312E37D3A /* DTColorFunctions.m */, + 31EC48DC08E838E541102AD5 /* DTCompatibility.h */, + 8FB81A3F3CC5C8EEE0D0A053 /* DTCoreText.h */, + 21F213E272C360C6D6AA4EA1 /* DTCoreTextConstants.h */, + 78085540FD8FB9B1E5B4B884 /* DTCoreTextConstants.m */, + EA0B8816C7DA1DA71122BE90 /* DTCoreTextFontCollection.h */, + EA66A1341B5A8EB596C03A0C /* DTCoreTextFontCollection.m */, + 6C4CF9E12F9F5431B1DB7424 /* DTCoreTextFontDescriptor.h */, + DF6A723334932A205E74EF3A /* DTCoreTextFontDescriptor.m */, + 32F92EAE13C59713EAC676DF /* DTCoreTextFunctions.h */, + 1593656BA44FF46A80E0DECD /* DTCoreTextFunctions.m */, + DBEF3AD8AF418F5D0DB9FA36 /* DTCoreTextGlyphRun.h */, + E4228BBDE97A358CCB899B7B /* DTCoreTextGlyphRun.m */, + A6384E42A1880883FE59B266 /* DTCoreTextLayoutFrame.h */, + 0E6DD371D30AD5BE472DB12F /* DTCoreTextLayoutFrame.m */, + 511326CEB4AAD81D048FC1CC /* DTCoreTextLayoutFrame+Cursor.h */, + 7CBCF8EDBCBA565559D7FD04 /* DTCoreTextLayoutFrame+Cursor.m */, + FA3CB55DAB2A2950F0DAF0C6 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h */, + 95E8E93955BD84AB7D61B561 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m */, + A72BEDBD2D2DB007DDBDC61A /* DTCoreTextLayoutLine.h */, + 9F3AA2982830D803D19E4E40 /* DTCoreTextLayoutLine.m */, + 17023854963F0DB77E8B6E1D /* DTCoreTextLayouter.h */, + D4429C837BD4425D786F352A /* DTCoreTextLayouter.m */, + 3141E70A8914DBA8EF4C8BF4 /* DTCoreTextMacros.h */, + 39928E568AFCA854CA213D64 /* DTCoreTextParagraphStyle.h */, + A758F0825180829D7CE75B87 /* DTCoreTextParagraphStyle.m */, + 8DABDE8AEC54A0F7DAE9ACED /* DTDictationPlaceholderTextAttachment.h */, + 6FF95BB286D9C557B742A904 /* DTDictationPlaceholderTextAttachment.m */, + B35D826DD7A9C3AED97F787C /* DTDictationPlaceholderView.h */, + 0E2BE295E0CA7EBC4570237D /* DTDictationPlaceholderView.m */, + D2B82CCEE3DEBD49E4E63DCB /* DTHTMLAttributedStringBuilder.h */, + D35A7D47AFBD0118F9B61044 /* DTHTMLAttributedStringBuilder.m */, + 034681345CE339CC0D7F4BC7 /* DTHTMLElement.h */, + 623DD018EBC66CF653AB4EB8 /* DTHTMLElement.m */, + 1E65128E94D4FDD10661ED58 /* DTHTMLParserNode.h */, + 2F808E117E65DD98C903DACD /* DTHTMLParserNode.m */, + 98B78BA7BD331BD145EF9497 /* DTHTMLParserTextNode.h */, + 5A91454224704755E8B78C0F /* DTHTMLParserTextNode.m */, + 2DDC4EE3C0AEEB5D12C523DC /* DTHTMLWriter.h */, + 9097904A80D8A7F24ADC07DB /* DTHTMLWriter.m */, + 1321C721D6711CABE43A8E72 /* DTHorizontalRuleHTMLElement.h */, + A3473269AF9E0EF0A56CF9B2 /* DTHorizontalRuleHTMLElement.m */, + 5F7CF4F74439B9D4D12F935C /* DTIframeTextAttachment.h */, + 862F1FEB67B7CEFE9074BE6E /* DTIframeTextAttachment.m */, + 2688EDDC3B016EC4E28E8C38 /* DTImage+HTML.h */, + 13305323CFB19F9B7DBBF7B7 /* DTImage+HTML.m */, + 03CDA45689F3CBAC4D698D25 /* DTImageTextAttachment.h */, + DDD2416791FED90FF21E02F8 /* DTImageTextAttachment.m */, + B10FE77E0C21FFBB51B6DE3A /* DTLazyImageView.h */, + 93CC40769EDB0F6EE0D5ED72 /* DTLazyImageView.m */, + 977BBA287FC7987841DABAD4 /* DTLinkButton.h */, + 4D5BD60D8CE96A58045E1EEF /* DTLinkButton.m */, + E28C861356A22F849E1F8A2E /* DTListItemHTMLElement.h */, + B03C715F5A8AEA36DC55688C /* DTListItemHTMLElement.m */, + 229A3A64BEB4B6DD9D5AB3CD /* DTObjectTextAttachment.h */, + 18FD09FA7EC7172A286E2375 /* DTObjectTextAttachment.m */, + 2C0EA53A4DA9ED8E20C9A1FE /* DTStylesheetHTMLElement.h */, + D3B28D24ACD99923AE54B04E /* DTStylesheetHTMLElement.m */, + 982D461DCD6CCFF9DE4D96AE /* DTTextAttachment.h */, + BAEAE04DC9D6049ECD98EADA /* DTTextAttachment.m */, + 08480DC911580F657E539083 /* DTTextAttachmentHTMLElement.h */, + 66C6218CF6E13E047F8CDD16 /* DTTextAttachmentHTMLElement.m */, + 5C699FE7FCD126A43DBC1131 /* DTTextBlock.h */, + 9678D7E8E9FB8406B0A8FBF9 /* DTTextBlock.m */, + CED305D27339CE40E6A4451C /* DTTextHTMLElement.h */, + C58EAF174D65FF96676BC746 /* DTTextHTMLElement.m */, + 8B5CBFFC0BEA9B64BB37BE4D /* DTVideoTextAttachment.h */, + 330A7FB9F02C86B2A3D1FB27 /* DTVideoTextAttachment.m */, + 2ADE193BEB78F37154DF3071 /* DTWebVideoView.h */, + E9A33E2EDAA2355F14CBDA34 /* DTWebVideoView.m */, + 1AB2BD7A7440128E0C379878 /* NSAttributedString+DTCoreText.h */, + C138EF04D1765832AB4C5AEC /* NSAttributedString+DTCoreText.m */, + 8EE25AF5774365C08845F9C4 /* NSAttributedString+DTDebug.h */, + F6B15107CDB30F016A3287B7 /* NSAttributedString+DTDebug.m */, + 0B33B1CFDC6A466868B6EE09 /* NSAttributedString+HTML.h */, + DFA1010AA36B765E499B1FDD /* NSAttributedString+HTML.m */, + 4F2EFD81D171B1FCF5D860C8 /* NSAttributedString+SmallCaps.h */, + FBE40E3E904127BB450B3FD4 /* NSAttributedString+SmallCaps.m */, + 00B21AE8AAA30A1308C14335 /* NSAttributedStringRunDelegates.h */, + 02DC7955EDC01BBD2A005D13 /* NSAttributedStringRunDelegates.m */, + 3586666B2105EBE27406E900 /* NSCharacterSet+HTML.h */, + 6E746C3CFC80A6AD3E9C140E /* NSCharacterSet+HTML.m */, + DDF6C7DE85237BEFA9C0EAD3 /* NSCoder+DTCompatibility.h */, + BD775CFA71EB688731C92C0A /* NSCoder+DTCompatibility.m */, + B439B8C90661AC8FFC66E04D /* NSDictionary+DTCoreText.h */, + 977A96477E21AFE119F2C818 /* NSDictionary+DTCoreText.m */, + 0BF40ECE7E4E0682190A71C4 /* NSMutableAttributedString+HTML.h */, + B249319A47C18D3B6B91AC78 /* NSMutableAttributedString+HTML.m */, + AC5A316ABA995034D8C3B6C8 /* NSMutableString+HTML.h */, + 83D2D6D8C8AFB62D63AAB400 /* NSMutableString+HTML.m */, + 4E8C90E8027986DF576B89C4 /* NSScanner+HTML.h */, + 718C8CE67FFF7CAE824B278D /* NSScanner+HTML.m */, + 749B8332FF564663E57BFCE9 /* NSString+CSS.h */, + 9C11C7730CECE62713E638B8 /* NSString+CSS.m */, + 6D228843B5620E10036CEF16 /* NSString+HTML.h */, + F36DFDC1D28DDE52EC3796E4 /* NSString+HTML.m */, + 4CAEDBC718BBC91DC5AB3CD6 /* NSString+Paragraphs.h */, + 0000202764C2F63046B6D1CF /* NSString+Paragraphs.m */, + 031D447444B32B34E8E8ABA3 /* UIFont+DTCoreText.h */, + 1F0F4805C5D575AADE2BB87D /* UIFont+DTCoreText.m */, + ACB76F8380C3F1D5515F2A4B /* default.css.c */, + 32C81C9845EAA3BC0DFD8C1E /* Support Files */, + ); + path = DTCoreText; + sourceTree = ""; + }; + E10704731447FDE68CC79AC7 /* Pods */ = { + isa = PBXGroup; + children = ( + 2F5B4C0801719E5E58C90585 /* AFNetworking */, + DCE7707371B3CDA8B8E8BEBC /* DTCoreText */, + D8B676D68A0DDF3998D4F27D /* DTFoundation */, + 23EF747865BF7806B3A5F26A /* EGOCache */, + 8BB14C39C30044A06E2DC9EA /* Godzippa */, + 7FE7EEA1A0BF0A3CA62D2CA9 /* MBProgressHUD */, + 7CEDFFDD03FC7AF4A468CD36 /* Mantle */, + 4DD9A328FB8954DC86E72FC3 /* Reachability */, + E980B5E93DFF2526CC0B51AF /* SBJson */, + F01DEB227294AD5116DF6FB3 /* SDWebImage */, + 88788B8DCE6A42535E13EA21 /* STableViewController */, + C5742A04A93053B56B331A51 /* SecureUDID */, + ); + name = Pods; + sourceTree = ""; + }; + E980B5E93DFF2526CC0B51AF /* SBJson */ = { + isa = PBXGroup; + children = ( + CA5F091D0B1700AFE4DF24AC /* SBJson4.h */, + 1E6483BA82724E795E88521B /* SBJson4Parser.h */, + F5F22809819A01C39B5C9566 /* SBJson4Parser.m */, + ED46C1D27547182DEDF1398C /* SBJson4StreamParser.h */, + CAB06CD8D214A93380CA359F /* SBJson4StreamParser.m */, + 99BDEC117EA48AB5F7091E99 /* SBJson4StreamParserState.h */, + 08BFFA49C32FC84AC3484233 /* SBJson4StreamParserState.m */, + 8631416B11CCD525941D9201 /* SBJson4StreamTokeniser.h */, + 6E6EDE8ED4679A5632162A1F /* SBJson4StreamTokeniser.m */, + A4189FA23F95F690EA94D56C /* SBJson4StreamWriter.h */, + 94D59B3E6671961F9FC0D3B9 /* SBJson4StreamWriter.m */, + 7A059B8990583D788A1583F5 /* SBJson4StreamWriterState.h */, + F371ABD4531A53010DFF1B0A /* SBJson4StreamWriterState.m */, + 306A576CB103E0DD61B011B4 /* SBJson4Writer.h */, + E8F7E1C043613475AE028FB4 /* SBJson4Writer.m */, + A63D21D1E1894280189C387D /* Support Files */, + ); + path = SBJson; + sourceTree = ""; + }; + ED20B50E96609DB52E139FAC /* Support Files */ = { + isa = PBXGroup; + children = ( + 9B9A846D33E7580CAF1F9228 /* Pods-Mantle.xcconfig */, + 462B84C37B10724E69874AC2 /* Pods-Mantle-Private.xcconfig */, + A8619D6089A77D57DF8CED95 /* Pods-Mantle-dummy.m */, + 49708DE5D2380D2CBA14932B /* Pods-Mantle-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-Mantle"; + sourceTree = ""; + }; + ED6B1E38B2C5525DE7EE7C84 /* Support Files */ = { + isa = PBXGroup; + children = ( + 2A145653E1D487150FE0CBA9 /* Pods-STableViewController.xcconfig */, + 994CD0C8E00BBA950FDC15C5 /* Pods-STableViewController-Private.xcconfig */, + B6CBABA8BF5C20E72993CAB8 /* Pods-STableViewController-dummy.m */, + DBFD3B43AEFF5EF15DD36197 /* Pods-STableViewController-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Pods-STableViewController"; + sourceTree = ""; + }; + F01DEB227294AD5116DF6FB3 /* SDWebImage */ = { + isa = PBXGroup; + children = ( + F5DD1CB2116BCD19AF11E86B /* Core */, + 13325DC3A19819C1243DCF54 /* Support Files */, + ); + path = SDWebImage; + sourceTree = ""; + }; + F5DD1CB2116BCD19AF11E86B /* Core */ = { + isa = PBXGroup; + children = ( + EB703E7EA6C7395F91F232CC /* NSData+ImageContentType.h */, + B4440BBB972015707D689FF1 /* NSData+ImageContentType.m */, + AA3E1D39F56369F6EDA19850 /* SDImageCache.h */, + 9135155F192247F273C4BFF1 /* SDImageCache.m */, + D75D8DD36B2DF3A94A8D8BDD /* SDWebImageCompat.h */, + 0A4E3976555492FF46129D87 /* SDWebImageCompat.m */, + BC7EB9B8C3DBE8D6402CCCF4 /* SDWebImageDecoder.h */, + 6763C598E5DC3B3A5A0655F9 /* SDWebImageDecoder.m */, + 463D2C1499B05E24A3C9466E /* SDWebImageDownloader.h */, + 349161E009EB67623A7A576E /* SDWebImageDownloader.m */, + EA1F63E998324A933B864BF1 /* SDWebImageDownloaderOperation.h */, + B4F7C13BA2FCD171CDD56CDC /* SDWebImageDownloaderOperation.m */, + 40835E80F4E2B55E419BA88E /* SDWebImageManager.h */, + 5B7F037412F1FDD7AE4A9587 /* SDWebImageManager.m */, + 3C095AE7D2B4E1C122FE2C0C /* SDWebImageOperation.h */, + 07197356167DAEDE87AD03C3 /* SDWebImagePrefetcher.h */, + 3E104C4243AE675E793B5BAB /* SDWebImagePrefetcher.m */, + 9E62EA74C8AE7E7D9633E921 /* UIButton+WebCache.h */, + BE8902F9D667F3CC87EB82C4 /* UIButton+WebCache.m */, + 5F69301F38456253353C2FB3 /* UIImage+GIF.h */, + 6B8EEF53333924DE92410F18 /* UIImage+GIF.m */, + 9DE1F401D6E30BE57ED5553E /* UIImage+MultiFormat.h */, + 98E8971B9A319D428BC49638 /* UIImage+MultiFormat.m */, + 60D2BD28B2F0D25CE81FC512 /* UIImageView+HighlightedWebCache.h */, + 2CF3B0023ABA3D5362C4C9EB /* UIImageView+HighlightedWebCache.m */, + DC97954CF46773DA323661B4 /* UIImageView+WebCache.h */, + 31C53FE22C24600E279C2A81 /* UIImageView+WebCache.m */, + 3CC250296BA77530564C8631 /* UIView+WebCacheOperation.h */, + 0263AD274F91008318446903 /* UIView+WebCacheOperation.m */, + ); + name = Core; + sourceTree = ""; + }; + FB6ED99F670A89E3F221FA8A /* Security */ = { + isa = PBXGroup; + children = ( + C336AC714C253351DB4D3E1D /* AFSecurityPolicy.h */, + 7AB780C1815C17A0D187A5D9 /* AFSecurityPolicy.m */, + ); + name = Security; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 065426E868E1B467CD918410 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 8615AFA7EACA0C898BAFB936 /* Godzippa.h in Headers */, + C13995FB16410EC19B591697 /* NSData+Godzippa.h in Headers */, + 0F0A2C6A2BB724885704612F /* NSFileManager+Godzippa.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0D11D2DB296DEE83AB3BCB07 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 814E1D72F788E010C724643E /* EGOCache.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 445BF1DC645266B0BF00EBC8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A5E557B33AA32F2059FB4E04 /* MBProgressHUD.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6686FE6A6815543F973E68B0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + AC3456F15937764410B8CBC7 /* SecureUDID.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6C806EBCFC8B61D4D4075726 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 65085786CA31DD9D3CB703AA /* Reachability.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 700D3A48751D5219455EB237 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A26E1BCAE7003001459B520 /* STableViewController.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 94D87363E915F7C76C592233 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 937BC16FD45E5333DDB02A99 /* AFHTTPRequestOperation.h in Headers */, + 6DB67A3F39F51BCB5722BD1F /* AFHTTPRequestOperationManager.h in Headers */, + 4E83DB75860E8D40A5926BFC /* AFHTTPSessionManager.h in Headers */, + 7352114F0969A1090E887BA2 /* AFNetworkActivityIndicatorManager.h in Headers */, + 20EBED8E298504927CB38D69 /* AFNetworkReachabilityManager.h in Headers */, + F05C84F2C7A451756D3E2C3D /* AFNetworking.h in Headers */, + 31A0AFF6C96292F12D3B9712 /* AFSecurityPolicy.h in Headers */, + A9EE3CC09E2DB29E60D9FC33 /* AFURLConnectionOperation.h in Headers */, + BC30233499EEA41D1B79C352 /* AFURLRequestSerialization.h in Headers */, + 7D596AC35EA03B3FB79E63C8 /* AFURLResponseSerialization.h in Headers */, + 421D4B741F5A803ED2C70860 /* AFURLSessionManager.h in Headers */, + A798D9B3034547C09E83F358 /* UIActivityIndicatorView+AFNetworking.h in Headers */, + 9B882F43716A56DFADA5566F /* UIAlertView+AFNetworking.h in Headers */, + B8A779CD128129DFDF61A0CB /* UIButton+AFNetworking.h in Headers */, + A3E4281B7B817B7973E8BF3A /* UIImageView+AFNetworking.h in Headers */, + 09665611DC56E1BB0AD3A8EF /* UIKit+AFNetworking.h in Headers */, + 914119B2F1C42A9394654CD1 /* UIProgressView+AFNetworking.h in Headers */, + D95B20BCEF61A201C12BE3E4 /* UIRefreshControl+AFNetworking.h in Headers */, + 4A41EC290E6E4993B5A8BEEF /* UIWebView+AFNetworking.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9ADE021BBD202D120864D652 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 93F806636B85BA09EC55E61D /* DTActivityTitleView.h in Headers */, + E50DD5C9A1D62DAF34719CB9 /* DTAnimatedGIF.h in Headers */, + 47E5693168FE0FD41B08E4D5 /* DTAsyncFileDeleter.h in Headers */, + 8ACA5FF363531E8533E14B3B /* DTBase64Coding.h in Headers */, + 1D6661D2BAE2603741406D09 /* DTBlockFunctions.h in Headers */, + 2095AB02626F82C3BFECE0BF /* DTCoreGraphicsUtils.h in Headers */, + E139CE6CC1EE4617174263DD /* DTCustomColoredAccessory.h in Headers */, + E58BE4878AC0529F7B16B51F /* DTExtendedFileAttributes.h in Headers */, + 1DF1511AED1ED3D7D108B1B4 /* DTFolderMonitor.h in Headers */, + DCE8615C91B2D5C1173FD4D1 /* DTFoundationConstants.h in Headers */, + 35E2FD9B211B81E86C4D6FF8 /* DTHTMLParser.h in Headers */, + B7F0DA5A42C46845F1929D92 /* DTLog.h in Headers */, + 7830A690D9A0ACB28817A52F /* DTPieProgressIndicator.h in Headers */, + E57FD66AC32DE0F8C7FBB973 /* DTSmartPagingScrollView.h in Headers */, + C6B0B8DAFD83D2C685C49397 /* DTTiledLayerWithoutFade.h in Headers */, + 025B8F23517C2E2D6C8E5B0A /* DTVersion.h in Headers */, + CE7EE0058A71A4D222CAA4E9 /* DTWeakSupport.h in Headers */, + C34FA0F6289DE254F12B1A6C /* NSArray+DTError.h in Headers */, + 47689BD55E998A4443FEC356 /* NSData+DTCrypto.h in Headers */, + ACB7546D8938B32152D42820 /* NSDictionary+DTError.h in Headers */, + B48CC4E852D8EBC259F5E934 /* NSFileWrapper+DTCopying.h in Headers */, + F087F348D1C60D4FABFC4F97 /* NSMutableArray+DTMoving.h in Headers */, + 9DC59B5AB2F4BEED7AABFDE1 /* NSString+DTFormatNumbers.h in Headers */, + FEEDC23525EBE5BA766FF715 /* NSString+DTPaths.h in Headers */, + 40439EB75949CCC27C74EEB2 /* NSString+DTURLEncoding.h in Headers */, + D49E76E28CA995A0C500A2EA /* NSString+DTUtilities.h in Headers */, + 5BC2EC66463E842B0B956ADC /* NSURL+DTAppLinks.h in Headers */, + CD42A2421AD4E769BD5D5A6B /* NSURL+DTComparing.h in Headers */, + D8A8525D5C07AA0BE9255726 /* NSURL+DTUnshorten.h in Headers */, + 873EF7F0BC972FB913ECC928 /* UIApplication+DTNetworkActivity.h in Headers */, + 4536A105F3E7CA56003ED6D7 /* UIImage+DTFoundation.h in Headers */, + 03FEB05066DFF6AF53B7D39C /* UIView+DTFoundation.h in Headers */, + CB90B296E1832BCB538E84CD /* UIWebView+DTFoundation.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2FB76598E2BF9D3BF776F2D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5A11C4C2E8900CBA9F87F867 /* NSData+ImageContentType.h in Headers */, + F205AFE36DAF280D295AB44A /* SDImageCache.h in Headers */, + C2E336F8768AAF053C034E9E /* SDWebImageCompat.h in Headers */, + 7C2BDE6C40775A8227268816 /* SDWebImageDecoder.h in Headers */, + 902EE13D641DB030842ED966 /* SDWebImageDownloader.h in Headers */, + 06A39D2C0BE4E5C0C58F925E /* SDWebImageDownloaderOperation.h in Headers */, + F4852FA60E2EC97814726C55 /* SDWebImageManager.h in Headers */, + C4D544EEA622A81517444651 /* SDWebImageOperation.h in Headers */, + FF5D332BB1254B7808BCB323 /* SDWebImagePrefetcher.h in Headers */, + D84657DAD8F43A566863C534 /* UIButton+WebCache.h in Headers */, + 32C63F178EBD39CA7AA9F05A /* UIImage+GIF.h in Headers */, + AAEBCF5D301CCB8790619B66 /* UIImage+MultiFormat.h in Headers */, + 6CF3735F2787AAB0F30F79F9 /* UIImageView+HighlightedWebCache.h in Headers */, + 45E1B3508DA65ABAE6B6CCFD /* UIImageView+WebCache.h in Headers */, + 461BBBA11D7AC5A56F57B76A /* UIView+WebCacheOperation.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B70A9BBF019E5CB0FE330B73 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 3E47CAC5074E18E77550A384 /* EXTKeyPathCoding.h in Headers */, + 4AE6DE6A90D76A5E825443E6 /* EXTRuntimeExtensions.h in Headers */, + 1BA8386868B95662C6607E0E /* EXTScope.h in Headers */, + 6BE18395789E145663DD8AAC /* MTLJSONAdapter.h in Headers */, + 58C73BE476258C1B13B8908B /* MTLManagedObjectAdapter.h in Headers */, + D28BE1F81FC1BB8E03643C33 /* MTLModel+NSCoding.h in Headers */, + 006E8A5DDB48AD0E82306788 /* MTLModel.h in Headers */, + 8B3331673A14B3E3F18D78BE /* MTLReflection.h in Headers */, + FA049A0D048A88922969E495 /* MTLValueTransformer.h in Headers */, + CBF38583A945F44960FED8C8 /* Mantle.h in Headers */, + 0FEBF5E40C70242ADEC557E0 /* NSArray+MTLManipulationAdditions.h in Headers */, + 15F3EB3135EA5C3BC80BCBC6 /* NSDictionary+MTLManipulationAdditions.h in Headers */, + FD77C063EEA3207EA01EB550 /* NSError+MTLModelException.h in Headers */, + E81F3DA70F2DE25048968CD3 /* NSObject+MTLComparisonAdditions.h in Headers */, + 268D6909B71C79CC18B6B959 /* NSValueTransformer+MTLInversionAdditions.h in Headers */, + 2D8D387E9FD546E2B03ABB39 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h in Headers */, + 91A4B793968B4E69F46A53DB /* metamacros.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ECB91AD660951F287C604A27 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F1C961363E12D2AFDAB8699 /* SBJson4.h in Headers */, + 90B220E0FBD8BFC9AFA1F6C0 /* SBJson4Parser.h in Headers */, + 8FF49F70E6E95B8563020000 /* SBJson4StreamParser.h in Headers */, + CCEE6FC7D3410A79A752031C /* SBJson4StreamParserState.h in Headers */, + 32C531ED8DE3B177D1A8ACF0 /* SBJson4StreamTokeniser.h in Headers */, + C7AFD84573BC801CDF859A41 /* SBJson4StreamWriter.h in Headers */, + D6E2BC2513B64A350CB868E7 /* SBJson4StreamWriterState.h in Headers */, + B5AC89CB2D9E01445724E2F8 /* SBJson4Writer.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F80FB7E6C0B0220BA0D2D9FA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 743213F71753153707379338 /* DTAccessibilityElement.h in Headers */, + 0D74B76307D3FFBA9CFD144E /* DTAccessibilityViewProxy.h in Headers */, + AE9AFBA6F8C69255ABB8B5FF /* DTAnchorHTMLElement.h in Headers */, + CBA4D8AC17AE1B1B33AC6698 /* DTAttributedLabel.h in Headers */, + A618F1AC97A49B6EFA20737A /* DTAttributedTextCell.h in Headers */, + AEC9D1786E7FD34490B50328 /* DTAttributedTextContentView.h in Headers */, + 6FC98B5B70DB1AFD81BE5ECB /* DTAttributedTextView.h in Headers */, + A3C11D206C9C8E8544A93AE6 /* DTBreakHTMLElement.h in Headers */, + 18E0D08DDB96160495E07051 /* DTCSSListStyle.h in Headers */, + 1142A10869BD826C4718A17C /* DTCSSStylesheet.h in Headers */, + 297D0CA90179129CC1C33C49 /* DTColor+Compatibility.h in Headers */, + 8676051929B4CB63D317DDEA /* DTColorFunctions.h in Headers */, + BCFEF4BD15F36298C36D1C6D /* DTCompatibility.h in Headers */, + 8CB422996C4C2C2E6F215DFA /* DTCoreText.h in Headers */, + 98B992D2F710F64D9811DDF6 /* DTCoreTextConstants.h in Headers */, + 8227B1DEDFDCF4F404706997 /* DTCoreTextFontCollection.h in Headers */, + 183D801DD22D9B6FC571203E /* DTCoreTextFontDescriptor.h in Headers */, + 9C81821B6B5AEDC7D35C05FB /* DTCoreTextFunctions.h in Headers */, + A0DE573AD455495418B70689 /* DTCoreTextGlyphRun.h in Headers */, + E77BB0E36975923F3B64D625 /* DTCoreTextLayoutFrame+Cursor.h in Headers */, + 78AD68EDB757EE08807BFE10 /* DTCoreTextLayoutFrame.h in Headers */, + 3169F1A2BB798470827CA2AC /* DTCoreTextLayoutFrameAccessibilityElementGenerator.h in Headers */, + 53C2CC0D9AC616DC5A1FD145 /* DTCoreTextLayoutLine.h in Headers */, + 89E8E51F68B762A5E498E955 /* DTCoreTextLayouter.h in Headers */, + F86C8BDF958E95140B09C440 /* DTCoreTextMacros.h in Headers */, + CCF9CB47C58FDFE6B85F788A /* DTCoreTextParagraphStyle.h in Headers */, + 7440F7C02AF2CF5670806875 /* DTDictationPlaceholderTextAttachment.h in Headers */, + 37569F8A96C035D32EE5E467 /* DTDictationPlaceholderView.h in Headers */, + 635CEB8A826BA00DEC25F107 /* DTHTMLAttributedStringBuilder.h in Headers */, + 3C49D84D9495797298A68717 /* DTHTMLElement.h in Headers */, + 938052F386CA31E805C89C2C /* DTHTMLParserNode.h in Headers */, + DB4BFC9A2071B0B162BBEE6D /* DTHTMLParserTextNode.h in Headers */, + 6E41C1B1DCFD1734ED45E655 /* DTHTMLWriter.h in Headers */, + B7B9AFCDB7D18A35AF42F963 /* DTHorizontalRuleHTMLElement.h in Headers */, + 2607530297B5305CE76E6E73 /* DTIframeTextAttachment.h in Headers */, + E2A651C267AF90FC069AF713 /* DTImage+HTML.h in Headers */, + 58B501E02AD95E7A9DAEE42C /* DTImageTextAttachment.h in Headers */, + 65169BB7EEE5F582D93E1532 /* DTLazyImageView.h in Headers */, + BCC1E43F38919F8458618D1A /* DTLinkButton.h in Headers */, + E6E67253E3A018A5258D5449 /* DTListItemHTMLElement.h in Headers */, + 749BE2234A38066B3F34009F /* DTObjectTextAttachment.h in Headers */, + B947160D514F27E2A92651DD /* DTStylesheetHTMLElement.h in Headers */, + BAAE263C13C07C2C764DEBEA /* DTTextAttachment.h in Headers */, + FD8842DB63CE74002BC66191 /* DTTextAttachmentHTMLElement.h in Headers */, + B2933834DD6328D056AD30C1 /* DTTextBlock.h in Headers */, + FC550C9621953C2570FAFC11 /* DTTextHTMLElement.h in Headers */, + B820EED8DE06567A092F4993 /* DTVideoTextAttachment.h in Headers */, + B0940F71B5AC2A8EA130FCA3 /* DTWebVideoView.h in Headers */, + AEF03B4A5B3944F2EF87C250 /* NSAttributedString+DTCoreText.h in Headers */, + 13EAAAEE13C61A26A470DE1D /* NSAttributedString+DTDebug.h in Headers */, + 7B660EAB3E3D508DEDE4938B /* NSAttributedString+HTML.h in Headers */, + D64996D2F94443EA203C14FF /* NSAttributedString+SmallCaps.h in Headers */, + 30E0C85B295BB7080C1D130D /* NSAttributedStringRunDelegates.h in Headers */, + 921424517EF3A81C2FAECFAD /* NSCharacterSet+HTML.h in Headers */, + 3C450D4A15AC3879D088DA0A /* NSCoder+DTCompatibility.h in Headers */, + 5F889348A787181D3DEB35DC /* NSDictionary+DTCoreText.h in Headers */, + 4A4492948E213192F36CFDF5 /* NSMutableAttributedString+HTML.h in Headers */, + 2DDB042B5FCC8BBB36942239 /* NSMutableString+HTML.h in Headers */, + F031A055A9D138B59D13EB53 /* NSScanner+HTML.h in Headers */, + 08D2FAAEB4028205D841FF4B /* NSString+CSS.h in Headers */, + 57553B499FA92F48C4E4CC2F /* NSString+HTML.h in Headers */, + 0C648546F9A6A55795BD788F /* NSString+Paragraphs.h in Headers */, + 8EC1828E3A868C0F540ABB7B /* UIFont+DTCoreText.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 081D18D023E185DE5065872B /* Pods-DTFoundation */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84F3F45C6DBCB0C76472378D /* Build configuration list for PBXNativeTarget "Pods-DTFoundation" */; + buildPhases = ( + E0657E6D272381A356E7EAFD /* Sources */, + 4692AD318E813A720D52710A /* Frameworks */, + 9ADE021BBD202D120864D652 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-DTFoundation"; + productName = "Pods-DTFoundation"; + productReference = 53744589D3AD1C9BA3FC3A13 /* libPods-DTFoundation.a */; + productType = "com.apple.product-type.library.static"; + }; + 1E660322F08C69E13AF91D0F /* Pods-SDWebImage */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8280045FF85BAAF43926C959 /* Build configuration list for PBXNativeTarget "Pods-SDWebImage" */; + buildPhases = ( + 8C01EE72B07BBDD9B0AEB655 /* Sources */, + 4EC37A1FA06F2D8BA89A4C82 /* Frameworks */, + B2FB76598E2BF9D3BF776F2D /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-SDWebImage"; + productName = "Pods-SDWebImage"; + productReference = 1429FB4555EBB97DE560F5EB /* libPods-SDWebImage.a */; + productType = "com.apple.product-type.library.static"; + }; + 2231DE2C5936A2D8C4DE7FBF /* Pods-DTCoreText */ = { + isa = PBXNativeTarget; + buildConfigurationList = 70C77834355BFEE06924CFFA /* Build configuration list for PBXNativeTarget "Pods-DTCoreText" */; + buildPhases = ( + EF1BF1141045174BCB0AB050 /* Sources */, + 1CC02958158B7F2940012F86 /* Frameworks */, + F80FB7E6C0B0220BA0D2D9FA /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 213AE57AA451A82EF3D48088 /* PBXTargetDependency */, + ); + name = "Pods-DTCoreText"; + productName = "Pods-DTCoreText"; + productReference = 7A63E6AC5C6B2A60F4A3EC9C /* libPods-DTCoreText.a */; + productType = "com.apple.product-type.library.static"; + }; + 3381CB8EE23A95628CC6C71F /* Pods-Mantle */ = { + isa = PBXNativeTarget; + buildConfigurationList = D1D7D8F709122FA04206070C /* Build configuration list for PBXNativeTarget "Pods-Mantle" */; + buildPhases = ( + 4658AD50FB08493E2158914C /* Sources */, + 1CE4724CDDADB9F6AC694684 /* Frameworks */, + B70A9BBF019E5CB0FE330B73 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-Mantle"; + productName = "Pods-Mantle"; + productReference = 439B2C42A0B6956DB02EBC70 /* libPods-Mantle.a */; + productType = "com.apple.product-type.library.static"; + }; + 55917C107FF891B225E9EF02 /* Pods-Godzippa */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5344A1AEFE356DC99694D09B /* Build configuration list for PBXNativeTarget "Pods-Godzippa" */; + buildPhases = ( + 662A92AAB3D9239CFF59E54F /* Sources */, + DBCFD7B737F1BA735B70A8DA /* Frameworks */, + 065426E868E1B467CD918410 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-Godzippa"; + productName = "Pods-Godzippa"; + productReference = B1C23B5198890B521A4DF725 /* libPods-Godzippa.a */; + productType = "com.apple.product-type.library.static"; + }; + 687D88D56283FDAD6B3057E4 /* Pods-SecureUDID */ = { + isa = PBXNativeTarget; + buildConfigurationList = F306A4B30D9F9B2C2128EFE6 /* Build configuration list for PBXNativeTarget "Pods-SecureUDID" */; + buildPhases = ( + D3C6CEA5080611575E150C05 /* Sources */, + BFFDEE9417D0FA49C4305055 /* Frameworks */, + 6686FE6A6815543F973E68B0 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-SecureUDID"; + productName = "Pods-SecureUDID"; + productReference = 32C11BFEBFEC9CE3AF6EBE87 /* libPods-SecureUDID.a */; + productType = "com.apple.product-type.library.static"; + }; + 7863761EC80891B08770A0B7 /* Pods-EGOCache */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9EB870635A419E642B119123 /* Build configuration list for PBXNativeTarget "Pods-EGOCache" */; + buildPhases = ( + 706AA0DD4538D312D3D5F253 /* Sources */, + 1744D52129F88E41AD4832B2 /* Frameworks */, + 0D11D2DB296DEE83AB3BCB07 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-EGOCache"; + productName = "Pods-EGOCache"; + productReference = 4222F268674ABE22FB012CE8 /* libPods-EGOCache.a */; + productType = "com.apple.product-type.library.static"; + }; + 7D2693D881B07E422984E1FA /* Pods-AFNetworking */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3C76A66FEEF9D15848839674 /* Build configuration list for PBXNativeTarget "Pods-AFNetworking" */; + buildPhases = ( + EA663784427713CF23D7DD2A /* Sources */, + F1B2C90A3E954D06E40E123C /* Frameworks */, + 94D87363E915F7C76C592233 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-AFNetworking"; + productName = "Pods-AFNetworking"; + productReference = EAAF615D779625D88C3803CC /* libPods-AFNetworking.a */; + productType = "com.apple.product-type.library.static"; + }; + 7EACA4AE3AF64CCDA69E50CB /* Pods-STableViewController */ = { + isa = PBXNativeTarget; + buildConfigurationList = A7DCB14E1703ECD181116E80 /* Build configuration list for PBXNativeTarget "Pods-STableViewController" */; + buildPhases = ( + F32896F920FB2851236FACF5 /* Sources */, + AD1D32C760443A3AC8E26434 /* Frameworks */, + 700D3A48751D5219455EB237 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-STableViewController"; + productName = "Pods-STableViewController"; + productReference = 275C328BCE7F67B37082744B /* libPods-STableViewController.a */; + productType = "com.apple.product-type.library.static"; + }; + 9F5764BE6373D3C604DC32DB /* Pods-MBProgressHUD */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4EF728C0E84383880F8B582D /* Build configuration list for PBXNativeTarget "Pods-MBProgressHUD" */; + buildPhases = ( + 3B3383A400A86D6185355690 /* Sources */, + 557A2EA9D5F29D69F9A17C26 /* Frameworks */, + 445BF1DC645266B0BF00EBC8 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-MBProgressHUD"; + productName = "Pods-MBProgressHUD"; + productReference = 9EE1E9C074198D7DBA970623 /* libPods-MBProgressHUD.a */; + productType = "com.apple.product-type.library.static"; + }; + A592E8303475C5A51F058D5D /* Pods-SBJson */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1CAF49562A0A2D03F0381BED /* Build configuration list for PBXNativeTarget "Pods-SBJson" */; + buildPhases = ( + DF83613891D26F9FCACD99A3 /* Sources */, + 9001FC43EFDB0FA7573FE283 /* Frameworks */, + ECB91AD660951F287C604A27 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-SBJson"; + productName = "Pods-SBJson"; + productReference = DDCCD2A03D4D4F3FFB0556C9 /* libPods-SBJson.a */; + productType = "com.apple.product-type.library.static"; + }; + A988749C1B404C65594792E1 /* Pods */ = { + isa = PBXNativeTarget; + buildConfigurationList = 48D30F92BCB80393088A00BB /* Build configuration list for PBXNativeTarget "Pods" */; + buildPhases = ( + 66D5413B2712229CDF8A68A0 /* Sources */, + 68FDA229229167E7F458CF3B /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 4A116EB7C3C6158E4CB0000D /* PBXTargetDependency */, + 658B6D89855DC0AB75973B29 /* PBXTargetDependency */, + E2AAA9B0990D030C9300B2DE /* PBXTargetDependency */, + 4223D98175E97E7C6BCAF7A0 /* PBXTargetDependency */, + E98E9DA236E550D3BBCD0049 /* PBXTargetDependency */, + 0D8CB0DEA2B2E20990B9FCBF /* PBXTargetDependency */, + E8A34ECAA5B23E8E4CF3B91F /* PBXTargetDependency */, + 8939E0C15C9DD92222F623EB /* PBXTargetDependency */, + 9868EFFB41DF743EC3D373BC /* PBXTargetDependency */, + C0370989DB5450FB36453EF5 /* PBXTargetDependency */, + 5A79FEE7AEC12CCE68C56D12 /* PBXTargetDependency */, + 79A0414C91D23B9095DBE0BC /* PBXTargetDependency */, + ); + name = Pods; + productName = Pods; + productReference = FFCA0EA913B070BF15B0823D /* libPods.a */; + productType = "com.apple.product-type.library.static"; + }; + E0126A5C2A3D1F035604EE6E /* Pods-Reachability */ = { + isa = PBXNativeTarget; + buildConfigurationList = 015A823E9383AF20AF5A67D4 /* Build configuration list for PBXNativeTarget "Pods-Reachability" */; + buildPhases = ( + 1319C47B1F8893A3FCC2AF08 /* Sources */, + 6323BD06B74E253065D1F5B5 /* Frameworks */, + 6C806EBCFC8B61D4D4075726 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-Reachability"; + productName = "Pods-Reachability"; + productReference = 857FA51AF993242E771D051A /* libPods-Reachability.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F4FC1885C0BD8C197F91B8F5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0510; + }; + buildConfigurationList = 08E7EB2FF90CDC7E026B6BF9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 9397C915BEAA0C8DB2D88FC8; + productRefGroup = C8804537B6D6407F76DF362E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A988749C1B404C65594792E1 /* Pods */, + 7D2693D881B07E422984E1FA /* Pods-AFNetworking */, + 2231DE2C5936A2D8C4DE7FBF /* Pods-DTCoreText */, + 081D18D023E185DE5065872B /* Pods-DTFoundation */, + 7863761EC80891B08770A0B7 /* Pods-EGOCache */, + 55917C107FF891B225E9EF02 /* Pods-Godzippa */, + 9F5764BE6373D3C604DC32DB /* Pods-MBProgressHUD */, + 3381CB8EE23A95628CC6C71F /* Pods-Mantle */, + E0126A5C2A3D1F035604EE6E /* Pods-Reachability */, + A592E8303475C5A51F058D5D /* Pods-SBJson */, + 1E660322F08C69E13AF91D0F /* Pods-SDWebImage */, + 7EACA4AE3AF64CCDA69E50CB /* Pods-STableViewController */, + 687D88D56283FDAD6B3057E4 /* Pods-SecureUDID */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1319C47B1F8893A3FCC2AF08 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 523BE366D987FD575975D4B8 /* Pods-Reachability-dummy.m in Sources */, + 519487F4FAF706EEE0B46BDA /* Reachability.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3B3383A400A86D6185355690 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A231ABD93492364A84B5A471 /* MBProgressHUD.m in Sources */, + A4C1E805180612ECBBE6B7B5 /* Pods-MBProgressHUD-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4658AD50FB08493E2158914C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1A5EA1AC22A38461089595F3 /* EXTRuntimeExtensions.m in Sources */, + 3422822F28C19EC7D762CFB6 /* EXTScope.m in Sources */, + B1B50550FF732C90C300955C /* MTLJSONAdapter.m in Sources */, + EF7B5F9C0E24CDA067CEADD5 /* MTLManagedObjectAdapter.m in Sources */, + C1F85729F5760C1810839FD1 /* MTLModel+NSCoding.m in Sources */, + C618D33D98ADF42A90174FF5 /* MTLModel.m in Sources */, + 39F1BC63303BA6AE31E077AC /* MTLReflection.m in Sources */, + AFB8BFB855C0C63967A251D3 /* MTLValueTransformer.m in Sources */, + 47863598A02CBE4276EE356B /* NSArray+MTLManipulationAdditions.m in Sources */, + A7BCABC62211F84CAE4BD062 /* NSDictionary+MTLManipulationAdditions.m in Sources */, + 8677D9D80E290ECC0B5F17CC /* NSError+MTLModelException.m in Sources */, + D6112D0DA51751CCACA8BEAF /* NSObject+MTLComparisonAdditions.m in Sources */, + 4C9258CC9F2171809204D548 /* NSValueTransformer+MTLInversionAdditions.m in Sources */, + E68D051B6DE12611D7D4B224 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m in Sources */, + F722B92F1822D49C0F28FA9E /* Pods-Mantle-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 662A92AAB3D9239CFF59E54F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3F2BD7C9310F9FAA075BCAD0 /* NSData+Godzippa.m in Sources */, + 24FA2C0C0F51B355ED36931E /* NSFileManager+Godzippa.m in Sources */, + 3E5A91A4DC637944A6AFCF49 /* Pods-Godzippa-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 66D5413B2712229CDF8A68A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 750A9EF27A2AAB925679C558 /* Pods-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 706AA0DD4538D312D3D5F253 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CD65DFA4E39BB6E139BDB51 /* EGOCache.m in Sources */, + 40C653B8C9F9F84A45FB434C /* Pods-EGOCache-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8C01EE72B07BBDD9B0AEB655 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FEF2324967A284F786FA8862 /* NSData+ImageContentType.m in Sources */, + 64460E5AD866F07D47465E5F /* Pods-SDWebImage-dummy.m in Sources */, + CEFB7FCC5635A278E307FE4F /* SDImageCache.m in Sources */, + 783986CDC5980EBFFF2E3F07 /* SDWebImageCompat.m in Sources */, + 339A5219261849C8589C1F2C /* SDWebImageDecoder.m in Sources */, + 158B46AE25066CEEF2355EB5 /* SDWebImageDownloader.m in Sources */, + 96DAFB592CA5953A2538932C /* SDWebImageDownloaderOperation.m in Sources */, + DD8DF24D1E04B745E9B4416A /* SDWebImageManager.m in Sources */, + DCB6A670A920D76D13AAED32 /* SDWebImagePrefetcher.m in Sources */, + 84C0C37012DC64A199A376C9 /* UIButton+WebCache.m in Sources */, + D81CCA3819928CECB8631372 /* UIImage+GIF.m in Sources */, + C9485CA9D0A2F491FE3C75A0 /* UIImage+MultiFormat.m in Sources */, + 38FC274AEEB6A7995723AA84 /* UIImageView+HighlightedWebCache.m in Sources */, + 318D92F094A58B116E355A29 /* UIImageView+WebCache.m in Sources */, + FC4E8B8BB82D31ACE287990B /* UIView+WebCacheOperation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D3C6CEA5080611575E150C05 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E711807D347FF56B48AAB48A /* Pods-SecureUDID-dummy.m in Sources */, + E5818A5906AB34360097C00F /* SecureUDID.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DF83613891D26F9FCACD99A3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EA403E002B3297A7BF4C7EA1 /* Pods-SBJson-dummy.m in Sources */, + D1F8E53B1F4E073935314C10 /* SBJson4Parser.m in Sources */, + 42D9CD0A8BCD3401BE27344D /* SBJson4StreamParser.m in Sources */, + 5F147E54FD8BEC7DBD17DB3E /* SBJson4StreamParserState.m in Sources */, + 4F07009C76CA7FC9A4668CF2 /* SBJson4StreamTokeniser.m in Sources */, + 3027E6DD7D492B53F8E19335 /* SBJson4StreamWriter.m in Sources */, + C8FA434179CEEDC1152F1C07 /* SBJson4StreamWriterState.m in Sources */, + C1E4CD9FF999577AC919EC62 /* SBJson4Writer.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E0657E6D272381A356E7EAFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A90AF96FDCC3A5071BAC4144 /* DTActivityTitleView.m in Sources */, + 66AEAB7C4A3AF87F5B13E119 /* DTAnimatedGIF.m in Sources */, + EEFA03C86F230EE8BBCE1E02 /* DTAsyncFileDeleter.m in Sources */, + A47FEBDC058AD86C254F94CB /* DTBase64Coding.m in Sources */, + 945D831328EEE14295EDA3CC /* DTBlockFunctions.m in Sources */, + 6BB3B14636CBE7F37DA0FDD8 /* DTCoreGraphicsUtils.m in Sources */, + AABA793C21EFA0E7816196A1 /* DTCustomColoredAccessory.m in Sources */, + 0177A3E3A5951C0FC1BE8A73 /* DTExtendedFileAttributes.m in Sources */, + 6D3120FC189517685446E4DB /* DTFolderMonitor.m in Sources */, + FD0AD22134E622A6ADB06198 /* DTFoundationConstants.m in Sources */, + F76CB54C7DCB204DE88A6A1D /* DTHTMLParser.m in Sources */, + 27CAF58D597A80E539024F2D /* DTLog.m in Sources */, + 3F7EAF8D6277AC4CF9361105 /* DTPieProgressIndicator.m in Sources */, + B15E483D877F94E784894E8E /* DTSmartPagingScrollView.m in Sources */, + 66B33DB1CFD813A00D2A76BF /* DTTiledLayerWithoutFade.m in Sources */, + CDCFE565F410F93E1B36A8F6 /* DTVersion.m in Sources */, + 9B67C67D8BA21DF1AD5DD48D /* NSArray+DTError.m in Sources */, + 69351A995F035FC3C63A1B7B /* NSData+DTCrypto.m in Sources */, + BFD497A40209BCD4B74DC3F3 /* NSDictionary+DTError.m in Sources */, + 1FCAD3D7DB8504F29AE83E5A /* NSFileWrapper+DTCopying.m in Sources */, + 573F80B0149DC5B099187EC4 /* NSMutableArray+DTMoving.m in Sources */, + 31A1CFEAF50D4CA62F837EB0 /* NSString+DTFormatNumbers.m in Sources */, + 729CDAD5F8AC0D4024B5A469 /* NSString+DTPaths.m in Sources */, + 85B122A0B790FBB766B5D6B5 /* NSString+DTURLEncoding.m in Sources */, + F372C5BEAE9180262BE0EE19 /* NSString+DTUtilities.m in Sources */, + D99EF764ECEFCB2A341D0B55 /* NSURL+DTAppLinks.m in Sources */, + 1667D80BB4AAC91719824B33 /* NSURL+DTComparing.m in Sources */, + 299178842E6970CB24FBB92D /* NSURL+DTUnshorten.m in Sources */, + 257AAAAD2AA19C108A49975A /* Pods-DTFoundation-dummy.m in Sources */, + 3E917B3F443312CB957CEFE7 /* UIApplication+DTNetworkActivity.m in Sources */, + 0086DDE85A22673FC7AD6859 /* UIImage+DTFoundation.m in Sources */, + D5B0BB5D8753C8C35DCE88D1 /* UIView+DTFoundation.m in Sources */, + 496E00F823F9F24A147EE3A8 /* UIWebView+DTFoundation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EA663784427713CF23D7DD2A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C8B318E55390767643C2EF6A /* AFHTTPRequestOperation.m in Sources */, + E1F604BF295C4183599F1B86 /* AFHTTPRequestOperationManager.m in Sources */, + 205B3EB176711FFF8C702899 /* AFHTTPSessionManager.m in Sources */, + CB19B20ADC234425401FD225 /* AFNetworkActivityIndicatorManager.m in Sources */, + 97796C608B74E393FDC9632D /* AFNetworkReachabilityManager.m in Sources */, + CFA7AE793E4E4AF99A9397BA /* AFSecurityPolicy.m in Sources */, + 3A3F04458C7BE8BE7DF0B851 /* AFURLConnectionOperation.m in Sources */, + FF1C5E0EA5C2147FAF01EC79 /* AFURLRequestSerialization.m in Sources */, + EAE11E3D0A11CF929F2D8028 /* AFURLResponseSerialization.m in Sources */, + B1E28880C0B75E49F11FCEA9 /* AFURLSessionManager.m in Sources */, + A1BF468F622C373AF92FD1E2 /* Pods-AFNetworking-dummy.m in Sources */, + 140F91F5E0A09CDBE4A6BA3D /* UIActivityIndicatorView+AFNetworking.m in Sources */, + C86A49C44B96DB1A46E8A3AA /* UIAlertView+AFNetworking.m in Sources */, + 39970542050FEB1D39277097 /* UIButton+AFNetworking.m in Sources */, + A0BCBD0CC7ADDACED9F31FF7 /* UIImageView+AFNetworking.m in Sources */, + 3799521D45A82B28D35246C3 /* UIProgressView+AFNetworking.m in Sources */, + 87FC8EA1F74F2A658826586C /* UIRefreshControl+AFNetworking.m in Sources */, + 6AA0C5FD4313213097AC8517 /* UIWebView+AFNetworking.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EF1BF1141045174BCB0AB050 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40AE0EECD87647C61827B8CD /* DTAccessibilityElement.m in Sources */, + DF230E5C2429185B6739CF5A /* DTAccessibilityViewProxy.m in Sources */, + B24342887751389B44819681 /* DTAnchorHTMLElement.m in Sources */, + 3869F2B3ABEA4E809754E34C /* DTAttributedLabel.m in Sources */, + B6E807D7D2B9FA5F38971684 /* DTAttributedTextCell.m in Sources */, + 4F1A4CC0BBEBD17C92FDA0D0 /* DTAttributedTextContentView.m in Sources */, + C598401584ECF2DBA9BDBB89 /* DTAttributedTextView.m in Sources */, + BC415F6750A9B46CA6F31025 /* DTBreakHTMLElement.m in Sources */, + E28716DF7951966EE3057A30 /* DTCSSListStyle.m in Sources */, + A402940FF1251FB6C56260C2 /* DTCSSStylesheet.m in Sources */, + 6964DAF676F34653C54A7F08 /* DTColor+Compatibility.m in Sources */, + A08CB28DABFD69124C599A34 /* DTColorFunctions.m in Sources */, + 71DE93520654BF2FADC152CD /* DTCoreTextConstants.m in Sources */, + 9072D69F7FE72C168B9AC69D /* DTCoreTextFontCollection.m in Sources */, + D121C44563D59F112EE63D69 /* DTCoreTextFontDescriptor.m in Sources */, + 67FB538314D2551EEE5BA225 /* DTCoreTextFunctions.m in Sources */, + 0521F2C2D800AF01FCC436BC /* DTCoreTextGlyphRun.m in Sources */, + DF5D0D70F433DAD7BCA70FC3 /* DTCoreTextLayoutFrame+Cursor.m in Sources */, + 9914CB02EC65EDDF8B3BA4A5 /* DTCoreTextLayoutFrame.m in Sources */, + 228F668BDA5E4D151B7B5191 /* DTCoreTextLayoutFrameAccessibilityElementGenerator.m in Sources */, + 1486AC8C3A7297133A92AFAA /* DTCoreTextLayoutLine.m in Sources */, + 20A5C2FAB1C3E995D9FD1E78 /* DTCoreTextLayouter.m in Sources */, + 349632871DB5BDD0380803AB /* DTCoreTextParagraphStyle.m in Sources */, + F6332302421A5A6DFCAA267F /* DTDictationPlaceholderTextAttachment.m in Sources */, + 0C3907DCC2D10CA43C1FFC09 /* DTDictationPlaceholderView.m in Sources */, + A9AA93D8F85244B1E17AF3DE /* DTHTMLAttributedStringBuilder.m in Sources */, + B3DB28AD0BACD96DCE448E8B /* DTHTMLElement.m in Sources */, + 39BF0CA8E5545D8D7E3A11B3 /* DTHTMLParserNode.m in Sources */, + 869651B749404A2D1A6D390A /* DTHTMLParserTextNode.m in Sources */, + 00D46518F2022BD4A57A8436 /* DTHTMLWriter.m in Sources */, + 3593973A551BBBE8377F98F0 /* DTHorizontalRuleHTMLElement.m in Sources */, + 756B0CD301D58061B2B0DD04 /* DTIframeTextAttachment.m in Sources */, + 3350FB460047A05968BEFC4A /* DTImage+HTML.m in Sources */, + C66A11537EB5FB169072545E /* DTImageTextAttachment.m in Sources */, + DAC028C6327B39D76993C57C /* DTLazyImageView.m in Sources */, + 39D83E6EA467FEED0EFE1990 /* DTLinkButton.m in Sources */, + 9E948B93DD7F42453584A543 /* DTListItemHTMLElement.m in Sources */, + B30FFA4960DC18DB298B921B /* DTObjectTextAttachment.m in Sources */, + 2AF1A3D27062859B92E89187 /* DTStylesheetHTMLElement.m in Sources */, + 4B669FF737A17B91306D9691 /* DTTextAttachment.m in Sources */, + 04848A85C8363BB27A420E1D /* DTTextAttachmentHTMLElement.m in Sources */, + C24A4C0C3BCF7D268BF709BD /* DTTextBlock.m in Sources */, + FA45F4D479B94C4B1B0D599F /* DTTextHTMLElement.m in Sources */, + F88FE806D71817CC2994F060 /* DTVideoTextAttachment.m in Sources */, + A90FF25FDC1D30E19CA81C01 /* DTWebVideoView.m in Sources */, + 92C46811EF0CAC7736025CA7 /* NSAttributedString+DTCoreText.m in Sources */, + D9E7AD43EFC13F34505C7E6E /* NSAttributedString+DTDebug.m in Sources */, + 0C9BD490C9DE484D0B24B639 /* NSAttributedString+HTML.m in Sources */, + 3CDB315891882709CD80A8FA /* NSAttributedString+SmallCaps.m in Sources */, + D3C3EFC6280843D121CE4715 /* NSAttributedStringRunDelegates.m in Sources */, + C41C9D347427FED569814048 /* NSCharacterSet+HTML.m in Sources */, + C59757460B5B2A66111DB1A3 /* NSCoder+DTCompatibility.m in Sources */, + BA6072EDB611D4165FEBAF63 /* NSDictionary+DTCoreText.m in Sources */, + 776A4C0A07984C3E3EC1455B /* NSMutableAttributedString+HTML.m in Sources */, + 8977B3AE81A03812AC22B958 /* NSMutableString+HTML.m in Sources */, + C4066C9CB0204E7C5607F1E3 /* NSScanner+HTML.m in Sources */, + 89B3A1365C6FFA518DC2EE22 /* NSString+CSS.m in Sources */, + 0DD2CB104A575EC16ED45F0C /* NSString+HTML.m in Sources */, + F4A4C10FA290EFC508E68005 /* NSString+Paragraphs.m in Sources */, + DADB597E48335C0E9DDBD93E /* Pods-DTCoreText-dummy.m in Sources */, + 405FD5CA64325AF2CFCE489B /* UIFont+DTCoreText.m in Sources */, + 0252B09D1C770C2D67C8690D /* default.css.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F32896F920FB2851236FACF5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EC5A18F75163F5870C53FBD4 /* Pods-STableViewController-dummy.m in Sources */, + C7D3E28327D690FAF0C1D8CC /* STableViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0D8CB0DEA2B2E20990B9FCBF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-MBProgressHUD"; + target = 9F5764BE6373D3C604DC32DB /* Pods-MBProgressHUD */; + targetProxy = 0AF119C304C7E6B97C8EAEDE /* PBXContainerItemProxy */; + }; + 213AE57AA451A82EF3D48088 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-DTFoundation"; + target = 081D18D023E185DE5065872B /* Pods-DTFoundation */; + targetProxy = 2C73F227D15ECF4C66684945 /* PBXContainerItemProxy */; + }; + 4223D98175E97E7C6BCAF7A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-EGOCache"; + target = 7863761EC80891B08770A0B7 /* Pods-EGOCache */; + targetProxy = 28C31C0BA09D5AE8533E10EF /* PBXContainerItemProxy */; + }; + 4A116EB7C3C6158E4CB0000D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-AFNetworking"; + target = 7D2693D881B07E422984E1FA /* Pods-AFNetworking */; + targetProxy = A626E2F1CFCE96457A3245AE /* PBXContainerItemProxy */; + }; + 5A79FEE7AEC12CCE68C56D12 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-STableViewController"; + target = 7EACA4AE3AF64CCDA69E50CB /* Pods-STableViewController */; + targetProxy = 58BFE040792B0926A4925ED1 /* PBXContainerItemProxy */; + }; + 658B6D89855DC0AB75973B29 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-DTCoreText"; + target = 2231DE2C5936A2D8C4DE7FBF /* Pods-DTCoreText */; + targetProxy = 72EF303CC913D1118C6AE72F /* PBXContainerItemProxy */; + }; + 79A0414C91D23B9095DBE0BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-SecureUDID"; + target = 687D88D56283FDAD6B3057E4 /* Pods-SecureUDID */; + targetProxy = 1AE2B067A1CA3D72AFABE6C0 /* PBXContainerItemProxy */; + }; + 8939E0C15C9DD92222F623EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-Reachability"; + target = E0126A5C2A3D1F035604EE6E /* Pods-Reachability */; + targetProxy = 6D57ED432EE98E625C3B5900 /* PBXContainerItemProxy */; + }; + 9868EFFB41DF743EC3D373BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-SBJson"; + target = A592E8303475C5A51F058D5D /* Pods-SBJson */; + targetProxy = 982F2A730A4CD84826FDF3A2 /* PBXContainerItemProxy */; + }; + C0370989DB5450FB36453EF5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-SDWebImage"; + target = 1E660322F08C69E13AF91D0F /* Pods-SDWebImage */; + targetProxy = 3790C3B2100E0426DD94ADD3 /* PBXContainerItemProxy */; + }; + E2AAA9B0990D030C9300B2DE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-DTFoundation"; + target = 081D18D023E185DE5065872B /* Pods-DTFoundation */; + targetProxy = 1FB9CA3998814F21ECB25FA7 /* PBXContainerItemProxy */; + }; + E8A34ECAA5B23E8E4CF3B91F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-Mantle"; + target = 3381CB8EE23A95628CC6C71F /* Pods-Mantle */; + targetProxy = 1E1A2277E8E1325DC879D4EB /* PBXContainerItemProxy */; + }; + E98E9DA236E550D3BBCD0049 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-Godzippa"; + target = 55917C107FF891B225E9EF02 /* Pods-Godzippa */; + targetProxy = 4785B5502507F907A4D71B72 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 030DADA9BD7E1EA6C422B2A5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D845CF0C240B6ED9C52C6423 /* Pods-SecureUDID-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-SecureUDID/Pods-SecureUDID-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 1337296E9AC96BF9E3F186E6 /* 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 = 6.0; + ONLY_ACTIVE_ARCH = YES; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 13577551AEA65D4902824A40 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D845CF0C240B6ED9C52C6423 /* Pods-SecureUDID-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-SecureUDID/Pods-SecureUDID-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 161848FE08304EFB72811CE3 /* 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 = 6.0; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 1EE2C44E1A10EE67A5B1E5D8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 994CD0C8E00BBA950FDC15C5 /* Pods-STableViewController-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-STableViewController/Pods-STableViewController-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 1FD4C43CDA63C6A8F66A5EBA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5F8D4344C9F779F3EC9A84CC /* Pods-MBProgressHUD-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-MBProgressHUD/Pods-MBProgressHUD-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 207A5FEBB221C83965EF08F4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 462B84C37B10724E69874AC2 /* Pods-Mantle-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-Mantle/Pods-Mantle-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 2E0AC85946777AAA8FE08D13 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D9B387604713395C983D6059 /* Pods-Godzippa-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-Godzippa/Pods-Godzippa-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 3B5B8DE218A717D205BE42F9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A79E2E040FCDDB7E3B284DB8 /* Pods.release.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 46164EE730073CC0CDE7C31E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0CAA48134B8A56EEB761E70F /* Pods-Reachability-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-Reachability/Pods-Reachability-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 47F2EC7703007DC5970E0323 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D9B387604713395C983D6059 /* Pods-Godzippa-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-Godzippa/Pods-Godzippa-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 59528C80ABD71B9B4DFE4D74 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 301AB1AD7F62B5AE55C9823C /* Pods-EGOCache-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-EGOCache/Pods-EGOCache-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 6A16A1A07355F42C4E2F8011 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2A874FAFF81C4D3E50640FB5 /* Pods-DTFoundation-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-DTFoundation/Pods-DTFoundation-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 72415179448CE12B4AC5FADD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9F2FC1F559C3E93CF482DDF4 /* Pods-DTCoreText-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-DTCoreText/Pods-DTCoreText-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 8DC42FD7FCA5211F7B4F7D3D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2A874FAFF81C4D3E50640FB5 /* Pods-DTFoundation-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-DTFoundation/Pods-DTFoundation-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 957D7422FB7C47E9886C4E2E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 51B757136856DB70036EF6E5 /* Pods-SDWebImage-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-SDWebImage/Pods-SDWebImage-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + A3D632033B9ABB8C5ED7C8C1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 301AB1AD7F62B5AE55C9823C /* Pods-EGOCache-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-EGOCache/Pods-EGOCache-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + A9FF42AEE1E48BBC4AA80A74 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 51B757136856DB70036EF6E5 /* Pods-SDWebImage-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-SDWebImage/Pods-SDWebImage-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + E0AC2675CC1EF135714A9EB7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5F8D4344C9F779F3EC9A84CC /* Pods-MBProgressHUD-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-MBProgressHUD/Pods-MBProgressHUD-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + E2205BDE801C0D5D74F76C45 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 59B4A447BE0C035D09F66B29 /* Pods-SBJson-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-SBJson/Pods-SBJson-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + E326E3EB6C2AA085BCAD5316 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4881CF962E85111BB932C11C /* Pods-AFNetworking-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-AFNetworking/Pods-AFNetworking-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + E85A4BF9CAAC6A6F6A10E48A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9F2FC1F559C3E93CF482DDF4 /* Pods-DTCoreText-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-DTCoreText/Pods-DTCoreText-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + E96F9104A6B7FB3A70790701 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 09E7556AF9D2BB4E65D06658 /* Pods.debug.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + EA60861672C05A265F3878E8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0CAA48134B8A56EEB761E70F /* Pods-Reachability-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-Reachability/Pods-Reachability-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + EAED357E2612E7B2375B4199 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 994CD0C8E00BBA950FDC15C5 /* Pods-STableViewController-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-STableViewController/Pods-STableViewController-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + EDC74303EACBBE5BA0CA5922 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 59B4A447BE0C035D09F66B29 /* Pods-SBJson-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-SBJson/Pods-SBJson-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + FBF7E4E178E932FFCB962A6C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 462B84C37B10724E69874AC2 /* Pods-Mantle-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-Mantle/Pods-Mantle-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + FFED4B5752FE327BA3D85C27 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4881CF962E85111BB932C11C /* Pods-AFNetworking-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Pods-AFNetworking/Pods-AFNetworking-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 015A823E9383AF20AF5A67D4 /* Build configuration list for PBXNativeTarget "Pods-Reachability" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EA60861672C05A265F3878E8 /* Debug */, + 46164EE730073CC0CDE7C31E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 08E7EB2FF90CDC7E026B6BF9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1337296E9AC96BF9E3F186E6 /* Debug */, + 161848FE08304EFB72811CE3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1CAF49562A0A2D03F0381BED /* Build configuration list for PBXNativeTarget "Pods-SBJson" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC74303EACBBE5BA0CA5922 /* Debug */, + E2205BDE801C0D5D74F76C45 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3C76A66FEEF9D15848839674 /* Build configuration list for PBXNativeTarget "Pods-AFNetworking" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E326E3EB6C2AA085BCAD5316 /* Debug */, + FFED4B5752FE327BA3D85C27 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 48D30F92BCB80393088A00BB /* Build configuration list for PBXNativeTarget "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E96F9104A6B7FB3A70790701 /* Debug */, + 3B5B8DE218A717D205BE42F9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4EF728C0E84383880F8B582D /* Build configuration list for PBXNativeTarget "Pods-MBProgressHUD" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E0AC2675CC1EF135714A9EB7 /* Debug */, + 1FD4C43CDA63C6A8F66A5EBA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5344A1AEFE356DC99694D09B /* Build configuration list for PBXNativeTarget "Pods-Godzippa" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2E0AC85946777AAA8FE08D13 /* Debug */, + 47F2EC7703007DC5970E0323 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 70C77834355BFEE06924CFFA /* Build configuration list for PBXNativeTarget "Pods-DTCoreText" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E85A4BF9CAAC6A6F6A10E48A /* Debug */, + 72415179448CE12B4AC5FADD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8280045FF85BAAF43926C959 /* Build configuration list for PBXNativeTarget "Pods-SDWebImage" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 957D7422FB7C47E9886C4E2E /* Debug */, + A9FF42AEE1E48BBC4AA80A74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 84F3F45C6DBCB0C76472378D /* Build configuration list for PBXNativeTarget "Pods-DTFoundation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DC42FD7FCA5211F7B4F7D3D /* Debug */, + 6A16A1A07355F42C4E2F8011 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9EB870635A419E642B119123 /* Build configuration list for PBXNativeTarget "Pods-EGOCache" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A3D632033B9ABB8C5ED7C8C1 /* Debug */, + 59528C80ABD71B9B4DFE4D74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A7DCB14E1703ECD181116E80 /* Build configuration list for PBXNativeTarget "Pods-STableViewController" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EAED357E2612E7B2375B4199 /* Debug */, + 1EE2C44E1A10EE67A5B1E5D8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D1D7D8F709122FA04206070C /* Build configuration list for PBXNativeTarget "Pods-Mantle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FBF7E4E178E932FFCB962A6C /* Debug */, + 207A5FEBB221C83965EF08F4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F306A4B30D9F9B2C2128EFE6 /* Build configuration list for PBXNativeTarget "Pods-SecureUDID" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 030DADA9BD7E1EA6C422B2A5 /* Debug */, + 13577551AEA65D4902824A40 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F4FC1885C0BD8C197F91B8F5 /* Project object */; +} diff --git a/Pods/Pods.xcodeproj/xcuserdata/xmfish.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/xmfish.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..17633fd --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/xmfish.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,142 @@ + + + + + SchemeUserState + + Pods-AFNetworking.xcscheme + + isShown + + + Pods-DTCoreText.xcscheme + + isShown + + + Pods-DTFoundation.xcscheme + + isShown + + + Pods-EGOCache.xcscheme + + isShown + + + Pods-Godzippa.xcscheme + + isShown + + + Pods-MBProgressHUD.xcscheme + + isShown + + + Pods-Mantle.xcscheme + + isShown + + + Pods-Reachability.xcscheme + + isShown + + + Pods-SBJson.xcscheme + + isShown + + + Pods-SDWebImage.xcscheme + + isShown + + + Pods-STableViewController.xcscheme + + isShown + + + Pods-SecureUDID.xcscheme + + isShown + + + Pods.xcscheme + + isShown + + + + SuppressBuildableAutocreation + + 081D18D023E185DE5065872B + + primary + + + 1E660322F08C69E13AF91D0F + + primary + + + 2231DE2C5936A2D8C4DE7FBF + + primary + + + 3381CB8EE23A95628CC6C71F + + primary + + + 55917C107FF891B225E9EF02 + + primary + + + 687D88D56283FDAD6B3057E4 + + primary + + + 7863761EC80891B08770A0B7 + + primary + + + 7D2693D881B07E422984E1FA + + primary + + + 7EACA4AE3AF64CCDA69E50CB + + primary + + + 9F5764BE6373D3C604DC32DB + + primary + + + A592E8303475C5A51F058D5D + + primary + + + A988749C1B404C65594792E1 + + primary + + + E0126A5C2A3D1F035604EE6E + + primary + + + + + diff --git a/Pods/Reachability/Reachability.h b/Pods/Reachability/Reachability.h new file mode 100644 index 0000000..c432152 --- /dev/null +++ b/Pods/Reachability/Reachability.h @@ -0,0 +1,95 @@ +/* + Copyright (c) 2011, Tony Million. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import + + +/** + * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X. + * + * @see http://nshipster.com/ns_enum-ns_options/ + **/ +#ifndef NS_ENUM +#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#endif + +extern NSString *const kReachabilityChangedNotification; + +typedef NS_ENUM(NSInteger, NetworkStatus) { + // Apple NetworkStatus Compatible Names. + NotReachable = 0, + ReachableViaWiFi = 2, + ReachableViaWWAN = 1 +}; + +@class Reachability; + +typedef void (^NetworkReachable)(Reachability * reachability); +typedef void (^NetworkUnreachable)(Reachability * reachability); + + +@interface Reachability : NSObject + +@property (nonatomic, copy) NetworkReachable reachableBlock; +@property (nonatomic, copy) NetworkUnreachable unreachableBlock; + +@property (nonatomic, assign) BOOL reachableOnWWAN; + + ++(Reachability*)reachabilityWithHostname:(NSString*)hostname; +// This is identical to the function above, but is here to maintain +//compatibility with Apples original code. (see .m) ++(Reachability*)reachabilityWithHostName:(NSString*)hostname; ++(Reachability*)reachabilityForInternetConnection; ++(Reachability*)reachabilityWithAddress:(void *)hostAddress; ++(Reachability*)reachabilityForLocalWiFi; + +-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref; + +-(BOOL)startNotifier; +-(void)stopNotifier; + +-(BOOL)isReachable; +-(BOOL)isReachableViaWWAN; +-(BOOL)isReachableViaWiFi; + +// WWAN may be available, but not active until a connection has been established. +// WiFi may require a connection for VPN on Demand. +-(BOOL)isConnectionRequired; // Identical DDG variant. +-(BOOL)connectionRequired; // Apple's routine. +// Dynamic, on demand connection? +-(BOOL)isConnectionOnDemand; +// Is user intervention required? +-(BOOL)isInterventionRequired; + +-(NetworkStatus)currentReachabilityStatus; +-(SCNetworkReachabilityFlags)reachabilityFlags; +-(NSString*)currentReachabilityString; +-(NSString*)currentReachabilityFlags; + +@end diff --git a/Pods/Reachability/Reachability.m b/Pods/Reachability/Reachability.m new file mode 100644 index 0000000..f2952d9 --- /dev/null +++ b/Pods/Reachability/Reachability.m @@ -0,0 +1,469 @@ +/* + Copyright (c) 2011, Tony Million. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#import "Reachability.h" + +#import +#import +#import +#import +#import +#import + + +NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification"; + + +@interface Reachability () + +@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef; +@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue; +@property (nonatomic, strong) id reachabilityObject; + +-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags; +-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags; + +@end + + +static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags) +{ + return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c", +#if TARGET_OS_IPHONE + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', +#else + 'X', +#endif + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; +} + +// Start listening for reachability notifications on the current run loop +static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target) + + Reachability *reachability = ((__bridge Reachability*)info); + + // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool, + // but what the heck eh? + @autoreleasepool + { + [reachability reachabilityChanged:flags]; + } +} + + +@implementation Reachability + +#pragma mark - Class Constructor Methods + ++(Reachability*)reachabilityWithHostName:(NSString*)hostname +{ + return [Reachability reachabilityWithHostname:hostname]; +} + ++(Reachability*)reachabilityWithHostname:(NSString*)hostname +{ + SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]); + if (ref) + { + id reachability = [[self alloc] initWithReachabilityRef:ref]; + + return reachability; + } + + return nil; +} + ++(Reachability *)reachabilityWithAddress:(void *)hostAddress +{ + SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + if (ref) + { + id reachability = [[self alloc] initWithReachabilityRef:ref]; + + return reachability; + } + + return nil; +} + ++(Reachability *)reachabilityForInternetConnection +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + return [self reachabilityWithAddress:&zeroAddress]; +} + ++(Reachability*)reachabilityForLocalWiFi +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + + return [self reachabilityWithAddress:&localWifiAddress]; +} + + +// Initialization methods + +-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref +{ + self = [super init]; + if (self != nil) + { + self.reachableOnWWAN = YES; + self.reachabilityRef = ref; + + // We need to create a serial queue. + // We allocate this once for the lifetime of the notifier. + + self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL); + } + + return self; +} + +-(void)dealloc +{ + [self stopNotifier]; + + if(self.reachabilityRef) + { + CFRelease(self.reachabilityRef); + self.reachabilityRef = nil; + } + + self.reachableBlock = nil; + self.unreachableBlock = nil; + self.reachabilitySerialQueue = nil; +} + +#pragma mark - Notifier Methods + +// Notifier +// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD +// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS. +// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want) + +-(BOOL)startNotifier +{ + // allow start notifier to be called multiple times + if(self.reachabilityObject && (self.reachabilityObject == self)) + { + return YES; + } + + + SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; + context.info = (__bridge void *)self; + + if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)) + { + // Set it as our reachability queue, which will retain the queue + if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)) + { + // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves + // woah + self.reachabilityObject = self; + return YES; + } + else + { +#ifdef DEBUG + NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError())); +#endif + + // UH OH - FAILURE - stop any callbacks! + SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); + } + } + else + { +#ifdef DEBUG + NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError())); +#endif + } + + // if we get here we fail at the internet + self.reachabilityObject = nil; + return NO; +} + +-(void)stopNotifier +{ + // First stop, any callbacks! + SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); + + // Unregister target from the GCD serial dispatch queue. + SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL); + + self.reachabilityObject = nil; +} + +#pragma mark - reachability tests + +// This is for the case where you flick the airplane mode; +// you end up getting something like this: +//Reachability: WR ct----- +//Reachability: -- ------- +//Reachability: WR ct----- +//Reachability: -- ------- +// We treat this as 4 UNREACHABLE triggers - really apple should do better than this + +#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection) + +-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags +{ + BOOL connectionUP = YES; + + if(!(flags & kSCNetworkReachabilityFlagsReachable)) + connectionUP = NO; + + if( (flags & testcase) == testcase ) + connectionUP = NO; + +#if TARGET_OS_IPHONE + if(flags & kSCNetworkReachabilityFlagsIsWWAN) + { + // We're on 3G. + if(!self.reachableOnWWAN) + { + // We don't want to connect when on 3G. + connectionUP = NO; + } + } +#endif + + return connectionUP; +} + +-(BOOL)isReachable +{ + SCNetworkReachabilityFlags flags; + + if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + return NO; + + return [self isReachableWithFlags:flags]; +} + +-(BOOL)isReachableViaWWAN +{ +#if TARGET_OS_IPHONE + + SCNetworkReachabilityFlags flags = 0; + + if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + { + // Check we're REACHABLE + if(flags & kSCNetworkReachabilityFlagsReachable) + { + // Now, check we're on WWAN + if(flags & kSCNetworkReachabilityFlagsIsWWAN) + { + return YES; + } + } + } +#endif + + return NO; +} + +-(BOOL)isReachableViaWiFi +{ + SCNetworkReachabilityFlags flags = 0; + + if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + { + // Check we're reachable + if((flags & kSCNetworkReachabilityFlagsReachable)) + { +#if TARGET_OS_IPHONE + // Check we're NOT on WWAN + if((flags & kSCNetworkReachabilityFlagsIsWWAN)) + { + return NO; + } +#endif + return YES; + } + } + + return NO; +} + + +// WWAN may be available, but not active until a connection has been established. +// WiFi may require a connection for VPN on Demand. +-(BOOL)isConnectionRequired +{ + return [self connectionRequired]; +} + +-(BOOL)connectionRequired +{ + SCNetworkReachabilityFlags flags; + + if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + { + return (flags & kSCNetworkReachabilityFlagsConnectionRequired); + } + + return NO; +} + +// Dynamic, on demand connection? +-(BOOL)isConnectionOnDemand +{ + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + { + return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && + (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand))); + } + + return NO; +} + +// Is user intervention required? +-(BOOL)isInterventionRequired +{ + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + { + return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && + (flags & kSCNetworkReachabilityFlagsInterventionRequired)); + } + + return NO; +} + + +#pragma mark - reachability status stuff + +-(NetworkStatus)currentReachabilityStatus +{ + if([self isReachable]) + { + if([self isReachableViaWiFi]) + return ReachableViaWiFi; + +#if TARGET_OS_IPHONE + return ReachableViaWWAN; +#endif + } + + return NotReachable; +} + +-(SCNetworkReachabilityFlags)reachabilityFlags +{ + SCNetworkReachabilityFlags flags = 0; + + if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + { + return flags; + } + + return 0; +} + +-(NSString*)currentReachabilityString +{ + NetworkStatus temp = [self currentReachabilityStatus]; + + if(temp == ReachableViaWWAN) + { + // Updated for the fact that we have CDMA phones now! + return NSLocalizedString(@"Cellular", @""); + } + if (temp == ReachableViaWiFi) + { + return NSLocalizedString(@"WiFi", @""); + } + + return NSLocalizedString(@"No Connection", @""); +} + +-(NSString*)currentReachabilityFlags +{ + return reachabilityFlags([self reachabilityFlags]); +} + +#pragma mark - Callback function calls this method + +-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags +{ + if([self isReachableWithFlags:flags]) + { + if(self.reachableBlock) + { + self.reachableBlock(self); + } + } + else + { + if(self.unreachableBlock) + { + self.unreachableBlock(self); + } + } + + // this makes sure the change notification happens on the MAIN THREAD + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification + object:self]; + }); +} + +#pragma mark - Debug Description + +- (NSString *) description +{ + NSString *description = [NSString stringWithFormat:@"<%@: %#x (%@)>", + NSStringFromClass([self class]), (unsigned int) self, [self currentReachabilityFlags]]; + return description; +} + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4.h b/Pods/SBJson/src/main/objc/SBJson4.h new file mode 100644 index 0000000..f996ed4 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2009-2011 Stig Brautaset. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJson4Writer.h" +#import "SBJson4StreamParser.h" +#import "SBJson4Parser.h" +#import "SBJson4StreamWriter.h" +#import "SBJson4StreamTokeniser.h" + diff --git a/Pods/SBJson/src/main/objc/SBJson4Parser.h b/Pods/SBJson/src/main/objc/SBJson4Parser.h new file mode 100644 index 0000000..b2290fb --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4Parser.h @@ -0,0 +1,248 @@ +/* + Copyright (c) 2010-2013, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import "SBJson4StreamParser.h" + +/** + Block called when the parser has parsed an item. This could be once + for each root document parsed, or once for each unwrapped root array element. + + @param item contains the parsed item. + @param stop set to YES if you want the parser to stop + */ +typedef void (^SBJson4ValueBlock)(id item, BOOL* stop); + +/** + Block called if an error occurs. + @param error the error. + */ +typedef void (^SBJson4ErrorBlock)(NSError* error); + +/** + Block used to process parsed tokens as they are encountered. You can use this + to transform strings containing dates into NSDate, for example. + @param item the parsed token + @param path the JSON Path of the token + */ +typedef id (^SBJson4ProcessBlock)(id item, NSString* path); + + +/** + Parse one or more chunks of JSON data. + + Using this class directly you can reduce the apparent latency for each + download/parse cycle of documents over a slow connection. You can start + parsing *and return chunks of the parsed document* before the entire + document is downloaded. + + Using this class is also useful to parse huge documents on disk + bit by bit so you don't have to keep them all in memory. + + JSON is mapped to Objective-C types in the following way: + + - null -> NSNull + - string -> NSString + - array -> NSMutableArray + - object -> NSMutableDictionary + - true -> NSNumber's -numberWithBool:YES + - false -> NSNumber's -numberWithBool:NO + - number -> NSNumber + + Since Objective-C doesn't have a dedicated class for boolean values, + these turns into NSNumber instances. However, since these are + initialised with the -initWithBool: method they round-trip back to JSON + properly. In other words, they won't silently suddenly become 0 or 1; + they'll be represented as 'true' and 'false' again. + + Integers are parsed into either a `long long` or `unsigned long long` + type if they fit, else a `double` is used. All real & exponential numbers + are represented using a `double`. Previous versions of this library used + an NSDecimalNumber in some cases, but this is no longer the case. + + The default behaviour is that your passed-in block is only called once the + entire input is parsed. If you set supportManyDocuments to YES and your input + contains multiple (whitespace limited) JSON documents your block will be called + for each document: + + SBJson4ValueBlock block = ^(id v, BOOL *stop) { + BOOL isArray = [v isKindOfClass:[NSArray class]]; + NSLog(@"Found: %@", isArray ? @"Array" : @"Object"); + } + + SBJson4ErrorBlock eh = ^(NSError* err) { + NSLog(@"OOPS: %@", err); + } + + id parser = [SBJson4Parser multiRootParserWithBlock:block + errorHandler:eh]; + + // Note that this input contains multiple top-level JSON documents + id data = [@"[]{}" dataWithEncoding:NSUTF8StringEncoding]; + [parser parse:data]; + [parser parse:data]; + + The above example will print: + + - Found: Array + - Found: Object + - Found: Array + - Found: Object + + Often you won't have control over the input you're parsing, so can't make use + of this feature. But, all is not lost: if you are parsing a long array you can + get the same effect by setting rootArrayItems to YES: + + id parser = [SBJson4Parser unwrapRootArrayParserWithBlock:block + errorHandler:eh]; + + // Note that this input contains A SINGLE top-level document + id data = [@"[[],{},[],{}]" dataWithEncoding:NSUTF8StringEncoding]; + [parser parse:data]; + + @note Stream based parsing does mean that you lose some of the correctness + verification you would have with a parser that considered the entire input + before returning an answer. It is technically possible to have some parts + of a document returned *as if they were correct* but then encounter an error + in a later part of the document. You should keep this in mind when + considering whether it would suit your application. + + +*/ +@interface SBJson4Parser : NSObject + +/** + Create a JSON Parser. + + This can be used to create a parser that accepts only one document, or one that parses + many documents any + + @param block Called for each element. Set *stop to `YES` if you have seen + enough and would like to skip the rest of the elements. + + @param allowMultiRoot Indicate that you are expecting multiple whitespace-separated + JSON documents, similar to what Twitter uses. + + @param unwrapRootArray If set the parser will pretend an root array does not exist + and the enumerator block will be called once for each item in it. This option + does nothing if the the JSON has an object at its root. + + @param eh Called if the parser encounters an error. + + @see -unwrapRootArrayParserWithBlock:errorHandler: + @see -multiRootParserWithBlock:errorHandler: + @see -initWithBlock:processBlock:multiRoot:unwrapRootArray:maxDepth:errorHandler: + + */ ++ (id)parserWithBlock:(SBJson4ValueBlock)block + allowMultiRoot:(BOOL)allowMultiRoot + unwrapRootArray:(BOOL)unwrapRootArray + errorHandler:(SBJson4ErrorBlock)eh; + + +/** + Create a JSON Parser that parses multiple whitespace separated documents. + This is useful for something like twitter's feed, which gives you one JSON + document per line. + + @param block Called for each element. Set *stop to `YES` if you have seen + enough and would like to skip the rest of the elements. + + @param eh Called if the parser encounters an error. + + @see +unwrapRootArrayParserWithBlock:errorHandler: + @see +parserWithBlock:allowMultiRoot:unwrapRootArray:errorHandler: + @see -initWithBlock:processBlock:multiRoot:unwrapRootArray:maxDepth:errorHandler: + */ ++ (id)multiRootParserWithBlock:(SBJson4ValueBlock)block + errorHandler:(SBJson4ErrorBlock)eh; + +/** + Create a JSON Parser that parses a huge array and calls for the value block for + each element in the outermost array. + + @param block Called for each element. Set *stop to `YES` if you have seen + enough and would like to skip the rest of the elements. + + @param eh Called if the parser encounters an error. + + @see +multiRootParserWithBlock:errorHandler: + @see +parserWithBlock:allowMultiRoot:unwrapRootArray:errorHandler: + @see -initWithBlock:processBlock:multiRoot:unwrapRootArray:maxDepth:errorHandler: + */ ++ (id)unwrapRootArrayParserWithBlock:(SBJson4ValueBlock)block + errorHandler:(SBJson4ErrorBlock)eh; + +/** + Create a JSON Parser. + + @param block Called for each element. Set *stop to `YES` if you have seen + enough and would like to skip the rest of the elements. + + @param processBlock A block that allows you to process individual values before being + returned. + + @param multiRoot Indicate that you are expecting multiple whitespace-separated + JSON documents, similar to what Twitter uses. + + @param unwrapRootArray If set the parser will pretend an root array does not exist + and the enumerator block will be called once for each item in it. This option + does nothing if the the JSON has an object at its root. + + @param maxDepth The max recursion depth of the parser. Defaults to 32. + + @param eh Called if the parser encounters an error. + + */ +- (id)initWithBlock:(SBJson4ValueBlock)block + processBlock:(SBJson4ProcessBlock)processBlock + multiRoot:(BOOL)multiRoot + unwrapRootArray:(BOOL)unwrapRootArray + maxDepth:(NSUInteger)maxDepth + errorHandler:(SBJson4ErrorBlock)eh; + +/** + Parse some JSON + + The JSON is assumed to be UTF8 encoded. This can be a full JSON document, or a part of one. + + @param data An NSData object containing the next chunk of JSON + + @return + - SBJson4ParserComplete if a full document was found + - SBJson4ParserWaitingForData if a partial document was found and more data is required to complete it + - SBJson4ParserError if an error occured. + + */ +- (SBJson4ParserStatus)parse:(NSData*)data; + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4Parser.m b/Pods/SBJson/src/main/objc/SBJson4Parser.m new file mode 100644 index 0000000..ec877a8 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4Parser.m @@ -0,0 +1,292 @@ +/* + Copyright (c) 2010-2013, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + +#import "SBJson4Parser.h" + +@interface SBJson4Parser () + +- (void)pop; +- (void)parser:(SBJson4StreamParser *)parser found:(id)obj; + +@end + +typedef enum { + SBJson4ChunkNone, + SBJson4ChunkArray, + SBJson4ChunkObject, +} SBJson4ChunkType; + +@implementation SBJson4Parser { + SBJson4StreamParser *_parser; + NSUInteger depth; + NSMutableArray *array; + NSMutableDictionary *dict; + NSMutableArray *keyStack; + NSMutableArray *stack; + NSMutableArray *path; + SBJson4ProcessBlock processBlock; + SBJson4ErrorBlock errorHandler; + SBJson4ValueBlock valueBlock; + SBJson4ChunkType currentType; + BOOL supportManyDocuments; + BOOL supportPartialDocuments; + NSUInteger _maxDepth; +} + +#pragma mark Housekeeping + +- (id)init { + @throw @"Not Implemented"; +} + ++ (id)multiRootParserWithBlock:(SBJson4ValueBlock)block errorHandler:(SBJson4ErrorBlock)eh { + return [self parserWithBlock:block + allowMultiRoot:YES + unwrapRootArray:NO + errorHandler:eh]; +} + ++ (id)unwrapRootArrayParserWithBlock:(SBJson4ValueBlock)block errorHandler:(SBJson4ErrorBlock)eh { + return [self parserWithBlock:block + allowMultiRoot:NO + unwrapRootArray:YES + errorHandler:eh]; +} + ++ (id)parserWithBlock:(SBJson4ValueBlock)block + allowMultiRoot:(BOOL)allowMultiRoot + unwrapRootArray:(BOOL)unwrapRootArray + errorHandler:(SBJson4ErrorBlock)eh { + + return [[self alloc] initWithBlock:block + processBlock:nil + multiRoot:allowMultiRoot + unwrapRootArray:unwrapRootArray + maxDepth:32 + errorHandler:eh]; +} + +- (id)initWithBlock:(SBJson4ValueBlock)block + processBlock:(SBJson4ProcessBlock)initialProcessBlock + multiRoot:(BOOL)multiRoot + unwrapRootArray:(BOOL)unwrapRootArray + maxDepth:(NSUInteger)maxDepth + errorHandler:(SBJson4ErrorBlock)eh { + + self = [super init]; + if (self) { + _parser = [[SBJson4StreamParser alloc] init]; + _parser.delegate = self; + + supportManyDocuments = multiRoot; + supportPartialDocuments = unwrapRootArray; + + valueBlock = block; + keyStack = [[NSMutableArray alloc] initWithCapacity:32]; + stack = [[NSMutableArray alloc] initWithCapacity:32]; + if (initialProcessBlock) + path = [[NSMutableArray alloc] initWithCapacity:32]; + processBlock = initialProcessBlock; + errorHandler = eh ? eh : ^(NSError*err) { NSLog(@"%@", err); }; + currentType = SBJson4ChunkNone; + _maxDepth = maxDepth; + } + return self; +} + + +#pragma mark Private methods + +- (void)pop { + [stack removeLastObject]; + array = nil; + dict = nil; + currentType = SBJson4ChunkNone; + + id value = [stack lastObject]; + + if ([value isKindOfClass:[NSArray class]]) { + array = value; + currentType = SBJson4ChunkArray; + } else if ([value isKindOfClass:[NSDictionary class]]) { + dict = value; + currentType = SBJson4ChunkObject; + } +} + +- (void)parser:(SBJson4StreamParser *)parser found:(id)obj { + [self parserFound:obj isValue:NO ]; +} + +- (void)parserFound:(id)obj isValue:(BOOL)isValue { + NSParameterAssert(obj); + + if(processBlock&&path) { + if(isValue) { + obj = processBlock(obj,[NSString stringWithFormat:@"%@.%@",[self pathString],[keyStack lastObject]]); + } + else { + [path removeLastObject]; + } + } + + switch (currentType) { + case SBJson4ChunkArray: + [array addObject:obj]; + break; + + case SBJson4ChunkObject: + NSParameterAssert(keyStack.count); + [dict setObject:obj forKey:[keyStack lastObject]]; + [keyStack removeLastObject]; + break; + + case SBJson4ChunkNone: { + __block BOOL stop = NO; + valueBlock(obj, &stop); + if (stop) [_parser stop]; + } + break; + + default: + break; + } +} + + +#pragma mark Delegate methods + +- (void)parserFoundObjectStart { + ++depth; + if (depth > _maxDepth) + [self maxDepthError]; + + if (path) + [self addToPath]; + dict = [NSMutableDictionary new]; + [stack addObject:dict]; + currentType = SBJson4ChunkObject; +} + +- (void)parserFoundObjectKey:(NSString *)key_ { + [keyStack addObject:key_]; +} + +- (void)parserFoundObjectEnd { + depth--; + id value = dict; + [self pop]; + [self parser:_parser found:value]; +} + +- (void)parserFoundArrayStart { + depth++; + if (depth > _maxDepth) + [self maxDepthError]; + + if (depth > 1 || !supportPartialDocuments) { + if(path) + [self addToPath]; + array = [NSMutableArray new]; + [stack addObject:array]; + currentType = SBJson4ChunkArray; + } +} + +- (void)parserFoundArrayEnd { + depth--; + if (depth > 1 || !supportPartialDocuments) { + id value = array; + [self pop]; + [self parser:_parser found:value]; + } +} + +- (void)maxDepthError { + id ui = @{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Input depth exceeds max depth of %lu", (unsigned long)_maxDepth]}; + errorHandler([NSError errorWithDomain:@"org.sbjson.parser" code:3 userInfo:ui]); + [_parser stop]; +} + +- (void)parserFoundBoolean:(BOOL)x { + [self parserFound:[NSNumber numberWithBool:x] isValue:YES ]; +} + +- (void)parserFoundNull { + [self parserFound:[NSNull null] isValue:YES ]; +} + +- (void)parserFoundNumber:(NSNumber *)num { + [self parserFound:num isValue:YES ]; +} + +- (void)parserFoundString:(NSString *)string { + [self parserFound:string isValue:YES ]; +} + +- (void)parserFoundError:(NSError *)err { + errorHandler(err); +} + +- (void)addToPath { + if([path count]==0) + [path addObject:@"$"]; + else if([[stack lastObject] isKindOfClass:[NSArray class]]) + [path addObject:@([[stack lastObject] count])]; + else + [path addObject:[keyStack lastObject]]; +} + +- (NSString *)pathString { + NSMutableString *pathString = [NSMutableString stringWithString:@"$"]; + for(NSUInteger i=1;i<[path count];i++) { + if([[path objectAtIndex:i] isKindOfClass:[NSNumber class]]) + [pathString appendString:[NSString stringWithFormat:@"[%@]",[path objectAtIndex:i]]]; + else + [pathString appendString:[NSString stringWithFormat:@".%@",[path objectAtIndex:i]]]; + } + return pathString; +} + +- (BOOL)parserShouldSupportManyDocuments { + return supportManyDocuments; +} + +- (SBJson4ParserStatus)parse:(NSData *)data { + return [_parser parse:data]; +} + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamParser.h b/Pods/SBJson/src/main/objc/SBJson4StreamParser.h new file mode 100644 index 0000000..6c9ab8b --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamParser.h @@ -0,0 +1,132 @@ +/* + Copyright (c) 2010-2013, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class SBJson4StreamParser; +@class SBJson4StreamParserState; + +typedef enum { + SBJson4ParserComplete, + SBJson4ParserStopped, + SBJson4ParserWaitingForData, + SBJson4ParserError, +} SBJson4ParserStatus; + + +/** + Delegate for interacting directly with the low-level parser + + You will most likely find it much more convenient to use the SBJson4Parser instead. + */ +@protocol SBJson4StreamParserDelegate < NSObject > + +/// Called when object start is found +- (void)parserFoundObjectStart; + +/// Called when object key is found +- (void)parserFoundObjectKey:(NSString *)key; + +/// Called when object end is found +- (void)parserFoundObjectEnd; + +/// Called when array start is found +- (void)parserFoundArrayStart; + +/// Called when array end is found +- (void)parserFoundArrayEnd; + +/// Called when a boolean value is found +- (void)parserFoundBoolean:(BOOL)x; + +/// Called when a null value is found +- (void)parserFoundNull; + +/// Called when a number is found +- (void)parserFoundNumber:(NSNumber *)num; + +/// Called when a string is found +- (void)parserFoundString:(NSString *)string; + +/// Called when an error occurs +- (void)parserFoundError:(NSError *)err; + +@optional + +/// Called to determine whether to allow multiple whitespace-separated documents +- (BOOL)parserShouldSupportManyDocuments; + +@end + +/** + Low-level Stream parser + + You most likely want to use the SBJson4Parser instead, but if you + really need low-level access to tokens one-by-one you can use this class. + */ +@interface SBJson4StreamParser : NSObject + +@property (nonatomic, weak) SBJson4StreamParserState *state; // Private +@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Private + +/** + Delegate to receive messages + + The object set here receives a series of messages as the parser breaks down the JSON stream + into valid tokens. + + Usually this should be an instance of SBJson4Parser, but you can + substitute your own implementation of the SBJson4StreamParserDelegate protocol if you need to. + */ +@property (nonatomic, weak) id delegate; + +/** + Parse some JSON + + The JSON is assumed to be UTF8 encoded. This can be a full JSON document, or a part of one. + + @param data An NSData object containing the next chunk of JSON + + @return + - SBJson4ParserComplete if a full document was found + - SBJson4ParserWaitingForData if a partial document was found and more data is required to complete it + - SBJson4ParserError if an error occured. + + */ +- (SBJson4ParserStatus)parse:(NSData*)data; + +/** + Call this to cause parsing to stop. + */ +- (void)stop; + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamParser.m b/Pods/SBJson/src/main/objc/SBJson4StreamParser.m new file mode 100644 index 0000000..59b66e1 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamParser.m @@ -0,0 +1,327 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + +#import "SBJson4StreamParser.h" +#import "SBJson4StreamTokeniser.h" +#import "SBJson4StreamParserState.h" + +#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL)) + +@implementation SBJson4StreamParser { + SBJson4StreamTokeniser *tokeniser; + BOOL stopped; +} + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + _stateStack = [[NSMutableArray alloc] initWithCapacity:32]; + _state = [SBJson4StreamParserStateStart sharedInstance]; + tokeniser = [[SBJson4StreamTokeniser alloc] init]; + } + return self; +} + + +#pragma mark Methods + +- (NSString*)tokenName:(sbjson4_token_t)token { + switch (token) { + case sbjson4_token_array_open: + return @"start of array"; + break; + + case sbjson4_token_array_close: + return @"end of array"; + break; + + case sbjson4_token_integer: + case sbjson4_token_real: + return @"number"; + break; + + case sbjson4_token_string: + case sbjson4_token_encoded: + return @"string"; + break; + + case sbjson4_token_bool: + return @"boolean"; + break; + + case sbjson4_token_null: + return @"null"; + break; + + case sbjson4_token_entry_sep: + return @"key-value separator"; + break; + + case sbjson4_token_value_sep: + return @"value separator"; + break; + + case sbjson4_token_object_open: + return @"start of object"; + break; + + case sbjson4_token_object_close: + return @"end of object"; + break; + + case sbjson4_token_eof: + case sbjson4_token_error: + break; + } + NSAssert(NO, @"Should not get here"); + return @""; +} + +- (void)handleObjectStart { + [_delegate parserFoundObjectStart]; + [_stateStack addObject:_state]; + _state = [SBJson4StreamParserStateObjectStart sharedInstance]; +} + +- (void)handleObjectEnd: (sbjson4_token_t) tok { + _state = [_stateStack lastObject]; + [_stateStack removeLastObject]; + [_state parser:self shouldTransitionTo:tok]; + [_delegate parserFoundObjectEnd]; +} + +- (void)handleArrayStart { + [_delegate parserFoundArrayStart]; + [_stateStack addObject:_state]; + _state = [SBJson4StreamParserStateArrayStart sharedInstance]; +} + +- (void)handleArrayEnd: (sbjson4_token_t) tok { + _state = [_stateStack lastObject]; + [_stateStack removeLastObject]; + [_state parser:self shouldTransitionTo:tok]; + [_delegate parserFoundArrayEnd]; +} + +- (void) handleTokenNotExpectedHere: (sbjson4_token_t) tok { + NSString *tokenName = [self tokenName:tok]; + NSString *stateName = [_state name]; + + _state = [SBJson4StreamParserStateError sharedInstance]; + id ui = @{ NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Token '%@' not expected %@", tokenName, stateName]}; + [_delegate parserFoundError:[NSError errorWithDomain:@"org.sbjson.parser" code:2 userInfo:ui]]; +} + +- (SBJson4ParserStatus)parse:(NSData *)data_ { + @autoreleasepool { + [tokeniser appendData:data_]; + + for (;;) { + + if (stopped) + return SBJson4ParserStopped; + + if ([_state isError]) + return SBJson4ParserError; + + char *token; + NSUInteger token_len; + sbjson4_token_t tok = [tokeniser getToken:&token length:&token_len]; + + switch (tok) { + case sbjson4_token_eof: + return [_state parserShouldReturn:self]; + break; + + case sbjson4_token_error: + _state = [SBJson4StreamParserStateError sharedInstance]; + [_delegate parserFoundError:[NSError errorWithDomain:@"org.sbjson.parser" code:3 + userInfo:@{NSLocalizedDescriptionKey : tokeniser.error}]]; + return SBJson4ParserError; + break; + + default: + + if (![_state parser:self shouldAcceptToken:tok]) { + [self handleTokenNotExpectedHere: tok]; + return SBJson4ParserError; + } + + switch (tok) { + case sbjson4_token_object_open: + [self handleObjectStart]; + break; + + case sbjson4_token_object_close: + [self handleObjectEnd: tok]; + break; + + case sbjson4_token_array_open: + [self handleArrayStart]; + break; + + case sbjson4_token_array_close: + [self handleArrayEnd: tok]; + break; + + case sbjson4_token_value_sep: + case sbjson4_token_entry_sep: + [_state parser:self shouldTransitionTo:tok]; + break; + + case sbjson4_token_bool: + [_delegate parserFoundBoolean:token[0] == 't']; + [_state parser:self shouldTransitionTo:tok]; + break; + + + case sbjson4_token_null: + [_delegate parserFoundNull]; + [_state parser:self shouldTransitionTo:tok]; + break; + + case sbjson4_token_integer: { + const int UNSIGNED_LONG_LONG_MAX_DIGITS = 20; + if (token_len <= UNSIGNED_LONG_LONG_MAX_DIGITS) { + if (*token == '-') + [_delegate parserFoundNumber:@(strtoll(token, NULL, 10))]; + else + [_delegate parserFoundNumber:@(strtoull(token, NULL, 10))]; + + [_state parser:self shouldTransitionTo:tok]; + break; + } + } + // FALLTHROUGH + + case sbjson4_token_real: { + [_delegate parserFoundNumber:@(strtod(token, NULL))]; + [_state parser:self shouldTransitionTo:tok]; + break; + } + + case sbjson4_token_string: { + NSString *string = [[NSString alloc] initWithBytes:token length:token_len encoding:NSUTF8StringEncoding]; + if ([_state needKey]) + [_delegate parserFoundObjectKey:string]; + else + [_delegate parserFoundString:string]; + [_state parser:self shouldTransitionTo:tok]; + break; + } + + case sbjson4_token_encoded: { + NSString *string = [self decodeStringToken:token length:token_len]; + if ([_state needKey]) + [_delegate parserFoundObjectKey:string]; + else + [_delegate parserFoundString:string]; + [_state parser:self shouldTransitionTo:tok]; + break; + } + + default: + break; + } + break; + } + } + return SBJson4ParserComplete; + } +} + +- (unichar)decodeHexQuad:(char *)quad { + unichar ch = 0; + for (NSUInteger i = 0; i < 4; i++) { + int c = quad[i]; + ch *= 16; + switch (c) { + case '0' ... '9': ch += c - '0'; break; + case 'a' ... 'f': ch += 10 + c - 'a'; break; + case 'A' ... 'F': ch += 10 + c - 'A'; break; + default: @throw @"FUT FUT FUT"; + } + } + return ch; +} + +- (NSString*)decodeStringToken:(char*)bytes length:(NSUInteger)len { + NSMutableData *buf = [NSMutableData dataWithCapacity:len]; + for (NSUInteger i = 0; i < len;) { + switch ((unsigned char)bytes[i]) { + case '\\': { + switch ((unsigned char)bytes[++i]) { + case '"': [buf appendBytes:"\"" length:1]; i++; break; + case '/': [buf appendBytes:"/" length:1]; i++; break; + case '\\': [buf appendBytes:"\\" length:1]; i++; break; + case 'b': [buf appendBytes:"\b" length:1]; i++; break; + case 'f': [buf appendBytes:"\f" length:1]; i++; break; + case 'n': [buf appendBytes:"\n" length:1]; i++; break; + case 'r': [buf appendBytes:"\r" length:1]; i++; break; + case 't': [buf appendBytes:"\t" length:1]; i++; break; + case 'u': { + unichar hi = [self decodeHexQuad:bytes + i + 1]; + i += 5; + if (SBStringIsSurrogateHighCharacter(hi)) { + // Skip past \u that we know is there.. + unichar lo = [self decodeHexQuad:bytes + i + 2]; + i += 6; + [buf appendData:[[NSString stringWithFormat:@"%C%C", hi, lo] dataUsingEncoding:NSUTF8StringEncoding]]; + } else { + [buf appendData:[[NSString stringWithFormat:@"%C", hi] dataUsingEncoding:NSUTF8StringEncoding]]; + } + break; + } + default: @throw @"FUT FUT FUT"; + } + break; + } + default: + [buf appendBytes:bytes + i length:1]; + i++; + break; + } + } + return [[NSString alloc] initWithData:buf encoding:NSUTF8StringEncoding]; +} + +- (void)stop { + stopped = YES; +} + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamParserState.h b/Pods/SBJson/src/main/objc/SBJson4StreamParserState.h new file mode 100644 index 0000000..d602fcd --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamParserState.h @@ -0,0 +1,82 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "SBJson4StreamTokeniser.h" +#import "SBJson4StreamParser.h" + +@interface SBJson4StreamParserState : NSObject ++ (id)sharedInstance; + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token; +- (SBJson4ParserStatus)parserShouldReturn:(SBJson4StreamParser *)parser; +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok; +- (BOOL)needKey; +- (BOOL)isError; + +- (NSString*)name; + +@end + +@interface SBJson4StreamParserStateStart : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateComplete : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateError : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateObjectStart : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateObjectGotKey : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateObjectSeparator : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateObjectGotValue : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateObjectNeedKey : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateArrayStart : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateArrayGotValue : SBJson4StreamParserState +@end + +@interface SBJson4StreamParserStateArrayNeedValue : SBJson4StreamParserState +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamParserState.m b/Pods/SBJson/src/main/objc/SBJson4StreamParserState.m new file mode 100644 index 0000000..d791e54 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamParserState.m @@ -0,0 +1,364 @@ +/* + Copyright (c) 2010-2013, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + +#import "SBJson4StreamParserState.h" + +#define SINGLETON \ ++ (id)sharedInstance { \ + static id state = nil; \ + if (!state) { \ + @synchronized(self) { \ + if (!state) state = [[self alloc] init]; \ + } \ + } \ + return state; \ +} + +@implementation SBJson4StreamParserState + ++ (id)sharedInstance { return nil; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + return NO; +} + +- (SBJson4ParserStatus)parserShouldReturn:(SBJson4StreamParser *)parser { + return SBJson4ParserWaitingForData; +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok {} + +- (BOOL)needKey { + return NO; +} + +- (NSString*)name { + return @""; +} + +- (BOOL)isError { + return NO; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateStart + +SINGLETON + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + return token == sbjson4_token_array_open || token == sbjson4_token_object_open; +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + + SBJson4StreamParserState *state = nil; + switch (tok) { + case sbjson4_token_array_open: + state = [SBJson4StreamParserStateArrayStart sharedInstance]; + break; + + case sbjson4_token_object_open: + state = [SBJson4StreamParserStateObjectStart sharedInstance]; + break; + + case sbjson4_token_array_close: + case sbjson4_token_object_close: + if ([parser.delegate respondsToSelector:@selector(parserShouldSupportManyDocuments)] && [parser.delegate parserShouldSupportManyDocuments]) + state = parser.state; + else + state = [SBJson4StreamParserStateComplete sharedInstance]; + break; + + case sbjson4_token_eof: + return; + + default: + state = [SBJson4StreamParserStateError sharedInstance]; + break; + } + + + parser.state = state; +} + +- (NSString*)name { return @"before outer-most array or object"; } + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateComplete + +SINGLETON + +- (NSString*)name { return @"after outer-most array or object"; } + +- (SBJson4ParserStatus)parserShouldReturn:(SBJson4StreamParser *)parser { + return SBJson4ParserComplete; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateError + +SINGLETON + +- (NSString*)name { return @"in error"; } + +- (SBJson4ParserStatus)parserShouldReturn:(SBJson4StreamParser *)parser { + return SBJson4ParserError; +} + +- (BOOL)isError { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateObjectStart + +SINGLETON + +- (NSString*)name { return @"at beginning of object"; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + switch (token) { + case sbjson4_token_object_close: + case sbjson4_token_string: + case sbjson4_token_encoded: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateObjectGotKey sharedInstance]; +} + +- (BOOL)needKey { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateObjectGotKey + +SINGLETON + +- (NSString*)name { return @"after object key"; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + return token == sbjson4_token_entry_sep; +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateObjectSeparator sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateObjectSeparator + +SINGLETON + +- (NSString*)name { return @"as object value"; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + switch (token) { + case sbjson4_token_object_open: + case sbjson4_token_array_open: + case sbjson4_token_bool: + case sbjson4_token_null: + case sbjson4_token_integer: + case sbjson4_token_real: + case sbjson4_token_string: + case sbjson4_token_encoded: + return YES; + break; + + default: + return NO; + break; + } +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateObjectGotValue sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateObjectGotValue + +SINGLETON + +- (NSString*)name { return @"after object value"; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + switch (token) { + case sbjson4_token_object_close: + case sbjson4_token_value_sep: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateObjectNeedKey sharedInstance]; +} + + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateObjectNeedKey + +SINGLETON + +- (NSString*)name { return @"in place of object key"; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + return sbjson4_token_string == token || sbjson4_token_encoded == token; +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateObjectGotKey sharedInstance]; +} + +- (BOOL)needKey { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateArrayStart + +SINGLETON + +- (NSString*)name { return @"at array start"; } + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + switch (token) { + case sbjson4_token_object_close: + case sbjson4_token_entry_sep: + case sbjson4_token_value_sep: + return NO; + break; + + default: + return YES; + break; + } +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateArrayGotValue sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateArrayGotValue + +SINGLETON + +- (NSString*)name { return @"after array value"; } + + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + return token == sbjson4_token_array_close || token == sbjson4_token_value_sep; +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + if (tok == sbjson4_token_value_sep) + parser.state = [SBJson4StreamParserStateArrayNeedValue sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJson4StreamParserStateArrayNeedValue + +SINGLETON + +- (NSString*)name { return @"as array value"; } + + +- (BOOL)parser:(SBJson4StreamParser *)parser shouldAcceptToken:(sbjson4_token_t)token { + switch (token) { + case sbjson4_token_array_close: + case sbjson4_token_entry_sep: + case sbjson4_token_object_close: + case sbjson4_token_value_sep: + return NO; + break; + + default: + return YES; + break; + } +} + +- (void)parser:(SBJson4StreamParser *)parser shouldTransitionTo:(sbjson4_token_t)tok { + parser.state = [SBJson4StreamParserStateArrayGotValue sharedInstance]; +} + +@end + diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamTokeniser.h b/Pods/SBJson/src/main/objc/SBJson4StreamTokeniser.h new file mode 100644 index 0000000..518b0f2 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamTokeniser.h @@ -0,0 +1,40 @@ +// +// Created by SuperPappi on 09/01/2013. +// +// To change the template use AppCode | Preferences | File Templates. +// + +#import + +typedef enum { + sbjson4_token_error = -1, + sbjson4_token_eof, + + sbjson4_token_array_open, + sbjson4_token_array_close, + sbjson4_token_value_sep, + + sbjson4_token_object_open, + sbjson4_token_object_close, + sbjson4_token_entry_sep, + + sbjson4_token_bool, + sbjson4_token_null, + + sbjson4_token_integer, + sbjson4_token_real, + + sbjson4_token_string, + sbjson4_token_encoded, +} sbjson4_token_t; + + +@interface SBJson4StreamTokeniser : NSObject + +@property (nonatomic, readonly, copy) NSString *error; + +- (void)appendData:(NSData*)data_; +- (sbjson4_token_t)getToken:(char**)tok length:(NSUInteger*)len; + +@end + diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamTokeniser.m b/Pods/SBJson/src/main/objc/SBJson4StreamTokeniser.m new file mode 100644 index 0000000..e9fee37 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamTokeniser.m @@ -0,0 +1,397 @@ +// +// Created by SuperPappi on 09/01/2013. +// +// To change the template use AppCode | Preferences | File Templates. +// + + +#import "SBJson4StreamTokeniser.h" + +#define SBStringIsIllegalSurrogateHighCharacter(character) (((character) >= 0xD800UL) && ((character) <= 0xDFFFUL)) +#define SBStringIsSurrogateLowCharacter(character) ((character >= 0xDC00UL) && (character <= 0xDFFFUL)) +#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL)) + +@implementation SBJson4StreamTokeniser { + NSMutableData *data; + const char *bytes; + NSUInteger index; + NSUInteger offset; +} + +- (void)setError:(NSString *)error { + _error = [NSString stringWithFormat:@"%@ at index %lu", error, (unsigned long)(offset + index)]; +} + +- (void)appendData:(NSData *)data_ { + if (!data) { + data = [data_ mutableCopy]; + + } else if (index) { + // Discard data we've already parsed + [data replaceBytesInRange:NSMakeRange(0, index) withBytes:"" length:0]; + [data appendData:data_]; + + // Add to the offset for reporting + offset += index; + + // Reset index to point to current position + index = 0u; + + } + else { + [data appendData:data_]; + } + + bytes = [data bytes]; +} + +- (void)skipWhitespace { + while (index < data.length) { + switch (bytes[index]) { + case ' ': + case '\t': + case '\r': + case '\n': + index++; + break; + default: + return; + } + } +} + +- (BOOL)getUnichar:(unichar *)ch { + if ([self haveRemainingCharacters:1]) { + *ch = (unichar) bytes[index]; + return YES; + } + return NO; +} + +- (BOOL)haveOneMoreCharacter { + return [self haveRemainingCharacters:1]; +} + +- (BOOL)haveRemainingCharacters:(NSUInteger)length { + return data.length - index >= length; +} + +- (sbjson4_token_t)match:(char *)str retval:(sbjson4_token_t)tok token:(char **)token length:(NSUInteger *)length { + NSUInteger len = strlen(str); + if ([self haveRemainingCharacters:len]) { + if (!memcmp(bytes + index, str, len)) { + *token = str; + *length = len; + index += len; + return tok; + } + [self setError: [NSString stringWithFormat:@"Expected '%s' after initial '%.1s'", str, str]]; + return sbjson4_token_error; + } + + return sbjson4_token_eof; +} + +- (BOOL)decodeHexQuad:(unichar*)quad { + unichar tmp = 0; + + for (int i = 0; i < 4; i++, index++) { + unichar c = bytes[index]; + tmp *= 16; + switch (c) { + case '0' ... '9': + tmp += c - '0'; + break; + + case 'a' ... 'f': + tmp += 10 + c - 'a'; + break; + + case 'A' ... 'F': + tmp += 10 + c - 'A'; + break; + + default: + return NO; + } + } + *quad = tmp; + return YES; +} + +- (sbjson4_token_t)getStringToken:(char **)token length:(NSUInteger *)length { + + // Skip initial " + index++; + + NSUInteger string_start = index; + sbjson4_token_t tok = sbjson4_token_string; + + for (;;) { + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + + switch (bytes[index]) { + case 0 ... 0x1F: + [self setError:[NSString stringWithFormat:@"Unescaped control character [0x%0.2X] in string", bytes[index]]]; + return sbjson4_token_error; + + case '"': + *token = (char *)(bytes + string_start); + *length = index - string_start; + index++; + return tok; + + case '\\': + tok = sbjson4_token_encoded; + index++; + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + + if (bytes[index] == 'u') { + index++; + if (![self haveRemainingCharacters:4]) + return sbjson4_token_eof; + + unichar hi; + if (![self decodeHexQuad:&hi]) { + [self setError:@"Invalid hex quad"]; + return sbjson4_token_error; + } + + if (SBStringIsSurrogateHighCharacter(hi)) { + if (![self haveRemainingCharacters:6]) + return sbjson4_token_eof; + + unichar lo; + if (bytes[index++] != '\\' || bytes[index++] != 'u' || ![self decodeHexQuad:&lo]) { + [self setError:@"Missing low character in surrogate pair"]; + return sbjson4_token_error; + } + + if (!SBStringIsSurrogateLowCharacter(lo)) { + [self setError:@"Invalid low character in surrogate pair"]; + return sbjson4_token_error; + } + + } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) { + [self setError:@"Invalid high character in surrogate pair"]; + return sbjson4_token_error; + + } + + + } else { + switch (bytes[index]) { + case '\\': + case '/': + case '"': + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + index++; + break; + + default: + [self setError:[NSString stringWithFormat:@"Illegal escape character [%x]", bytes[index]]]; + return sbjson4_token_error; + } + } + + break; + + default: + index++; + break; + } + } + + @throw @"FUT FUT FUT"; +} + +- (sbjson4_token_t)getNumberToken:(char **)token length:(NSUInteger *)length { + NSUInteger num_start = index; + if (bytes[index] == '-') { + index++; + + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + } + + sbjson4_token_t tok = sbjson4_token_integer; + if (bytes[index] == '0') { + index++; + + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + + if (isdigit(bytes[index])) { + [self setError:@"Leading zero is illegal in number"]; + return sbjson4_token_error; + } + } + + while (isdigit(bytes[index])) { + index++; + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + } + + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + + + if (bytes[index] == '.') { + index++; + tok = sbjson4_token_real; + + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + + NSUInteger frac_start = index; + while (isdigit(bytes[index])) { + index++; + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + } + + if (frac_start == index) { + [self setError:@"No digits after decimal point"]; + return sbjson4_token_error; + } + } + + if (bytes[index] == 'e' || bytes[index] == 'E') { + index++; + tok = sbjson4_token_real; + + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + + if (bytes[index] == '-' || bytes[index] == '+') { + index++; + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + } + + NSUInteger exp_start = index; + while (isdigit(bytes[index])) { + index++; + if (![self haveOneMoreCharacter]) + return sbjson4_token_eof; + } + + if (exp_start == index) { + [self setError:@"No digits in exponent"]; + return sbjson4_token_error; + } + + } + + if (num_start + 1 == index && bytes[num_start] == '-') { + [self setError:@"No digits after initial minus"]; + return sbjson4_token_error; + } + + *token = (char *)(bytes + num_start); + *length = index - num_start; + return tok; +} + + +- (sbjson4_token_t)getToken:(char **)token length:(NSUInteger *)length { + [self skipWhitespace]; + NSUInteger copyOfIndex = index; + + unichar ch; + if (![self getUnichar:&ch]) + return sbjson4_token_eof; + + sbjson4_token_t tok; + switch (ch) { + case '{': { + index++; + tok = sbjson4_token_object_open; + break; + } + case '}': { + index++; + tok = sbjson4_token_object_close; + break; + + } + case '[': { + index++; + tok = sbjson4_token_array_open; + break; + + } + case ']': { + index++; + tok = sbjson4_token_array_close; + break; + + } + case 't': { + tok = [self match:"true" retval:sbjson4_token_bool token:token length:length]; + break; + + } + case 'f': { + tok = [self match:"false" retval:sbjson4_token_bool token:token length:length]; + break; + + } + case 'n': { + tok = [self match:"null" retval:sbjson4_token_null token:token length:length]; + break; + + } + case ',': { + index++; + tok = sbjson4_token_value_sep; + break; + + } + case ':': { + index++; + tok = sbjson4_token_entry_sep; + break; + + } + case '"': { + tok = [self getStringToken:token length:length]; + break; + + } + case '-': + case '0' ... '9': { + tok = [self getNumberToken:token length:length]; + break; + + } + case '+': { + self.error = @"Leading + is illegal in number"; + tok = sbjson4_token_error; + break; + + } + default: { + self.error = [NSString stringWithFormat:@"Illegal start of token [%c]", ch]; + tok = sbjson4_token_error; + break; + } + } + + if (tok == sbjson4_token_eof) { + // We ran out of bytes before we could finish parsing the current token. + // Back up to the start & wait for more data. + index = copyOfIndex; + } + + return tok; +} + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamWriter.h b/Pods/SBJson/src/main/objc/SBJson4StreamWriter.h new file mode 100644 index 0000000..65e5410 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamWriter.h @@ -0,0 +1,210 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +/// Enable JSON writing for non-native objects +@interface NSObject (SBProxyForJson) + +/** + Allows generation of JSON for otherwise unsupported classes. + + If you have a custom class that you want to create a JSON representation + for you can implement this method in your class. It should return a + representation of your object defined in terms of objects that can be + translated into JSON. For example, a Person object might implement it like this: + + - (id)proxyForJson { + return [NSDictionary dictionaryWithObjectsAndKeys: + name, @"name", + phone, @"phone", + email, @"email", + nil]; + } + + */ +- (id)proxyForJson; + +@end + +@class SBJson4StreamWriter; + +@protocol SBJson4StreamWriterDelegate + +- (void)writer:(SBJson4StreamWriter *)writer appendBytes:(const void *)bytes length:(NSUInteger)length; + +@end + +@class SBJson4StreamWriterState; + +/** + The Stream Writer class. + + Accepts a stream of messages and writes JSON of these to its delegate object. + + This class provides a range of high-, mid- and low-level methods. You can mix + and match calls to these. For example, you may want to call -writeArrayOpen + to start an array and then repeatedly call -writeObject: with various objects + before finishing off with a -writeArrayClose call. + + Objective-C types are mapped to JSON types in the following way: + + - NSNull -> null + - NSString -> string + - NSArray -> array + - NSDictionary -> object + - NSNumber's -initWithBool:YES -> true + - NSNumber's -initWithBool:NO -> false + - NSNumber -> number + + NSNumber instances created with the -numberWithBool: method are + converted into the JSON boolean "true" and "false" values, and vice + versa. Any other NSNumber instances are converted to a JSON number the + way you would expect. + + @warning: In JSON the keys of an object must be strings. NSDictionary + keys need not be, but attempting to convert an NSDictionary with + non-string keys into JSON will throw an exception.* + + */ + +@interface SBJson4StreamWriter : NSObject { + NSMutableDictionary *cache; +} + +@property (nonatomic, weak) SBJson4StreamWriterState *state; // Internal +@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Internal + +/** + delegate to receive JSON output + Delegate that will receive messages with output. + */ +@property (nonatomic, weak) id delegate; + +/** + The maximum recursing depth. + + Defaults to 512. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property(nonatomic) NSUInteger maxDepth; + +/** + Whether we are generating human-readable (multiline) JSON. + + Set whether or not to generate human-readable JSON. The default is NO, which produces + JSON without any whitespace between tokens. If set to YES, generates human-readable + JSON with linebreaks after each array value and dictionary key/value pair, indented two + spaces per nesting level. + */ +@property(nonatomic) BOOL humanReadable; + +/** + Whether or not to sort the dictionary keys in the output. + + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. + (This is useful if you need to compare two structures, for example.) The default is NO. + */ +@property(nonatomic) BOOL sortKeys; + +/** + An optional comparator to be used if sortKeys is YES. + + If this is nil, sorting will be done via @selector(compare:). + */ +@property (nonatomic, copy) NSComparator sortKeysComparator; + +/// Contains the error description after an error has occured. +@property (nonatomic, copy) NSString *error; + +/** + Write an NSDictionary to the JSON stream. + @return YES if successful, or NO on failure + */ +- (BOOL)writeObject:(NSDictionary*)dict; + +/** + Write an NSArray to the JSON stream. + @return YES if successful, or NO on failure + */ +- (BOOL)writeArray:(NSArray *)array; + +/** + Start writing an Object to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeObjectOpen; + +/** + Close the current object being written + @return YES if successful, or NO on failure +*/ +- (BOOL)writeObjectClose; + +/** Start writing an Array to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeArrayOpen; + +/** Close the current Array being written + @return YES if successful, or NO on failure +*/ +- (BOOL)writeArrayClose; + +/** Write a null to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeNull; + +/** Write a boolean to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeBool:(BOOL)x; + +/** Write a Number to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeNumber:(NSNumber*)n; + +/** Write a String to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeString:(NSString*)s; + +@end + +@interface SBJson4StreamWriter (Private) +- (BOOL)writeValue:(id)v; +- (void)appendBytes:(const void *)bytes length:(NSUInteger)length; +@end + diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamWriter.m b/Pods/SBJson/src/main/objc/SBJson4StreamWriter.m new file mode 100644 index 0000000..6cbc520 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamWriter.m @@ -0,0 +1,357 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + +#import "SBJson4StreamWriter.h" +#import "SBJson4StreamWriterState.h" + +static NSNumber *kTrue; +static NSNumber *kFalse; +static NSNumber *kPositiveInfinity; +static NSNumber *kNegativeInfinity; + + +@implementation SBJson4StreamWriter + ++ (void)initialize { + kPositiveInfinity = [NSNumber numberWithDouble:+HUGE_VAL]; + kNegativeInfinity = [NSNumber numberWithDouble:-HUGE_VAL]; + kTrue = [NSNumber numberWithBool:YES]; + kFalse = [NSNumber numberWithBool:NO]; +} + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + _maxDepth = 32u; + _stateStack = [[NSMutableArray alloc] initWithCapacity:_maxDepth]; + _state = [SBJson4StreamWriterStateStart sharedInstance]; + cache = [[NSMutableDictionary alloc] initWithCapacity:32]; + } + return self; +} + +#pragma mark Methods + +- (void)appendBytes:(const void *)bytes length:(NSUInteger)length { + [_delegate writer:self appendBytes:bytes length:length]; +} + +- (BOOL)writeObject:(NSDictionary *)dict { + if (![self writeObjectOpen]) + return NO; + + NSArray *keys = [dict allKeys]; + + if (_sortKeys) { + if (_sortKeysComparator) { + keys = [keys sortedArrayWithOptions:NSSortStable usingComparator:_sortKeysComparator]; + } + else{ + keys = [keys sortedArrayUsingSelector:@selector(compare:)]; + } + } + + for (id k in keys) { + if (![k isKindOfClass:[NSString class]]) { + self.error = [NSString stringWithFormat:@"JSON object key must be string: %@", k]; + return NO; + } + + if (![self writeString:k]) + return NO; + if (![self writeValue:[dict objectForKey:k]]) + return NO; + } + + return [self writeObjectClose]; +} + +- (BOOL)writeArray:(NSArray*)array { + if (![self writeArrayOpen]) + return NO; + for (id v in array) + if (![self writeValue:v]) + return NO; + return [self writeArrayClose]; +} + + +- (BOOL)writeObjectOpen { + if ([_state isInvalidState:self]) return NO; + if ([_state expectingKey:self]) return NO; + [_state appendSeparator:self]; + if (_humanReadable && _stateStack.count) [_state appendWhitespace:self]; + + [_stateStack addObject:_state]; + self.state = [SBJson4StreamWriterStateObjectStart sharedInstance]; + + if (_maxDepth && _stateStack.count > _maxDepth) { + self.error = @"Nested too deep"; + return NO; + } + + [_delegate writer:self appendBytes:"{" length:1]; + return YES; +} + +- (BOOL)writeObjectClose { + if ([_state isInvalidState:self]) return NO; + + SBJson4StreamWriterState *prev = _state; + + self.state = [_stateStack lastObject]; + [_stateStack removeLastObject]; + + if (_humanReadable) [prev appendWhitespace:self]; + [_delegate writer:self appendBytes:"}" length:1]; + + [_state transitionState:self]; + return YES; +} + +- (BOOL)writeArrayOpen { + if ([_state isInvalidState:self]) return NO; + if ([_state expectingKey:self]) return NO; + [_state appendSeparator:self]; + if (_humanReadable && _stateStack.count) [_state appendWhitespace:self]; + + [_stateStack addObject:_state]; + self.state = [SBJson4StreamWriterStateArrayStart sharedInstance]; + + if (_maxDepth && _stateStack.count > _maxDepth) { + self.error = @"Nested too deep"; + return NO; + } + + [_delegate writer:self appendBytes:"[" length:1]; + return YES; +} + +- (BOOL)writeArrayClose { + if ([_state isInvalidState:self]) return NO; + if ([_state expectingKey:self]) return NO; + + SBJson4StreamWriterState *prev = _state; + + self.state = [_stateStack lastObject]; + [_stateStack removeLastObject]; + + if (_humanReadable) [prev appendWhitespace:self]; + [_delegate writer:self appendBytes:"]" length:1]; + + [_state transitionState:self]; + return YES; +} + +- (BOOL)writeNull { + if ([_state isInvalidState:self]) return NO; + if ([_state expectingKey:self]) return NO; + [_state appendSeparator:self]; + if (_humanReadable) [_state appendWhitespace:self]; + + [_delegate writer:self appendBytes:"null" length:4]; + [_state transitionState:self]; + return YES; +} + +- (BOOL)writeBool:(BOOL)x { + if ([_state isInvalidState:self]) return NO; + if ([_state expectingKey:self]) return NO; + [_state appendSeparator:self]; + if (_humanReadable) [_state appendWhitespace:self]; + + if (x) + [_delegate writer:self appendBytes:"true" length:4]; + else + [_delegate writer:self appendBytes:"false" length:5]; + [_state transitionState:self]; + return YES; +} + + +- (BOOL)writeValue:(id)o { + if ([o isKindOfClass:[NSDictionary class]]) { + return [self writeObject:o]; + + } else if ([o isKindOfClass:[NSArray class]]) { + return [self writeArray:o]; + + } else if ([o isKindOfClass:[NSString class]]) { + [self writeString:o]; + return YES; + + } else if ([o isKindOfClass:[NSNumber class]]) { + return [self writeNumber:o]; + + } else if ([o isKindOfClass:[NSNull class]]) { + return [self writeNull]; + + } else if ([o respondsToSelector:@selector(proxyForJson)]) { + return [self writeValue:[o proxyForJson]]; + + } + + self.error = [NSString stringWithFormat:@"JSON serialisation not supported for %@", [o class]]; + return NO; +} + +static const char *strForChar(int c) { + switch (c) { + case 0: return "\\u0000"; break; + case 1: return "\\u0001"; break; + case 2: return "\\u0002"; break; + case 3: return "\\u0003"; break; + case 4: return "\\u0004"; break; + case 5: return "\\u0005"; break; + case 6: return "\\u0006"; break; + case 7: return "\\u0007"; break; + case 8: return "\\b"; break; + case 9: return "\\t"; break; + case 10: return "\\n"; break; + case 11: return "\\u000b"; break; + case 12: return "\\f"; break; + case 13: return "\\r"; break; + case 14: return "\\u000e"; break; + case 15: return "\\u000f"; break; + case 16: return "\\u0010"; break; + case 17: return "\\u0011"; break; + case 18: return "\\u0012"; break; + case 19: return "\\u0013"; break; + case 20: return "\\u0014"; break; + case 21: return "\\u0015"; break; + case 22: return "\\u0016"; break; + case 23: return "\\u0017"; break; + case 24: return "\\u0018"; break; + case 25: return "\\u0019"; break; + case 26: return "\\u001a"; break; + case 27: return "\\u001b"; break; + case 28: return "\\u001c"; break; + case 29: return "\\u001d"; break; + case 30: return "\\u001e"; break; + case 31: return "\\u001f"; break; + case 34: return "\\\""; break; + case 92: return "\\\\"; break; + } + NSLog(@"FUTFUTFUT: -->'%c'<---", c); + return "FUTFUTFUT"; +} + +- (BOOL)writeString:(NSString*)string { + if ([_state isInvalidState:self]) return NO; + [_state appendSeparator:self]; + if (_humanReadable) [_state appendWhitespace:self]; + + NSMutableData *buf = [cache objectForKey:string]; + if (!buf) { + + NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + const char *utf8 = [string UTF8String]; + NSUInteger written = 0, i = 0; + + buf = [NSMutableData dataWithCapacity:(NSUInteger)(len * 1.1f)]; + [buf appendBytes:"\"" length:1]; + + for (i = 0; i < len; i++) { + int c = utf8[i]; + BOOL isControlChar = c >= 0 && c < 32; + if (isControlChar || c == '"' || c == '\\') { + if (i - written) + [buf appendBytes:utf8 + written length:i - written]; + written = i + 1; + + const char *t = strForChar(c); + [buf appendBytes:t length:strlen(t)]; + } + } + + if (i - written) + [buf appendBytes:utf8 + written length:i - written]; + + [buf appendBytes:"\"" length:1]; + [cache setObject:buf forKey:string]; + } + + [_delegate writer:self appendBytes:[buf bytes] length:[buf length]]; + [_state transitionState:self]; + return YES; +} + +- (BOOL)writeNumber:(NSNumber*)number { + if (number == kTrue || number == kFalse) + return [self writeBool:[number boolValue]]; + + if ([_state isInvalidState:self]) return NO; + if ([_state expectingKey:self]) return NO; + [_state appendSeparator:self]; + if (_humanReadable) [_state appendWhitespace:self]; + + if ([kPositiveInfinity isEqualToNumber:number]) { + self.error = @"+Infinity is not a valid number in JSON"; + return NO; + + } else if ([kNegativeInfinity isEqualToNumber:number]) { + self.error = @"-Infinity is not a valid number in JSON"; + return NO; + + } else if (isnan([number doubleValue])) { + self.error = @"NaN is not a valid number in JSON"; + return NO; + } + + const char *objcType = [number objCType]; + char num[128]; + size_t len; + + switch (objcType[0]) { + case 'c': case 'i': case 's': case 'l': case 'q': + len = snprintf(num, sizeof num, "%lld", [number longLongValue]); + break; + case 'C': case 'I': case 'S': case 'L': case 'Q': + len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]); + break; + case 'f': case 'd': default: { + len = snprintf(num, sizeof num, "%.17g", [number doubleValue]); + break; + } + } + [_delegate writer:self appendBytes:num length: len]; + [_state transitionState:self]; + return YES; +} + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamWriterState.h b/Pods/SBJson/src/main/objc/SBJson4StreamWriterState.h new file mode 100644 index 0000000..020f715 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamWriterState.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class SBJson4StreamWriter; + +@interface SBJson4StreamWriterState : NSObject ++ (id)sharedInstance; +- (BOOL)isInvalidState:(SBJson4StreamWriter *)writer; +- (void)appendSeparator:(SBJson4StreamWriter *)writer; +- (BOOL)expectingKey:(SBJson4StreamWriter *)writer; +- (void)transitionState:(SBJson4StreamWriter *)writer; +- (void)appendWhitespace:(SBJson4StreamWriter *)writer; +@end + +@interface SBJson4StreamWriterStateObjectStart : SBJson4StreamWriterState +@end + +@interface SBJson4StreamWriterStateObjectKey : SBJson4StreamWriterStateObjectStart +@end + +@interface SBJson4StreamWriterStateObjectValue : SBJson4StreamWriterState +@end + +@interface SBJson4StreamWriterStateArrayStart : SBJson4StreamWriterState +@end + +@interface SBJson4StreamWriterStateArrayValue : SBJson4StreamWriterState +@end + +@interface SBJson4StreamWriterStateStart : SBJson4StreamWriterState +@end + +@interface SBJson4StreamWriterStateComplete : SBJson4StreamWriterState +@end + +@interface SBJson4StreamWriterStateError : SBJson4StreamWriterState +@end + diff --git a/Pods/SBJson/src/main/objc/SBJson4StreamWriterState.m b/Pods/SBJson/src/main/objc/SBJson4StreamWriterState.m new file mode 100644 index 0000000..ed7890a --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4StreamWriterState.m @@ -0,0 +1,147 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + +#import "SBJson4StreamWriterState.h" +#import "SBJson4StreamWriter.h" + +#define SINGLETON \ ++ (id)sharedInstance { \ + static id state = nil; \ + if (!state) { \ + @synchronized(self) { \ + if (!state) state = [[self alloc] init]; \ + } \ + } \ + return state; \ +} + + +@implementation SBJson4StreamWriterState ++ (id)sharedInstance { return nil; } +- (BOOL)isInvalidState:(SBJson4StreamWriter *)writer { return NO; } +- (void)appendSeparator:(SBJson4StreamWriter *)writer {} +- (BOOL)expectingKey:(SBJson4StreamWriter *)writer { return NO; } +- (void)transitionState:(SBJson4StreamWriter *)writer {} +- (void)appendWhitespace:(SBJson4StreamWriter *)writer { + [writer appendBytes:"\n" length:1]; + for (NSUInteger i = 0; i < writer.stateStack.count; i++) + [writer appendBytes:" " length:2]; +} +@end + +@implementation SBJson4StreamWriterStateObjectStart + +SINGLETON + +- (void)transitionState:(SBJson4StreamWriter *)writer { + writer.state = [SBJson4StreamWriterStateObjectValue sharedInstance]; +} +- (BOOL)expectingKey:(SBJson4StreamWriter *)writer { + writer.error = @"JSON object key must be string"; + return YES; +} +@end + +@implementation SBJson4StreamWriterStateObjectKey + +SINGLETON + +- (void)appendSeparator:(SBJson4StreamWriter *)writer { + [writer appendBytes:"," length:1]; +} +@end + +@implementation SBJson4StreamWriterStateObjectValue + +SINGLETON + +- (void)appendSeparator:(SBJson4StreamWriter *)writer { + [writer appendBytes:":" length:1]; +} +- (void)transitionState:(SBJson4StreamWriter *)writer { + writer.state = [SBJson4StreamWriterStateObjectKey sharedInstance]; +} +- (void)appendWhitespace:(SBJson4StreamWriter *)writer { + [writer appendBytes:" " length:1]; +} +@end + +@implementation SBJson4StreamWriterStateArrayStart + +SINGLETON + +- (void)transitionState:(SBJson4StreamWriter *)writer { + writer.state = [SBJson4StreamWriterStateArrayValue sharedInstance]; +} +@end + +@implementation SBJson4StreamWriterStateArrayValue + +SINGLETON + +- (void)appendSeparator:(SBJson4StreamWriter *)writer { + [writer appendBytes:"," length:1]; +} +@end + +@implementation SBJson4StreamWriterStateStart + +SINGLETON + + +- (void)transitionState:(SBJson4StreamWriter *)writer { + writer.state = [SBJson4StreamWriterStateComplete sharedInstance]; +} +- (void)appendSeparator:(SBJson4StreamWriter *)writer { +} +@end + +@implementation SBJson4StreamWriterStateComplete + +SINGLETON + +- (BOOL)isInvalidState:(SBJson4StreamWriter *)writer { + writer.error = @"Stream is closed"; + return YES; +} +@end + +@implementation SBJson4StreamWriterStateError + +SINGLETON + +@end + diff --git a/Pods/SBJson/src/main/objc/SBJson4Writer.h b/Pods/SBJson/src/main/objc/SBJson4Writer.h new file mode 100644 index 0000000..17328e9 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4Writer.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +/** + The JSON writer class. + + This uses SBJson4StreamWriter internally. + + */ + +@interface SBJson4Writer : NSObject + +/** + The maximum recursing depth. + + Defaults to 32. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property(nonatomic) NSUInteger maxDepth; + +/** + Return an error trace, or nil if there was no errors. + + Note that this method returns the trace of the last method that failed. + You need to check the return value of the call you're making to figure out + if the call actually failed, before you know call this method. + */ +@property (nonatomic, readonly, copy) NSString *error; + +/** + Whether we are generating human-readable (multiline) JSON. + + Set whether or not to generate human-readable JSON. The default is NO, which produces + JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable + JSON with linebreaks after each array value and dictionary key/value pair, indented two + spaces per nesting level. + */ +@property(nonatomic) BOOL humanReadable; + +/** + Whether or not to sort the dictionary keys in the output. + + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. + (This is useful if you need to compare two structures, for example.) The default is NO. + */ +@property(nonatomic) BOOL sortKeys; + +/** + An optional comparator to be used if sortKeys is YES. + + If this is nil, sorting will be done via @selector(compare:). + */ +@property (nonatomic, copy) NSComparator sortKeysComparator; + +/** + Generates string with JSON representation for the given object. + + Returns a string containing JSON representation of the passed in value, or nil on error. + If nil is returned and error is not NULL, *error can be interrogated to find the cause of the error. + + @param value any instance that can be represented as JSON text. + */ +- (NSString*)stringWithObject:(id)value; + +/** + Generates JSON representation for the given object. + + Returns an NSData object containing JSON represented as UTF8 text, or nil on error. + + @param value any instance that can be represented as JSON text. + */ +- (NSData*)dataWithObject:(id)value; + +@end diff --git a/Pods/SBJson/src/main/objc/SBJson4Writer.m b/Pods/SBJson/src/main/objc/SBJson4Writer.m new file mode 100644 index 0000000..9eec555 --- /dev/null +++ b/Pods/SBJson/src/main/objc/SBJson4Writer.m @@ -0,0 +1,102 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error "This source file must be compiled with ARC enabled!" +#endif + +#import "SBJson4Writer.h" +#import "SBJson4StreamWriter.h" + + +@interface SBJson4Writer () < SBJson4StreamWriterDelegate > +@property (nonatomic, copy) NSString *error; +@property (nonatomic, strong) NSMutableData *acc; +@end + +@implementation SBJson4Writer + +- (id)init { + self = [super init]; + if (self) { + self.maxDepth = 32u; + } + return self; +} + + +- (NSString*)stringWithObject:(id)value { + NSData *data = [self dataWithObject:value]; + if (data) + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + return nil; +} + +- (NSData*)dataWithObject:(id)object { + self.error = nil; + + self.acc = [[NSMutableData alloc] initWithCapacity:8096u]; + + SBJson4StreamWriter *streamWriter = [[SBJson4StreamWriter alloc] init]; + streamWriter.sortKeys = self.sortKeys; + streamWriter.maxDepth = self.maxDepth; + streamWriter.sortKeysComparator = self.sortKeysComparator; + streamWriter.humanReadable = self.humanReadable; + streamWriter.delegate = self; + + BOOL ok = NO; + if ([object isKindOfClass:[NSDictionary class]]) + ok = [streamWriter writeObject:object]; + + else if ([object isKindOfClass:[NSArray class]]) + ok = [streamWriter writeArray:object]; + + else if ([object respondsToSelector:@selector(proxyForJson)]) + return [self dataWithObject:[object proxyForJson]]; + else { + self.error = @"Not valid type for JSON"; + return nil; + } + + if (ok) + return self.acc; + + self.error = streamWriter.error; + return nil; +} + +#pragma mark SBJson4StreamWriterDelegate + +- (void)writer:(SBJson4StreamWriter *)writer appendBytes:(const void *)bytes length:(NSUInteger)length { + [self.acc appendBytes:bytes length:length]; +} + + + +@end diff --git a/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h b/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h new file mode 100644 index 0000000..69c76dc --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h @@ -0,0 +1,26 @@ +// +// Created by Fabrice Aneche on 06/01/14. +// Copyright (c) 2014 Dailymotion. All rights reserved. +// + +#import + +@interface NSData (ImageContentType) + +/** + * Compute the content type for an image data + * + * @param data the input data + * + * @return the content type as string (i.e. image/jpeg, image/gif) + */ ++ (NSString *)sd_contentTypeForImageData:(NSData *)data; + +@end + + +@interface NSData (ImageContentTypeDeprecated) + ++ (NSString *)contentTypeForImageData:(NSData *)data __deprecated_msg("Use `sd_contentTypeForImageData:`"); + +@end diff --git a/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m b/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m new file mode 100644 index 0000000..0941cfa --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m @@ -0,0 +1,49 @@ +// +// Created by Fabrice Aneche on 06/01/14. +// Copyright (c) 2014 Dailymotion. All rights reserved. +// + +#import "NSData+ImageContentType.h" + + +@implementation NSData (ImageContentType) + ++ (NSString *)sd_contentTypeForImageData:(NSData *)data { + uint8_t c; + [data getBytes:&c length:1]; + switch (c) { + case 0xFF: + return @"image/jpeg"; + case 0x89: + return @"image/png"; + case 0x47: + return @"image/gif"; + case 0x49: + case 0x4D: + return @"image/tiff"; + case 0x52: + // R as RIFF for WEBP + if ([data length] < 12) { + return nil; + } + + NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; + if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { + return @"image/webp"; + } + + return nil; + } + return nil; +} + +@end + + +@implementation NSData (ImageContentTypeDeprecated) + ++ (NSString *)contentTypeForImageData:(NSData *)data { + return [self sd_contentTypeForImageData:data]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDImageCache.h b/Pods/SDWebImage/SDWebImage/SDImageCache.h new file mode 100644 index 0000000..f639dd6 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDImageCache.h @@ -0,0 +1,247 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" + +typedef NS_ENUM(NSInteger, SDImageCacheType) { + /** + * The image wasn't available the SDWebImage caches, but was downloaded from the web. + */ + SDImageCacheTypeNone, + /** + * The image was obtained from the disk cache. + */ + SDImageCacheTypeDisk, + /** + * The image was obtained from the memory cache. + */ + SDImageCacheTypeMemory +}; + +typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType); + +typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); + +typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); + +/** + * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed + * asynchronous so it doesn’t add unnecessary latency to the UI. + */ +@interface SDImageCache : NSObject + +/** + * Decompressing images that are downloaded and cached can improve peformance but can consume lot of memory. + * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. + */ +@property (assign, nonatomic) BOOL shouldDecompressImages; + +/** + * The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory. + */ +@property (assign, nonatomic) NSUInteger maxMemoryCost; + +/** + * The maximum length of time to keep an image in the cache, in seconds + */ +@property (assign, nonatomic) NSInteger maxCacheAge; + +/** + * The maximum size of the cache, in bytes. + */ +@property (assign, nonatomic) NSUInteger maxCacheSize; + +/** + * Returns global shared cache instance + * + * @return SDImageCache global instance + */ ++ (SDImageCache *)sharedImageCache; + +/** + * Init a new cache store with a specific namespace + * + * @param ns The namespace to use for this cache store + */ +- (id)initWithNamespace:(NSString *)ns; + +/** + * Add a read-only cache path to search for images pre-cached by SDImageCache + * Useful if you want to bundle pre-loaded images with your app + * + * @param path The path to use for this read-only cache path + */ +- (void)addReadOnlyCachePath:(NSString *)path; + +/** + * Store an image into memory and disk cache at the given key. + * + * @param image The image to store + * @param key The unique image cache key, usually it's image absolute URL + */ +- (void)storeImage:(UIImage *)image forKey:(NSString *)key; + +/** + * Store an image into memory and optionally disk cache at the given key. + * + * @param image The image to store + * @param key The unique image cache key, usually it's image absolute URL + * @param toDisk Store the image to disk cache if YES + */ +- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; + +/** + * Store an image into memory and optionally disk cache at the given key. + * + * @param image The image to store + * @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage + * @param imageData The image data as returned by the server, this representation will be used for disk storage + * instead of converting the given image object into a storable/compressed image format in order + * to save quality and CPU + * @param key The unique image cache key, usually it's image absolute URL + * @param toDisk Store the image to disk cache if YES + */ +- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk; + +/** + * Query the disk cache asynchronously. + * + * @param key The unique key used to store the wanted image + */ +- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock; + +/** + * Query the memory cache synchronously. + * + * @param key The unique key used to store the wanted image + */ +- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key; + +/** + * Query the disk cache synchronously after checking the memory cache. + * + * @param key The unique key used to store the wanted image + */ +- (UIImage *)imageFromDiskCacheForKey:(NSString *)key; + +/** + * Remove the image from memory and disk cache synchronously + * + * @param key The unique image cache key + */ +- (void)removeImageForKey:(NSString *)key; + + +/** + * Remove the image from memory and disk cache synchronously + * + * @param key The unique image cache key + * @param completion An block that should be executed after the image has been removed (optional) + */ +- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion; + +/** + * Remove the image from memory and optionally disk cache synchronously + * + * @param key The unique image cache key + * @param fromDisk Also remove cache entry from disk if YES + */ +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk; + +/** + * Remove the image from memory and optionally disk cache synchronously + * + * @param key The unique image cache key + * @param fromDisk Also remove cache entry from disk if YES + * @param completion An block that should be executed after the image has been removed (optional) + */ +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion; + +/** + * Clear all memory cached images + */ +- (void)clearMemory; + +/** + * Clear all disk cached images. Non-blocking method - returns immediately. + * @param completion An block that should be executed after cache expiration completes (optional) + */ +- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion; + +/** + * Clear all disk cached images + * @see clearDiskOnCompletion: + */ +- (void)clearDisk; + +/** + * Remove all expired cached image from disk. Non-blocking method - returns immediately. + * @param completionBlock An block that should be executed after cache expiration completes (optional) + */ +- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock; + +/** + * Remove all expired cached image from disk + * @see cleanDiskWithCompletionBlock: + */ +- (void)cleanDisk; + +/** + * Get the size used by the disk cache + */ +- (NSUInteger)getSize; + +/** + * Get the number of images in the disk cache + */ +- (NSUInteger)getDiskCount; + +/** + * Asynchronously calculate the disk cache's size. + */ +- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock; + +/** + * Async check if image exists in disk cache already (does not load the image) + * + * @param key the key describing the url + * @param completionBlock the block to be executed when the check is done. + * @note the completion block will be always executed on the main queue + */ +- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; + +/** + * Check if image exists in disk cache already (does not load the image) + * + * @param key the key describing the url + * + * @return YES if an image exists for the given key + */ +- (BOOL)diskImageExistsWithKey:(NSString *)key; + +/** + * Get the cache path for a certain key (needs the cache path root folder) + * + * @param key the key (can be obtained from url using cacheKeyForURL) + * @param path the cach path root folder + * + * @return the cache path + */ +- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path; + +/** + * Get the default cache path for a certain key + * + * @param key the key (can be obtained from url using cacheKeyForURL) + * + * @return the default cache path + */ +- (NSString *)defaultCachePathForKey:(NSString *)key; + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDImageCache.m b/Pods/SDWebImage/SDWebImage/SDImageCache.m new file mode 100644 index 0000000..0052903 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDImageCache.m @@ -0,0 +1,541 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDImageCache.h" +#import "SDWebImageDecoder.h" +#import "UIImage+MultiFormat.h" +#import + +static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week +// PNG signature bytes and data (below) +static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; +static NSData *kPNGSignatureData = nil; + +BOOL ImageDataHasPNGPreffix(NSData *data); + +BOOL ImageDataHasPNGPreffix(NSData *data) { + NSUInteger pngSignatureLength = [kPNGSignatureData length]; + if ([data length] >= pngSignatureLength) { + if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) { + return YES; + } + } + + return NO; +} + +@interface SDImageCache () + +@property (strong, nonatomic) NSCache *memCache; +@property (strong, nonatomic) NSString *diskCachePath; +@property (strong, nonatomic) NSMutableArray *customPaths; +@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue; + +@end + + +@implementation SDImageCache { + NSFileManager *_fileManager; +} + ++ (SDImageCache *)sharedImageCache { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + return [self initWithNamespace:@"default"]; +} + +- (id)initWithNamespace:(NSString *)ns { + if ((self = [super init])) { + NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; + + // initialise PNG signature data + kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8]; + + // Create IO serial queue + _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); + + // Init default values + _maxCacheAge = kDefaultCacheMaxCacheAge; + + // Init the memory cache + _memCache = [[NSCache alloc] init]; + _memCache.name = fullNamespace; + + // Init the disk cache + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + _diskCachePath = [paths[0] stringByAppendingPathComponent:fullNamespace]; + + // Set decompression to YES + _shouldDecompressImages = YES; + + dispatch_sync(_ioQueue, ^{ + _fileManager = [NSFileManager new]; + }); + +#if TARGET_OS_IPHONE + // Subscribe to app events + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(clearMemory) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(cleanDisk) + name:UIApplicationWillTerminateNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(backgroundCleanDisk) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; +#endif + } + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + SDDispatchQueueRelease(_ioQueue); +} + +- (void)addReadOnlyCachePath:(NSString *)path { + if (!self.customPaths) { + self.customPaths = [NSMutableArray new]; + } + + if (![self.customPaths containsObject:path]) { + [self.customPaths addObject:path]; + } +} + +- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path { + NSString *filename = [self cachedFileNameForKey:key]; + return [path stringByAppendingPathComponent:filename]; +} + +- (NSString *)defaultCachePathForKey:(NSString *)key { + return [self cachePathForKey:key inPath:self.diskCachePath]; +} + +#pragma mark SDImageCache (private) + +- (NSString *)cachedFileNameForKey:(NSString *)key { + const char *str = [key UTF8String]; + if (str == NULL) { + str = ""; + } + unsigned char r[CC_MD5_DIGEST_LENGTH]; + CC_MD5(str, (CC_LONG)strlen(str), r); + NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]]; + + return filename; +} + +#pragma mark ImageCache + +- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk { + if (!image || !key) { + return; + } + + [self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale * image.scale]; + + if (toDisk) { + dispatch_async(self.ioQueue, ^{ + NSData *data = imageData; + + if (image && (recalculate || !data)) { +#if TARGET_OS_IPHONE + // We need to determine if the image is a PNG or a JPEG + // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html) + // The first eight bytes of a PNG file always contain the following (decimal) values: + // 137 80 78 71 13 10 26 10 + + // We assume the image is PNG, in case the imageData is nil (i.e. if trying to save a UIImage directly), + // we will consider it PNG to avoid loosing the transparency + BOOL imageIsPng = YES; + + // But if we have an image data, we will look at the preffix + if ([imageData length] >= [kPNGSignatureData length]) { + imageIsPng = ImageDataHasPNGPreffix(imageData); + } + + if (imageIsPng) { + data = UIImagePNGRepresentation(image); + } + else { + data = UIImageJPEGRepresentation(image, (CGFloat)1.0); + } +#else + data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil]; +#endif + } + + if (data) { + if (![_fileManager fileExistsAtPath:_diskCachePath]) { + [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; + } + + [_fileManager createFileAtPath:[self defaultCachePathForKey:key] contents:data attributes:nil]; + } + }); + } +} + +- (void)storeImage:(UIImage *)image forKey:(NSString *)key { + [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES]; +} + +- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk { + [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk]; +} + +- (BOOL)diskImageExistsWithKey:(NSString *)key { + BOOL exists = NO; + + // this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance + // from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely. + exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]]; + + return exists; +} + +- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { + dispatch_async(_ioQueue, ^{ + BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionBlock(exists); + }); + } + }); +} + +- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key { + return [self.memCache objectForKey:key]; +} + +- (UIImage *)imageFromDiskCacheForKey:(NSString *)key { + // First check the in-memory cache... + UIImage *image = [self imageFromMemoryCacheForKey:key]; + if (image) { + return image; + } + + // Second check the disk cache... + UIImage *diskImage = [self diskImageForKey:key]; + if (diskImage) { + CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale * diskImage.scale; + [self.memCache setObject:diskImage forKey:key cost:cost]; + } + + return diskImage; +} + +- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key { + NSString *defaultPath = [self defaultCachePathForKey:key]; + NSData *data = [NSData dataWithContentsOfFile:defaultPath]; + if (data) { + return data; + } + + for (NSString *path in self.customPaths) { + NSString *filePath = [self cachePathForKey:key inPath:path]; + NSData *imageData = [NSData dataWithContentsOfFile:filePath]; + if (imageData) { + return imageData; + } + } + + return nil; +} + +- (UIImage *)diskImageForKey:(NSString *)key { + NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; + if (data) { + UIImage *image = [UIImage sd_imageWithData:data]; + image = [self scaledImageForKey:key image:image]; + if (self.shouldDecompressImages) { + image = [UIImage decodedImageWithImage:image]; + } + return image; + } + else { + return nil; + } +} + +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { + return SDScaledImageForKey(key, image); +} + +- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock { + if (!doneBlock) { + return nil; + } + + if (!key) { + doneBlock(nil, SDImageCacheTypeNone); + return nil; + } + + // First check the in-memory cache... + UIImage *image = [self imageFromMemoryCacheForKey:key]; + if (image) { + doneBlock(image, SDImageCacheTypeMemory); + return nil; + } + + NSOperation *operation = [NSOperation new]; + dispatch_async(self.ioQueue, ^{ + if (operation.isCancelled) { + return; + } + + @autoreleasepool { + UIImage *diskImage = [self diskImageForKey:key]; + if (diskImage) { + CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale * diskImage.scale; + [self.memCache setObject:diskImage forKey:key cost:cost]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + doneBlock(diskImage, SDImageCacheTypeDisk); + }); + } + }); + + return operation; +} + +- (void)removeImageForKey:(NSString *)key { + [self removeImageForKey:key withCompletion:nil]; +} + +- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion { + [self removeImageForKey:key fromDisk:YES withCompletion:completion]; +} + +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk { + [self removeImageForKey:key fromDisk:fromDisk withCompletion:nil]; +} + +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion { + + if (key == nil) { + return; + } + + [self.memCache removeObjectForKey:key]; + + if (fromDisk) { + dispatch_async(self.ioQueue, ^{ + [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; + + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + } + }); + } else if (completion){ + completion(); + } + +} + +- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost { + self.memCache.totalCostLimit = maxMemoryCost; +} + +- (NSUInteger)maxMemoryCost { + return self.memCache.totalCostLimit; +} + +- (void)clearMemory { + [self.memCache removeAllObjects]; +} + +- (void)clearDisk { + [self clearDiskOnCompletion:nil]; +} + +- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion +{ + dispatch_async(self.ioQueue, ^{ + [_fileManager removeItemAtPath:self.diskCachePath error:nil]; + [_fileManager createDirectoryAtPath:self.diskCachePath + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + } + }); +} + +- (void)cleanDisk { + [self cleanDiskWithCompletionBlock:nil]; +} + +- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock { + dispatch_async(self.ioQueue, ^{ + NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; + NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey]; + + // This enumerator prefetches useful properties for our cache files. + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL + includingPropertiesForKeys:resourceKeys + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:NULL]; + + NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge]; + NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary]; + NSUInteger currentCacheSize = 0; + + // Enumerate all of the files in the cache directory. This loop has two purposes: + // + // 1. Removing files that are older than the expiration date. + // 2. Storing file attributes for the size-based cleanup pass. + NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init]; + for (NSURL *fileURL in fileEnumerator) { + NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL]; + + // Skip directories. + if ([resourceValues[NSURLIsDirectoryKey] boolValue]) { + continue; + } + + // Remove files that are older than the expiration date; + NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; + if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) { + [urlsToDelete addObject:fileURL]; + continue; + } + + // Store a reference to this file and account for its total size. + NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; + currentCacheSize += [totalAllocatedSize unsignedIntegerValue]; + [cacheFiles setObject:resourceValues forKey:fileURL]; + } + + for (NSURL *fileURL in urlsToDelete) { + [_fileManager removeItemAtURL:fileURL error:nil]; + } + + // If our remaining disk cache exceeds a configured maximum size, perform a second + // size-based cleanup pass. We delete the oldest files first. + if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) { + // Target half of our maximum cache size for this cleanup pass. + const NSUInteger desiredCacheSize = self.maxCacheSize / 2; + + // Sort the remaining cache files by their last modification time (oldest first). + NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent + usingComparator:^NSComparisonResult(id obj1, id obj2) { + return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]]; + }]; + + // Delete files until we fall below our desired cache size. + for (NSURL *fileURL in sortedFiles) { + if ([_fileManager removeItemAtURL:fileURL error:nil]) { + NSDictionary *resourceValues = cacheFiles[fileURL]; + NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; + currentCacheSize -= [totalAllocatedSize unsignedIntegerValue]; + + if (currentCacheSize < desiredCacheSize) { + break; + } + } + } + } + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionBlock(); + }); + } + }); +} + +- (void)backgroundCleanDisk { + UIApplication *application = [UIApplication sharedApplication]; + __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ + // Clean up any unfinished task business by marking where you + // stopped or ending the task outright. + [application endBackgroundTask:bgTask]; + bgTask = UIBackgroundTaskInvalid; + }]; + + // Start the long-running task and return immediately. + [self cleanDiskWithCompletionBlock:^{ + [application endBackgroundTask:bgTask]; + bgTask = UIBackgroundTaskInvalid; + }]; +} + +- (NSUInteger)getSize { + __block NSUInteger size = 0; + dispatch_sync(self.ioQueue, ^{ + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath]; + for (NSString *fileName in fileEnumerator) { + NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName]; + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; + size += [attrs fileSize]; + } + }); + return size; +} + +- (NSUInteger)getDiskCount { + __block NSUInteger count = 0; + dispatch_sync(self.ioQueue, ^{ + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath]; + count = [[fileEnumerator allObjects] count]; + }); + return count; +} + +- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock { + NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; + + dispatch_async(self.ioQueue, ^{ + NSUInteger fileCount = 0; + NSUInteger totalSize = 0; + + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL + includingPropertiesForKeys:@[NSFileSize] + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:NULL]; + + for (NSURL *fileURL in fileEnumerator) { + NSNumber *fileSize; + [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL]; + totalSize += [fileSize unsignedIntegerValue]; + fileCount += 1; + } + + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionBlock(fileCount, totalSize); + }); + } + }); +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h b/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h new file mode 100644 index 0000000..a0555fd --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h @@ -0,0 +1,70 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * (c) Jamie Pinkham + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import + +#ifdef __OBJC_GC__ +#error SDWebImage does not support Objective-C Garbage Collection +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 +#error SDWebImage doesn't support Deployement Target version < 5.0 +#endif + +#if !TARGET_OS_IPHONE +#import +#ifndef UIImage +#define UIImage NSImage +#endif +#ifndef UIImageView +#define UIImageView NSImageView +#endif +#else + +#import + +#endif + +#ifndef NS_ENUM +#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#endif + +#ifndef NS_OPTIONS +#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type +#endif + +#if OS_OBJECT_USE_OBJC + #undef SDDispatchQueueRelease + #undef SDDispatchQueueSetterSementics + #define SDDispatchQueueRelease(q) + #define SDDispatchQueueSetterSementics strong +#else +#undef SDDispatchQueueRelease +#undef SDDispatchQueueSetterSementics +#define SDDispatchQueueRelease(q) (dispatch_release(q)) +#define SDDispatchQueueSetterSementics assign +#endif + +extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image); + +typedef void(^SDWebImageNoParamsBlock)(); + +#define dispatch_main_sync_safe(block)\ + if ([NSThread isMainThread]) {\ + block();\ + } else {\ + dispatch_sync(dispatch_get_main_queue(), block);\ + } + +#define dispatch_main_async_safe(block)\ + if ([NSThread isMainThread]) {\ + block();\ + } else {\ + dispatch_async(dispatch_get_main_queue(), block);\ + } diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m b/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m new file mode 100644 index 0000000..8c7d345 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m @@ -0,0 +1,45 @@ +// +// SDWebImageCompat.m +// SDWebImage +// +// Created by Olivier Poitrey on 11/12/12. +// Copyright (c) 2012 Dailymotion. All rights reserved. +// + +#import "SDWebImageCompat.h" + +#if !__has_feature(objc_arc) +#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag +#endif + +inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image) { + if (!image) { + return nil; + } + + if ([image.images count] > 0) { + NSMutableArray *scaledImages = [NSMutableArray array]; + + for (UIImage *tempImage in image.images) { + [scaledImages addObject:SDScaledImageForKey(key, tempImage)]; + } + + return [UIImage animatedImageWithImages:scaledImages duration:image.duration]; + } + else { + if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { + CGFloat scale = 1.0; + if (key.length >= 8) { + // Search @2x. at the end of the string, before a 3 to 4 extension length (only if key len is 8 or more @2x. + 4 len ext) + NSRange range = [key rangeOfString:@"@2x." options:0 range:NSMakeRange(key.length - 8, 5)]; + if (range.location != NSNotFound) { + scale = 2.0; + } + } + + UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + image = scaledImage; + } + return image; + } +} diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h b/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h new file mode 100644 index 0000000..0176a7b --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h @@ -0,0 +1,18 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * Created by james on 9/28/11. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" + +@interface UIImage (ForceDecode) + ++ (UIImage *)decodedImageWithImage:(UIImage *)image; + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.m b/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.m new file mode 100644 index 0000000..79ddb30 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.m @@ -0,0 +1,72 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * Created by james on 9/28/11. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDecoder.h" + +@implementation UIImage (ForceDecode) + ++ (UIImage *)decodedImageWithImage:(UIImage *)image { + if (image.images) { + // Do not decode animated images + return image; + } + + CGImageRef imageRef = image.CGImage; + CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); + CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize}; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); + + int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask); + BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone || + infoMask == kCGImageAlphaNoneSkipFirst || + infoMask == kCGImageAlphaNoneSkipLast); + + // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB. + // https://developer.apple.com/library/mac/#qa/qa1037/_index.html + if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) { + // Unset the old alpha info. + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + + // Set noneSkipFirst. + bitmapInfo |= kCGImageAlphaNoneSkipFirst; + } + // Some PNGs tell us they have alpha but only 3 components. Odd. + else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) { + // Unset the old alpha info. + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaPremultipliedFirst; + } + + // It calculates the bytes-per-row based on the bitsPerComponent and width arguments. + CGContextRef context = CGBitmapContextCreate(NULL, + imageSize.width, + imageSize.height, + CGImageGetBitsPerComponent(imageRef), + 0, + colorSpace, + bitmapInfo); + CGColorSpaceRelease(colorSpace); + + // If failed, return undecompressed image + if (!context) return image; + + CGContextDrawImage(context, imageRect, imageRef); + CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context); + + CGContextRelease(context); + + UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation]; + CGImageRelease(decompressedImageRef); + return decompressedImage; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.h b/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.h new file mode 100644 index 0000000..b8db86f --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.h @@ -0,0 +1,186 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" +#import "SDWebImageOperation.h" + +typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) { + SDWebImageDownloaderLowPriority = 1 << 0, + SDWebImageDownloaderProgressiveDownload = 1 << 1, + + /** + * By default, request prevent the of NSURLCache. With this flag, NSURLCache + * is used with default policies. + */ + SDWebImageDownloaderUseNSURLCache = 1 << 2, + + /** + * Call completion block with nil image/imageData if the image was read from NSURLCache + * (to be combined with `SDWebImageDownloaderUseNSURLCache`). + */ + + SDWebImageDownloaderIgnoreCachedResponse = 1 << 3, + /** + * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for + * extra time in background to let the request finish. If the background task expires the operation will be cancelled. + */ + + SDWebImageDownloaderContinueInBackground = 1 << 4, + + /** + * Handles cookies stored in NSHTTPCookieStore by setting + * NSMutableURLRequest.HTTPShouldHandleCookies = YES; + */ + SDWebImageDownloaderHandleCookies = 1 << 5, + + /** + * Enable to allow untrusted SSL ceriticates. + * Useful for testing purposes. Use with caution in production. + */ + SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6, + + /** + * Put the image in the high priority queue. + */ + SDWebImageDownloaderHighPriority = 1 << 7, +}; + +typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { + /** + * Default value. All download operations will execute in queue style (first-in-first-out). + */ + SDWebImageDownloaderFIFOExecutionOrder, + + /** + * All download operations will execute in stack style (last-in-first-out). + */ + SDWebImageDownloaderLIFOExecutionOrder +}; + +extern NSString *const SDWebImageDownloadStartNotification; +extern NSString *const SDWebImageDownloadStopNotification; + +typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize); + +typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSData *data, NSError *error, BOOL finished); + +typedef NSDictionary *(^SDWebImageDownloaderHeadersFilterBlock)(NSURL *url, NSDictionary *headers); + +/** + * Asynchronous downloader dedicated and optimized for image loading. + */ +@interface SDWebImageDownloader : NSObject + +/** + * Decompressing images that are downloaded and cached can improve peformance but can consume lot of memory. + * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. + */ +@property (assign, nonatomic) BOOL shouldDecompressImages; + +@property (assign, nonatomic) NSInteger maxConcurrentDownloads; + +/** + * Shows the current amount of downloads that still need to be downloaded + */ +@property (readonly, nonatomic) NSUInteger currentDownloadCount; + + +/** + * The timeout value (in seconds) for the download operation. Default: 15.0. + */ +@property (assign, nonatomic) NSTimeInterval downloadTimeout; + + +/** + * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. + */ +@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder; + +/** + * Singleton method, returns the shared instance + * + * @return global shared instance of downloader class + */ ++ (SDWebImageDownloader *)sharedDownloader; + +/** + * Set username + */ +@property (strong, nonatomic) NSString *username; + +/** + * Set password + */ +@property (strong, nonatomic) NSString *password; + +/** + * Set filter to pick headers for downloading image HTTP request. + * + * This block will be invoked for each downloading image request, returned + * NSDictionary will be used as headers in corresponding HTTP request. + */ +@property (nonatomic, copy) SDWebImageDownloaderHeadersFilterBlock headersFilter; + +/** + * Set a value for a HTTP header to be appended to each download HTTP request. + * + * @param value The value for the header field. Use `nil` value to remove the header. + * @param field The name of the header field to set. + */ +- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field; + +/** + * Returns the value of the specified HTTP header field. + * + * @return The value associated with the header field field, or `nil` if there is no corresponding header field. + */ +- (NSString *)valueForHTTPHeaderField:(NSString *)field; + +/** + * Sets a subclass of `SDWebImageDownloaderOperation` as the default + * `NSOperation` to be used each time SDWebImage constructs a request + * operation to download an image. + * + * @param operationClass The subclass of `SDWebImageDownloaderOperation` to set + * as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`. + */ +- (void)setOperationClass:(Class)operationClass; + +/** + * Creates a SDWebImageDownloader async downloader instance with a given URL + * + * The delegate will be informed when the image is finish downloaded or an error has happen. + * + * @see SDWebImageDownloaderDelegate + * + * @param url The URL to the image to download + * @param options The options to be used for this download + * @param progressBlock A block called repeatedly while the image is downloading + * @param completedBlock A block called once the download is completed. + * If the download succeeded, the image parameter is set, in case of error, + * error parameter is set with the error. The last parameter is always YES + * if SDWebImageDownloaderProgressiveDownload isn't use. With the + * SDWebImageDownloaderProgressiveDownload option, this block is called + * repeatedly with the partial image object and the finished argument set to NO + * before to be called a last time with the full image and finished argument + * set to YES. In case of error, the finished argument is always YES. + * + * @return A cancellable SDWebImageOperation + */ +- (id )downloadImageWithURL:(NSURL *)url + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock; + +/** + * Sets the download queue suspension state + */ +- (void)setSuspended:(BOOL)suspended; + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.m b/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.m new file mode 100644 index 0000000..aa3f1cb --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageDownloader.m @@ -0,0 +1,227 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDownloader.h" +#import "SDWebImageDownloaderOperation.h" +#import + +NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification"; +NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification"; + +static NSString *const kProgressCallbackKey = @"progress"; +static NSString *const kCompletedCallbackKey = @"completed"; + +@interface SDWebImageDownloader () + +@property (strong, nonatomic) NSOperationQueue *downloadQueue; +@property (weak, nonatomic) NSOperation *lastAddedOperation; +@property (assign, nonatomic) Class operationClass; +@property (strong, nonatomic) NSMutableDictionary *URLCallbacks; +@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders; +// This queue is used to serialize the handling of the network responses of all the download operation in a single queue +@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; + +@end + +@implementation SDWebImageDownloader + ++ (void)initialize { + // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator ) + // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import + if (NSClassFromString(@"SDNetworkActivityIndicator")) { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; +#pragma clang diagnostic pop + + // Remove observer in case it was previously added. + [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:activityIndicator + selector:NSSelectorFromString(@"startActivity") + name:SDWebImageDownloadStartNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:activityIndicator + selector:NSSelectorFromString(@"stopActivity") + name:SDWebImageDownloadStopNotification object:nil]; + } +} + ++ (SDWebImageDownloader *)sharedDownloader { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + if ((self = [super init])) { + _operationClass = [SDWebImageDownloaderOperation class]; + _shouldDecompressImages = YES; + _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; + _downloadQueue = [NSOperationQueue new]; + _downloadQueue.maxConcurrentOperationCount = 6; + _URLCallbacks = [NSMutableDictionary new]; + _HTTPHeaders = [NSMutableDictionary dictionaryWithObject:@"image/webp,image/*;q=0.8" forKey:@"Accept"]; + _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); + _downloadTimeout = 15.0; + } + return self; +} + +- (void)dealloc { + [self.downloadQueue cancelAllOperations]; + SDDispatchQueueRelease(_barrierQueue); +} + +- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { + if (value) { + self.HTTPHeaders[field] = value; + } + else { + [self.HTTPHeaders removeObjectForKey:field]; + } +} + +- (NSString *)valueForHTTPHeaderField:(NSString *)field { + return self.HTTPHeaders[field]; +} + +- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { + _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; +} + +- (NSUInteger)currentDownloadCount { + return _downloadQueue.operationCount; +} + +- (NSInteger)maxConcurrentDownloads { + return _downloadQueue.maxConcurrentOperationCount; +} + +- (void)setOperationClass:(Class)operationClass { + _operationClass = operationClass ?: [SDWebImageDownloaderOperation class]; +} + +- (id )downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { + __block SDWebImageDownloaderOperation *operation; + __weak SDWebImageDownloader *wself = self; + + [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{ + NSTimeInterval timeoutInterval = wself.downloadTimeout; + if (timeoutInterval == 0.0) { + timeoutInterval = 15.0; + } + + // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; + request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); + request.HTTPShouldUsePipelining = YES; + if (wself.headersFilter) { + request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]); + } + else { + request.allHTTPHeaderFields = wself.HTTPHeaders; + } + operation = [[wself.operationClass alloc] initWithRequest:request + options:options + progress:^(NSInteger receivedSize, NSInteger expectedSize) { + SDWebImageDownloader *sself = wself; + if (!sself) return; + __block NSArray *callbacksForURL; + dispatch_sync(sself.barrierQueue, ^{ + callbacksForURL = [sself.URLCallbacks[url] copy]; + }); + for (NSDictionary *callbacks in callbacksForURL) { + SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey]; + if (callback) callback(receivedSize, expectedSize); + } + } + completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { + SDWebImageDownloader *sself = wself; + if (!sself) return; + __block NSArray *callbacksForURL; + dispatch_barrier_sync(sself.barrierQueue, ^{ + callbacksForURL = [sself.URLCallbacks[url] copy]; + if (finished) { + [sself.URLCallbacks removeObjectForKey:url]; + } + }); + for (NSDictionary *callbacks in callbacksForURL) { + SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey]; + if (callback) callback(image, data, error, finished); + } + } + cancelled:^{ + SDWebImageDownloader *sself = wself; + if (!sself) return; + dispatch_barrier_async(sself.barrierQueue, ^{ + [sself.URLCallbacks removeObjectForKey:url]; + }); + }]; + operation.shouldDecompressImages = wself.shouldDecompressImages; + + if (wself.username && wself.password) { + operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession]; + } + + if (options & SDWebImageDownloaderHighPriority) { + operation.queuePriority = NSOperationQueuePriorityHigh; + } else if (options & SDWebImageDownloaderLowPriority) { + operation.queuePriority = NSOperationQueuePriorityLow; + } + + [wself.downloadQueue addOperation:operation]; + if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { + // Emulate LIFO execution order by systematically adding new operations as last operation's dependency + [wself.lastAddedOperation addDependency:operation]; + wself.lastAddedOperation = operation; + } + }]; + + return operation; +} + +- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback { + // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data. + if (url == nil) { + if (completedBlock != nil) { + completedBlock(nil, nil, nil, NO); + } + return; + } + + dispatch_barrier_sync(self.barrierQueue, ^{ + BOOL first = NO; + if (!self.URLCallbacks[url]) { + self.URLCallbacks[url] = [NSMutableArray new]; + first = YES; + } + + // Handle single download of simultaneous download request for the same URL + NSMutableArray *callbacksForURL = self.URLCallbacks[url]; + NSMutableDictionary *callbacks = [NSMutableDictionary new]; + if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy]; + if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy]; + [callbacksForURL addObject:callbacks]; + self.URLCallbacks[url] = callbacksForURL; + + if (first) { + createCallback(); + } + }); +} + +- (void)setSuspended:(BOOL)suspended { + [self.downloadQueue setSuspended:suspended]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h b/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h new file mode 100644 index 0000000..fd70b48 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h @@ -0,0 +1,63 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageDownloader.h" +#import "SDWebImageOperation.h" + +@interface SDWebImageDownloaderOperation : NSOperation + +/** + * The request used by the operation's connection. + */ +@property (strong, nonatomic, readonly) NSURLRequest *request; + + +@property (assign, nonatomic) BOOL shouldDecompressImages; + +/** + * Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. + * + * This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. + */ +@property (nonatomic, assign) BOOL shouldUseCredentialStorage; + +/** + * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. + * + * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. + */ +@property (nonatomic, strong) NSURLCredential *credential; + +/** + * The SDWebImageDownloaderOptions for the receiver. + */ +@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; + +/** + * Initializes a `SDWebImageDownloaderOperation` object + * + * @see SDWebImageDownloaderOperation + * + * @param request the URL request + * @param options downloader options + * @param progressBlock the block executed when a new chunk of data arrives. + * @note the progress block is executed on a background queue + * @param completedBlock the block executed when the download is done. + * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue + * @param cancelBlock the block executed if the download (operation) is cancelled + * + * @return the initialized instance + */ +- (id)initWithRequest:(NSURLRequest *)request + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock + cancelled:(SDWebImageNoParamsBlock)cancelBlock; + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.m b/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.m new file mode 100644 index 0000000..025a080 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.m @@ -0,0 +1,448 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDownloaderOperation.h" +#import "SDWebImageDecoder.h" +#import "UIImage+MultiFormat.h" +#import +#import "SDWebImageManager.h" + +@interface SDWebImageDownloaderOperation () + +@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock; +@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock; +@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; + +@property (assign, nonatomic, getter = isExecuting) BOOL executing; +@property (assign, nonatomic, getter = isFinished) BOOL finished; +@property (assign, nonatomic) NSInteger expectedSize; +@property (strong, nonatomic) NSMutableData *imageData; +@property (strong, nonatomic) NSURLConnection *connection; +@property (strong, atomic) NSThread *thread; + +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 +@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId; +#endif + +@end + +@implementation SDWebImageDownloaderOperation { + size_t width, height; + UIImageOrientation orientation; + BOOL responseFromCached; +} + +@synthesize executing = _executing; +@synthesize finished = _finished; + +- (id)initWithRequest:(NSURLRequest *)request + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock + cancelled:(SDWebImageNoParamsBlock)cancelBlock { + if ((self = [super init])) { + _request = request; + _shouldDecompressImages = YES; + _shouldUseCredentialStorage = YES; + _options = options; + _progressBlock = [progressBlock copy]; + _completedBlock = [completedBlock copy]; + _cancelBlock = [cancelBlock copy]; + _executing = NO; + _finished = NO; + _expectedSize = 0; + responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called + } + return self; +} + +- (void)start { + @synchronized (self) { + if (self.isCancelled) { + self.finished = YES; + [self reset]; + return; + } + +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 + if ([self shouldContinueWhenAppEntersBackground]) { + __weak __typeof__ (self) wself = self; + self.backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + __strong __typeof (wself) sself = wself; + + if (sself) { + [sself cancel]; + + [[UIApplication sharedApplication] endBackgroundTask:sself.backgroundTaskId]; + sself.backgroundTaskId = UIBackgroundTaskInvalid; + } + }]; + } +#endif + + self.executing = YES; + self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; + self.thread = [NSThread currentThread]; + } + + [self.connection start]; + + if (self.connection) { + if (self.progressBlock) { + self.progressBlock(0, NSURLResponseUnknownLength); + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; + }); + + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) { + // Make sure to run the runloop in our background thread so it can process downloaded data + // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5 + // not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); + } + else { + CFRunLoopRun(); + } + + if (!self.isFinished) { + [self.connection cancel]; + [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]]; + } + } + else { + if (self.completedBlock) { + self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); + } + } + +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 + if (self.backgroundTaskId != UIBackgroundTaskInvalid) { + [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId]; + self.backgroundTaskId = UIBackgroundTaskInvalid; + } +#endif +} + +- (void)cancel { + @synchronized (self) { + if (self.thread) { + [self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO]; + } + else { + [self cancelInternal]; + } + } +} + +- (void)cancelInternalAndStop { + if (self.isFinished) return; + [self cancelInternal]; + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +- (void)cancelInternal { + if (self.isFinished) return; + [super cancel]; + if (self.cancelBlock) self.cancelBlock(); + + if (self.connection) { + [self.connection cancel]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; + }); + + // As we cancelled the connection, its callback won't be called and thus won't + // maintain the isFinished and isExecuting flags. + if (self.isExecuting) self.executing = NO; + if (!self.isFinished) self.finished = YES; + } + + [self reset]; +} + +- (void)done { + self.finished = YES; + self.executing = NO; + [self reset]; +} + +- (void)reset { + self.cancelBlock = nil; + self.completedBlock = nil; + self.progressBlock = nil; + self.connection = nil; + self.imageData = nil; + self.thread = nil; +} + +- (void)setFinished:(BOOL)finished { + [self willChangeValueForKey:@"isFinished"]; + _finished = finished; + [self didChangeValueForKey:@"isFinished"]; +} + +- (void)setExecuting:(BOOL)executing { + [self willChangeValueForKey:@"isExecuting"]; + _executing = executing; + [self didChangeValueForKey:@"isExecuting"]; +} + +- (BOOL)isConcurrent { + return YES; +} + +#pragma mark NSURLConnection (delegate) + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { + + //'304 Not Modified' is an exceptional one + if ((![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) && [((NSHTTPURLResponse *)response) statusCode] != 304) { + NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0; + self.expectedSize = expected; + if (self.progressBlock) { + self.progressBlock(0, expected); + } + + self.imageData = [[NSMutableData alloc] initWithCapacity:expected]; + } + else { + NSUInteger code = [((NSHTTPURLResponse *)response) statusCode]; + + //This is the case when server returns '304 Not Modified'. It means that remote image is not changed. + //In case of 304 we need just cancel the operation and return cached image from the cache. + if (code == 304) { + [self cancelInternal]; + } else { + [self.connection cancel]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + }); + + if (self.completedBlock) { + self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES); + } + CFRunLoopStop(CFRunLoopGetCurrent()); + [self done]; + } +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + [self.imageData appendData:data]; + + if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) { + // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ + // Thanks to the author @Nyx0uf + + // Get the total bytes downloaded + const NSInteger totalSize = self.imageData.length; + + // Update the data source, we must pass ALL the data, not just the new bytes + CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL); + + if (width + height == 0) { + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + if (properties) { + NSInteger orientationValue = -1; + CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); + if (val) CFNumberGetValue(val, kCFNumberLongType, &height); + val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); + if (val) CFNumberGetValue(val, kCFNumberLongType, &width); + val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); + if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue); + CFRelease(properties); + + // When we draw to Core Graphics, we lose orientation information, + // which means the image below born of initWithCGIImage will be + // oriented incorrectly sometimes. (Unlike the image born of initWithData + // in connectionDidFinishLoading.) So save it here and pass it on later. + orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)]; + } + + } + + if (width + height > 0 && totalSize < self.expectedSize) { + // Create the image + CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); + +#ifdef TARGET_OS_IPHONE + // Workaround for iOS anamorphic image + if (partialImageRef) { + const size_t partialHeight = CGImageGetHeight(partialImageRef); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(colorSpace); + if (bmContext) { + CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef); + CGImageRelease(partialImageRef); + partialImageRef = CGBitmapContextCreateImage(bmContext); + CGContextRelease(bmContext); + } + else { + CGImageRelease(partialImageRef); + partialImageRef = nil; + } + } +#endif + + if (partialImageRef) { + UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation]; + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; + UIImage *scaledImage = [self scaledImageForKey:key image:image]; + if (self.shouldDecompressImages) { + image = [UIImage decodedImageWithImage:scaledImage]; + } + else { + image = scaledImage; + } + CGImageRelease(partialImageRef); + dispatch_main_sync_safe(^{ + if (self.completedBlock) { + self.completedBlock(image, nil, nil, NO); + } + }); + } + } + + CFRelease(imageSource); + } + + if (self.progressBlock) { + self.progressBlock(self.imageData.length, self.expectedSize); + } +} + ++ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value { + switch (value) { + case 1: + return UIImageOrientationUp; + case 3: + return UIImageOrientationDown; + case 8: + return UIImageOrientationLeft; + case 6: + return UIImageOrientationRight; + case 2: + return UIImageOrientationUpMirrored; + case 4: + return UIImageOrientationDownMirrored; + case 5: + return UIImageOrientationLeftMirrored; + case 7: + return UIImageOrientationRightMirrored; + default: + return UIImageOrientationUp; + } +} + +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { + return SDScaledImageForKey(key, image); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { + SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; + @synchronized(self) { + CFRunLoopStop(CFRunLoopGetCurrent()); + self.thread = nil; + self.connection = nil; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + }); + } + + if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) { + responseFromCached = NO; + } + + if (completionBlock) { + if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) { + completionBlock(nil, nil, nil, YES); + } + else { + UIImage *image = [UIImage sd_imageWithData:self.imageData]; + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; + image = [self scaledImageForKey:key image:image]; + + // Do not force decoding animated GIFs + if (!image.images) { + if (self.shouldDecompressImages) { + image = [UIImage decodedImageWithImage:image]; + } + } + if (CGSizeEqualToSize(image.size, CGSizeZero)) { + completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES); + } + else { + completionBlock(image, self.imageData, nil, YES); + } + } + } + self.completionBlock = nil; + [self done]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + @synchronized(self) { + CFRunLoopStop(CFRunLoopGetCurrent()); + self.thread = nil; + self.connection = nil; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + }); + } + + if (self.completedBlock) { + self.completedBlock(nil, nil, error, YES); + } + self.completionBlock = nil; + [self done]; +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { + responseFromCached = NO; // If this method is called, it means the response wasn't read from cache + if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) { + // Prevents caching of responses + return nil; + } + else { + return cachedResponse; + } +} + +- (BOOL)shouldContinueWhenAppEntersBackground { + return self.options & SDWebImageDownloaderContinueInBackground; +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { + return self.shouldUseCredentialStorage; +} + +- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates) && + [challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) { + [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; + } else { + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + } + } else { + if ([challenge previousFailureCount] == 0) { + if (self.credential) { + [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageManager.h b/Pods/SDWebImage/SDWebImage/SDWebImageManager.h new file mode 100644 index 0000000..9a630a7 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageManager.h @@ -0,0 +1,292 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import "SDWebImageOperation.h" +#import "SDWebImageDownloader.h" +#import "SDImageCache.h" + +typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { + /** + * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. + * This flag disable this blacklisting. + */ + SDWebImageRetryFailed = 1 << 0, + + /** + * By default, image downloads are started during UI interactions, this flags disable this feature, + * leading to delayed download on UIScrollView deceleration for instance. + */ + SDWebImageLowPriority = 1 << 1, + + /** + * This flag disables on-disk caching + */ + SDWebImageCacheMemoryOnly = 1 << 2, + + /** + * This flag enables progressive download, the image is displayed progressively during download as a browser would do. + * By default, the image is only displayed once completely downloaded. + */ + SDWebImageProgressiveDownload = 1 << 3, + + /** + * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed. + * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation. + * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. + * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image. + * + * Use this flag only if you can't make your URLs static with embeded cache busting parameter. + */ + SDWebImageRefreshCached = 1 << 4, + + /** + * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for + * extra time in background to let the request finish. If the background task expires the operation will be cancelled. + */ + SDWebImageContinueInBackground = 1 << 5, + + /** + * Handles cookies stored in NSHTTPCookieStore by setting + * NSMutableURLRequest.HTTPShouldHandleCookies = YES; + */ + SDWebImageHandleCookies = 1 << 6, + + /** + * Enable to allow untrusted SSL ceriticates. + * Useful for testing purposes. Use with caution in production. + */ + SDWebImageAllowInvalidSSLCertificates = 1 << 7, + + /** + * By default, image are loaded in the order they were queued. This flag move them to + * the front of the queue and is loaded immediately instead of waiting for the current queue to be loaded (which + * could take a while). + */ + SDWebImageHighPriority = 1 << 8, + + /** + * By default, placeholder images are loaded while the image is loading. This flag will delay the loading + * of the placeholder image until after the image has finished loading. + */ + SDWebImageDelayPlaceholder = 1 << 9, + + /** + * We usually don't call transformDownloadedImage delegate method on animated images, + * as most transformation code would mangle it. + * Use this flag to transform them anyway. + */ + SDWebImageTransformAnimatedImage = 1 << 10, +}; + +typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL); + +typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL); + +typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url); + + +@class SDWebImageManager; + +@protocol SDWebImageManagerDelegate + +@optional + +/** + * Controls which image should be downloaded when the image is not found in the cache. + * + * @param imageManager The current `SDWebImageManager` + * @param imageURL The url of the image to be downloaded + * + * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied. + */ +- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL; + +/** + * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory. + * NOTE: This method is called from a global queue in order to not to block the main thread. + * + * @param imageManager The current `SDWebImageManager` + * @param image The image to transform + * @param imageURL The url of the image to transform + * + * @return The transformed image object. + */ +- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL; + +@end + +/** + * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes. + * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache). + * You can use this class directly to benefit from web image downloading with caching in another context than + * a UIView. + * + * Here is a simple example of how to use SDWebImageManager: + * + * @code + +SDWebImageManager *manager = [SDWebImageManager sharedManager]; +[manager downloadWithURL:imageURL + options:0 + progress:nil + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (image) { + // do something with image + } + }]; + + * @endcode + */ +@interface SDWebImageManager : NSObject + +@property (weak, nonatomic) id delegate; + +@property (strong, nonatomic, readonly) SDImageCache *imageCache; +@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader; + +/** + * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can + * be used to remove dynamic part of an image URL. + * + * The following example sets a filter in the application delegate that will remove any query-string from the + * URL before to use it as a cache key: + * + * @code + +[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) { + url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; + return [url absoluteString]; +}]; + + * @endcode + */ +@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter; + +/** + * Returns global SDWebImageManager instance. + * + * @return SDWebImageManager shared instance + */ ++ (SDWebImageManager *)sharedManager; + +/** + * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * + * @param url The URL to the image + * @param options A mask to specify options to use for this request + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. + * + * This parameter is required. + * + * This block has no return value and takes the requested UIImage as first parameter. + * In case of error the image parameter is nil and the second parameter may contain an NSError. + * + * The third parameter is an `SDImageCacheType` enum indicating if the image was retrived from the local cache + * or from the memory cache or from the network. + * + * The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is + * downloading. This block is thus called repetidly with a partial image. When image is fully downloaded, the + * block is called a last time with the full image and the last parameter set to YES. + * + * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation + */ +- (id )downloadImageWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletionWithFinishedBlock)completedBlock; + +/** + * Saves image to cache for given URL + * + * @param image The image to cache + * @param url The URL to the image + * + */ + +- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url; + +/** + * Cancel all current opreations + */ +- (void)cancelAll; + +/** + * Check one or more operations running + */ +- (BOOL)isRunning; + +/** + * Check if image has already been cached + * + * @param url image url + * + * @return if the image was already cached + */ +- (BOOL)cachedImageExistsForURL:(NSURL *)url; + +/** + * Check if image has already been cached on disk only + * + * @param url image url + * + * @return if the image was already cached (disk only) + */ +- (BOOL)diskImageExistsForURL:(NSURL *)url; + +/** + * Async check if image has already been cached + * + * @param url image url + * @param completionBlock the block to be executed when the check is finished + * + * @note the completion block is always executed on the main queue + */ +- (void)cachedImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; + +/** + * Async check if image has already been cached on disk only + * + * @param url image url + * @param completionBlock the block to be executed when the check is finished + * + * @note the completion block is always executed on the main queue + */ +- (void)diskImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; + + +/** + *Return the cache key for a given URL + */ +- (NSString *)cacheKeyForURL:(NSURL *)url; + +@end + + +#pragma mark - Deprecated + +typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionBlock`"); +typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionWithFinishedBlock`"); + + +@interface SDWebImageManager (Deprecated) + +/** + * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * + * @deprecated This method has been deprecated. Use `downloadImageWithURL:options:progress:completed:` + */ +- (id )downloadWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletedWithFinishedBlock)completedBlock __deprecated_msg("Method deprecated. Use `downloadImageWithURL:options:progress:completed:`"); + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageManager.m b/Pods/SDWebImage/SDWebImage/SDWebImageManager.m new file mode 100644 index 0000000..24167c1 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageManager.m @@ -0,0 +1,348 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageManager.h" +#import + +@interface SDWebImageCombinedOperation : NSObject + +@property (assign, nonatomic, getter = isCancelled) BOOL cancelled; +@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; +@property (strong, nonatomic) NSOperation *cacheOperation; + +@end + +@interface SDWebImageManager () + +@property (strong, nonatomic, readwrite) SDImageCache *imageCache; +@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader; +@property (strong, nonatomic) NSMutableArray *failedURLs; +@property (strong, nonatomic) NSMutableArray *runningOperations; + +@end + +@implementation SDWebImageManager + ++ (id)sharedManager { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + if ((self = [super init])) { + _imageCache = [self createCache]; + _imageDownloader = [SDWebImageDownloader sharedDownloader]; + _failedURLs = [NSMutableArray new]; + _runningOperations = [NSMutableArray new]; + } + return self; +} + +- (SDImageCache *)createCache { + return [SDImageCache sharedImageCache]; +} + +- (NSString *)cacheKeyForURL:(NSURL *)url { + if (self.cacheKeyFilter) { + return self.cacheKeyFilter(url); + } + else { + return [url absoluteString]; + } +} + +- (BOOL)cachedImageExistsForURL:(NSURL *)url { + NSString *key = [self cacheKeyForURL:url]; + if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES; + return [self.imageCache diskImageExistsWithKey:key]; +} + +- (BOOL)diskImageExistsForURL:(NSURL *)url { + NSString *key = [self cacheKeyForURL:url]; + return [self.imageCache diskImageExistsWithKey:key]; +} + +- (void)cachedImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { + NSString *key = [self cacheKeyForURL:url]; + + BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil); + + if (isInMemoryCache) { + // making sure we call the completion block on the main queue + dispatch_async(dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(YES); + } + }); + return; + } + + [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { + // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch + if (completionBlock) { + completionBlock(isInDiskCache); + } + }]; +} + +- (void)diskImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { + NSString *key = [self cacheKeyForURL:url]; + + [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { + // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch + if (completionBlock) { + completionBlock(isInDiskCache); + } + }]; +} + +- (id )downloadImageWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { + // Invoking this method without a completedBlock is pointless + NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); + + // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't + // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. + if ([url isKindOfClass:NSString.class]) { + url = [NSURL URLWithString:(NSString *)url]; + } + + // Prevents app crashing on argument type error like sending NSNull instead of NSURL + if (![url isKindOfClass:NSURL.class]) { + url = nil; + } + + __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; + __weak SDWebImageCombinedOperation *weakOperation = operation; + + BOOL isFailedUrl = NO; + @synchronized (self.failedURLs) { + isFailedUrl = [self.failedURLs containsObject:url]; + } + + if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { + dispatch_main_sync_safe(^{ + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]; + completedBlock(nil, error, SDImageCacheTypeNone, YES, url); + }); + return operation; + } + + @synchronized (self.runningOperations) { + [self.runningOperations addObject:operation]; + } + NSString *key = [self cacheKeyForURL:url]; + + operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { + if (operation.isCancelled) { + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + + return; + } + + if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { + if (image && options & SDWebImageRefreshCached) { + dispatch_main_sync_safe(^{ + // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image + // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. + completedBlock(image, nil, cacheType, YES, url); + }); + } + + // download if no image or requested to refresh anyway, and download allowed by delegate + SDWebImageDownloaderOptions downloaderOptions = 0; + if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; + if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; + if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; + if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; + if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; + if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; + if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; + if (image && options & SDWebImageRefreshCached) { + // force progressive off if image already cached but forced refreshing + downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; + // ignore image read from NSURLCache if image if cached but force refreshing + downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; + } + id subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { + if (weakOperation.isCancelled) { + // Do nothing if the operation was cancelled + // See #699 for more details + // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data + } + else if (error) { + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(nil, error, SDImageCacheTypeNone, finished, url); + } + }); + + if (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut) { + @synchronized (self.failedURLs) { + if (![self.failedURLs containsObject:url]) { + [self.failedURLs addObject:url]; + } + } + } + } + else { + BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); + + if (options & SDWebImageRefreshCached && image && !downloadedImage) { + // Image refresh hit the NSURLCache cache, do not call the completion block + } + else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; + + if (transformedImage && finished) { + BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; + [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk]; + } + + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url); + } + }); + }); + } + else { + if (downloadedImage && finished) { + [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; + } + + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url); + } + }); + } + } + + if (finished) { + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + } + }]; + operation.cancelBlock = ^{ + [subOperation cancel]; + + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:weakOperation]; + } + }; + } + else if (image) { + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(image, nil, cacheType, YES, url); + } + }); + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + } + else { + // Image not in cache and download disallowed by delegate + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(nil, nil, SDImageCacheTypeNone, YES, url); + } + }); + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + } + }]; + + return operation; +} + +- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url { + if (image && url) { + NSString *key = [self cacheKeyForURL:url]; + [self.imageCache storeImage:image forKey:key toDisk:YES]; + } +} + +- (void)cancelAll { + @synchronized (self.runningOperations) { + NSArray *copiedOperations = [self.runningOperations copy]; + [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; + [self.runningOperations removeObjectsInArray:copiedOperations]; + } +} + +- (BOOL)isRunning { + return self.runningOperations.count > 0; +} + +@end + + +@implementation SDWebImageCombinedOperation + +- (void)setCancelBlock:(SDWebImageNoParamsBlock)cancelBlock { + // check if the operation is already cancelled, then we just call the cancelBlock + if (self.isCancelled) { + if (cancelBlock) { + cancelBlock(); + } + _cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes + } else { + _cancelBlock = [cancelBlock copy]; + } +} + +- (void)cancel { + self.cancelled = YES; + if (self.cacheOperation) { + [self.cacheOperation cancel]; + self.cacheOperation = nil; + } + if (self.cancelBlock) { + self.cancelBlock(); + + // TODO: this is a temporary fix to #809. + // Until we can figure the exact cause of the crash, going with the ivar instead of the setter +// self.cancelBlock = nil; + _cancelBlock = nil; + } +} + +@end + + +@implementation SDWebImageManager (Deprecated) + +// deprecated method, uses the non deprecated method +// adapter for the completion block +- (id )downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock { + return [self downloadImageWithURL:url + options:options + progress:progressBlock + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType, finished); + } + }]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h b/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h new file mode 100644 index 0000000..71094ee --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h @@ -0,0 +1,15 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import + +@protocol SDWebImageOperation + +- (void)cancel; + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h b/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h new file mode 100644 index 0000000..7bb67ac --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h @@ -0,0 +1,103 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageManager.h" + +@class SDWebImagePrefetcher; + +@protocol SDWebImagePrefetcherDelegate + +@optional + +/** + * Called when an image was prefetched. + * + * @param imagePrefetcher The current image prefetcher + * @param imageURL The image url that was prefetched + * @param finishedCount The total number of images that were prefetched (successful or not) + * @param totalCount The total number of images that were to be prefetched + */ +- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount; + +/** + * Called when all images are prefetched. + * @param imagePrefetcher The current image prefetcher + * @param totalCount The total number of images that were prefetched (whether successful or not) + * @param skippedCount The total number of images that were skipped + */ +- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount; + +@end + +typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls); +typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls); + +/** + * Prefetch some URLs in the cache for future use. Images are downloaded in low priority. + */ +@interface SDWebImagePrefetcher : NSObject + +/** + * The web image manager + */ +@property (strong, nonatomic, readonly) SDWebImageManager *manager; + +/** + * Maximum number of URLs to prefetch at the same time. Defaults to 3. + */ +@property (nonatomic, assign) NSUInteger maxConcurrentDownloads; + +/** + * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority. + */ +@property (nonatomic, assign) SDWebImageOptions options; + +/** + * Queue options for Prefetcher. Defaults to Main Queue. + */ +@property (nonatomic, assign) dispatch_queue_t prefetcherQueue; + +@property (weak, nonatomic) id delegate; + +/** + * Return the global image prefetcher instance. + */ ++ (SDWebImagePrefetcher *)sharedImagePrefetcher; + +/** + * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, + * currently one image is downloaded at a time, + * and skips images for failed downloads and proceed to the next image in the list + * + * @param urls list of URLs to prefetch + */ +- (void)prefetchURLs:(NSArray *)urls; + +/** + * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, + * currently one image is downloaded at a time, + * and skips images for failed downloads and proceed to the next image in the list + * + * @param urls list of URLs to prefetch + * @param progressBlock block to be called when progress updates; + * first parameter is the number of completed (successful or not) requests, + * second parameter is the total number of images originally requested to be prefetched + * @param completionBlock block to be called when prefetching is completed + * first param is the number of completed (successful or not) requests, + * second parameter is the number of skipped requests + */ +- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock; + +/** + * Remove and cancel queued list + */ +- (void)cancelPrefetching; + + +@end diff --git a/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m b/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m new file mode 100644 index 0000000..235a0a5 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m @@ -0,0 +1,144 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImagePrefetcher.h" + +#if (!defined(DEBUG) && !defined (SD_VERBOSE)) || defined(SD_LOG_NONE) +#define NSLog(...) +#endif + +@interface SDWebImagePrefetcher () + +@property (strong, nonatomic) SDWebImageManager *manager; +@property (strong, nonatomic) NSArray *prefetchURLs; +@property (assign, nonatomic) NSUInteger requestedCount; +@property (assign, nonatomic) NSUInteger skippedCount; +@property (assign, nonatomic) NSUInteger finishedCount; +@property (assign, nonatomic) NSTimeInterval startedTime; +@property (copy, nonatomic) SDWebImagePrefetcherCompletionBlock completionBlock; +@property (copy, nonatomic) SDWebImagePrefetcherProgressBlock progressBlock; + +@end + +@implementation SDWebImagePrefetcher + ++ (SDWebImagePrefetcher *)sharedImagePrefetcher { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + if ((self = [super init])) { + _manager = [SDWebImageManager new]; + _options = SDWebImageLowPriority; + _prefetcherQueue = dispatch_get_main_queue(); + self.maxConcurrentDownloads = 3; + } + return self; +} + +- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { + self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; +} + +- (NSUInteger)maxConcurrentDownloads { + return self.manager.imageDownloader.maxConcurrentDownloads; +} + +- (void)startPrefetchingAtIndex:(NSUInteger)index { + if (index >= self.prefetchURLs.count) return; + self.requestedCount++; + [self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!finished) return; + self.finishedCount++; + + if (image) { + if (self.progressBlock) { + self.progressBlock(self.finishedCount,[self.prefetchURLs count]); + } + NSLog(@"Prefetched %@ out of %@", @(self.finishedCount), @(self.prefetchURLs.count)); + } + else { + if (self.progressBlock) { + self.progressBlock(self.finishedCount,[self.prefetchURLs count]); + } + NSLog(@"Prefetched %@ out of %@ (Failed)", @(self.finishedCount), @(self.prefetchURLs.count)); + + // Add last failed + self.skippedCount++; + } + if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) { + [self.delegate imagePrefetcher:self + didPrefetchURL:self.prefetchURLs[index] + finishedCount:self.finishedCount + totalCount:self.prefetchURLs.count + ]; + } + if (self.prefetchURLs.count > self.requestedCount) { + dispatch_async(self.prefetcherQueue, ^{ + [self startPrefetchingAtIndex:self.requestedCount]; + }); + } + else if (self.finishedCount == self.requestedCount) { + [self reportStatus]; + if (self.completionBlock) { + self.completionBlock(self.finishedCount, self.skippedCount); + self.completionBlock = nil; + } + } + }]; +} + +- (void)reportStatus { + NSUInteger total = [self.prefetchURLs count]; + NSLog(@"Finished prefetching (%@ successful, %@ skipped, timeElasped %.2f)", @(total - self.skippedCount), @(self.skippedCount), CFAbsoluteTimeGetCurrent() - self.startedTime); + if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) { + [self.delegate imagePrefetcher:self + didFinishWithTotalCount:(total - self.skippedCount) + skippedCount:self.skippedCount + ]; + } +} + +- (void)prefetchURLs:(NSArray *)urls { + [self prefetchURLs:urls progress:nil completed:nil]; +} + +- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock { + [self cancelPrefetching]; // Prevent duplicate prefetch request + self.startedTime = CFAbsoluteTimeGetCurrent(); + self.prefetchURLs = urls; + self.completionBlock = completionBlock; + self.progressBlock = progressBlock; + + if(urls.count == 0){ + if(completionBlock){ + completionBlock(0,0); + } + }else{ + // Starts prefetching from the very first image on the list with the max allowed concurrency + NSUInteger listCount = self.prefetchURLs.count; + for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) { + [self startPrefetchingAtIndex:i]; + } + } +} + +- (void)cancelPrefetching { + self.prefetchURLs = nil; + self.skippedCount = 0; + self.requestedCount = 0; + self.finishedCount = 0; + [self.manager cancelAll]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIButton+WebCache.h b/Pods/SDWebImage/SDWebImage/UIButton+WebCache.h new file mode 100644 index 0000000..48ad0d5 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIButton+WebCache.h @@ -0,0 +1,229 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with UIButtonView. + */ +@interface UIButton (WebCache) + +/** + * Get the current image URL. + */ +- (NSURL *)sd_currentImageURL; + +/** + * Get the image URL for a control state. + * + * @param state Which state you want to know the URL for. The values are described in UIControlState. + */ +- (NSURL *)sd_imageURLForState:(UIControlState)state; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state; + +/** + * Set the imageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the backgroundImageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state; + +/** + * Set the backgroundImageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; + +/** + * Set the backgroundImageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the backgroundImageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the backgroundImageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the backgroundImageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Cancel the current image download + */ +- (void)sd_cancelImageLoadForState:(UIControlState)state; + +/** + * Cancel the current backgroundImage download + */ +- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state; + +@end + + +@interface UIButton (WebCacheDeprecated) + +- (NSURL *)currentImageURL __deprecated_msg("Use `sd_currentImageURL`"); +- (NSURL *)imageURLForState:(UIControlState)state __deprecated_msg("Use `sd_imageURLForState:`"); + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:`"); + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:completed:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:completed:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:completed:`"); + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:`"); + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:completed:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:completed:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:completed:`"); + +- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelImageLoadForState:`"); +- (void)cancelBackgroundImageLoadForState:(UIControlState)state __deprecated_msg("Use `sd_cancelBackgroundImageLoadForState:`"); + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m b/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m new file mode 100644 index 0000000..8e076ae --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m @@ -0,0 +1,260 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIButton+WebCache.h" +#import "objc/runtime.h" +#import "UIView+WebCacheOperation.h" + +static char imageURLStorageKey; + +@implementation UIButton (WebCache) + +- (NSURL *)sd_currentImageURL { + NSURL *url = self.imageURLStorage[@(self.state)]; + + if (!url) { + url = self.imageURLStorage[@(UIControlStateNormal)]; + } + + return url; +} + +- (NSURL *)sd_imageURLForState:(UIControlState)state { + return self.imageURLStorage[@(state)]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + + [self setImage:placeholder forState:state]; + [self sd_cancelImageLoadForState:state]; + + if (!url) { + [self.imageURLStorage removeObjectForKey:@(state)]; + + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + + return; + } + + self.imageURLStorage[@(state)] = url; + + __weak UIButton *wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong UIButton *sself = wself; + if (!sself) return; + if (image) { + [sself setImage:image forState:state]; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forState:state]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelImageLoadForState:state]; + + [self setBackgroundImage:placeholder forState:state]; + + if (url) { + __weak UIButton *wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong UIButton *sself = wself; + if (!sself) return; + if (image) { + [sself setBackgroundImage:image forState:state]; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setBackgroundImageLoadOperation:operation forState:state]; + } else { + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state { + [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; +} + +- (void)sd_cancelImageLoadForState:(UIControlState)state { + [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; +} + +- (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state { + [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; +} + +- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state { + [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; +} + +- (NSMutableDictionary *)imageURLStorage { + NSMutableDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey); + if (!storage) + { + storage = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + return storage; +} + +@end + + +@implementation UIButton (WebCacheDeprecated) + +- (NSURL *)currentImageURL { + return [self sd_currentImageURL]; +} + +- (NSURL *)imageURLForState:(UIControlState)state { + return [self sd_imageURLForState:state]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentImageLoad { + // in a backwards compatible manner, cancel for current state + [self sd_cancelImageLoadForState:self.state]; +} + +- (void)cancelBackgroundImageLoadForState:(UIControlState)state { + [self sd_cancelBackgroundImageLoadForState:state]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+GIF.h b/Pods/SDWebImage/SDWebImage/UIImage+GIF.h new file mode 100755 index 0000000..084f424 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImage+GIF.h @@ -0,0 +1,19 @@ +// +// UIImage+GIF.h +// LBGIFImage +// +// Created by Laurin Brandner on 06.01.12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface UIImage (GIF) + ++ (UIImage *)sd_animatedGIFNamed:(NSString *)name; + ++ (UIImage *)sd_animatedGIFWithData:(NSData *)data; + +- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size; + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+GIF.m b/Pods/SDWebImage/SDWebImage/UIImage+GIF.m new file mode 100755 index 0000000..a703637 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImage+GIF.m @@ -0,0 +1,158 @@ +// +// UIImage+GIF.m +// LBGIFImage +// +// Created by Laurin Brandner on 06.01.12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "UIImage+GIF.h" +#import + +@implementation UIImage (GIF) + ++ (UIImage *)sd_animatedGIFWithData:(NSData *)data { + if (!data) { + return nil; + } + + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); + + size_t count = CGImageSourceGetCount(source); + + UIImage *animatedImage; + + if (count <= 1) { + animatedImage = [[UIImage alloc] initWithData:data]; + } + else { + NSMutableArray *images = [NSMutableArray array]; + + NSTimeInterval duration = 0.0f; + + for (size_t i = 0; i < count; i++) { + CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); + + duration += [self sd_frameDurationAtIndex:i source:source]; + + [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; + + CGImageRelease(image); + } + + if (!duration) { + duration = (1.0f / 10.0f) * count; + } + + animatedImage = [UIImage animatedImageWithImages:images duration:duration]; + } + + CFRelease(source); + + return animatedImage; +} + ++ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { + float frameDuration = 0.1f; + CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); + NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; + NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; + + NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; + if (delayTimeUnclampedProp) { + frameDuration = [delayTimeUnclampedProp floatValue]; + } + else { + + NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; + if (delayTimeProp) { + frameDuration = [delayTimeProp floatValue]; + } + } + + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify + // a duration of <= 10 ms. See and + // for more information. + + if (frameDuration < 0.011f) { + frameDuration = 0.100f; + } + + CFRelease(cfFrameProperties); + return frameDuration; +} + ++ (UIImage *)sd_animatedGIFNamed:(NSString *)name { + CGFloat scale = [UIScreen mainScreen].scale; + + if (scale > 1.0f) { + NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"]; + + NSData *data = [NSData dataWithContentsOfFile:retinaPath]; + + if (data) { + return [UIImage sd_animatedGIFWithData:data]; + } + + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; + + data = [NSData dataWithContentsOfFile:path]; + + if (data) { + return [UIImage sd_animatedGIFWithData:data]; + } + + return [UIImage imageNamed:name]; + } + else { + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; + + NSData *data = [NSData dataWithContentsOfFile:path]; + + if (data) { + return [UIImage sd_animatedGIFWithData:data]; + } + + return [UIImage imageNamed:name]; + } +} + +- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size { + if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) { + return self; + } + + CGSize scaledSize = size; + CGPoint thumbnailPoint = CGPointZero; + + CGFloat widthFactor = size.width / self.size.width; + CGFloat heightFactor = size.height / self.size.height; + CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor; + scaledSize.width = self.size.width * scaleFactor; + scaledSize.height = self.size.height * scaleFactor; + + if (widthFactor > heightFactor) { + thumbnailPoint.y = (size.height - scaledSize.height) * 0.5; + } + else if (widthFactor < heightFactor) { + thumbnailPoint.x = (size.width - scaledSize.width) * 0.5; + } + + NSMutableArray *scaledImages = [NSMutableArray array]; + + UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); + + for (UIImage *image in self.images) { + [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + [scaledImages addObject:newImage]; + } + + UIGraphicsEndImageContext(); + + return [UIImage animatedImageWithImages:scaledImages duration:self.duration]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h b/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h new file mode 100644 index 0000000..186ebc0 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h @@ -0,0 +1,15 @@ +// +// UIImage+MultiFormat.h +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import + +@interface UIImage (MultiFormat) + ++ (UIImage *)sd_imageWithData:(NSData *)data; + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m b/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m new file mode 100644 index 0000000..5395280 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m @@ -0,0 +1,114 @@ +// +// UIImage+MultiFormat.m +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import "UIImage+MultiFormat.h" +#import "UIImage+GIF.h" +#import "NSData+ImageContentType.h" +#import + +#ifdef SD_WEBP +#import "UIImage+WebP.h" +#endif + +@implementation UIImage (MultiFormat) + ++ (UIImage *)sd_imageWithData:(NSData *)data { + UIImage *image; + NSString *imageContentType = [NSData sd_contentTypeForImageData:data]; + if ([imageContentType isEqualToString:@"image/gif"]) { + image = [UIImage sd_animatedGIFWithData:data]; + } +#ifdef SD_WEBP + else if ([imageContentType isEqualToString:@"image/webp"]) + { + image = [UIImage sd_imageWithWebPData:data]; + } +#endif + else { + image = [[UIImage alloc] initWithData:data]; + UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data]; + if (orientation != UIImageOrientationUp) { + image = [UIImage imageWithCGImage:image.CGImage + scale:image.scale + orientation:orientation]; + } + } + + + return image; +} + + ++(UIImageOrientation)sd_imageOrientationFromImageData:(NSData *)imageData { + UIImageOrientation result = UIImageOrientationUp; + CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); + if (imageSource) { + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + if (properties) { + CFTypeRef val; + int exifOrientation; + val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); + if (val) { + CFNumberGetValue(val, kCFNumberIntType, &exifOrientation); + result = [self sd_exifOrientationToiOSOrientation:exifOrientation]; + } // else - if it's not set it remains at up + CFRelease((CFTypeRef) properties); + } else { + //NSLog(@"NO PROPERTIES, FAIL"); + } + CFRelease(imageSource); + } + return result; +} + +#pragma mark EXIF orientation tag converter +// Convert an EXIF image orientation to an iOS one. +// reference see here: http://sylvana.net/jpegcrop/exif_orientation.html ++ (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation { + UIImageOrientation orientation = UIImageOrientationUp; + switch (exifOrientation) { + case 1: + orientation = UIImageOrientationUp; + break; + + case 3: + orientation = UIImageOrientationDown; + break; + + case 8: + orientation = UIImageOrientationLeft; + break; + + case 6: + orientation = UIImageOrientationRight; + break; + + case 2: + orientation = UIImageOrientationUpMirrored; + break; + + case 4: + orientation = UIImageOrientationDownMirrored; + break; + + case 5: + orientation = UIImageOrientationLeftMirrored; + break; + + case 7: + orientation = UIImageOrientationRightMirrored; + break; + default: + break; + } + return orientation; +} + + + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h b/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h new file mode 100644 index 0000000..57f708f --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h @@ -0,0 +1,100 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state. + */ +@interface UIImageView (HighlightedWebCache) + +/** + * Set the imageView `highlightedImage` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url; + +/** + * Set the imageView `highlightedImage` with an `url` and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options; + +/** + * Set the imageView `highlightedImage` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `highlightedImage` with an `url` and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `highlightedImage` with an `url` and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Cancel the current download + */ +- (void)sd_cancelCurrentHighlightedImageLoad; + +@end + + +@interface UIImageView (HighlightedWebCacheDeprecated) + +- (void)setHighlightedImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:completed:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:completed:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:progress:completed:`"); + +- (void)cancelCurrentHighlightedImageLoad __deprecated_msg("Use `sd_cancelCurrentHighlightedImageLoad`"); + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m b/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m new file mode 100644 index 0000000..ae73610 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m @@ -0,0 +1,107 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIImageView+HighlightedWebCache.h" +#import "UIView+WebCacheOperation.h" + +#define UIImageViewHighlightedWebCacheOperationKey @"highlightedImage" + +@implementation UIImageView (HighlightedWebCache) + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelCurrentHighlightedImageLoad]; + + if (url) { + __weak UIImageView *wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe (^ + { + if (!wself) return; + if (image) { + wself.highlightedImage = image; + [wself setNeedsLayout]; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forKey:UIImageViewHighlightedWebCacheOperationKey]; + } else { + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_cancelCurrentHighlightedImageLoad { + [self sd_cancelImageLoadOperationWithKey:UIImageViewHighlightedWebCacheOperationKey]; +} + +@end + + +@implementation UIImageView (HighlightedWebCacheDeprecated) + +- (void)setHighlightedImageWithURL:(NSURL *)url { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:0 progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentHighlightedImageLoad { + [self sd_cancelCurrentHighlightedImageLoad]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h b/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h new file mode 100644 index 0000000..e7489f4 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h @@ -0,0 +1,201 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with UIImageView. + * + * Usage with a UITableViewCell sub-class: + * + * @code + +#import + +... + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *MyIdentifier = @"MyIdentifier"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; + + if (cell == nil) { + cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] + autorelease]; + } + + // Here we use the provided sd_setImageWithURL: method to load the web image + // Ensure you use a placeholder image otherwise cells will be initialized with no image + [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"] + placeholderImage:[UIImage imageNamed:@"placeholder"]]; + + cell.textLabel.text = @"My Text"; + return cell; +} + + * @endcode + */ +@interface UIImageView (WebCache) + +/** + * Get the current image URL. + * + * Note that because of the limitations of categories this property can get out of sync + * if you use sd_setImage: directly. + */ +- (NSURL *)sd_imageURL; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + */ +- (void)sd_setImageWithURL:(NSURL *)url; + +/** + * Set the imageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url` and a optionaly placeholder image. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url andPlaceholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Download an array of images and starts them in an animation loop + * + * @param arrayOfURLs An array of NSURL + */ +- (void)sd_setAnimationImagesWithURLs:(NSArray *)arrayOfURLs; + +/** + * Cancel the current download + */ +- (void)sd_cancelCurrentImageLoad; + +- (void)sd_cancelCurrentAnimationImagesLoad; + +@end + + +@interface UIImageView (WebCacheDeprecated) + +- (NSURL *)imageURL __deprecated_msg("Use `sd_imageURL`"); + +- (void)setImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options`"); + +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:progress:completed:`"); + +- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs __deprecated_msg("Use `sd_setAnimationImagesWithURLs:`"); + +- (void)cancelCurrentArrayLoad __deprecated_msg("Use `sd_cancelCurrentAnimationImagesLoad`"); + +- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelCurrentImageLoad`"); + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m b/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m new file mode 100644 index 0000000..1e225eb --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m @@ -0,0 +1,197 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIImageView+WebCache.h" +#import "objc/runtime.h" +#import "UIView+WebCacheOperation.h" + +static char imageURLKey; + +@implementation UIImageView (WebCache) + +- (void)sd_setImageWithURL:(NSURL *)url { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelCurrentImageLoad]; + objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + if (!(options & SDWebImageDelayPlaceholder)) { + dispatch_main_async_safe(^{ + self.image = placeholder; + }); + } + + if (url) { + __weak UIImageView *wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + if (!wself) return; + if (image) { + wself.image = image; + [wself setNeedsLayout]; + } else { + if ((options & SDWebImageDelayPlaceholder)) { + wself.image = placeholder; + [wself setNeedsLayout]; + } + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"]; + } else { + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url andPlaceholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url]; + UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:key]; + + [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock]; +} + +- (NSURL *)sd_imageURL { + return objc_getAssociatedObject(self, &imageURLKey); +} + +- (void)sd_setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { + [self sd_cancelCurrentAnimationImagesLoad]; + __weak UIImageView *wself = self; + + NSMutableArray *operationsArray = [[NSMutableArray alloc] init]; + + for (NSURL *logoImageURL in arrayOfURLs) { + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong UIImageView *sself = wself; + [sself stopAnimating]; + if (sself && image) { + NSMutableArray *currentImages = [[sself animationImages] mutableCopy]; + if (!currentImages) { + currentImages = [[NSMutableArray alloc] init]; + } + [currentImages addObject:image]; + + sself.animationImages = currentImages; + [sself setNeedsLayout]; + } + [sself startAnimating]; + }); + }]; + [operationsArray addObject:operation]; + } + + [self sd_setImageLoadOperation:[NSArray arrayWithArray:operationsArray] forKey:@"UIImageViewAnimationImages"]; +} + +- (void)sd_cancelCurrentImageLoad { + [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"]; +} + +- (void)sd_cancelCurrentAnimationImagesLoad { + [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"]; +} + +@end + + +@implementation UIImageView (WebCacheDeprecated) + +- (NSURL *)imageURL { + return [self sd_imageURL]; +} + +- (void)setImageWithURL:(NSURL *)url { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentArrayLoad { + [self sd_cancelCurrentAnimationImagesLoad]; +} + +- (void)cancelCurrentImageLoad { + [self sd_cancelCurrentImageLoad]; +} + +- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { + [self sd_setAnimationImagesWithURLs:arrayOfURLs]; +} + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h b/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h new file mode 100644 index 0000000..6719036 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h @@ -0,0 +1,36 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageManager.h" + +@interface UIView (WebCacheOperation) + +/** + * Set the image load operation (storage in a UIView based dictionary) + * + * @param operation the operation + * @param key key for storing the operation + */ +- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key; + +/** + * Cancel all operations for the current UIView and key + * + * @param key key for identifying the operations + */ +- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key; + +/** + * Just remove the operations corresponding to the current UIView and key without cancelling them + * + * @param key key for identifying the operations + */ +- (void)sd_removeImageLoadOperationWithKey:(NSString *)key; + +@end diff --git a/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m b/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m new file mode 100644 index 0000000..9219478 --- /dev/null +++ b/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m @@ -0,0 +1,55 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIView+WebCacheOperation.h" +#import "objc/runtime.h" + +static char loadOperationKey; + +@implementation UIView (WebCacheOperation) + +- (NSMutableDictionary *)operationDictionary { + NSMutableDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); + if (operations) { + return operations; + } + operations = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return operations; +} + +- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key { + [self sd_cancelImageLoadOperationWithKey:key]; + NSMutableDictionary *operationDictionary = [self operationDictionary]; + [operationDictionary setObject:operation forKey:key]; +} + +- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key { + // Cancel in progress downloader from queue + NSMutableDictionary *operationDictionary = [self operationDictionary]; + id operations = [operationDictionary objectForKey:key]; + if (operations) { + if ([operations isKindOfClass:[NSArray class]]) { + for (id operation in operations) { + if (operation) { + [operation cancel]; + } + } + } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ + [(id) operations cancel]; + } + [operationDictionary removeObjectForKey:key]; + } +} + +- (void)sd_removeImageLoadOperationWithKey:(NSString *)key { + NSMutableDictionary *operationDictionary = [self operationDictionary]; + [operationDictionary removeObjectForKey:key]; +} + +@end diff --git a/Pods/STableViewController/STableViewController/STableViewController.h b/Pods/STableViewController/STableViewController/STableViewController.h new file mode 100644 index 0000000..f99f98b --- /dev/null +++ b/Pods/STableViewController/STableViewController/STableViewController.h @@ -0,0 +1,103 @@ +// +// STableViewController.h +// +// @author Shiki +// + +#import + + +@interface STableViewController : UIViewController { + +@protected + + BOOL isDragging; + BOOL isRefreshing; + BOOL isLoadingMore; + + // had to store this because the headerView's frame seems to be changed somewhere during scrolling + // and I couldn't figure out why >.< + CGRect headerViewFrame; +} + +// The view used for "pull to refresh" +@property (nonatomic, retain) UIView *headerView; + +// The view used for "load more" +@property (nonatomic, retain) UIView *footerView; + +@property (nonatomic, retain) UITableView *tableView; +@property (readonly) BOOL isDragging; +@property (readonly) BOOL isRefreshing; +@property (readonly) BOOL isLoadingMore; +@property (nonatomic) BOOL canLoadMore; + +@property (nonatomic) BOOL pullToRefreshEnabled; + +// Defaults to YES +@property (nonatomic) BOOL clearsSelectionOnViewWillAppear; + +// Just a common initialize method +- (void) initialize; + +#pragma mark - Pull to Refresh + +// The minimum height that the user should drag down in order to trigger a "refresh" when +// dragging ends. +- (CGFloat) headerRefreshHeight; + +// Will be called if the user drags down which will show the header view. Override this to +// update the header view (e.g. change the label to "Pull down to refresh"). +- (void) willShowHeaderView:(UIScrollView *)scrollView; + +// If the user is dragging, will be called on every scroll event that the headerView is shown. +// The value of willRefreshOnRelease will be YES if the user scrolled down enough to trigger a +// "refresh" when the user releases the drag. +- (void) headerViewDidScroll:(BOOL)willRefreshOnRelease scrollView:(UIScrollView *)scrollView; + +// By default, will permanently show the headerView by setting the tableView's contentInset. +- (void) pinHeaderView; + +// Reverse of pinHeaderView. +- (void) unpinHeaderView; + +// Called when the user stops dragging and, if the conditions are met, will trigger a refresh. +- (void) willBeginRefresh; + +// Override to perform fetching of data. The parent method [super refresh] should be called first. +// If the value is NO, -refresh should be aborted. +- (BOOL) refresh; + +// Call to signal that refresh has completed. This will then hide the headerView. +- (void) refreshCompleted; + +#pragma mark - Load More + +// The value of the height starting from the bottom that the user needs to scroll down to in order +// to trigger -loadMore. By default, this will be the height of -footerView. +- (CGFloat) footerLoadMoreHeight; + +// Override to perform fetching of next page of data. It's important to call and get the value of +// of [super loadMore] first. If it's NO, -loadMore should be aborted. +- (BOOL) loadMore; + +// Called when all the conditions are met and -loadMore will begin. +- (void) willBeginLoadingMore; + +// Call to signal that "load more" was completed. This should be called so -isLoadingMore is +// properly set to NO. +- (void) loadMoreCompleted; + +// Helper to show/hide -footerView +- (void) setFooterViewVisibility:(BOOL)visible; + +#pragma mark - + +// A helper method that calls refreshCompleted and/or loadMoreCompleted if any are active. +- (void) allLoadingCompleted; + +#pragma mark - + +- (void) releaseViewComponents; + +@end diff --git a/Pods/STableViewController/STableViewController/STableViewController.m b/Pods/STableViewController/STableViewController/STableViewController.m new file mode 100644 index 0000000..63a8720 --- /dev/null +++ b/Pods/STableViewController/STableViewController/STableViewController.m @@ -0,0 +1,326 @@ +// +// STableViewController.m +// +// @author Shiki +// + +#import "STableViewController.h" + +#define DEFAULT_HEIGHT_OFFSET 52.0f + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +@implementation STableViewController + +@synthesize tableView; +@synthesize headerView; +@synthesize footerView; + +@synthesize isDragging; +@synthesize isRefreshing; +@synthesize isLoadingMore; + +@synthesize canLoadMore; + +@synthesize pullToRefreshEnabled; + +@synthesize clearsSelectionOnViewWillAppear; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) initialize +{ + pullToRefreshEnabled = YES; + + canLoadMore = YES; + + clearsSelectionOnViewWillAppear = YES; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (id) init +{ + if ((self = [super init])) + [self initialize]; + return self; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (id) initWithCoder:(NSCoder *)aDecoder +{ + if ((self = [super initWithCoder:aDecoder])) + [self initialize]; + return self; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) viewDidLoad +{ + [super viewDidLoad]; + + self.tableView = [[[UITableView alloc] init] autorelease]; + tableView.frame = self.view.bounds; + tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + tableView.dataSource = self; + tableView.delegate = self; + + [self.view addSubview:tableView]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (clearsSelectionOnViewWillAppear) { + NSIndexPath *selected = [self.tableView indexPathForSelectedRow]; + if (selected) + [self.tableView deselectRowAtIndexPath:selected animated:animated]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Pull to Refresh + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) setHeaderView:(UIView *)aView +{ + if (!tableView) + return; + + if (headerView && [headerView isDescendantOfView:tableView]) + [headerView removeFromSuperview]; + [headerView release]; headerView = nil; + + if (aView) { + headerView = [aView retain]; + + CGRect f = headerView.frame; + headerView.frame = CGRectMake(f.origin.x, 0 - f.size.height, f.size.width, f.size.height); + headerViewFrame = headerView.frame; + + [tableView addSubview:headerView]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (CGFloat) headerRefreshHeight +{ + if (!CGRectIsEmpty(headerViewFrame)) + return headerViewFrame.size.height; + else + return DEFAULT_HEIGHT_OFFSET; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) pinHeaderView +{ + [UIView animateWithDuration:0.3 animations:^(void) { + self.tableView.contentInset = UIEdgeInsetsMake([self headerRefreshHeight], 0, 0, 0); + }]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) unpinHeaderView +{ + [UIView animateWithDuration:0.3 animations:^(void) { + self.tableView.contentInset = UIEdgeInsetsZero; + }]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) willBeginRefresh +{ + if (pullToRefreshEnabled) + [self pinHeaderView]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) willShowHeaderView:(UIScrollView *)scrollView +{ + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) headerViewDidScroll:(BOOL)willRefreshOnRelease scrollView:(UIScrollView *)scrollView +{ + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (BOOL) refresh +{ + if (isRefreshing) + return NO; + + [self willBeginRefresh]; + isRefreshing = YES; + return YES; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) refreshCompleted +{ + isRefreshing = NO; + + if (pullToRefreshEnabled) + [self unpinHeaderView]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Load More + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) setFooterView:(UIView *)aView +{ + if (!tableView) + return; + + tableView.tableFooterView = nil; + [footerView release]; footerView = nil; + + if (aView) { + footerView = [aView retain]; + + tableView.tableFooterView = footerView; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) willBeginLoadingMore +{ + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) loadMoreCompleted +{ + isLoadingMore = NO; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (BOOL) loadMore +{ + if (isLoadingMore) + return NO; + + [self willBeginLoadingMore]; + isLoadingMore = YES; + return YES; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (CGFloat) footerLoadMoreHeight +{ + if (footerView) + return footerView.frame.size.height; + else + return DEFAULT_HEIGHT_OFFSET; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) setFooterViewVisibility:(BOOL)visible +{ + if (visible && self.tableView.tableFooterView != footerView) + self.tableView.tableFooterView = footerView; + else if (!visible) + self.tableView.tableFooterView = nil; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) allLoadingCompleted +{ + if (isRefreshing) + [self refreshCompleted]; + if (isLoadingMore) + [self loadMoreCompleted]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - UIScrollViewDelegate + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + if (isRefreshing) + return; + isDragging = YES; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) scrollViewDidScroll:(UIScrollView *)scrollView +{ + if (!isRefreshing && isDragging && scrollView.contentOffset.y < 0) { + [self headerViewDidScroll:scrollView.contentOffset.y < 0 - [self headerRefreshHeight] + scrollView:scrollView]; + } else if (!isLoadingMore && canLoadMore) { + CGFloat scrollPosition = scrollView.contentSize.height - scrollView.frame.size.height - scrollView.contentOffset.y; + if (scrollPosition < [self footerLoadMoreHeight]) { + [self loadMore]; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + if (isRefreshing) + return; + + isDragging = NO; + if (scrollView.contentOffset.y <= 0 - [self headerRefreshHeight]) { + if (pullToRefreshEnabled) + [self refresh]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - UITableViewDelegate + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return nil; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) releaseViewComponents +{ + [headerView release]; headerView = nil; + [footerView release]; footerView = nil; + [tableView release]; tableView = nil; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) dealloc +{ + [self releaseViewComponents]; + [super dealloc]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) viewDidUnload +{ + [self releaseViewComponents]; + [super viewDidUnload]; +} + +@end diff --git a/Pods/SecureUDID/SecureUDID.h b/Pods/SecureUDID/SecureUDID.h new file mode 100644 index 0000000..b53d393 --- /dev/null +++ b/Pods/SecureUDID/SecureUDID.h @@ -0,0 +1,75 @@ +// +// SecureUDID.h +// SecureUDID +// +// Created by Crashlytics Team on 3/22/12. +// Copyright (c) 2012 Crashlytics, Inc. All rights reserved. +// http://www.crashlytics.com +// info@crashlytics.com +// + +/* + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#import + +@interface SecureUDID : NSObject + +/* + Returns a unique id for the device, sandboxed to the domain and salt provided. This is a potentially + expensive call. You should not do this on the main thread, especially during launch. + + retrieveUDIDForDomain:usingKey:completion: is provided as an alternative for your 4.0+ coding convenience. + + Example usage: + #import "SecureUDID.h" + + NSString *udid = [SecureUDID UDIDForDomain:@"com.example.myapp" key:@"difficult-to-guess-key"]; + + */ ++ (NSString *)UDIDForDomain:(NSString *)domain usingKey:(NSString *)key; + +/* + Getting a SecureUDID can be very expensive. Use this call to derive an identifier in the background, + and invoke a block when ready. Use of this method implies a device running >= iOS 4.0. + + Example usage: + #import "SecureUDID.h" + + [SecureUDID retrieveUDIDForDomain:@"com.example.myapp" usingKey:@"difficult-to-guess-key" completion:^(NSString *identifier) { + // make use of identifier here + }]; + + */ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 ++ (void)retrieveUDIDForDomain:(NSString *)domain usingKey:(NSString *)key completion:(void (^)(NSString* identifier))completion; +#endif + +/* + Indicates that the system has been disabled via the Opt-Out mechansim. +*/ ++ (BOOL)isOptedOut; + +@end + +/* + This identifier is returned when Opt-Out is enabled. + */ +extern NSString *const SUUIDDefaultIdentifier; diff --git a/Pods/SecureUDID/SecureUDID.m b/Pods/SecureUDID/SecureUDID.m new file mode 100644 index 0000000..ed71e53 --- /dev/null +++ b/Pods/SecureUDID/SecureUDID.m @@ -0,0 +1,687 @@ +// +// SecureUDID.m +// SecureUDID +// +// Created by Crashlytics Team on 3/22/12. +// Copyright (c) 2012 Crashlytics, Inc. All rights reserved. +// http://www.crashlytics.com +// info@crashlytics.com +// + +/* + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#import "SecureUDID.h" +#import +#import +#import +#import + +#define SUUID_SCHEMA_VERSION (1) +#define SUUID_MAX_STORAGE_LOCATIONS (64) + +NSString *const SUUIDDefaultIdentifier = @"00000000-0000-0000-0000-000000000000"; + +NSString *const SUUIDTypeDataDictionary = @"public.secureudid"; +NSString *const SUUIDTimeStampKey = @"SUUIDTimeStampKey"; +NSString *const SUUIDOwnerKey = @"SUUIDOwnerKey"; +NSString *const SUUIDLastAccessedKey = @"SUUIDLastAccessedKey"; +NSString *const SUUIDIdentifierKey = @"SUUIDIdentifierKey"; +NSString *const SUUIDOptOutKey = @"SUUIDOptOutKey"; +NSString *const SUUIDModelHashKey = @"SUUIDModelHashKey"; +NSString *const SUUIDSchemaVersionKey = @"SUUIDSchemaVersionKey"; +NSString *const SUUIDPastboardFileFormat = @"org.secureudid-%d"; + +NSData *SUUIDCryptorToData(CCOperation operation, NSData *value, NSData *key); +NSString *SUUIDCryptorToString(CCOperation operation, NSData *value, NSData *key); +NSData *SUUIDHash(NSData* data); +NSData *SUUIDModelHash(void); + +void SUUIDMarkOptedOut(void); +void SUUIDMarkOptedIn(void); +void SUUIDRemoveAllSecureUDIDData(void); +NSString *SUUIDPasteboardNameForNumber(NSInteger number); +NSInteger SUUIDStorageLocationForOwnerKey(NSData *key, NSMutableDictionary** dictionary); +NSDictionary *SUUIDDictionaryForStorageLocation(NSInteger number); +NSDictionary *SUUIDMostRecentDictionary(void); +void SUUIDWriteDictionaryToStorageLocation(NSInteger number, NSDictionary* dictionary); +void SUUIDDeleteStorageLocation(NSInteger number); + +BOOL SUUIDValidTopLevelObject(id object); +BOOL SUUIDValidOwnerObject(id object); + +@implementation SecureUDID + +/* + Returns a unique id for the device, sandboxed to the domain and salt provided. + + Example usage: + #import "SecureUDID.h" + + NSString *udid = [SecureUDID UDIDForDomain:@"com.example.myapp" salt:@"superSecretCodeHere!@##%#$#%$^"]; + + */ ++ (NSString *)UDIDForDomain:(NSString *)domain usingKey:(NSString *)key { + NSString *identifier = SUUIDDefaultIdentifier; + + // Salt the domain to make the crypt keys affectively unguessable. + NSData *domainAndKey = [[NSString stringWithFormat:@"%@%@", domain, key] dataUsingEncoding:NSUTF8StringEncoding]; + NSData *ownerKey = SUUIDHash(domainAndKey); + + // Encrypt the salted domain key and load the pasteboard on which to store data + NSData *encryptedOwnerKey = SUUIDCryptorToData(kCCEncrypt, [domain dataUsingEncoding:NSUTF8StringEncoding], ownerKey); + + // @synchronized introduces an implicit @try-@finally, so care needs to be taken with the return value + @synchronized (self) { + NSMutableDictionary *topLevelDictionary = nil; + + // Retrieve an appropriate storage index for this owner + NSInteger ownerIndex = SUUIDStorageLocationForOwnerKey(encryptedOwnerKey, &topLevelDictionary); + + // If the model hash key is present, verify it, otherwise add it + NSData *storedModelHash = [topLevelDictionary objectForKey:SUUIDModelHashKey]; + NSData *modelHash = SUUIDModelHash(); + + if (storedModelHash) { + if (![modelHash isEqual:storedModelHash]) { + // The model hashes do not match - this structure is invalid + [topLevelDictionary removeAllObjects]; + } + } + + // store the current model hash + [topLevelDictionary setObject:modelHash forKey:SUUIDModelHashKey]; + + // check for the opt-out flag and return the default identifier if we find it + if ([[topLevelDictionary objectForKey:SUUIDOptOutKey] boolValue] == YES) { + return identifier; + } + + // If we encounter a schema version greater than we support, there is no simple alternative + // other than to simulate Opt Out. Any writes to the store risk corruption. + if ([[topLevelDictionary objectForKey:SUUIDSchemaVersionKey] intValue] > SUUID_SCHEMA_VERSION) { + return identifier; + } + + // Attempt to get the owner's dictionary. Should we get back nil from the encryptedDomain key, we'll still + // get a valid, empty mutable dictionary + NSMutableDictionary *ownerDictionary = [NSMutableDictionary dictionaryWithDictionary:[topLevelDictionary objectForKey:encryptedOwnerKey]]; + + // Set our last access time and claim ownership for this storage location. + NSDate* lastAccessDate = [NSDate date]; + + [ownerDictionary setObject:lastAccessDate forKey:SUUIDLastAccessedKey]; + [topLevelDictionary setObject:lastAccessDate forKey:SUUIDTimeStampKey]; + [topLevelDictionary setObject:encryptedOwnerKey forKey:SUUIDOwnerKey]; + + [topLevelDictionary setObject:[NSNumber numberWithInt:SUUID_SCHEMA_VERSION] forKey:SUUIDSchemaVersionKey]; + + // Make sure our owner dictionary is in the top level structure + [topLevelDictionary setObject:ownerDictionary forKey:encryptedOwnerKey]; + + + NSData *identifierData = [ownerDictionary objectForKey:SUUIDIdentifierKey]; + if (identifierData) { + identifier = SUUIDCryptorToString(kCCDecrypt, identifierData, ownerKey); + if (!identifier) { + // We've failed to decrypt our identifier. This is a sign of storage corruption. + SUUIDDeleteStorageLocation(ownerIndex); + + // return here - do not write values back to the store + return SUUIDDefaultIdentifier; + } + } else { + // Otherwise, create a new RFC-4122 Version 4 UUID + // http://en.wikipedia.org/wiki/Universally_unique_identifier + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + identifier = [(NSString*)CFUUIDCreateString(kCFAllocatorDefault, uuid) autorelease]; + CFRelease(uuid); + + // Encrypt it for storage. + NSData *data = SUUIDCryptorToData(kCCEncrypt, [identifier dataUsingEncoding:NSUTF8StringEncoding], ownerKey); + + [ownerDictionary setObject:data forKey:SUUIDIdentifierKey]; + } + + SUUIDWriteDictionaryToStorageLocation(ownerIndex, topLevelDictionary); + } + + return identifier; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 ++ (void)retrieveUDIDForDomain:(NSString *)domain usingKey:(NSString *)key completion:(void (^)(NSString* identifier))completion { + // retreive the identifier on a low-priority thread + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSString* identifier; + + identifier = [SecureUDID UDIDForDomain:domain usingKey:key]; + + completion(identifier); + }); +} +#endif + +/* + API to determine if a device has opted out of SecureUDID. + */ ++ (BOOL)isOptedOut { + for (NSInteger i = 0; i < SUUID_MAX_STORAGE_LOCATIONS; ++i) { + NSDictionary* topLevelDictionary; + + topLevelDictionary = SUUIDDictionaryForStorageLocation(i); + if (!topLevelDictionary) { + continue; + } + + if ([[topLevelDictionary objectForKey:SUUIDOptOutKey] boolValue] == YES) { + return YES; + } + } + + return NO; +} + +/* + Applies the operation (encrypt or decrypt) to the NSData value with the provided NSData key + and returns the value as NSData. + */ +NSData *SUUIDCryptorToData(CCOperation operation, NSData *value, NSData *key) { + NSMutableData *output = [NSMutableData dataWithLength:value.length + kCCBlockSizeAES128]; + + size_t numBytes = 0; + CCCryptorStatus cryptStatus = CCCrypt(operation, + kCCAlgorithmAES128, + kCCOptionPKCS7Padding, + [key bytes], + kCCKeySizeAES128, + NULL, + value.bytes, + value.length, + output.mutableBytes, + output.length, + &numBytes); + + if (cryptStatus == kCCSuccess) { + return [[[NSData alloc] initWithBytes:output.bytes length:numBytes] autorelease]; + } + + return nil; +} + +/* + Applies the operation (encrypt or decrypt) to the NSData value with the provided NSData key + and returns the value as an NSString. + */ +NSString *SUUIDCryptorToString(CCOperation operation, NSData *value, NSData *key) { + NSData* data; + + data = SUUIDCryptorToData(operation, value, key); + if (!data) { + return nil; + } + + return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; +} + +/* + Compute a SHA1 of the input. + */ +NSData *SUUIDHash(NSData __unsafe_unretained * data) { + uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0}; + + CC_SHA1(data.bytes, data.length, digest); + + return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; +} + +NSData* SUUIDModelHash(void) { + NSString* result; + + result = @"Unknown"; + + do { + size_t size; + char* value; + + value = NULL; + + // first get the size + if (sysctlbyname("hw.machine", NULL, &size, NULL, 0) != 0) { + break; + } + + value = malloc(size); + if (!value) { + break; + } + + // now get the value + if (sysctlbyname("hw.machine", value, &size, NULL, 0) != 0) { + break; + } + + // convert the value to an NSString + result = [NSString stringWithCString:value encoding:NSUTF8StringEncoding]; + if (!result) { + break; + } + + // free our buffer + free(value); + } while (0); + + return SUUIDHash([result dataUsingEncoding:NSUTF8StringEncoding]); +} + +/* + Finds the most recent structure, and adds the Opt-Out flag to it. Then writes that structure back + out to all used storage locations, making sure to preserve ownership. + */ +void SUUIDMarkOptedOut(void) { + NSMutableDictionary* mostRecentDictionary; + + mostRecentDictionary = [NSMutableDictionary dictionaryWithDictionary:SUUIDMostRecentDictionary()]; + + [mostRecentDictionary setObject:[NSDate date] forKey:SUUIDTimeStampKey]; + [mostRecentDictionary setObject:[NSNumber numberWithBool:YES] forKey:SUUIDOptOutKey]; + + for (NSInteger i = 0; i < SUUID_MAX_STORAGE_LOCATIONS; ++i) { + NSData* owner; + + // Inherit the owner, if it is present. This makes some schema assumptions. + owner = [SUUIDDictionaryForStorageLocation(i) objectForKey:SUUIDOwnerKey]; + if (owner) { + [mostRecentDictionary setObject:owner forKey:SUUIDOwnerKey]; + } + + // write the opt-out data even if the location was previously empty + SUUIDWriteDictionaryToStorageLocation(i, mostRecentDictionary); + } +} + +void SUUIDMarkOptedIn(void) { + NSDate* accessedDate; + + accessedDate = [NSDate date]; + + // Opting back in is trickier. We need to remove top-level Opt-Out markers. Also makes some + // schema assumptions. + for (NSInteger i = 0; i < SUUID_MAX_STORAGE_LOCATIONS; ++i) { + NSMutableDictionary* dictionary; + + dictionary = [NSMutableDictionary dictionaryWithDictionary:SUUIDDictionaryForStorageLocation(i)]; + if (!dictionary) { + // This is a possible indiction of storage corruption. However, SUUIDDictionaryForStorageLocation + // will have already cleaned it up for us, so there's not much to do here. + continue; + } + + [dictionary removeObjectForKey:SUUIDOptOutKey]; + + // quick check for the minimum set of keys. If the dictionary previously held just + // an Opt-Out marker + timestamp, dictionary is not invalid. Writing will fail in this + // case, leaving the data that was there. We need to delete. + if (!SUUIDValidTopLevelObject(dictionary)) { + SUUIDDeleteStorageLocation(i); + continue; + } + + [dictionary setObject:accessedDate forKey:SUUIDTimeStampKey]; + + SUUIDWriteDictionaryToStorageLocation(i, dictionary); + } +} + +/* + Removes all SecureUDID data from storage with the exception of Opt-Out flags, which + are never removed. Removing the Opt-Out flags would effectively opt a user back in. +*/ +void SUUIDRemoveAllSecureUDIDData(void) { + NSDictionary* optOutPlaceholder = nil; + + if ([SecureUDID isOptedOut]) { + optOutPlaceholder = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:SUUIDOptOutKey]; + } + + for (NSInteger i = 0; i < SUUID_MAX_STORAGE_LOCATIONS; ++i) { + if (optOutPlaceholder) { + SUUIDWriteDictionaryToStorageLocation(i, optOutPlaceholder); + continue; + } + + SUUIDDeleteStorageLocation(i); + } +} + +/* + Returns an NSString formatted with the supplied number. + */ +NSString *SUUIDPasteboardNameForNumber(NSInteger number) { + return [NSString stringWithFormat:SUUIDPastboardFileFormat, number]; +} + +/* + Reads a dictionary from a storage location. Validation occurs once data + is read, but before it is returned. If something fails, or if the read structure + is invalid, the location is cleared. + + Returns the data dictionary, or nil on failure. + */ +NSDictionary *SUUIDDictionaryForStorageLocation(NSInteger number) { + id decodedObject; + UIPasteboard* pasteboard; + NSData* data; + + // Don't even bother if the index is outside our limits + if (number < 0 || number >= SUUID_MAX_STORAGE_LOCATIONS) { + return nil; + } + + pasteboard = [UIPasteboard pasteboardWithName:SUUIDPasteboardNameForNumber(number) create:NO]; + if (!pasteboard) { + return nil; + } + + data = [pasteboard valueForPasteboardType:SUUIDTypeDataDictionary]; + if (!data) { + return nil; + } + + @try { + decodedObject = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } @catch (NSException* exception) { + // Catching an exception like this is risky. However, crashing here is + // not acceptable, and unarchiveObjectWithData can throw. + [pasteboard setData:nil forPasteboardType:SUUIDTypeDataDictionary]; + + return nil; + } + + if (!SUUIDValidTopLevelObject(decodedObject)) { + [pasteboard setData:nil forPasteboardType:SUUIDTypeDataDictionary]; + + return nil; + } + + return decodedObject; +} + +NSDictionary *SUUIDMostRecentDictionary(void) { + NSDictionary* mostRecentDictionary; + BOOL found; + + mostRecentDictionary = [NSDictionary dictionaryWithObject:[NSDate distantPast] forKey:SUUIDTimeStampKey]; + + // scan all locations looking for the most recent + for (NSUInteger i = 0; i < SUUID_MAX_STORAGE_LOCATIONS; ++i) { + NSDictionary* dictionary; + NSDate* date; + + dictionary = SUUIDDictionaryForStorageLocation(i); + if (!dictionary) { + continue; + } + + // Schema assumption + date = [dictionary objectForKey:SUUIDTimeStampKey]; + if ([date compare:[mostRecentDictionary objectForKey:SUUIDTimeStampKey]] == NSOrderedDescending) { + mostRecentDictionary = dictionary; + found = YES; + } + } + + if (!found) { + return nil; + } + + return mostRecentDictionary; +} + +/* + Writes out a dictionary to a storage location. That dictionary must be a 'valid' + SecureUDID structure, and the location must be within range. A new location is + created if is didn't already exist. + */ +void SUUIDWriteDictionaryToStorageLocation(NSInteger number, NSDictionary* dictionary) { + UIPasteboard* pasteboard; + + // be sure to respect our limits + if (number < 0 || number >= SUUID_MAX_STORAGE_LOCATIONS) { + return; + } + + // only write out valid structures + if (!SUUIDValidTopLevelObject(dictionary)) { + return; + } + + pasteboard = [UIPasteboard pasteboardWithName:SUUIDPasteboardNameForNumber(number) create:YES]; + if (!pasteboard) { + return; + } + + pasteboard.persistent = YES; + + [pasteboard setData:[NSKeyedArchiver archivedDataWithRootObject:dictionary] + forPasteboardType:SUUIDTypeDataDictionary]; +} + +/* + Clear a storage location, removing anything stored there. Useful for dealing with + potential corruption. Be careful with this function, as it can remove Opt-Out markers. +*/ +void SUUIDDeleteStorageLocation(NSInteger number) { + UIPasteboard* pasteboard; + NSString* name; + + if (number < 0 || number >= SUUID_MAX_STORAGE_LOCATIONS) { + return; + } + + name = SUUIDPasteboardNameForNumber(number); + pasteboard = [UIPasteboard pasteboardWithName:name create:NO]; + if (!pasteboard) + return; + + // While setting pasteboard data to nil seems to always remove contents, the + // removePasteboardWithName: call doesn't appear to always work. Using both seems + // like the safest thing to do + [pasteboard setData:nil forPasteboardType:SUUIDTypeDataDictionary]; + [UIPasteboard removePasteboardWithName:name]; +} + +/* + SecureUDID leverages UIPasteboards to persistently store its data. + UIPasteboards marked as 'persistent' have the following attributes: + - They persist across application relaunches, device reboots, and OS upgrades. + - They are destroyed when the application that created them is deleted from the device. + + To protect against the latter case, SecureUDID leverages multiple pasteboards (up to + SUUID_MAX_STORAGE_LOCATIONS), creating one for each distinct domain/app that + leverages the system. The permanence of SecureUDIDs increases exponentially with the + number of apps that use it. + + This function searches for a suitable storage location for a SecureUDID structure. It + attempts to find the structure written by ownerKey. If no owner is found and there are + still open locations, the lowest numbered location is selected. If there are no + available locations, the last-written is selected. + + Once a spot is found, the most-recent data is re-written over this location. The location + is then, finally, returned. + */ +NSInteger SUUIDStorageLocationForOwnerKey(NSData *ownerKey, NSMutableDictionary** ownerDictionary) { + NSInteger ownerIndex; + NSInteger lowestUnusedIndex; + NSInteger oldestUsedIndex; + NSDate* mostRecentDate; + NSDate* oldestUsedDate; + NSDictionary* mostRecentDictionary; + BOOL optedOut; + + ownerIndex = -1; + lowestUnusedIndex = -1; + oldestUsedIndex = 0; // make sure this value is always in range + mostRecentDate = [NSDate distantPast]; + oldestUsedDate = [NSDate distantFuture]; + mostRecentDictionary = nil; + optedOut = NO; + + // The array of SecureUDID pasteboards can be sparse, since any number of + // apps may have been deleted. To find a pasteboard owned by the the current + // domain, iterate all of them. + for (NSInteger i = 0; i < SUUID_MAX_STORAGE_LOCATIONS; ++i) { + NSDate* modifiedDate; + NSDictionary* dictionary; + + dictionary = SUUIDDictionaryForStorageLocation(i); + if (!dictionary) { + if (lowestUnusedIndex == -1) { + lowestUnusedIndex = i; + } + + continue; + } + + // Check the 'modified' timestamp of this pasteboard + modifiedDate = [dictionary valueForKey:SUUIDTimeStampKey]; + optedOut = optedOut || [[dictionary valueForKey:SUUIDOptOutKey] boolValue]; + + // Hold a copy of the data if this is the newest we've found so far. + if ([modifiedDate compare:mostRecentDate] == NSOrderedDescending) { + mostRecentDate = modifiedDate; + mostRecentDictionary = dictionary; + } + + // Check for the oldest entry in the structure, used for eviction + if ([modifiedDate compare:oldestUsedDate] == NSOrderedAscending) { + oldestUsedDate = modifiedDate; + oldestUsedIndex = i; + } + + // Finally, check if this is the pasteboard owned by the requesting domain. + if ([[dictionary objectForKey:SUUIDOwnerKey] isEqual:ownerKey]) { + ownerIndex = i; + } + } + + // If no pasteboard is owned by this domain, establish a new one to increase the + // likelihood of permanence. + if (ownerIndex == -1) { + // Unless there are no available slots, then evict the oldest entry + if ((lowestUnusedIndex < 0) || (lowestUnusedIndex >= SUUID_MAX_STORAGE_LOCATIONS)) { + ownerIndex = oldestUsedIndex; + } else { + ownerIndex = lowestUnusedIndex; + } + } + + // pass back the dictionary, by reference + *ownerDictionary = [NSMutableDictionary dictionaryWithDictionary:mostRecentDictionary]; + + // make sure our Opt-Out flag is consistent + if (optedOut) { + [*ownerDictionary setObject:[NSNumber numberWithBool:YES] forKey:SUUIDOptOutKey]; + } + + // Make sure to write the most recent structure to the new location + SUUIDWriteDictionaryToStorageLocation(ownerIndex, mostRecentDictionary); + + return ownerIndex; +} + +/* + Attempts to validate the full SecureUDID structure. + */ +BOOL SUUIDValidTopLevelObject(id object) { + if (![object isKindOfClass:[NSDictionary class]]) { + return NO; + } + + // Now, we need to verify the current schema. There are a few possible valid states: + // - SUUIDTimeStampKey + SUUIDOwnerKey + at least one additional key that is not SUUIDOptOutKey + // - SUUIDTimeStampKey + SUUIDOwnerKey + SUUIDOptOutKey + + if ([object objectForKey:SUUIDTimeStampKey] && [object objectForKey:SUUIDOwnerKey]) { + NSMutableDictionary* ownersOnlyDictionary; + NSData* ownerField; + + if ([object objectForKey:SUUIDOptOutKey]) { + return YES; + } + + // We have to trust future schema versions. Note that the lack of a schema version key will + // always fail this check, since the first schema version was 1. + if ([[object objectForKey:SUUIDSchemaVersionKey] intValue] > SUUID_SCHEMA_VERSION) { + return YES; + } + + ownerField = [object objectForKey:SUUIDOwnerKey]; + if (![ownerField isKindOfClass:[NSData class]]) { + return NO; + } + + ownersOnlyDictionary = [NSMutableDictionary dictionaryWithDictionary:object]; + + [ownersOnlyDictionary removeObjectForKey:SUUIDTimeStampKey]; + [ownersOnlyDictionary removeObjectForKey:SUUIDOwnerKey]; + [ownersOnlyDictionary removeObjectForKey:SUUIDOptOutKey]; + [ownersOnlyDictionary removeObjectForKey:SUUIDModelHashKey]; + [ownersOnlyDictionary removeObjectForKey:SUUIDSchemaVersionKey]; + + // now, iterate through to verify each internal structure + for (id key in [ownersOnlyDictionary allKeys]) { + if ([key isEqual:SUUIDTimeStampKey] || [key isEqual:SUUIDOwnerKey] || [key isEqual:SUUIDOptOutKey]) + continue; + + if (![key isKindOfClass:[NSData class]]) { + return NO; + } + + if (!SUUIDValidOwnerObject([ownersOnlyDictionary objectForKey:key])) { + return NO; + } + } + + // if all these tests pass, this structure is valid + return YES; + } + + // Maybe just the SUUIDOptOutKey, on its own + if ([[object objectForKey:SUUIDOptOutKey] boolValue] == YES) { + return YES; + } + + return NO; +} + +/* + Attempts to validate the structure for an "owner dictionary". + */ +BOOL SUUIDValidOwnerObject(id object) { + if (![object isKindOfClass:[NSDictionary class]]) { + return NO; + } + + return [object valueForKey:SUUIDLastAccessedKey] && [object valueForKey:SUUIDIdentifierKey]; +} + +@end