diff --git a/Resources/Back.png b/Resources/Back.png index b9a92c0..1bc6cf6 100644 Binary files a/Resources/Back.png and b/Resources/Back.png differ diff --git a/Resources/Back@2x.png b/Resources/Back@2x.png index f687b9f..ffa8db3 100644 Binary files a/Resources/Back@2x.png and b/Resources/Back@2x.png differ diff --git a/Resources/Back_button_item.png b/Resources/Back_button_item.png new file mode 100644 index 0000000..445cf4f Binary files /dev/null and b/Resources/Back_button_item.png differ diff --git a/Resources/Back_button_item@2x.png b/Resources/Back_button_item@2x.png new file mode 100644 index 0000000..6d434df Binary files /dev/null and b/Resources/Back_button_item@2x.png differ diff --git a/Resources/Bar_item.png b/Resources/Bar_item.png new file mode 100644 index 0000000..af4682a Binary files /dev/null and b/Resources/Bar_item.png differ diff --git a/Resources/Bar_item@2x.png b/Resources/Bar_item@2x.png new file mode 100644 index 0000000..23a7045 Binary files /dev/null and b/Resources/Bar_item@2x.png differ diff --git a/Resources/Forward.png b/Resources/Forward.png index 20c86ec..a80eae4 100644 Binary files a/Resources/Forward.png and b/Resources/Forward.png differ diff --git a/Resources/Forward@2x.png b/Resources/Forward@2x.png index e912cc3..72f6c6d 100644 Binary files a/Resources/Forward@2x.png and b/Resources/Forward@2x.png differ diff --git a/Resources/Item.png b/Resources/Item.png new file mode 100644 index 0000000..eaf3e80 Binary files /dev/null and b/Resources/Item.png differ diff --git a/Resources/Item@2x.png b/Resources/Item@2x.png new file mode 100644 index 0000000..cbbe6bd Binary files /dev/null and b/Resources/Item@2x.png differ diff --git a/Resources/Item_highlighted.png b/Resources/Item_highlighted.png new file mode 100644 index 0000000..d4dbc96 Binary files /dev/null and b/Resources/Item_highlighted.png differ diff --git a/Resources/Item_highlighted@2x.png b/Resources/Item_highlighted@2x.png new file mode 100644 index 0000000..5a6bd40 Binary files /dev/null and b/Resources/Item_highlighted@2x.png differ diff --git a/Resources/Item_selected.png b/Resources/Item_selected.png new file mode 100644 index 0000000..d4dbc96 Binary files /dev/null and b/Resources/Item_selected.png differ diff --git a/Resources/Item_selected@2x.png b/Resources/Item_selected@2x.png new file mode 100644 index 0000000..5a6bd40 Binary files /dev/null and b/Resources/Item_selected@2x.png differ diff --git a/Resources/Navigation.png b/Resources/Navigation.png new file mode 100644 index 0000000..8783420 Binary files /dev/null and b/Resources/Navigation.png differ diff --git a/Resources/Navigation@2x.png b/Resources/Navigation@2x.png new file mode 100644 index 0000000..aebd06b Binary files /dev/null and b/Resources/Navigation@2x.png differ diff --git a/Resources/Recent.png b/Resources/Recent.png new file mode 100644 index 0000000..4e74bfa Binary files /dev/null and b/Resources/Recent.png differ diff --git a/Resources/Recent@2x.png b/Resources/Recent@2x.png new file mode 100644 index 0000000..2c3b74b Binary files /dev/null and b/Resources/Recent@2x.png differ diff --git a/Resources/Refresh.png b/Resources/Refresh.png new file mode 100644 index 0000000..ca3d64e Binary files /dev/null and b/Resources/Refresh.png differ diff --git a/Resources/Refresh@2x.png b/Resources/Refresh@2x.png new file mode 100644 index 0000000..865a0f9 Binary files /dev/null and b/Resources/Refresh@2x.png differ diff --git a/Resources/Settings.png b/Resources/Settings.png new file mode 100644 index 0000000..699445e Binary files /dev/null and b/Resources/Settings.png differ diff --git a/Resources/Settings@2x.png b/Resources/Settings@2x.png new file mode 100644 index 0000000..dc88022 Binary files /dev/null and b/Resources/Settings@2x.png differ diff --git a/Stage1st.xcodeproj/project.pbxproj b/Stage1st.xcodeproj/project.pbxproj index adf96a7..f47aa9d 100644 --- a/Stage1st.xcodeproj/project.pbxproj +++ b/Stage1st.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + C0401DB316EB0F2000E52A9A /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C083353416E4E7C40035C1AA /* Social.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; C05286C316D3428E000B0DBD /* S1Parser.m in Sources */ = {isa = PBXBuildFile; fileRef = C05286C216D3428E000B0DBD /* S1Parser.m */; }; C05286C616D3B881000B0DBD /* S1HTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C05286C516D3B881000B0DBD /* S1HTTPClient.m */; }; C05286C916D3CE49000B0DBD /* AsyncCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C05286C816D3CE49000B0DBD /* AsyncCell.m */; }; @@ -14,6 +15,32 @@ C05286CE16D3E7FD000B0DBD /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C05286CD16D3E7FD000B0DBD /* CoreText.framework */; }; C08334EC16E1E6B90035C1AA /* Toolbar_background.png in Resources */ = {isa = PBXBuildFile; fileRef = C08334EA16E1E6B90035C1AA /* Toolbar_background.png */; }; C08334ED16E1E6B90035C1AA /* Toolbar_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C08334EB16E1E6B90035C1AA /* Toolbar_background@2x.png */; }; + C08334F516E2245B0035C1AA /* ODRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = C08334F416E2245B0035C1AA /* ODRefreshControl.m */; }; + C08334F816E22E1F0035C1AA /* S1TabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = C08334F716E22E1F0035C1AA /* S1TabBar.m */; }; + C08334FD16E22F6A0035C1AA /* Item_selected.png in Resources */ = {isa = PBXBuildFile; fileRef = C08334F916E22F6A0035C1AA /* Item_selected.png */; }; + C08334FE16E22F6A0035C1AA /* Item_selected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C08334FA16E22F6A0035C1AA /* Item_selected@2x.png */; }; + C08334FF16E22F6A0035C1AA /* Item.png in Resources */ = {isa = PBXBuildFile; fileRef = C08334FB16E22F6A0035C1AA /* Item.png */; }; + C083350016E22F6A0035C1AA /* Item@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C08334FC16E22F6A0035C1AA /* Item@2x.png */; }; + C083350316E2FD7E0035C1AA /* Item_highlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = C083350116E2FD7E0035C1AA /* Item_highlighted.png */; }; + C083350416E2FD7E0035C1AA /* Item_highlighted@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C083350216E2FD7E0035C1AA /* Item_highlighted@2x.png */; }; + C083350916E3047B0035C1AA /* Bar_item.png in Resources */ = {isa = PBXBuildFile; fileRef = C083350516E3047B0035C1AA /* Bar_item.png */; }; + C083350A16E3047B0035C1AA /* Bar_item@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C083350616E3047B0035C1AA /* Bar_item@2x.png */; }; + C083350F16E32FFE0035C1AA /* Navigation.png in Resources */ = {isa = PBXBuildFile; fileRef = C083350D16E32FFE0035C1AA /* Navigation.png */; }; + C083351016E32FFE0035C1AA /* Navigation@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C083350E16E32FFE0035C1AA /* Navigation@2x.png */; }; + C083351316E336130035C1AA /* Back_button_item.png in Resources */ = {isa = PBXBuildFile; fileRef = C083351116E336130035C1AA /* Back_button_item.png */; }; + C083351416E336130035C1AA /* Back_button_item@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C083351216E336130035C1AA /* Back_button_item@2x.png */; }; + C083351616E34DCF0035C1AA /* Threads.plist in Resources */ = {isa = PBXBuildFile; fileRef = C083351516E34DCF0035C1AA /* Threads.plist */; }; + C083351916E363870035C1AA /* S1Tracer.m in Sources */ = {isa = PBXBuildFile; fileRef = C083351816E363870035C1AA /* S1Tracer.m */; }; + C083352E16E4E1550035C1AA /* DEComposeRuledView.m in Sources */ = {isa = PBXBuildFile; fileRef = C083352416E4E1550035C1AA /* DEComposeRuledView.m */; }; + C083352F16E4E1550035C1AA /* DEComposeTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C083352616E4E1550035C1AA /* DEComposeTextView.m */; }; + C083353016E4E1550035C1AA /* REComposeBackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = C083352816E4E1550035C1AA /* REComposeBackgroundView.m */; }; + C083353116E4E1550035C1AA /* REComposeSheetView.m in Sources */ = {isa = PBXBuildFile; fileRef = C083352A16E4E1550035C1AA /* REComposeSheetView.m */; }; + C083353316E4E1550035C1AA /* REComposeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C083352D16E4E1550035C1AA /* REComposeViewController.m */; }; + C083353716E4FB220035C1AA /* InitialOrder.plist in Resources */ = {isa = PBXBuildFile; fileRef = C083353616E4FB220035C1AA /* InitialOrder.plist */; }; + C083353A16E503F60035C1AA /* S1LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C083353916E503F60035C1AA /* S1LoginViewController.m */; }; + C083353F16E647A80035C1AA /* GTMNSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = C083353E16E647A80035C1AA /* GTMNSString+HTML.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + C083354216E6DCF40035C1AA /* Refresh.png in Resources */ = {isa = PBXBuildFile; fileRef = C083354016E6DCF30035C1AA /* Refresh.png */; }; + C083354316E6DCF40035C1AA /* Refresh@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C083354116E6DCF30035C1AA /* Refresh@2x.png */; }; C08A441016DA779E003BF44C /* S1URLCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C08A440F16DA779E003BF44C /* S1URLCache.m */; }; C08B601216E175E300591827 /* Forward@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C08B600E16E175E300591827 /* Forward@2x.png */; }; C08B601316E175E300591827 /* Forward.png in Resources */ = {isa = PBXBuildFile; fileRef = C08B600F16E175E300591827 /* Forward.png */; }; @@ -55,6 +82,11 @@ C09587A116CB61FC007E0119 /* GSStaticTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C095879A16CB61FC007E0119 /* GSStaticTableViewController.m */; }; C09587A216CB61FC007E0119 /* UIControl+BlockWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = C095879C16CB61FC007E0119 /* UIControl+BlockWrapper.m */; }; C09587A616CB623E007E0119 /* S1SettingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C09587A516CB623E007E0119 /* S1SettingViewController.m */; }; + C096CD3A16EADD3300D4FF7F /* SVWebViewController.strings in Resources */ = {isa = PBXBuildFile; fileRef = C096CD2E16EADD3300D4FF7F /* SVWebViewController.strings */; }; + C096CD3B16EADD3300D4FF7F /* SVModalWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C096CD3416EADD3300D4FF7F /* SVModalWebViewController.m */; }; + C096CD3C16EADD3300D4FF7F /* SVWebViewController.bundle in Resources */ = {isa = PBXBuildFile; fileRef = C096CD3516EADD3300D4FF7F /* SVWebViewController.bundle */; }; + C096CD3D16EADD3300D4FF7F /* SVWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C096CD3716EADD3300D4FF7F /* SVWebViewController.m */; }; + C096CD3F16EADE0000D4FF7F /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C096CD3E16EADE0000D4FF7F /* MessageUI.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -69,6 +101,48 @@ C05286CD16D3E7FD000B0DBD /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; C08334EA16E1E6B90035C1AA /* Toolbar_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Toolbar_background.png; path = Resources/Toolbar_background.png; sourceTree = ""; }; C08334EB16E1E6B90035C1AA /* Toolbar_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Toolbar_background@2x.png"; path = "Resources/Toolbar_background@2x.png"; sourceTree = ""; }; + C08334F316E2245B0035C1AA /* ODRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ODRefreshControl.h; sourceTree = ""; }; + C08334F416E2245B0035C1AA /* ODRefreshControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ODRefreshControl.m; sourceTree = ""; }; + C08334F616E22E1F0035C1AA /* S1TabBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S1TabBar.h; path = Stage1st/S1TabBar.h; sourceTree = ""; }; + C08334F716E22E1F0035C1AA /* S1TabBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = S1TabBar.m; path = Stage1st/S1TabBar.m; sourceTree = ""; }; + C08334F916E22F6A0035C1AA /* Item_selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Item_selected.png; path = Resources/Item_selected.png; sourceTree = ""; }; + C08334FA16E22F6A0035C1AA /* Item_selected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Item_selected@2x.png"; path = "Resources/Item_selected@2x.png"; sourceTree = ""; }; + C08334FB16E22F6A0035C1AA /* Item.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Item.png; path = Resources/Item.png; sourceTree = ""; }; + C08334FC16E22F6A0035C1AA /* Item@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Item@2x.png"; path = "Resources/Item@2x.png"; sourceTree = ""; }; + C083350116E2FD7E0035C1AA /* Item_highlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Item_highlighted.png; path = Resources/Item_highlighted.png; sourceTree = ""; }; + C083350216E2FD7E0035C1AA /* Item_highlighted@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Item_highlighted@2x.png"; path = "Resources/Item_highlighted@2x.png"; sourceTree = ""; }; + C083350516E3047B0035C1AA /* Bar_item.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Bar_item.png; path = Resources/Bar_item.png; sourceTree = ""; }; + C083350616E3047B0035C1AA /* Bar_item@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Bar_item@2x.png"; path = "Resources/Bar_item@2x.png"; sourceTree = ""; }; + C083350D16E32FFE0035C1AA /* Navigation.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Navigation.png; path = Resources/Navigation.png; sourceTree = ""; }; + C083350E16E32FFE0035C1AA /* Navigation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Navigation@2x.png"; path = "Resources/Navigation@2x.png"; sourceTree = ""; }; + C083351116E336130035C1AA /* Back_button_item.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Back_button_item.png; path = Resources/Back_button_item.png; sourceTree = ""; }; + C083351216E336130035C1AA /* Back_button_item@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Back_button_item@2x.png"; path = "Resources/Back_button_item@2x.png"; sourceTree = ""; }; + C083351516E34DCF0035C1AA /* Threads.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Threads.plist; sourceTree = ""; }; + C083351716E363870035C1AA /* S1Tracer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S1Tracer.h; path = Stage1st/S1Tracer.h; sourceTree = ""; }; + C083351816E363870035C1AA /* S1Tracer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = S1Tracer.m; path = Stage1st/S1Tracer.m; sourceTree = ""; }; + C083351A16E3704B0035C1AA /* Recent.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Recent.png; path = Resources/Recent.png; sourceTree = ""; }; + C083351B16E3704C0035C1AA /* Recent@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Recent@2x.png"; path = "Resources/Recent@2x.png"; sourceTree = ""; }; + C083351C16E3704C0035C1AA /* Settings.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Settings.png; path = Resources/Settings.png; sourceTree = ""; }; + C083351D16E3704C0035C1AA /* Settings@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Settings@2x.png"; path = "Resources/Settings@2x.png"; sourceTree = ""; }; + C083352316E4E1550035C1AA /* DEComposeRuledView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DEComposeRuledView.h; sourceTree = ""; }; + C083352416E4E1550035C1AA /* DEComposeRuledView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DEComposeRuledView.m; sourceTree = ""; }; + C083352516E4E1550035C1AA /* DEComposeTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DEComposeTextView.h; sourceTree = ""; }; + C083352616E4E1550035C1AA /* DEComposeTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DEComposeTextView.m; sourceTree = ""; }; + C083352716E4E1550035C1AA /* REComposeBackgroundView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REComposeBackgroundView.h; sourceTree = ""; }; + C083352816E4E1550035C1AA /* REComposeBackgroundView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REComposeBackgroundView.m; sourceTree = ""; }; + C083352916E4E1550035C1AA /* REComposeSheetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REComposeSheetView.h; sourceTree = ""; }; + C083352A16E4E1550035C1AA /* REComposeSheetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REComposeSheetView.m; sourceTree = ""; }; + C083352C16E4E1550035C1AA /* REComposeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REComposeViewController.h; sourceTree = ""; }; + C083352D16E4E1550035C1AA /* REComposeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REComposeViewController.m; sourceTree = ""; }; + C083353416E4E7C40035C1AA /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; }; + C083353616E4FB220035C1AA /* InitialOrder.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InitialOrder.plist; sourceTree = ""; }; + C083353816E503F60035C1AA /* S1LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S1LoginViewController.h; path = Stage1st/S1LoginViewController.h; sourceTree = ""; }; + C083353916E503F60035C1AA /* S1LoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = S1LoginViewController.m; path = Stage1st/S1LoginViewController.m; sourceTree = ""; }; + C083353C16E647A80035C1AA /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = ""; }; + C083353D16E647A80035C1AA /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = ""; }; + C083353E16E647A80035C1AA /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = ""; }; + C083354016E6DCF30035C1AA /* Refresh.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Refresh.png; path = Resources/Refresh.png; sourceTree = ""; }; + C083354116E6DCF30035C1AA /* Refresh@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Refresh@2x.png"; path = "Resources/Refresh@2x.png"; sourceTree = ""; }; C08A440E16DA779E003BF44C /* S1URLCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S1URLCache.h; path = Stage1st/S1URLCache.h; sourceTree = ""; }; C08A440F16DA779E003BF44C /* S1URLCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = S1URLCache.m; path = Stage1st/S1URLCache.m; sourceTree = ""; }; C08B600E16E175E300591827 /* Forward@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Forward@2x.png"; path = "Resources/Forward@2x.png"; sourceTree = ""; }; @@ -137,6 +211,18 @@ C095879C16CB61FC007E0119 /* UIControl+BlockWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+BlockWrapper.m"; sourceTree = ""; }; C09587A416CB623E007E0119 /* S1SettingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S1SettingViewController.h; path = Stage1st/S1SettingViewController.h; sourceTree = ""; }; C09587A516CB623E007E0119 /* S1SettingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = S1SettingViewController.m; path = Stage1st/S1SettingViewController.m; sourceTree = ""; }; + C096CD2F16EADD3300D4FF7F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/SVWebViewController.strings; sourceTree = ""; }; + C096CD3016EADD3300D4FF7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SVWebViewController.strings; sourceTree = ""; }; + C096CD3116EADD3300D4FF7F /* es-ES */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-ES"; path = "es-ES.lproj/SVWebViewController.strings"; sourceTree = ""; }; + C096CD3216EADD3300D4FF7F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SVWebViewController.strings; sourceTree = ""; }; + C096CD3316EADD3300D4FF7F /* SVModalWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVModalWebViewController.h; sourceTree = ""; }; + C096CD3416EADD3300D4FF7F /* SVModalWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SVModalWebViewController.m; sourceTree = ""; }; + C096CD3516EADD3300D4FF7F /* SVWebViewController.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SVWebViewController.bundle; sourceTree = ""; }; + C096CD3616EADD3300D4FF7F /* SVWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVWebViewController.h; sourceTree = ""; }; + C096CD3716EADD3300D4FF7F /* SVWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SVWebViewController.m; sourceTree = ""; }; + C096CD3816EADD3300D4FF7F /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/SVWebViewController.strings"; sourceTree = ""; }; + C096CD3916EADD3300D4FF7F /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/SVWebViewController.strings"; sourceTree = ""; }; + C096CD3E16EADE0000D4FF7F /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -144,6 +230,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C0401DB316EB0F2000E52A9A /* Social.framework in Frameworks */, + C096CD3F16EADE0000D4FF7F /* MessageUI.framework in Frameworks */, C05286CE16D3E7FD000B0DBD /* CoreText.framework in Frameworks */, C095877816CA8D19007E0119 /* SystemConfiguration.framework in Frameworks */, C095877616CA8D0F007E0119 /* MobileCoreServices.framework in Frameworks */, @@ -157,6 +245,45 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + C08334F216E2245B0035C1AA /* ODRefreshControl */ = { + isa = PBXGroup; + children = ( + C08334F316E2245B0035C1AA /* ODRefreshControl.h */, + C08334F416E2245B0035C1AA /* ODRefreshControl.m */, + ); + name = ODRefreshControl; + path = Vendors/ODRefreshControl; + sourceTree = ""; + }; + C083352216E4E1550035C1AA /* REComposeViewController */ = { + isa = PBXGroup; + children = ( + C083352316E4E1550035C1AA /* DEComposeRuledView.h */, + C083352416E4E1550035C1AA /* DEComposeRuledView.m */, + C083352516E4E1550035C1AA /* DEComposeTextView.h */, + C083352616E4E1550035C1AA /* DEComposeTextView.m */, + C083352716E4E1550035C1AA /* REComposeBackgroundView.h */, + C083352816E4E1550035C1AA /* REComposeBackgroundView.m */, + C083352916E4E1550035C1AA /* REComposeSheetView.h */, + C083352A16E4E1550035C1AA /* REComposeSheetView.m */, + C083352C16E4E1550035C1AA /* REComposeViewController.h */, + C083352D16E4E1550035C1AA /* REComposeViewController.m */, + ); + name = REComposeViewController; + path = Vendors/REComposeViewController; + sourceTree = ""; + }; + C083353B16E647A70035C1AA /* GoogleToolbox */ = { + isa = PBXGroup; + children = ( + C083353C16E647A80035C1AA /* GTMDefines.h */, + C083353D16E647A80035C1AA /* GTMNSString+HTML.h */, + C083353E16E647A80035C1AA /* GTMNSString+HTML.m */, + ); + name = GoogleToolbox; + path = Vendors/GoogleToolbox; + sourceTree = ""; + }; C08A440D16DA775F003BF44C /* Networking */ = { isa = PBXGroup; children = ( @@ -168,13 +295,6 @@ name = Networking; sourceTree = ""; }; - C08B602516E1E1C300591827 /* Tabbar */ = { - isa = PBXGroup; - children = ( - ); - name = Tabbar; - sourceTree = ""; - }; C08DB46216DE2E52002B4A92 /* Cells */ = { isa = PBXGroup; children = ( @@ -211,6 +331,8 @@ C095873616CA8A36007E0119 /* Frameworks */ = { isa = PBXGroup; children = ( + C096CD3E16EADE0000D4FF7F /* MessageUI.framework */, + C083353416E4E7C40035C1AA /* Social.framework */, C05286CD16D3E7FD000B0DBD /* CoreText.framework */, C095877716CA8D19007E0119 /* SystemConfiguration.framework */, C095877516CA8D0F007E0119 /* MobileCoreServices.framework */, @@ -243,6 +365,8 @@ C095874916CA8A36007E0119 /* Default.png */, C095874B16CA8A36007E0119 /* Default@2x.png */, C095874D16CA8A36007E0119 /* Default-568h@2x.png */, + C083351516E34DCF0035C1AA /* Threads.plist */, + C083353616E4FB220035C1AA /* InitialOrder.plist */, ); name = "Supporting Files"; sourceTree = ""; @@ -250,6 +374,10 @@ C095875416CA8A3E007E0119 /* Vendors */ = { isa = PBXGroup; children = ( + C096CD2D16EADD3300D4FF7F /* SVWebViewController */, + C083353B16E647A70035C1AA /* GoogleToolbox */, + C083352216E4E1550035C1AA /* REComposeViewController */, + C08334F216E2245B0035C1AA /* ODRefreshControl */, C095878F16CB61FC007E0119 /* StaticTableViewBuilder */, C095875816CA8CD4007E0119 /* AFNetworking */, ); @@ -259,6 +387,24 @@ C095875516CA8A43007E0119 /* Resources */ = { isa = PBXGroup; children = ( + C083354016E6DCF30035C1AA /* Refresh.png */, + C083354116E6DCF30035C1AA /* Refresh@2x.png */, + C083351A16E3704B0035C1AA /* Recent.png */, + C083351B16E3704C0035C1AA /* Recent@2x.png */, + C083351C16E3704C0035C1AA /* Settings.png */, + C083351D16E3704C0035C1AA /* Settings@2x.png */, + C083351116E336130035C1AA /* Back_button_item.png */, + C083351216E336130035C1AA /* Back_button_item@2x.png */, + C083350D16E32FFE0035C1AA /* Navigation.png */, + C083350E16E32FFE0035C1AA /* Navigation@2x.png */, + C083350516E3047B0035C1AA /* Bar_item.png */, + C083350616E3047B0035C1AA /* Bar_item@2x.png */, + C083350116E2FD7E0035C1AA /* Item_highlighted.png */, + C083350216E2FD7E0035C1AA /* Item_highlighted@2x.png */, + C08334F916E22F6A0035C1AA /* Item_selected.png */, + C08334FA16E22F6A0035C1AA /* Item_selected@2x.png */, + C08334FB16E22F6A0035C1AA /* Item.png */, + C08334FC16E22F6A0035C1AA /* Item@2x.png */, C08334EA16E1E6B90035C1AA /* Toolbar_background.png */, C08334EB16E1E6B90035C1AA /* Toolbar_background@2x.png */, C08B602116E1B5CE00591827 /* Logo.png */, @@ -313,6 +459,8 @@ C095878116CA8EAC007E0119 /* S1Reply.m */, C05286C116D3428E000B0DBD /* S1Parser.h */, C05286C216D3428E000B0DBD /* S1Parser.m */, + C083351716E363870035C1AA /* S1Tracer.h */, + C083351816E363870035C1AA /* S1Tracer.m */, ); name = Model; sourceTree = ""; @@ -320,10 +468,11 @@ C095877B16CA8DBE007E0119 /* View */ = { isa = PBXGroup; children = ( - C08B602516E1E1C300591827 /* Tabbar */, C08DB46216DE2E52002B4A92 /* Cells */, C090DA8D16D8F39F00BF7780 /* S1HUD.h */, C090DA8E16D8F39F00BF7780 /* S1HUD.m */, + C08334F616E22E1F0035C1AA /* S1TabBar.h */, + C08334F716E22E1F0035C1AA /* S1TabBar.m */, ); name = View; sourceTree = ""; @@ -368,10 +517,26 @@ children = ( C09587A416CB623E007E0119 /* S1SettingViewController.h */, C09587A516CB623E007E0119 /* S1SettingViewController.m */, + C083353816E503F60035C1AA /* S1LoginViewController.h */, + C083353916E503F60035C1AA /* S1LoginViewController.m */, ); name = Settings; sourceTree = ""; }; + C096CD2D16EADD3300D4FF7F /* SVWebViewController */ = { + isa = PBXGroup; + children = ( + C096CD2E16EADD3300D4FF7F /* SVWebViewController.strings */, + C096CD3316EADD3300D4FF7F /* SVModalWebViewController.h */, + C096CD3416EADD3300D4FF7F /* SVModalWebViewController.m */, + C096CD3516EADD3300D4FF7F /* SVWebViewController.bundle */, + C096CD3616EADD3300D4FF7F /* SVWebViewController.h */, + C096CD3716EADD3300D4FF7F /* SVWebViewController.m */, + ); + name = SVWebViewController; + path = Vendors/SVWebViewController; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -408,6 +573,11 @@ hasScannedForEncodings = 0; knownRegions = ( en, + da, + "es-ES", + es, + "zh-Hans", + "zh-Hant", ); mainGroup = C095872B16CA8A36007E0119; productRefGroup = C095873516CA8A36007E0119 /* Products */; @@ -438,6 +608,24 @@ C08B602416E1B5CE00591827 /* Logo@2x.png in Resources */, C08334EC16E1E6B90035C1AA /* Toolbar_background.png in Resources */, C08334ED16E1E6B90035C1AA /* Toolbar_background@2x.png in Resources */, + C08334FD16E22F6A0035C1AA /* Item_selected.png in Resources */, + C08334FE16E22F6A0035C1AA /* Item_selected@2x.png in Resources */, + C08334FF16E22F6A0035C1AA /* Item.png in Resources */, + C083350016E22F6A0035C1AA /* Item@2x.png in Resources */, + C083350316E2FD7E0035C1AA /* Item_highlighted.png in Resources */, + C083350416E2FD7E0035C1AA /* Item_highlighted@2x.png in Resources */, + C083350916E3047B0035C1AA /* Bar_item.png in Resources */, + C083350A16E3047B0035C1AA /* Bar_item@2x.png in Resources */, + C083350F16E32FFE0035C1AA /* Navigation.png in Resources */, + C083351016E32FFE0035C1AA /* Navigation@2x.png in Resources */, + C083351316E336130035C1AA /* Back_button_item.png in Resources */, + C083351416E336130035C1AA /* Back_button_item@2x.png in Resources */, + C083351616E34DCF0035C1AA /* Threads.plist in Resources */, + C083353716E4FB220035C1AA /* InitialOrder.plist in Resources */, + C083354216E6DCF40035C1AA /* Refresh.png in Resources */, + C083354316E6DCF40035C1AA /* Refresh@2x.png in Resources */, + C096CD3A16EADD3300D4FF7F /* SVWebViewController.strings in Resources */, + C096CD3C16EADD3300D4FF7F /* SVWebViewController.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -475,6 +663,18 @@ C090DA8F16D8F39F00BF7780 /* S1HUD.m in Sources */, C08A441016DA779E003BF44C /* S1URLCache.m in Sources */, C08DB46916DE3152002B4A92 /* S1TopicListCell.m in Sources */, + C08334F516E2245B0035C1AA /* ODRefreshControl.m in Sources */, + C08334F816E22E1F0035C1AA /* S1TabBar.m in Sources */, + C083351916E363870035C1AA /* S1Tracer.m in Sources */, + C083352E16E4E1550035C1AA /* DEComposeRuledView.m in Sources */, + C083352F16E4E1550035C1AA /* DEComposeTextView.m in Sources */, + C083353016E4E1550035C1AA /* REComposeBackgroundView.m in Sources */, + C083353116E4E1550035C1AA /* REComposeSheetView.m in Sources */, + C083353316E4E1550035C1AA /* REComposeViewController.m in Sources */, + C083353A16E503F60035C1AA /* S1LoginViewController.m in Sources */, + C083353F16E647A80035C1AA /* GTMNSString+HTML.m in Sources */, + C096CD3B16EADD3300D4FF7F /* SVModalWebViewController.m in Sources */, + C096CD3D16EADD3300D4FF7F /* SVWebViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -489,6 +689,19 @@ name = InfoPlist.strings; sourceTree = ""; }; + C096CD2E16EADD3300D4FF7F /* SVWebViewController.strings */ = { + isa = PBXVariantGroup; + children = ( + C096CD2F16EADD3300D4FF7F /* da */, + C096CD3016EADD3300D4FF7F /* en */, + C096CD3116EADD3300D4FF7F /* es-ES */, + C096CD3216EADD3300D4FF7F /* es */, + C096CD3816EADD3300D4FF7F /* zh-Hans */, + C096CD3916EADD3300D4FF7F /* zh-Hant */, + ); + name = SVWebViewController.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -555,7 +768,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Stage1st/Stage1st-Prefix.pch"; INFOPLIST_FILE = "Stage1st/Stage1st-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "FB79F8DC-74C0-477B-B546-85145211C9C9"; WRAPPER_EXTENSION = app; @@ -569,7 +782,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Stage1st/Stage1st-Prefix.pch"; INFOPLIST_FILE = "Stage1st/Stage1st-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "FB79F8DC-74C0-477B-B546-85145211C9C9"; WRAPPER_EXTENSION = app; diff --git a/Stage1st/InitialOrder.plist b/Stage1st/InitialOrder.plist new file mode 100644 index 0000000..1b78b66 --- /dev/null +++ b/Stage1st/InitialOrder.plist @@ -0,0 +1,29 @@ + + + + + + 外野 + 动漫论坛 + 游戏论坛 + PC数码 + 影视论坛 + + + 文史沙龙 + 彼岸文化 + 八卦体育 + 音乐论坛 + 二手交易区 + 英雄联盟 + 吃货 + 火暴 + 马叉虫 + 车欠女未 + 犭苗犭句 + 摄影区 + 开箱区 + 魔兽世界 + + + diff --git a/Stage1st/S1AppDelegate.m b/Stage1st/S1AppDelegate.m index 72ef862..c08b417 100644 --- a/Stage1st/S1AppDelegate.m +++ b/Stage1st/S1AppDelegate.m @@ -16,18 +16,36 @@ @implementation S1AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - // Override point for customization after application launch. + + //User Defaults; + if (![[NSUserDefaults standardUserDefaults] valueForKey:@"Order"]) { + NSString *path = [[NSBundle mainBundle] pathForResource:@"InitialOrder" ofType:@"plist"]; + NSArray *order = [NSArray arrayWithContentsOfFile:path]; + [[NSUserDefaults standardUserDefaults] setValue:order forKey:@"Order"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + + if (![[NSUserDefaults standardUserDefaults] valueForKey:@"Display"]) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"Display"]; + } + + //URL Cache S1URLCache *URLCache = [[S1URLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:10 * 1024 * 1024 diskPath:nil]; [NSURLCache setSharedURLCache:URLCache]; + + //Appearence + [[UIToolbar appearance] setBackgroundImage:[UIImage imageNamed:@"Toolbar_background.png"] forToolbarPosition:UIToolbarPositionBottom barMetrics:UIBarMetricsDefault]; + [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"Navigation.png"] forBarMetrics:UIBarMetricsDefault]; + [[UIBarButtonItem appearance] setBackgroundImage:[UIImage imageNamed:@"Bar_item.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; + [[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:@"Back_button_item.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 14, 0, 7)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; + self.window.backgroundColor = [UIColor blackColor]; S1TopicListViewController *controller = [[S1TopicListViewController alloc] init]; S1RootViewController *rootVC = [[S1RootViewController alloc] initWithMasterViewController:controller]; self.window.rootViewController = rootVC; - [[UIToolbar appearance] setBackgroundImage:[UIImage imageNamed:@"Toolbar_background.png"] forToolbarPosition:UIToolbarPositionBottom barMetrics:UIBarMetricsDefault]; - [self.window makeKeyAndVisible]; return YES; } diff --git a/Stage1st/S1ContentViewController.h b/Stage1st/S1ContentViewController.h index ea7ec3c..6497589 100644 --- a/Stage1st/S1ContentViewController.h +++ b/Stage1st/S1ContentViewController.h @@ -8,11 +8,13 @@ #import -@class S1Topic; +@class S1Topic, S1Tracer; @interface S1ContentViewController : UIViewController +@property (nonatomic, copy) NSString *fid; @property (nonatomic, strong) S1Topic *topic; +@property (nonatomic, strong) S1Tracer *tracer; @end diff --git a/Stage1st/S1ContentViewController.m b/Stage1st/S1ContentViewController.m index 82a4c9d..b5fae16 100644 --- a/Stage1st/S1ContentViewController.m +++ b/Stage1st/S1ContentViewController.m @@ -8,22 +8,41 @@ #define _URL_PATTERN @"http://bbs.saraba1st.com/2b/simple/?t%@.html" +#import +#import "S1RootViewController.h" #import "S1ContentViewController.h" #import "S1HTTPClient.h" #import "S1Topic.h" #import "S1Parser.h" +#import "S1Tracer.h" #import "S1HUD.h" +#import "REComposeViewController.h" +#import "SVModalWebViewController.h" -@interface S1ContentViewController () +#define _REPLY_PER_PAGE 50 +#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) + + +@interface S1ContentViewController () @property (nonatomic, strong) UIToolbar *toolbar; @property (nonatomic, strong) UIWebView *webView; @property (nonatomic, strong) UIView *maskView; +@property (nonatomic, strong) UILabel *pageLabel; + +@property (nonatomic, strong) REComposeViewController *replyController; + @end -@implementation S1ContentViewController +@implementation S1ContentViewController { + NSInteger _currentPage; + NSInteger _totalPages; + + BOOL _needToScrollToBottom; + NSURL *_urlToOpen; +} - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { @@ -31,7 +50,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil if (self) { // Custom initialization _webView = [[UIWebView alloc] init]; - + _currentPage = 1; + _needToScrollToBottom = NO; } return self; } @@ -49,9 +69,9 @@ - (void)viewDidLoad self.webView.dataDetectorTypes = UIDataDetectorTypeNone; self.webView.scrollView.scrollsToTop = YES; self.webView.scrollView.delegate = self; -// self.webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 44, 0); -// self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 44, 0); + self.webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; self.webView.backgroundColor = [UIColor colorWithWhite:0.20f alpha:1.0f]; + [self.webView loadHTMLString:@"" baseURL:nil]; [self.view addSubview:self.webView]; self.maskView = [[UIView alloc] initWithFrame:self.webView.bounds]; @@ -65,31 +85,78 @@ - (void)viewDidLoad self.toolbar.tintColor = [UIColor colorWithWhite:0.10f alpha:1.0]; self.toolbar.alpha = 1.0; - UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"Back.png"] style:UIBarButtonItemStylePlain target:self action:@selector(back:)]; - backItem.imageInsets = UIEdgeInsetsMake(1, 0, -1, 0); - UIBarButtonItem *forwardItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"Forward.png"] style:UIBarButtonItemStylePlain target:self action:@selector(forward:)]; - forwardItem.imageInsets = UIEdgeInsetsMake(1, 0, -1, 0); + + UIButton *button = nil; + button = [UIButton buttonWithType:UIButtonTypeCustom]; + button.frame = CGRectMake(0, 0, 30, 30); + [button setImage:[UIImage imageNamed:@"Back.png"] forState:UIControlStateNormal]; + [button addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside]; + [button setShowsTouchWhenHighlighted:YES]; + [button setTag:99]; + UILongPressGestureRecognizer *backLongPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(backLongPressed:)]; + backLongPressGR.minimumPressDuration = 0.5; + [button addGestureRecognizer:backLongPressGR]; + UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithCustomView:button]; + + button = [UIButton buttonWithType:UIButtonTypeCustom]; + button.frame = CGRectMake(0, 0, 30, 30); + [button setImage:[UIImage imageNamed:@"Forward.png"] forState:UIControlStateNormal]; + [button addTarget:self action:@selector(forward:) forControlEvents:UIControlEventTouchUpInside]; + [button setShowsTouchWhenHighlighted:YES]; + [button setTag:100]; + UILongPressGestureRecognizer *forwardLongPressGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(forwardLongPressed:)]; + forwardLongPressGR.minimumPressDuration = 0.5; + [button addGestureRecognizer:forwardLongPressGR]; + UIBarButtonItem *forwardItem = [[UIBarButtonItem alloc] initWithCustomView:button]; + + self.pageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 80, 30)]; + self.pageLabel.font = [UIFont boldSystemFontOfSize:13.0f]; + self.pageLabel.textColor = [UIColor whiteColor]; + self.pageLabel.backgroundColor = [UIColor clearColor]; + self.pageLabel.textAlignment = NSTextAlignmentCenter; + [self updatePageLabel]; + + UIBarButtonItem *labelItem = [[UIBarButtonItem alloc] initWithCustomView:self.pageLabel]; + labelItem.width = 80; + + UIBarButtonItem *actionItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(action:)]; UIBarButtonItem *fixItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; fixItem.width = 36.0f; UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - [self.toolbar setItems:@[backItem, fixItem, forwardItem, flexItem, actionItem]]; + [self.toolbar setItems:@[backItem, fixItem, forwardItem, flexItem, labelItem, flexItem, actionItem]]; [self.view addSubview:self.toolbar]; - #undef _BAR_HEIGHT + + [self fetchContentOfPage]; } - (void)viewWillAppear:(BOOL)animated { NSAssert(self.topic != nil, @"topic object can not be nil"); - [self fetchContent]; } - (void)viewDidAppear:(BOOL)animated { + [UIView animateWithDuration:1.0 delay:1.5 options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + self.maskView.alpha = 0.0; + } + completion:^(BOOL finished) { + self.maskView.hidden = YES; + }]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + NSString *path = [NSString stringWithFormat:@"simple/?t%@.html", self.topic.topicID]; + [[S1HTTPClient sharedClient] cancelAllHTTPOperationsWithMethod:@"GET" path:path]; + + [self.topic setLastViewedPage:[NSString stringWithFormat:@"%d", _currentPage]]; + [self.tracer hasViewed:self.topic]; } - (void)didReceiveMemoryWarning @@ -98,25 +165,163 @@ - (void)didReceiveMemoryWarning // Dispose of any resources that can be recreated. } +#pragma mark - Setters and Getters + +- (void)setTopic:(S1Topic *)topic +{ + _topic = topic; + _totalPages = ([topic.replyCount integerValue] / _REPLY_PER_PAGE) + 1; + if (topic.lastViewedPage) { + _currentPage = [topic.lastViewedPage integerValue]; + } +} + #pragma mark - Bar Button Actions - (void)back:(id)sender { - if ([self.webView canGoBack]) { - [self.webView goBack]; + if (_currentPage - 1 >= 1) { + _currentPage -= 1; + [self fetchContentOfPage]; + } else { + [[self rootViewController] dismissDetailViewController]; } - } - (void)forward:(id)sender { - - + if (_currentPage + 1 <= _totalPages) { + _currentPage += 1; + [self fetchContentOfPage]; + } else { + if (![self atBottom]) { + [self scrollToButtomAnimated:YES]; + } else { + [self fetchContentOfPage]; + _needToScrollToBottom = YES; + } + } +} + +- (void)backLongPressed:(UIGestureRecognizer *)gr +{ + if (gr.state == UIGestureRecognizerStateBegan) { + if (_currentPage > 1) { + _currentPage = 1; + [self fetchContentOfPage]; + } + } +} + +- (void)forwardLongPressed:(UIGestureRecognizer *)gr +{ + if (gr.state == UIGestureRecognizerStateBegan) { + if (_currentPage < _totalPages) { + _currentPage = _totalPages; + [self fetchContentOfPage]; + } + } } - (void)action:(id)sender { + UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil + delegate:self + cancelButtonTitle:NSLocalizedString(@"取消", @"Cancel") + destructiveButtonTitle:nil + otherButtonTitles:NSLocalizedString(@"回复", @"Reply"), NSLocalizedString(@"分享到微博", @"Weibo"), NSLocalizedString(@"打开原网页", @"Origin"), nil]; + actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque; + [actionSheet showFromToolbar:self.toolbar]; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex +{ +// NSLog(@"%d", buttonIndex); + //Reply + if (0 == buttonIndex) { + if (![[NSUserDefaults standardUserDefaults] valueForKey:@"UserID"]) { + [[[UIAlertView alloc] initWithTitle:@"请先获取登录状态" message:nil delegate:nil cancelButtonTitle:@"完成" otherButtonTitles:nil] show]; + return; + } + [self rootViewController].modalPresentationStyle = UIModalPresentationCurrentContext; + if (!self.replyController) { + self.replyController = [[REComposeViewController alloc] init]; + } + REComposeViewController *replyController = self.replyController; + replyController.title = NSLocalizedString(@"回复", @"Reply"); + replyController.navigationItem.leftBarButtonItem.title = @"取消"; + replyController.navigationItem.rightBarButtonItem.title = @"发送"; + + + [replyController setCompletionHandler:^(REComposeViewController *composeViewController, REComposeResult result){ + if (result == REComposeResultCancelled) { + [composeViewController dismissViewControllerAnimated:NO completion:nil]; + } + else if (result == REComposeResultPosted) { + if (composeViewController.text.length > 0) { + NSString *suffix = @"\n\n——— 来自Stage1st Reader Evolution"; + NSString *replyWithSuffix = [composeViewController.text stringByAppendingString:suffix]; + NSString *timestamp = [[NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]] substringToIndex:10]; + NSString *path = [NSString stringWithFormat:@"m/post.php?action=reply&tid=%@&tmp=%@", self.topic.topicID, timestamp]; + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + [[S1HTTPClient sharedClient] postPath:path + parameters:@{ @"fid":self.fid, @"content":replyWithSuffix } + success:^(AFHTTPRequestOperation *operation, id responseObject) { + NSString *HTMLString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; + NSLog(@"Reply Response: %@", HTMLString); + if (_currentPage == _totalPages) { + _needToScrollToBottom = YES; + [self fetchContentOfPage]; + } + [composeViewController dismissViewControllerAnimated:NO completion:nil]; +// if (!HTMLString) { +// [composeViewController dismissViewControllerAnimated:NO completion:nil]; +// } + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"%@", error); + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + }]; + } + } + }]; + + [self presentViewController:replyController animated:NO completion:nil]; + } + //Weibo + if (1 == buttonIndex) { + if (!NSClassFromString(@"SLComposeViewController")) { + [[[UIAlertView alloc] initWithTitle:@"需要6.0以上的系统才能使用" message:nil delegate:nil cancelButtonTitle:@"完成" otherButtonTitles:nil] show]; + return; + } + SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeSinaWeibo]; + [controller setInitialText:[NSString stringWithFormat:@"%@ #Stage1st Reader#", self.topic.title]]; + [controller addURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://bbs.saraba1st.com/2b/read-htm-tid-%@.html", self.topic.topicID]]]; + [controller addImage:[self screenShot]]; + + __weak SLComposeViewController *weakController = controller; + [self presentViewController:controller animated:YES completion:nil]; + [controller setCompletionHandler:^(SLComposeViewControllerResult result){ + [weakController dismissViewControllerAnimated:YES completion:nil]; + }]; + } + if (2 == buttonIndex) { + [self rootViewController].modalPresentationStyle = UIModalPresentationFullScreen; + SVModalWebViewController *controller = [[SVModalWebViewController alloc] initWithAddress:[NSString stringWithFormat:@"http://bbs.saraba1st.com/2b/read-htm-tid-%@.html", self.topic.topicID]]; + [self presentViewController:controller animated:YES completion:nil]; + } +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + if (1 == buttonIndex) { + NSLog(@"%@", _urlToOpen); + SVModalWebViewController *controller = [[SVModalWebViewController alloc] initWithAddress:_urlToOpen.absoluteString]; + [self rootViewController].modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:controller animated:YES completion:nil]; + } } #pragma mark - UIWebView Delegate @@ -127,16 +332,19 @@ - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *) if ([request.URL.absoluteString isEqualToString:@"about:blank"]) { return YES; } + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"打开链接" message:request.URL.absoluteString delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"打开", nil]; + _urlToOpen = request.URL; + [alertView show]; return NO; +// NSLog(@"Request: %@", request.URL.absoluteString); +// return YES; } - (void)webViewDidFinishLoad:(UIWebView *)webView { - [UIView animateWithDuration:0.8 animations:^{ - self.maskView.alpha = 0.0; - } completion:^(BOOL finished) { - self.maskView.hidden = YES; - }]; + if (_needToScrollToBottom) { + [self scrollToButtomAnimated:YES]; + } } #pragma mark - UIScrollView Delegate @@ -148,25 +356,68 @@ - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView #pragma mark - Networking -- (void)fetchContent +- (void)fetchContentOfPage { - S1HUD *HUD = [S1HUD showHUDInView:self.webView]; - NSString *path = [NSString stringWithFormat:@"?t%@.html", self.topic.topicID]; + [self updatePageLabel]; + S1HUD *HUD = [S1HUD showHUDInView:self.view]; + [HUD showActivityIndicator]; + [HUD setRefreshEventHandler:^(S1HUD *aHUD) { + [aHUD hideWithDelay:0.0]; + [self fetchContentOfPage]; + }]; + NSString *path = [NSString stringWithFormat:@"simple/?t%@_%d.html", self.topic.topicID, _currentPage]; [[S1HTTPClient sharedClient] getPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { NSMutableString *HTMLString = [[NSMutableString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; if (HTMLString) { - NSString *string = [S1Parser contentsFromHTMLString:HTMLString]; + NSString *string = [S1Parser contentsFromHTMLString:HTMLString withOffset:_currentPage]; [self.webView loadHTMLString:string baseURL:nil]; } [HUD hideWithDelay:0.5]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"%@", error); - [HUD hideWithDelay:0.5]; + [HUD showRefreshButton]; }]; } +#pragma mark - Helpers + +- (void)scrollToButtomAnimated:(BOOL)animated +{ + [self.webView.scrollView setContentOffset:CGPointMake(0, self.webView.scrollView.contentSize.height-self.webView.scrollView.bounds.size.height) animated:animated]; + _needToScrollToBottom = NO; +} + +- (BOOL)atBottom; +{ + UIScrollView *scrollView = self.webView.scrollView; + return (scrollView.contentOffset.y >= (scrollView.contentSize.height - self.webView.bounds.size.height)); +} + +- (void)updatePageLabel +{ + self.pageLabel.text = [NSString stringWithFormat:@"%d/%d", _currentPage, _totalPages]; +} + +- (S1RootViewController *)rootViewController +{ + UIViewController *controller = [self parentViewController]; + while (![controller isKindOfClass:[S1RootViewController class]] || !controller) { + controller = [controller parentViewController]; + } + return (S1RootViewController *)controller; +} + +- (UIImage *)screenShot +{ + UIGraphicsBeginImageContext(self.view.bounds.size); + [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return viewImage; + +} @end diff --git a/Stage1st/S1HTTPClient.m b/Stage1st/S1HTTPClient.m index eaccc4a..5a6b540 100644 --- a/Stage1st/S1HTTPClient.m +++ b/Stage1st/S1HTTPClient.m @@ -9,7 +9,7 @@ #import "S1HTTPClient.h" #import "AFNetworking.h" -static NSString * const baseURLString = @"http://bbs.saraba1st.com/2b/simple/"; +static NSString * const baseURLString = @"http://bbs.saraba1st.com/2b/"; @implementation S1HTTPClient diff --git a/Stage1st/S1HUD.h b/Stage1st/S1HUD.h index 206d947..3ac212c 100644 --- a/Stage1st/S1HUD.h +++ b/Stage1st/S1HUD.h @@ -10,8 +10,15 @@ @interface S1HUD : UIView +@property (nonatomic, copy) NSString *text; + +@property (nonatomic, copy) void (^refreshEventHandler)(S1HUD *aHUD); + + (S1HUD *)showHUDInView:(UIView *)view; +- (void)showActivityIndicator; +- (void)showRefreshButton; + - (void)hideWithDelay:(NSTimeInterval)delay; @end diff --git a/Stage1st/S1HUD.m b/Stage1st/S1HUD.m index e84fcad..0595cb1 100644 --- a/Stage1st/S1HUD.m +++ b/Stage1st/S1HUD.m @@ -7,14 +7,29 @@ // #import "S1HUD.h" +#import "UIControl+BlockWrapper.h" -@implementation S1HUD +typedef enum { + S1HUDStateShowText, + S1HUDStateShowControl +} S1HUDState; + + +@implementation S1HUD { + S1HUDState _state; +} + (S1HUD *)showHUDInView:(UIView *)view { S1HUD *HUD = [[self alloc] initWithFrame:CGRectMake(0, 0, 80, 80)]; - HUD.center = view.center; + HUD.center = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)); + HUD.alpha = 0.0; + HUD.transform = CGAffineTransformMakeScale(0.85, 0.85); [view addSubview:HUD]; + [UIView animateWithDuration:0.25 animations:^{ + HUD.alpha = 1.0; + HUD.transform = CGAffineTransformIdentity; + }]; return HUD; } @@ -24,18 +39,56 @@ - (id)initWithFrame:(CGRect)frame if (self) { // Initialization code self.backgroundColor = [UIColor clearColor]; + _state = S1HUDStateShowControl; } return self; } -- (void)layoutSubviews +- (void)setText:(NSString *)text +{ + _text = text; + [self removeSubviews]; + self.bounds = CGRectMake(0, 0, 120, 60); + _state = S1HUDStateShowText; + [self setNeedsDisplay]; +} + + +- (void)showActivityIndicator { + [self removeSubviews]; UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; indicatorView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); [self addSubview:indicatorView]; + _state = S1HUDStateShowControl; [indicatorView startAnimating]; } +- (void)showRefreshButton +{ + [self removeSubviews]; + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + button.alpha = 0.8; + [button setImage:[UIImage imageNamed:@"Refresh.png"] forState:UIControlStateNormal]; + [button addEventHandler:^(id sender, UIEvent *event) { + if (self.refreshEventHandler) { + self.refreshEventHandler(self); + } + } forControlEvent:UIControlEventTouchUpInside]; + button.frame = CGRectMake(0, 0, 40, 40); + button.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + [self addSubview:button]; + _state = S1HUDStateShowControl; +} + +- (void)removeSubviews +{ + [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + UIView *view = obj; + [view removeFromSuperview]; + }]; +} + - (void)drawRect:(CGRect)rect { @@ -71,7 +124,7 @@ - (void)drawRect:(CGRect)rect //// Abstracted Attributes CGRect roundedRectangleRect = CGRectInset(rect, 7.5, 7.5); - NSString* hUDTextContent = @""; + NSString* hUDTextContent = self.text; //// Rounded Rectangle Drawing @@ -120,11 +173,14 @@ - (void)drawRect:(CGRect)rect //// HUDText Drawing - UIFont *textFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f]; - CGFloat fontHeight = [textFont lineHeight]; - CGRect textRect = CGRectMake(8.0, floorf((frame.size.height-fontHeight)/2), frame.size.width-2*8.0, fontHeight); - [textColor setFill]; - [hUDTextContent drawInRect:textRect withFont:textFont lineBreakMode:NSLineBreakByTruncatingMiddle alignment:NSTextAlignmentCenter]; + if (_state == S1HUDStateShowText) { + UIFont *textFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:13.0f]; + CGFloat fontHeight = [textFont lineHeight]; + CGRect textRect = CGRectMake(8.0, floorf((frame.size.height-fontHeight)/2), frame.size.width-2*8.0, fontHeight); + [textColor setFill]; + [hUDTextContent drawInRect:textRect withFont:textFont lineBreakMode:NSLineBreakByTruncatingMiddle alignment:NSTextAlignmentCenter]; + } + //// Cleanup diff --git a/Stage1st/S1LoginViewController.h b/Stage1st/S1LoginViewController.h new file mode 100644 index 0000000..faa0036 --- /dev/null +++ b/Stage1st/S1LoginViewController.h @@ -0,0 +1,13 @@ +// +// S1LoginViewController.h +// Stage1st +// +// Created by Suen Gabriel on 3/5/13. +// Copyright (c) 2013 Renaissance. All rights reserved. +// + +#import "GSStaticTableViewController.h" + +@interface S1LoginViewController : GSStaticTableViewController + +@end diff --git a/Stage1st/S1LoginViewController.m b/Stage1st/S1LoginViewController.m new file mode 100644 index 0000000..bbf64b6 --- /dev/null +++ b/Stage1st/S1LoginViewController.m @@ -0,0 +1,145 @@ +// +// S1LoginViewController.m +// Stage1st +// +// Created by Suen Gabriel on 3/5/13. +// Copyright (c) 2013 Renaissance. All rights reserved. +// + +#import "S1LoginViewController.h" +#import "S1HTTPClient.h" +#import "AFNetworkActivityIndicatorManager.h" + +@interface S1LoginViewController () + +@property (nonatomic, strong) UITextField *userIDField; +@property (nonatomic, strong) UITextField *userPasswordField; + +@end + +@implementation S1LoginViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + self.navigationItem.title = NSLocalizedString(@"登录设置", @"Login"); + + self.userIDField = [[UITextField alloc] initWithFrame:CGRectMake(120, 11, 170, 21)]; + self.userIDField.text = [[NSUserDefaults standardUserDefaults] stringForKey:@"UserID"]; + self.userIDField.textColor = [UIColor colorWithRed:56.0f/255.0f green:84.0f/255.0f blue:135.0f/255.0f alpha:1.0f]; + self.userIDField.tag = 99; + self.userIDField.placeholder = NSLocalizedString(@"用户名", @"User Id"); + self.userIDField.autocapitalizationType = UITextAutocapitalizationTypeNone; + self.userIDField.autocorrectionType = UITextAutocorrectionTypeNo; + self.userIDField.delegate = self; + + self.userPasswordField = [[UITextField alloc] initWithFrame:CGRectMake(120, 11, 170, 21)]; + self.userPasswordField.text = [[NSUserDefaults standardUserDefaults] stringForKey:@"UserPassword"]; + self.userPasswordField.textColor = [UIColor colorWithRed:56.0f/255.0f green:84.0f/255.0f blue:135.0f/255.0f alpha:1.0f]; + self.userPasswordField.tag = 100; + self.userPasswordField.placeholder = NSLocalizedString(@"密码", @"Password"); + self.userPasswordField.delegate = self; + self.userPasswordField.secureTextEntry = YES; + __weak typeof(self) myself = self; + [self addSection:^(GSSection *section) { + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell) { + cell.textLabel.text = NSLocalizedString(@"用户名", @"ID"); + if (cell.contentView.subviews.count < 2) { + [cell.contentView addSubview:myself.userIDField]; + } + }]; + }]; + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell) { + cell.textLabel.text = NSLocalizedString(@"密码", @"Password"); + if (cell.contentView.subviews.count < 2) { + [cell.contentView addSubview:myself.userPasswordField]; + } + }]; + }]; + }]; + + [self addSection:^(GSSection *section) { + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell){ + cell.textLabel.text = NSLocalizedString(@"获取登录状态", @"Get Login Status"); + cell.selectionStyle = UITableViewCellSelectionStyleBlue; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + }]; + [row setEventHandlerBlock:^(UITableViewCell *cell){ + [myself clearCookiers]; + if (myself.userIDField.text.length > 0 && myself.userPasswordField.text.length > 0) { + [[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount]; + [[S1HTTPClient sharedClient] postPath:@"m/login.php" + parameters:@{ @"pwuser":myself.userIDField.text, @"pwpwd":myself.userPasswordField.text, @"lgt":@"0" } + success:^(AFHTTPRequestOperation *operation, id responseObject) { + NSString *result = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; + NSLog(@"%@", result); + NSLog(@"%@", [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]); + if (result) { + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"登录" message:@"获取登录状态成功" delegate:nil cancelButtonTitle:@"完成" otherButtonTitles:nil]; + [alertView show]; + NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults]; + [userDefault setValue:myself.userIDField.text forKey:@"UserID"]; + [userDefault setValue:myself.userPasswordField.text forKey:@"UserPassword"]; + + } else { + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"登录" message:@"获取登录状态未成功" delegate:nil cancelButtonTitle:@"完成" otherButtonTitles:nil]; + [alertView show]; + } + [[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount]; + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"%@", error); + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"登录" message:@"获取登录状态未成功" delegate:nil cancelButtonTitle:@"完成" otherButtonTitles:nil]; + [alertView show]; + [[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount]; + }]; + } + }]; + }]; + }]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +#pragma mark - UITextField Delegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + [textField resignFirstResponder]; + return YES; +} + +#pragma mark - Helper + +- (void)clearCookiers +{ + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray *cookies = [cookieStorage cookies]; + [cookies enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + NSHTTPCookie *cookie = obj; + [cookieStorage deleteCookie:cookie]; + }]; +} + +@end diff --git a/Stage1st/S1Parser.h b/Stage1st/S1Parser.h index 2837e42..2d7dad3 100644 --- a/Stage1st/S1Parser.h +++ b/Stage1st/S1Parser.h @@ -12,6 +12,6 @@ + (NSArray *)topicsFromHTMLString:(NSString *)HTMLString; -+ (NSString *)contentsFromHTMLString:(NSString *)HTMLString; ++ (NSString *)contentsFromHTMLString:(NSMutableString *)HTMLString withOffset:(NSInteger)offset; @end diff --git a/Stage1st/S1Parser.m b/Stage1st/S1Parser.m index 94e117e..b050d4b 100644 --- a/Stage1st/S1Parser.m +++ b/Stage1st/S1Parser.m @@ -8,16 +8,21 @@ #import "S1Parser.h" #import "S1Topic.h" +#import "GTMNSString+HTML.h" + +#define kNumberPerPage 50 static NSString * const topicPattern = @"
  • (.*?).*?\\((\\d+)"; static NSString * const cssPattern = @""; static NSString * const cleanupPattern = @"(
    (
    )?\\r\\n
    .*?
    )|(.*?)|(src=\"http://bbs\\.saraba1st\\.com/2b/images/back\\.gif\")"; +static NSString * const indexPattern = @"td>(.*?)\\r\\n"]; if (rangeToReplace.location != NSNotFound) { diff --git a/Stage1st/S1RootViewController.h b/Stage1st/S1RootViewController.h index 7212e55..3a35c2e 100644 --- a/Stage1st/S1RootViewController.h +++ b/Stage1st/S1RootViewController.h @@ -13,5 +13,6 @@ - (id)initWithMasterViewController:(UIViewController *)controller; - (void)presentDetailViewController:(UIViewController *)controller; +- (void)dismissDetailViewController; @end diff --git a/Stage1st/S1SettingViewController.h b/Stage1st/S1SettingViewController.h index 9e74386..133f579 100644 --- a/Stage1st/S1SettingViewController.h +++ b/Stage1st/S1SettingViewController.h @@ -10,4 +10,6 @@ @interface S1SettingViewController : GSStaticTableViewController +@property (nonatomic, copy) void (^completionHandler)(BOOL needToUpdate); + @end diff --git a/Stage1st/S1SettingViewController.m b/Stage1st/S1SettingViewController.m index f8f4c77..a477214 100644 --- a/Stage1st/S1SettingViewController.m +++ b/Stage1st/S1SettingViewController.m @@ -7,10 +7,14 @@ // #import "S1SettingViewController.h" +#import "S1TopicListViewController.h" +#import "S1LoginViewController.h" #import "GSStaticTableViewBuilder.h" @interface S1SettingViewController () +@property (nonatomic, assign) BOOL modified; + @end @implementation S1SettingViewController @@ -19,16 +23,76 @@ @implementation S1SettingViewController - (void)viewDidLoad { [super viewDidLoad]; - self.navigationItem.title = NSLocalizedString(@"Setting", @"Setting"); - UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Cancel", @"Cancel") style:UIBarButtonItemStyleBordered target:self action:@selector(cancel:)]; + self.navigationItem.title = NSLocalizedString(@"设置", @"Setting"); + UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"返回", @"Back") style:UIBarButtonItemStyleDone target:self action:@selector(cancel:)]; self.navigationItem.leftBarButtonItem = cancelItem; - self.navigationController.navigationBar.tintColor = [UIColor colorWithWhite:0.15 alpha:1.0]; - // Do any additional setup after loading the view. + + self.modified = NO; + __weak typeof(self) myself = self; + [self addSection:^(GSSection *section) { + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell){ + cell.textLabel.text = NSLocalizedString(@"登录", @"Login"); + cell.selectionStyle = UITableViewCellSelectionStyleBlue; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + }]; + [row setEventHandlerBlock:^(UITableViewCell *cell){ + S1LoginViewController *controller = [[S1LoginViewController alloc] initWithStyle:UITableViewStyleGrouped]; + [myself.navigationController pushViewController:controller animated:YES]; + }]; + }]; [section addRow:^(GSRow *row) { [row setConfigurationBlock:^(UITableViewCell *cell) { - cell.textLabel.text = @"Stage1st"; + cell.textLabel.text = @"板块顺序"; cell.selectionStyle = UITableViewCellSelectionStyleBlue; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + }]; + [row setEventHandlerBlock:^(UITableViewCell *cell) { + GSReorderableTableViewController *controller = [[GSReorderableTableViewController alloc] initWithKeys:[[NSUserDefaults standardUserDefaults] arrayForKey:@"Order"]]; + controller.title = NSLocalizedString(@"板块顺序", @"Order"); + [controller setCompletionHandler:^(NSArray *keys) { + [[NSUserDefaults standardUserDefaults] setValue:keys forKey:@"Order"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + }]; + myself.modified = YES; + [myself.navigationController pushViewController:controller animated:YES]; + }]; + }]; + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell){ + cell.textLabel.text = NSLocalizedString(@"显示图片", @"Display Image"); + if (!cell.accessoryView) { + UISwitch *switcher = [[UISwitch alloc] initWithFrame:CGRectZero]; + switcher.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"Display"]; + [switcher addEventHandler:^(id sender, UIEvent *event) { + UISwitch *s = sender; + [[NSUserDefaults standardUserDefaults] setBool:s.on forKey:@"Display"]; + } forControlEvent:UIControlEventValueChanged]; + cell.accessoryView = switcher; + } + }]; + }]; + + }]; + + [self addSection:^(GSSection *section) { + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell){ + cell.textLabel.text = NSLocalizedString(@"版本", @"Version"); + cell.detailTextLabel.text = @"3.0"; + }]; + }]; + + [section addRow:^(GSRow *row) { + [row setConfigurationBlock:^(UITableViewCell *cell){ + cell.textLabel.text = NSLocalizedString(@"开发者", @"Developer"); + cell.detailTextLabel.text = @"Gabriel"; + cell.selectionStyle = UITableViewCellSelectionStyleBlue; + }]; + [row setEventHandlerBlock:^(UITableViewCell *cell){ + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Gabriel" message:@"" delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles:nil]; + [alertView show]; }]; }]; }]; @@ -45,7 +109,11 @@ - (void)didReceiveMemoryWarning - (void)cancel:(id)sender { - [self dismissViewControllerAnimated:YES completion:nil]; + [self dismissViewControllerAnimated:YES completion:^{ + if (self.completionHandler) { + self.completionHandler(self.modified); + } + }]; } @end diff --git a/Stage1st/S1TabBar.h b/Stage1st/S1TabBar.h new file mode 100644 index 0000000..e7e3f21 --- /dev/null +++ b/Stage1st/S1TabBar.h @@ -0,0 +1,31 @@ +// +// S1TabBar.h +// Stage1st +// +// Created by Suen Gabriel on 3/2/13. +// Copyright (c) 2013 Renaissance. All rights reserved. +// + +#import + +@protocol S1TabBarDelegate; + +@interface S1TabBar : UIScrollView + +- (id)initWithFrame:(CGRect)frame andKeys:(NSArray *)keys; +- (void)deselectAll; + +@property (nonatomic, assign) id tabbarDelegate; + +//@property (nonatomic, copy) void (^eventHandler)(NSString *key); + +@property (nonatomic, strong) NSArray *keys; + + +@end + +@protocol S1TabBarDelegate + +- (void)tabbar:(S1TabBar *)tabbar didSelectedKey:(NSString *)key; + +@end diff --git a/Stage1st/S1TabBar.m b/Stage1st/S1TabBar.m new file mode 100644 index 0000000..438caec --- /dev/null +++ b/Stage1st/S1TabBar.m @@ -0,0 +1,144 @@ +// +// S1TabBar.m +// Stage1st +// +// Created by Suen Gabriel on 3/2/13. +// Copyright (c) 2013 Renaissance. All rights reserved. +// + +#import "S1TabBar.h" + +#define _DEFAULT_WIDTH 80.0f + +@implementation S1TabBar { + NSMutableArray *_buttons; + NSInteger _index; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + } + return self; +} + +- (id)initWithFrame:(CGRect)frame andKeys:(NSArray *)keys +{ + self = [super initWithFrame:frame]; + if (!self) return nil; + _keys = keys; + _index = -1; + _buttons = [NSMutableArray arrayWithCapacity:keys.count]; + self.backgroundColor = [UIColor blackColor]; + self.bounces = NO; + self.showsHorizontalScrollIndicator = NO; + self.scrollsToTop = NO; + self.delegate = self; +// self.decelerationRate = UIScrollViewDecelerationRateFast; + [self addItems]; + return self; +} + +- (void)setKeys:(NSArray *)keys +{ + [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [(UIView *)obj removeFromSuperview]; + }]; + [self setContentSize:CGSizeMake(0.0f, 0.0f)]; + _keys = keys; + _index = -1; + if (_keys.count == 0) { + UIToolbar *maskToolbar = [[UIToolbar alloc] initWithFrame:self.bounds]; + [self addSubview:maskToolbar]; + } else { + _buttons = [NSMutableArray arrayWithCapacity:_keys.count]; + [self addItems]; + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + CGPoint offset = scrollView.contentOffset; + CGFloat n = roundf(offset.x / _DEFAULT_WIDTH); + if ((offset.x - n*_DEFAULT_WIDTH) < ((n+1)*_DEFAULT_WIDTH -offset.x)) + offset.x = n*_DEFAULT_WIDTH; + else + offset.x = (n+1)*_DEFAULT_WIDTH; + [scrollView setContentOffset:offset animated:YES]; + return; +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + if (!decelerate) { + CGPoint offset = scrollView.contentOffset; + CGFloat n = roundf(offset.x / _DEFAULT_WIDTH); + if ((offset.x - n*_DEFAULT_WIDTH) < ((n+1)*_DEFAULT_WIDTH -offset.x)) + offset.x = n*_DEFAULT_WIDTH; + else + offset.x = (n+1)*_DEFAULT_WIDTH; + [scrollView setContentOffset:offset animated:YES]; + } + return; +} + +- (void)tapped:(UIButton *)sender +{ + if (_index >= 0) + [_buttons[_index] setSelected:NO]; + _index = sender.tag; + sender.selected = YES; + [self.tabbarDelegate tabbar:self didSelectedKey:self.keys[_index]]; + return; +} + +- (void)deselectAll +{ + if (_index >= 0) { + [[_buttons objectAtIndex:_index] setSelected:NO]; + _index = -1; + } +} + +- (void)addItems +{ + if (_keys.count == 0) return; + + CGFloat widthPerItem = (_keys.count >= 4 ? _DEFAULT_WIDTH : self.bounds.size.width/_keys.count); + __block CGFloat width = 0.0; + [_keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; + CGRect rect = CGRectMake(width, 0, widthPerItem, self.bounds.size.height); + [btn setFrame:CGRectInset(rect, 0.5, 2)]; + btn.showsTouchWhenHighlighted = NO; + [btn setBackgroundImage:[UIImage imageNamed:@"Item.png"] forState:UIControlStateNormal]; + [btn setBackgroundImage:[[UIImage imageNamed:@"Item_highlighted.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 15, 5, 15)] forState:UIControlStateSelected]; + [btn setBackgroundImage:[[UIImage imageNamed:@"Item_selected.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 15, 5, 15)] forState:UIControlStateSelected]; + [btn setTitle:[obj description] forState:UIControlStateNormal]; + [btn setTitle:[obj description] forState:UIControlStateHighlighted]; + [btn setTitle:[obj description] forState:UIControlStateSelected]; + btn.titleLabel.textColor = [UIColor whiteColor]; + btn.titleLabel.shadowColor = [UIColor blackColor]; + btn.titleLabel.shadowOffset = CGSizeMake(0.0, 1.5); + btn.titleLabel.font = [UIFont boldSystemFontOfSize:14.0]; + [btn setTag:idx]; + [btn addTarget:self action:@selector(tapped:) forControlEvents:UIControlEventTouchUpInside]; + [_buttons addObject:btn]; + width += widthPerItem; + [self addSubview:btn]; + }]; + + self.contentSize = CGSizeMake(width, self.bounds.size.height); +} + +/* +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect +{ + // Drawing code +} +*/ + +@end diff --git a/Stage1st/S1Topic.h b/Stage1st/S1Topic.h index 7dc2997..1f38604 100644 --- a/Stage1st/S1Topic.h +++ b/Stage1st/S1Topic.h @@ -8,10 +8,14 @@ #import -@interface S1Topic : NSObject +@interface S1Topic : NSObject @property (nonatomic, copy) NSString *topicID; @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *replyCount; +//For Tracing +@property (nonatomic, copy) NSString *lastViewedPage; +@property (nonatomic, copy) NSDate *lastViewedDate; + @end diff --git a/Stage1st/S1Topic.m b/Stage1st/S1Topic.m index 1966cf3..1f4e836 100644 --- a/Stage1st/S1Topic.m +++ b/Stage1st/S1Topic.m @@ -13,7 +13,31 @@ @implementation S1Topic - (NSString *)description { - return [NSString stringWithFormat:@"[Topic]ID:%@; Title:%@; Reply:%@", self.topicID, self.title, self.replyCount]; + return [NSString stringWithFormat:@"[Topic] ID:%@, TimeStamp:%@", self.topicID, self.lastViewedDate]; +} + + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeObject:self.topicID forKey:@"TopicID"]; + [encoder encodeObject:self.title forKey:@"Title"]; + [encoder encodeObject:self.replyCount forKey:@"ReplyCount"]; + [encoder encodeObject:self.lastViewedPage forKey:@"LastViewedPage"]; + [encoder encodeObject:self.lastViewedDate forKey:@"LastViewedDate"]; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + if (self = [super init]) { + self.topicID = [decoder decodeObjectForKey:@"TopicID"]; + self.title = [decoder decodeObjectForKey:@"Title"]; + self.replyCount = [decoder decodeObjectForKey:@"ReplyCount"]; + self.lastViewedPage = [decoder decodeObjectForKey:@"LastViewedPage"]; + self.lastViewedDate = [decoder decodeObjectForKey:@"LastViewedDate"]; + } + return self; } @end diff --git a/Stage1st/S1TopicListCell.m b/Stage1st/S1TopicListCell.m index edcccc7..20ac68a 100644 --- a/Stage1st/S1TopicListCell.m +++ b/Stage1st/S1TopicListCell.m @@ -73,7 +73,7 @@ - (void)drawRect:(CGRect)rect [rectanglePath fill]; //// Rounded Rectangle Drawing - UIBezierPath* roundedRectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(20, 17, 35, 20) cornerRadius:2]; + UIBezierPath* roundedRectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(19, 17, 37, 20) cornerRadius:2]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, outerShadowOffset, outerShadowBlurRadius, outerShadow.CGColor); [fill setFill]; diff --git a/Stage1st/S1TopicListViewController.h b/Stage1st/S1TopicListViewController.h index 55b543e..1b2b66f 100644 --- a/Stage1st/S1TopicListViewController.h +++ b/Stage1st/S1TopicListViewController.h @@ -10,4 +10,6 @@ @interface S1TopicListViewController : UIViewController +- (void)update; + @end diff --git a/Stage1st/S1TopicListViewController.m b/Stage1st/S1TopicListViewController.m index 89f7b3d..1d74aa1 100644 --- a/Stage1st/S1TopicListViewController.m +++ b/Stage1st/S1TopicListViewController.m @@ -10,22 +10,32 @@ #import "S1ContentViewController.h" #import "S1RootViewController.h" #import "S1SettingViewController.h" -#import "UIControl+BlockWrapper.h" #import "S1HTTPClient.h" #import "S1Parser.h" #import "S1TopicCell.h" #import "S1TopicListCell.h" #import "S1HUD.h" +#import "S1Topic.h" +#import "S1TabBar.h" +#import "S1Tracer.h" + +#import "ODRefreshControl.h" static NSString * const cellIdentifier = @"TopicCell"; -@interface S1TopicListViewController () +@interface S1TopicListViewController () @property (nonatomic, strong) UINavigationBar *navigationBar; - +@property (nonatomic, strong) UINavigationItem *naviItem; @property (nonatomic, strong) UITableView *tableView; +@property (nonatomic, strong) ODRefreshControl *refreshControl; +@property (nonatomic, strong) S1TabBar *scrollTabBar; +@property (nonatomic, strong) S1Tracer *tracer; +@property (nonatomic, strong) NSString *currentKey; @property (nonatomic, strong) NSArray *topics; +@property (nonatomic, strong) NSMutableDictionary *cache; +@property (nonatomic, strong) NSDictionary *threadsInfo; @end @@ -45,34 +55,44 @@ - (void)viewDidLoad #define _BAR_HEIGHT 44.0f [super viewDidLoad]; - self.view.backgroundColor = [UIColor blackColor]; + self.tracer = [[S1Tracer alloc] initWithTracerName:@"RecentViewed.tracer"]; + self.tracer.identifyKey = @"topicID"; + self.tracer.timeStampKey = @"lastViewedDate"; + + self.view.backgroundColor = [UIColor colorWithRed: 0.96 green: 0.97 blue: 0.92 alpha: 1]; self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, _BAR_HEIGHT, self.view.bounds.size.width, self.view.bounds.size.height-2*_BAR_HEIGHT) style:UITableViewStylePlain]; self.tableView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - [self.tableView registerClass:[S1TopicListCell class] forCellReuseIdentifier:cellIdentifier]; self.tableView.rowHeight = 54.0f; self.tableView.delegate = self; self.tableView.dataSource = self; self.tableView.separatorColor = [UIColor colorWithRed:0.82 green:0.85 blue:0.76 alpha:1.0]; self.tableView.backgroundColor = [UIColor colorWithRed: 0.96 green: 0.97 blue: 0.92 alpha: 1]; + self.tableView.hidden = YES; [self.view addSubview:self.tableView]; + self.refreshControl = [[ODRefreshControl alloc] initInScrollView:self.tableView]; + self.refreshControl.tintColor = [UIColor colorWithRed: 0.92 green: 0.92 blue: 0.86 alpha: 1]; + [self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged]; + self.navigationBar = [[UINavigationBar alloc] init]; self.navigationBar.frame = CGRectMake(0, 0, self.view.bounds.size.width, _BAR_HEIGHT); self.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; self.navigationBar.tintColor = [UIColor colorWithWhite:0.15 alpha:1.0]; UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:@"Stage1st"]; - UIBarButtonItem *settingItem = [[UIBarButtonItem alloc] initWithTitle:@"Settings" style:UIBarButtonItemStyleBordered target:self action:@selector(settings:)]; - UIBarButtonItem *recentItem = [[UIBarButtonItem alloc] initWithTitle:@"Recent" style:UIBarButtonItemStyleBordered target:self action:@selector(recent:)]; + self.naviItem = item; + UIBarButtonItem *settingItem = [[UIBarButtonItem alloc] initWithTitle:@"设置" style:UIBarButtonItemStyleBordered target:self action:@selector(settings:)]; item.leftBarButtonItem = settingItem; + UIBarButtonItem *recentItem = [[UIBarButtonItem alloc] initWithTitle:@"最近浏览" style:UIBarButtonItemStyleBordered target:self action:@selector(recent:)]; item.rightBarButtonItem = recentItem; [self.navigationBar pushNavigationItem:item animated:NO]; - [self.view addSubview:self.navigationBar]; - [self fetchTopics]; + [self.view addSubview:self.navigationBar]; - UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height-_BAR_HEIGHT, self.view.bounds.size.width, _BAR_HEIGHT)]; - [self.view addSubview:toolbar]; + self.scrollTabBar = [[S1TabBar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height-_BAR_HEIGHT, self.view.bounds.size.width, _BAR_HEIGHT) andKeys:[self keys]]; + self.scrollTabBar.tabbarDelegate = self; + + [self.view addSubview:self.scrollTabBar]; #undef _BAR_HEIGHT } @@ -86,52 +106,151 @@ - (void)viewWillAppear:(BOOL)animated - (void)viewWillDisappear:(BOOL)animated { - [self.tableView setUserInteractionEnabled:YES]; + [self.tableView setUserInteractionEnabled:NO]; [self.tableView setScrollsToTop:NO]; - + [super viewWillDisappear:animated]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; + self.cache = nil; + self.threadsInfo = nil; // Dispose of any resources that can be recreated. } -#pragma mark - Bar Item Actions +#pragma mark - Getters and Setters + +- (NSDictionary *)threadsInfo +{ + if (_threadsInfo) + return _threadsInfo; + NSString *path = [[NSBundle mainBundle] pathForResource:@"Threads" ofType:@"plist"]; + _threadsInfo = [NSDictionary dictionaryWithContentsOfFile:path]; + return _threadsInfo; +} + +- (NSMutableDictionary *)cache +{ + if (_cache) + return _cache; + _cache = [NSMutableDictionary dictionary]; + return _cache; +} + +#pragma mark - Item Actions - (void)settings:(id)sender { + [self rootViewController].modalPresentationStyle = UIModalPresentationFullScreen; S1SettingViewController *controller = [[S1SettingViewController alloc] initWithStyle:UITableViewStyleGrouped]; - [self presentViewController:[[UINavigationController alloc] initWithRootViewController:controller] animated:YES completion:^{}]; + [controller setCompletionHandler:^(BOOL needToUpdate){ + if (needToUpdate) [self update]; + }]; + [self presentViewController:[[UINavigationController alloc] initWithRootViewController:controller] animated:YES completion:nil]; } - (void)recent:(id)sender { + self.naviItem.title = @"Recent"; + if (self.tableView.hidden == YES) { + self.tableView.hidden = NO; + } + self.refreshControl.hidden = YES; + self.topics = [self.tracer recentViewedObjects]; + [self.tableView reloadData]; + if (self.topics && self.topics.count > 0) { + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; + } + + [self.scrollTabBar deselectAll]; + self.currentKey = nil; } +- (void)refresh:(id)sender +{ + if (self.scrollTabBar.userInteractionEnabled) { + [self fetchTopicsForKeyFromServer:self.currentKey scrollToTop:NO]; + } else { + [self.refreshControl endRefreshing]; + } +} + +#pragma mark - Tab Bar Delegate + +- (void)tabbar:(S1TabBar *)tabbar didSelectedKey:(NSString *)key +{ + self.naviItem.title = @"Stage1st"; + if (self.tableView.hidden) { + self.tableView.hidden = NO; + } + if (self.refreshControl.hidden) { + self.refreshControl.hidden = NO; + } + if (![self.currentKey isEqualToString:key]) { + self.currentKey = key; + [self fetchTopicsForKey:key]; + if (self.refreshControl.refreshing) { + [self.refreshControl endRefreshing]; + } + } +} + + + #pragma mark - Networking -- (void)fetchTopics +- (void)fetchTopicsForKey:(NSString *)key +{ + if (self.cache[key]) { + self.topics = self.cache[key]; + [self.tableView reloadData]; + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; + return; + } + [self fetchTopicsForKeyFromServer:key scrollToTop:YES]; +} + +- (void)fetchTopicsForKeyFromServer:(NSString *)key scrollToTop:(BOOL)toTop { + self.scrollTabBar.userInteractionEnabled = NO; S1HUD *HUD = [S1HUD showHUDInView:self.view]; - [[S1HTTPClient sharedClient] getPath:@"?f75.html" + [HUD showActivityIndicator]; + NSString *path = [NSString stringWithFormat:@"simple/?f%@.html", self.threadsInfo[key]]; + [[S1HTTPClient sharedClient] getPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { NSString *HTMLString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; if (HTMLString) { - self.topics = [S1Parser topicsFromHTMLString:HTMLString]; - [self.tableView reloadData]; + NSArray *topics = [S1Parser topicsFromHTMLString:HTMLString]; + if (topics.count > 0) { + self.topics = topics; + self.cache[key] = topics; + [self.tableView reloadData]; + if (toTop) { + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; + } + } } - [HUD hideWithDelay:0.5]; + if (self.refreshControl.refreshing) { + [self.refreshControl endRefreshing]; + } + [HUD hideWithDelay:0.3]; + self.scrollTabBar.userInteractionEnabled = YES; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - NSLog(@"%@", error); - [HUD hideWithDelay:0.5]; + [HUD setText:@"Request Failed"]; + [HUD hideWithDelay:0.3]; + self.scrollTabBar.userInteractionEnabled = YES; + if (self.refreshControl.refreshing) { + [self.refreshControl endRefreshing]; + } }]; } + #pragma mark - UITableView @@ -147,8 +266,10 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - S1TopicCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier - forIndexPath:indexPath]; + S1TopicListCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (!cell) { + cell = [[S1TopicListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; + } cell.selectionStyle = UITableViewCellSelectionStyleNone; [cell setTopic:self.topics[indexPath.row]]; return cell; @@ -158,7 +279,14 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath { [tableView deselectRowAtIndexPath:indexPath animated:NO]; S1ContentViewController *controller = [[S1ContentViewController alloc] init]; - [controller setTopic:self.topics[indexPath.row]]; + [controller setFid:self.threadsInfo[self.currentKey]]; + [controller setTracer:self.tracer]; + S1Topic *topicToShow = self.topics[indexPath.row]; + S1Topic *tracedTopic = [self.tracer objectInRecentForKey:topicToShow.topicID]; + if (tracedTopic) { + topicToShow.lastViewedPage = tracedTopic.lastViewedPage; + } + [controller setTopic:topicToShow]; [[self rootViewController] presentDetailViewController:controller]; } @@ -173,4 +301,17 @@ - (S1RootViewController *)rootViewController return (S1RootViewController *)controller; } +- (NSArray *)keys +{ + return [[[NSUserDefaults standardUserDefaults] arrayForKey:@"Order"] objectAtIndex:0]; +} + +- (void)update +{ + self.currentKey = @""; + self.topics = [NSArray array]; + [self.tableView reloadData]; + [self.scrollTabBar setKeys:[self keys]]; +} + @end diff --git a/Stage1st/S1Tracer.h b/Stage1st/S1Tracer.h new file mode 100644 index 0000000..d8ca617 --- /dev/null +++ b/Stage1st/S1Tracer.h @@ -0,0 +1,24 @@ +// +// S1Tracer.h +// Stage1st +// +// Created by Suen Gabriel on 3/3/13. +// Copyright (c) 2013 Renaissance. All rights reserved. +// + +#import + +@interface S1Tracer : NSObject + +@property (nonatomic, copy) NSString *identifyKey; +@property (nonatomic, copy) NSString *timeStampKey; + +- (id)initWithTracerName:(NSString *)name; + +- (void)hasViewed:(id)object; + +- (NSArray *)recentViewedObjects; + +- (id)objectInRecentForKey:(NSString *)key; + +@end diff --git a/Stage1st/S1Tracer.m b/Stage1st/S1Tracer.m new file mode 100644 index 0000000..b9960a9 --- /dev/null +++ b/Stage1st/S1Tracer.m @@ -0,0 +1,117 @@ +// +// S1Tracer.m +// Stage1st +// +// Created by Suen Gabriel on 3/3/13. +// Copyright (c) 2013 Renaissance. All rights reserved. +// + +#import "S1Tracer.h" + +NSTimeInterval const kDefaultDuration = 259200; // 3 days + +@implementation S1Tracer { + NSString *_tracerName; + NSMutableDictionary *_tracerDictionary; +} + +- (id)initWithTracerName:(NSString *)name +{ + self = [super init]; + if (!self) return nil; + + _tracerName = [name copy]; + _tracerDictionary = [self tracerDictionaryFromPersistence]; + + if (!_tracerDictionary) { + _tracerDictionary = [NSMutableDictionary dictionary]; + } + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(synchronize) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(synchronize) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(synchronize) + name:UIApplicationWillTerminateNotification + object:nil]; + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIApplicationWillTerminateNotification object:nil]; +} + +- (void)hasViewed:(id)object +{ + [object setValue:[NSDate date] forKey:self.timeStampKey]; + [_tracerDictionary setObject:object forKey:[object valueForKey:self.identifyKey]]; + NSLog(@"Tracer has traced:%@", object); +} + +- (NSArray *)recentViewedObjects +{ + NSArray *all = [_tracerDictionary allValues]; + NSLog(@"%@", all); + return [[[all sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + NSDate *date1 = [obj1 valueForKey:self.timeStampKey]; + NSDate *date2 = [obj2 valueForKey:self.timeStampKey]; + return [date1 compare:date2]; + }] reverseObjectEnumerator] allObjects]; +} + +- (id)objectInRecentForKey:(NSString *)key +{ + return _tracerDictionary[key]; +} + +#pragma mark - Archiver + +- (void)synchronize +{ + [self purgeStaleItem]; + NSLog(@"Write to Disk: %@", [self tracerPath]); + [NSKeyedArchiver archiveRootObject:_tracerDictionary toFile:[self tracerPath]]; +} + + +- (NSMutableDictionary *)tracerDictionaryFromPersistence +{ + NSString *path = [self tracerPath]; + NSData *data = [NSData dataWithContentsOfFile:path]; + NSMutableDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + return dict; +} + + +- (NSString *)tracerPath +{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *tracerDirectory = [paths objectAtIndex:0]; + return [tracerDirectory stringByAppendingPathComponent:_tracerName]; +} + +- (void)purgeStaleItem +{ + __block NSMutableArray *keysToRemove = [NSMutableArray array]; + [_tracerDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + NSDate *expireDate = [(NSDate *)[obj valueForKey:_timeStampKey] dateByAddingTimeInterval:kDefaultDuration]; + NSDate *now = [NSDate date]; + if ([[now earlierDate:expireDate] isEqualToDate:expireDate]) { + NSLog(@"Purge Item:%@", obj); + [keysToRemove addObject:key]; + } + }]; + [_tracerDictionary removeObjectsForKeys:keysToRemove]; +} + + + +@end diff --git a/Stage1st/S1URLCache.m b/Stage1st/S1URLCache.m index 8a887e4..b67938c 100644 --- a/Stage1st/S1URLCache.m +++ b/Stage1st/S1URLCache.m @@ -35,17 +35,22 @@ - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request return [super cachedResponseForRequest:request]; } else { - NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"placeholder" ofType:@"png"]]; - NSURLResponse *response = - [[NSURLResponse alloc] initWithURL:request.URL - MIMEType:@"image/png" - expectedContentLength:[imageData length] - textEncodingName:nil]; - - NSCachedURLResponse *cachedResponse = - [[NSCachedURLResponse alloc] initWithResponse:response - data:imageData]; - return cachedResponse; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"Display"]) { + return [super cachedResponseForRequest:request]; + } + else { + NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"placeholder" ofType:@"png"]]; + NSURLResponse *response = + [[NSURLResponse alloc] initWithURL:request.URL + MIMEType:@"image/png" + expectedContentLength:[imageData length] + textEncodingName:nil]; + + NSCachedURLResponse *cachedResponse = + [[NSCachedURLResponse alloc] initWithResponse:response + data:imageData]; + return cachedResponse; + } } } diff --git a/Stage1st/Stage1st-Info.plist b/Stage1st/Stage1st-Info.plist index 334063d..b69debe 100644 --- a/Stage1st/Stage1st-Info.plist +++ b/Stage1st/Stage1st-Info.plist @@ -30,11 +30,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 3.0 CFBundleSignature ???? CFBundleVersion - 1.0 + 3000 LSRequiresIPhoneOS UIPrerenderedIcon diff --git a/Stage1st/Threads.plist b/Stage1st/Threads.plist new file mode 100644 index 0000000..cb68193 --- /dev/null +++ b/Stage1st/Threads.plist @@ -0,0 +1,44 @@ + + + + + 外野 + 75 + 动漫论坛 + 6 + 游戏论坛 + 4 + PC数码 + 51 + 影视论坛 + 48 + 文史沙龙 + 50 + 彼岸文化 + 31 + 八卦体育 + 77 + 音乐论坛 + 24 + 二手交易区 + 115 + 英雄联盟 + 111 + 吃货 + 123 + 火暴 + 93 + 马叉虫 + 74 + 车欠女未 + 76 + 犭苗犭句 + 101 + 摄影区 + 80 + 开箱区 + 124 + 魔兽世界 + 53 + + diff --git a/Stage1st/content.css b/Stage1st/content.css index b371830..86d71d8 100644 --- a/Stage1st/content.css +++ b/Stage1st/content.css @@ -1,5 +1,7 @@ -* { - max-width: 600px; + +img { + max-width: 288px; + display: inline-block; } iframe { @@ -8,6 +10,16 @@ iframe { display: none !important; } +.text img { + max-width: 250px; +} + +.text { + border-left: #022c80 4px solid; + padding-left: 10px; + margin-top: 10px; + font-style: italic; +} .i_table { box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75), inset 1px 1px 0px 0px #fff; @@ -23,15 +35,8 @@ iframe { line-height: 150%; } -.head { - font-size: 11px; - background-color: #f6f7eb; - border-bottom: #ccc 1px solid; - display: block; - color: #aaa; -} - font { + font-size: 15px; font-family: Helvetica; } @@ -47,6 +52,16 @@ a { color: #022c80; } +a:link { + color: #022c80; +} + +a:visited { + color: #022c80; +} + + + h1 { font-size: 17px; text-align:center; @@ -56,13 +71,6 @@ center { display: none; } -.text { - border-left: #022c80 4px solid; - padding-left: 10px; - margin-top: 10px; - font-style: italic; -} - .quote { display: none; } @@ -70,4 +78,16 @@ center { .blockquote3 { margin: 0; margin: 5px; -} \ No newline at end of file +} + +.head { + font-size: 12px; + background-color: #f6f7eb; + border-bottom: #ccc 1px solid; + display: block; + color: #aaa; +} + +.smalltext { + font-size: 12px; +} diff --git a/Vendors/AFNetworking/AFHTTPClient.m b/Vendors/AFNetworking/AFHTTPClient.m index 5bb3a7c..0f9fdf0 100755 --- a/Vendors/AFNetworking/AFHTTPClient.m +++ b/Vendors/AFNetworking/AFHTTPClient.m @@ -441,7 +441,7 @@ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setTimeoutInterval:20.0f]; + [request setTimeoutInterval:15.0]; [request setHTTPMethod:method]; [request setAllHTTPHeaderFields:self.defaultHeaders]; diff --git a/Vendors/GoogleToolbox/GTMDefines.h b/Vendors/GoogleToolbox/GTMDefines.h new file mode 100644 index 0000000..c295848 --- /dev/null +++ b/Vendors/GoogleToolbox/GTMDefines.h @@ -0,0 +1,441 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// ============================================================================ + +#include +#include + +#ifdef __OBJC__ +#include +#endif // __OBJC__ + +#if TARGET_OS_IPHONE +#include +#endif // TARGET_OS_IPHONE + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif +#ifndef MAC_OS_X_VERSION_10_7 + #define MAC_OS_X_VERSION_10_7 1070 +#endif + +// Not all __IPHONE_X macros defined in past SDKs +#ifndef __IPHONE_3_0 + #define __IPHONE_3_0 30000 +#endif +#ifndef __IPHONE_3_1 + #define __IPHONE_3_1 30100 +#endif +#ifndef __IPHONE_3_2 + #define __IPHONE_3_2 30200 +#endif +#ifndef __IPHONE_4_0 + #define __IPHONE_4_0 40000 +#endif +#ifndef __IPHONE_4_3 + #define __IPHONE_4_3 40300 +#endif +#ifndef __IPHONE_5_0 + #define __IPHONE_5_0 50000 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #define GTM_EXTERN_C_BEGIN extern "C" { + #define GTM_EXTERN_C_END } + #else + #define GTM_EXTERN extern + #define GTM_EXTERN_C_BEGIN + #define GTM_EXTERN_C_END + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// Give ourselves a consistent way of declaring something as unused. This +// doesn't use __unused because that is only supported in gcc 4.2 and greater. +#if !defined (GTM_UNUSED) +#define GTM_UNUSED(x) ((void)(x)) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #endif // TARGET_IPHONE_SIMULATOR + // By default, GTM has provided it's own unittesting support, define this + // to use the support provided by Xcode, especially for the Xcode4 support + // for unittesting. + #ifndef GTM_IPHONE_USE_SENTEST + #define GTM_IPHONE_USE_SENTEST 0 + #endif +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 +#endif + +// Some of our own availability macros +#if GTM_MACOS_SDK +#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE +#define GTM_AVAILABLE_ONLY_ON_MACOS +#else +#define GTM_AVAILABLE_ONLY_ON_IPHONE +#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE +#endif + +// GC was dropped by Apple, define the old constant incase anyone still keys +// off of it. +#ifndef GTM_SUPPORT_GC + #define GTM_SUPPORT_GC 0 +#endif + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +// Some support for advanced clang static analysis functionality +// See http://clang-analyzer.llvm.org/annotations.html +#ifndef __has_feature // Optional. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef NS_RETURNS_RETAINED + #if __has_feature(attribute_ns_returns_retained) + #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) + #else + #define NS_RETURNS_RETAINED + #endif +#endif + +#ifndef NS_RETURNS_NOT_RETAINED + #if __has_feature(attribute_ns_returns_not_retained) + #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) + #else + #define NS_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_RETAINED + #if __has_feature(attribute_cf_returns_retained) + #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) + #else + #define CF_RETURNS_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_NOT_RETAINED + #if __has_feature(attribute_cf_returns_not_retained) + #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) + #else + #define CF_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef NS_CONSUMED + #if __has_feature(attribute_ns_consumed) + #define NS_CONSUMED __attribute__((ns_consumed)) + #else + #define NS_CONSUMED + #endif +#endif + +#ifndef CF_CONSUMED + #if __has_feature(attribute_cf_consumed) + #define CF_CONSUMED __attribute__((cf_consumed)) + #else + #define CF_CONSUMED + #endif +#endif + +#ifndef NS_CONSUMES_SELF + #if __has_feature(attribute_ns_consumes_self) + #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) + #else + #define NS_CONSUMES_SELF + #endif +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_ARGUMENT + #define NS_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_FUNCTION + #define NS_FORMAT_FUNCTION(F,A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_ARGUMENT + #define CF_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_FUNCTION + #define CF_FORMAT_FUNCTION(F,A) +#endif + +#ifndef GTM_NONNULL + #if defined(__has_attribute) + #if __has_attribute(nonnull) + #define GTM_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTM_NONNULL(x) + #endif + #else + #define GTM_NONNULL(x) + #endif +#endif + +// Invalidates the initializer from which it's called. +#ifndef GTMInvalidateInitializer + #if __has_feature(objc_arc) + #define GTMInvalidateInitializer() \ + do { \ + [self class]; /* Avoid warning of dead store to |self|. */ \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #else + #define GTMInvalidateInitializer() \ + do { \ + [self release]; \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #endif +#endif + +#ifdef __OBJC__ + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); + +// Macro to allow you to create NSStrings out of other macros. +// #define FOO foo +// NSString *fooString = GTM_NSSTRINGIFY(FOO); +#if !defined (GTM_NSSTRINGIFY) + #define GTM_NSSTRINGIFY_INNER(x) @#x + #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x) +#endif + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (element in enumeration) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (NSEnumerator *_ ## element ## _enum = enumeration; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_OBJECT(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator]) + #define GTM_FOREACH_KEY(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator]) + #endif +#endif + +// ============================================================================ + +// To simplify support for both Leopard and Snow Leopard we declare +// the Snow Leopard protocols that we need here. +#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) +#define GTM_10_6_PROTOCOLS_DEFINED 1 +@protocol NSConnectionDelegate +@end +@protocol NSAnimationDelegate +@end +@protocol NSImageDelegate +@end +@protocol NSTabViewDelegate +@end +#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + +// GTM_SEL_STRING is for specifying selector (usually property) names to KVC +// or KVO methods. +// In debug it will generate warnings for undeclared selectors if +// -Wunknown-selector is turned on. +// In release it will have no runtime overhead. +#ifndef GTM_SEL_STRING + #ifdef DEBUG + #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName)) + #else + #define GTM_SEL_STRING(selName) @#selName + #endif // DEBUG +#endif // GTM_SEL_STRING + +#endif // __OBJC__ diff --git a/Vendors/GoogleToolbox/GTMNSString+HTML.h b/Vendors/GoogleToolbox/GTMNSString+HTML.h new file mode 100644 index 0000000..1273cc3 --- /dev/null +++ b/Vendors/GoogleToolbox/GTMNSString+HTML.h @@ -0,0 +1,66 @@ +// +// GTMNSString+HTML.h +// Dealing with NSStrings that contain HTML +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +/// Utilities for NSStrings containing HTML +@interface NSString (GTMNSStringHTMLAdditions) + +/// Get a string where internal characters that need escaping for HTML are escaped +// +/// For example, '&' become '&'. This will only cover characters from table +/// A.2.2 of http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters +/// which is what you want for a unicode encoded webpage. If you have a ascii +/// or non-encoded webpage, please use stringByEscapingAsciiHTML which will +/// encode all characters. +/// +/// For obvious reasons this call is only safe once. +// +// Returns: +// Autoreleased NSString +// +- (NSString *)gtm_stringByEscapingForHTML; + +/// Get a string where internal characters that need escaping for HTML are escaped +// +/// For example, '&' become '&' +/// All non-mapped characters (unicode that don't have a &keyword; mapping) +/// will be converted to the appropriate &#xxx; value. If your webpage is +/// unicode encoded (UTF16 or UTF8) use stringByEscapingHTML instead as it is +/// faster, and produces less bloated and more readable HTML (as long as you +/// are using a unicode compliant HTML reader). +/// +/// For obvious reasons this call is only safe once. +// +// Returns: +// Autoreleased NSString +// +- (NSString *)gtm_stringByEscapingForAsciiHTML; + +/// Get a string where internal characters that are escaped for HTML are unescaped +// +/// For example, '&' becomes '&' +/// Handles and 2 cases as well +/// +// Returns: +// Autoreleased NSString +// +- (NSString *)gtm_stringByUnescapingFromHTML; + +@end diff --git a/Vendors/GoogleToolbox/GTMNSString+HTML.m b/Vendors/GoogleToolbox/GTMNSString+HTML.m new file mode 100644 index 0000000..d580b9e --- /dev/null +++ b/Vendors/GoogleToolbox/GTMNSString+HTML.m @@ -0,0 +1,522 @@ +// +// GTMNSString+HTML.m +// Dealing with NSStrings that contain HTML +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMDefines.h" +#import "GTMNSString+HTML.h" + +typedef struct { + NSString *escapeSequence; + unichar uchar; +} HTMLEscapeMap; + +// Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters +// Ordered by uchar lowest to highest for bsearching +static HTMLEscapeMap gAsciiHTMLEscapeMap[] = { + // A.2.2. Special characters + { @""", 34 }, + { @"&", 38 }, + { @"'", 39 }, + { @"<", 60 }, + { @">", 62 }, + + // A.2.1. Latin-1 characters + { @" ", 160 }, + { @"¡", 161 }, + { @"¢", 162 }, + { @"£", 163 }, + { @"¤", 164 }, + { @"¥", 165 }, + { @"¦", 166 }, + { @"§", 167 }, + { @"¨", 168 }, + { @"©", 169 }, + { @"ª", 170 }, + { @"«", 171 }, + { @"¬", 172 }, + { @"­", 173 }, + { @"®", 174 }, + { @"¯", 175 }, + { @"°", 176 }, + { @"±", 177 }, + { @"²", 178 }, + { @"³", 179 }, + { @"´", 180 }, + { @"µ", 181 }, + { @"¶", 182 }, + { @"·", 183 }, + { @"¸", 184 }, + { @"¹", 185 }, + { @"º", 186 }, + { @"»", 187 }, + { @"¼", 188 }, + { @"½", 189 }, + { @"¾", 190 }, + { @"¿", 191 }, + { @"À", 192 }, + { @"Á", 193 }, + { @"Â", 194 }, + { @"Ã", 195 }, + { @"Ä", 196 }, + { @"Å", 197 }, + { @"Æ", 198 }, + { @"Ç", 199 }, + { @"È", 200 }, + { @"É", 201 }, + { @"Ê", 202 }, + { @"Ë", 203 }, + { @"Ì", 204 }, + { @"Í", 205 }, + { @"Î", 206 }, + { @"Ï", 207 }, + { @"Ð", 208 }, + { @"Ñ", 209 }, + { @"Ò", 210 }, + { @"Ó", 211 }, + { @"Ô", 212 }, + { @"Õ", 213 }, + { @"Ö", 214 }, + { @"×", 215 }, + { @"Ø", 216 }, + { @"Ù", 217 }, + { @"Ú", 218 }, + { @"Û", 219 }, + { @"Ü", 220 }, + { @"Ý", 221 }, + { @"Þ", 222 }, + { @"ß", 223 }, + { @"à", 224 }, + { @"á", 225 }, + { @"â", 226 }, + { @"ã", 227 }, + { @"ä", 228 }, + { @"å", 229 }, + { @"æ", 230 }, + { @"ç", 231 }, + { @"è", 232 }, + { @"é", 233 }, + { @"ê", 234 }, + { @"ë", 235 }, + { @"ì", 236 }, + { @"í", 237 }, + { @"î", 238 }, + { @"ï", 239 }, + { @"ð", 240 }, + { @"ñ", 241 }, + { @"ò", 242 }, + { @"ó", 243 }, + { @"ô", 244 }, + { @"õ", 245 }, + { @"ö", 246 }, + { @"÷", 247 }, + { @"ø", 248 }, + { @"ù", 249 }, + { @"ú", 250 }, + { @"û", 251 }, + { @"ü", 252 }, + { @"ý", 253 }, + { @"þ", 254 }, + { @"ÿ", 255 }, + + // A.2.2. Special characters cont'd + { @"Œ", 338 }, + { @"œ", 339 }, + { @"Š", 352 }, + { @"š", 353 }, + { @"Ÿ", 376 }, + + // A.2.3. Symbols + { @"ƒ", 402 }, + + // A.2.2. Special characters cont'd + { @"ˆ", 710 }, + { @"˜", 732 }, + + // A.2.3. Symbols cont'd + { @"Α", 913 }, + { @"Β", 914 }, + { @"Γ", 915 }, + { @"Δ", 916 }, + { @"Ε", 917 }, + { @"Ζ", 918 }, + { @"Η", 919 }, + { @"Θ", 920 }, + { @"Ι", 921 }, + { @"Κ", 922 }, + { @"Λ", 923 }, + { @"Μ", 924 }, + { @"Ν", 925 }, + { @"Ξ", 926 }, + { @"Ο", 927 }, + { @"Π", 928 }, + { @"Ρ", 929 }, + { @"Σ", 931 }, + { @"Τ", 932 }, + { @"Υ", 933 }, + { @"Φ", 934 }, + { @"Χ", 935 }, + { @"Ψ", 936 }, + { @"Ω", 937 }, + { @"α", 945 }, + { @"β", 946 }, + { @"γ", 947 }, + { @"δ", 948 }, + { @"ε", 949 }, + { @"ζ", 950 }, + { @"η", 951 }, + { @"θ", 952 }, + { @"ι", 953 }, + { @"κ", 954 }, + { @"λ", 955 }, + { @"μ", 956 }, + { @"ν", 957 }, + { @"ξ", 958 }, + { @"ο", 959 }, + { @"π", 960 }, + { @"ρ", 961 }, + { @"ς", 962 }, + { @"σ", 963 }, + { @"τ", 964 }, + { @"υ", 965 }, + { @"φ", 966 }, + { @"χ", 967 }, + { @"ψ", 968 }, + { @"ω", 969 }, + { @"ϑ", 977 }, + { @"ϒ", 978 }, + { @"ϖ", 982 }, + + // A.2.2. Special characters cont'd + { @" ", 8194 }, + { @" ", 8195 }, + { @" ", 8201 }, + { @"‌", 8204 }, + { @"‍", 8205 }, + { @"‎", 8206 }, + { @"‏", 8207 }, + { @"–", 8211 }, + { @"—", 8212 }, + { @"‘", 8216 }, + { @"’", 8217 }, + { @"‚", 8218 }, + { @"“", 8220 }, + { @"”", 8221 }, + { @"„", 8222 }, + { @"†", 8224 }, + { @"‡", 8225 }, + // A.2.3. Symbols cont'd + { @"•", 8226 }, + { @"…", 8230 }, + + // A.2.2. Special characters cont'd + { @"‰", 8240 }, + + // A.2.3. Symbols cont'd + { @"′", 8242 }, + { @"″", 8243 }, + + // A.2.2. Special characters cont'd + { @"‹", 8249 }, + { @"›", 8250 }, + + // A.2.3. Symbols cont'd + { @"‾", 8254 }, + { @"⁄", 8260 }, + + // A.2.2. Special characters cont'd + { @"€", 8364 }, + + // A.2.3. Symbols cont'd + { @"ℑ", 8465 }, + { @"℘", 8472 }, + { @"ℜ", 8476 }, + { @"™", 8482 }, + { @"ℵ", 8501 }, + { @"←", 8592 }, + { @"↑", 8593 }, + { @"→", 8594 }, + { @"↓", 8595 }, + { @"↔", 8596 }, + { @"↵", 8629 }, + { @"⇐", 8656 }, + { @"⇑", 8657 }, + { @"⇒", 8658 }, + { @"⇓", 8659 }, + { @"⇔", 8660 }, + { @"∀", 8704 }, + { @"∂", 8706 }, + { @"∃", 8707 }, + { @"∅", 8709 }, + { @"∇", 8711 }, + { @"∈", 8712 }, + { @"∉", 8713 }, + { @"∋", 8715 }, + { @"∏", 8719 }, + { @"∑", 8721 }, + { @"−", 8722 }, + { @"∗", 8727 }, + { @"√", 8730 }, + { @"∝", 8733 }, + { @"∞", 8734 }, + { @"∠", 8736 }, + { @"∧", 8743 }, + { @"∨", 8744 }, + { @"∩", 8745 }, + { @"∪", 8746 }, + { @"∫", 8747 }, + { @"∴", 8756 }, + { @"∼", 8764 }, + { @"≅", 8773 }, + { @"≈", 8776 }, + { @"≠", 8800 }, + { @"≡", 8801 }, + { @"≤", 8804 }, + { @"≥", 8805 }, + { @"⊂", 8834 }, + { @"⊃", 8835 }, + { @"⊄", 8836 }, + { @"⊆", 8838 }, + { @"⊇", 8839 }, + { @"⊕", 8853 }, + { @"⊗", 8855 }, + { @"⊥", 8869 }, + { @"⋅", 8901 }, + { @"⌈", 8968 }, + { @"⌉", 8969 }, + { @"⌊", 8970 }, + { @"⌋", 8971 }, + { @"⟨", 9001 }, + { @"⟩", 9002 }, + { @"◊", 9674 }, + { @"♠", 9824 }, + { @"♣", 9827 }, + { @"♥", 9829 }, + { @"♦", 9830 } +}; + +// Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters +// This is table A.2.2 Special Characters +static HTMLEscapeMap gUnicodeHTMLEscapeMap[] = { + // C0 Controls and Basic Latin + { @""", 34 }, + { @"&", 38 }, + { @"'", 39 }, + { @"<", 60 }, + { @">", 62 }, + + // Latin Extended-A + { @"Œ", 338 }, + { @"œ", 339 }, + { @"Š", 352 }, + { @"š", 353 }, + { @"Ÿ", 376 }, + + // Spacing Modifier Letters + { @"ˆ", 710 }, + { @"˜", 732 }, + + // General Punctuation + { @" ", 8194 }, + { @" ", 8195 }, + { @" ", 8201 }, + { @"‌", 8204 }, + { @"‍", 8205 }, + { @"‎", 8206 }, + { @"‏", 8207 }, + { @"–", 8211 }, + { @"—", 8212 }, + { @"‘", 8216 }, + { @"’", 8217 }, + { @"‚", 8218 }, + { @"“", 8220 }, + { @"”", 8221 }, + { @"„", 8222 }, + { @"†", 8224 }, + { @"‡", 8225 }, + { @"‰", 8240 }, + { @"‹", 8249 }, + { @"›", 8250 }, + { @"€", 8364 }, +}; + + +// Utility function for Bsearching table above +static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) { + const unichar *uchar = (const unichar*)ucharVoid; + const HTMLEscapeMap *map = (const HTMLEscapeMap*)mapVoid; + int val; + if (*uchar > map->uchar) { + val = 1; + } else if (*uchar < map->uchar) { + val = -1; + } else { + val = 0; + } + return val; +} + +@implementation NSString (GTMNSStringHTMLAdditions) + +- (NSString *)gtm_stringByEscapingHTMLUsingTable:(HTMLEscapeMap*)table + ofSize:(NSUInteger)size + escapingUnicode:(BOOL)escapeUnicode { + NSUInteger length = [self length]; + if (!length) { + return self; + } + + NSMutableString *finalString = [NSMutableString string]; + NSMutableData *data2 = [NSMutableData dataWithCapacity:sizeof(unichar) * length]; + + // this block is common between GTMNSString+HTML and GTMNSString+XML but + // it's so short that it isn't really worth trying to share. + const unichar *buffer = CFStringGetCharactersPtr((CFStringRef)self); + if (!buffer) { + // We want this buffer to be autoreleased. + NSMutableData *data = [NSMutableData dataWithLength:length * sizeof(UniChar)]; + if (!data) { + // COV_NF_START - Memory fail case + _GTMDevLog(@"couldn't alloc buffer"); + return nil; + // COV_NF_END + } + [self getCharacters:[data mutableBytes]]; + buffer = [data bytes]; + } + + if (!buffer || !data2) { + // COV_NF_START + _GTMDevLog(@"Unable to allocate buffer or data2"); + return nil; + // COV_NF_END + } + + unichar *buffer2 = (unichar *)[data2 mutableBytes]; + + NSUInteger buffer2Length = 0; + + for (NSUInteger i = 0; i < length; ++i) { + HTMLEscapeMap *val = bsearch(&buffer[i], table, + size / sizeof(HTMLEscapeMap), + sizeof(HTMLEscapeMap), EscapeMapCompare); + if (val || (escapeUnicode && buffer[i] > 127)) { + if (buffer2Length) { + CFStringAppendCharacters((CFMutableStringRef)finalString, + buffer2, + buffer2Length); + buffer2Length = 0; + } + if (val) { + [finalString appendString:val->escapeSequence]; + } + else { + _GTMDevAssert(escapeUnicode && buffer[i] > 127, @"Illegal Character"); + [finalString appendFormat:@"&#%d;", buffer[i]]; + } + } else { + buffer2[buffer2Length] = buffer[i]; + buffer2Length += 1; + } + } + if (buffer2Length) { + CFStringAppendCharacters((CFMutableStringRef)finalString, + buffer2, + buffer2Length); + } + return finalString; +} + +- (NSString *)gtm_stringByEscapingForHTML { + return [self gtm_stringByEscapingHTMLUsingTable:gUnicodeHTMLEscapeMap + ofSize:sizeof(gUnicodeHTMLEscapeMap) + escapingUnicode:NO]; +} // gtm_stringByEscapingHTML + +- (NSString *)gtm_stringByEscapingForAsciiHTML { + return [self gtm_stringByEscapingHTMLUsingTable:gAsciiHTMLEscapeMap + ofSize:sizeof(gAsciiHTMLEscapeMap) + escapingUnicode:YES]; +} // gtm_stringByEscapingAsciiHTML + +- (NSString *)gtm_stringByUnescapingFromHTML { + NSRange range = NSMakeRange(0, [self length]); + NSRange subrange = [self rangeOfString:@"&" options:NSBackwardsSearch range:range]; + + // if no ampersands, we've got a quick way out + if (subrange.length == 0) return self; + NSMutableString *finalString = [NSMutableString stringWithString:self]; + do { + NSRange semiColonRange = NSMakeRange(subrange.location, NSMaxRange(range) - subrange.location); + semiColonRange = [self rangeOfString:@";" options:0 range:semiColonRange]; + range = NSMakeRange(0, subrange.location); + // if we don't find a semicolon in the range, we don't have a sequence + if (semiColonRange.location == NSNotFound) { + continue; + } + NSRange escapeRange = NSMakeRange(subrange.location, semiColonRange.location - subrange.location + 1); + NSString *escapeString = [self substringWithRange:escapeRange]; + NSUInteger length = [escapeString length]; + // a squence must be longer than 3 (<) and less than 11 (ϑ) + if (length > 3 && length < 11) { + if ([escapeString characterAtIndex:1] == '#') { + unichar char2 = [escapeString characterAtIndex:2]; + if (char2 == 'x' || char2 == 'X') { + // Hex escape squences £ + NSString *hexSequence = [escapeString substringWithRange:NSMakeRange(3, length - 4)]; + NSScanner *scanner = [NSScanner scannerWithString:hexSequence]; + unsigned value; + if ([scanner scanHexInt:&value] && + value < USHRT_MAX && + value > 0 + && [scanner scanLocation] == length - 4) { + unichar uchar = (unichar)value; + NSString *charString = [NSString stringWithCharacters:&uchar length:1]; + [finalString replaceCharactersInRange:escapeRange withString:charString]; + } + + } else { + // Decimal Sequences { + NSString *numberSequence = [escapeString substringWithRange:NSMakeRange(2, length - 3)]; + NSScanner *scanner = [NSScanner scannerWithString:numberSequence]; + int value; + if ([scanner scanInt:&value] && + value < USHRT_MAX && + value > 0 + && [scanner scanLocation] == length - 3) { + unichar uchar = (unichar)value; + NSString *charString = [NSString stringWithCharacters:&uchar length:1]; + [finalString replaceCharactersInRange:escapeRange withString:charString]; + } + } + } else { + // "standard" sequences + for (unsigned i = 0; i < sizeof(gAsciiHTMLEscapeMap) / sizeof(HTMLEscapeMap); ++i) { + if ([escapeString isEqualToString:gAsciiHTMLEscapeMap[i].escapeSequence]) { + [finalString replaceCharactersInRange:escapeRange withString:[NSString stringWithCharacters:&gAsciiHTMLEscapeMap[i].uchar length:1]]; + break; + } + } + } + } + } while ((subrange = [self rangeOfString:@"&" options:NSBackwardsSearch range:range]).length != 0); + return finalString; +} // gtm_stringByUnescapingHTML + + + +@end diff --git a/Vendors/ODRefreshControl/ODRefreshControl.h b/Vendors/ODRefreshControl/ODRefreshControl.h new file mode 100755 index 0000000..ef518b1 --- /dev/null +++ b/Vendors/ODRefreshControl/ODRefreshControl.h @@ -0,0 +1,44 @@ +// +// ODRefreshControl.h +// ODRefreshControl +// +// Created by Fabio Ritrovato on 6/13/12. +// Copyright (c) 2012 orange in a day. All rights reserved. +// +// https://github.com/Sephiroth87/ODRefreshControl +// + +#import +#import + +@interface ODRefreshControl : UIControl { + CAShapeLayer *_shapeLayer; + CAShapeLayer *_arrowLayer; + CAShapeLayer *_highlightLayer; + UIView *_activity; + BOOL _refreshing; + BOOL _canRefresh; + BOOL _ignoreInset; + BOOL _ignoreOffset; + BOOL _didSetInset; + BOOL _hasSectionHeaders; + CGFloat _lastOffset; +} + +@property (nonatomic, readonly) BOOL refreshing; +@property (nonatomic, strong) UIColor *tintColor; +@property (nonatomic, assign) UIActivityIndicatorViewStyle activityIndicatorViewStyle; +@property (nonatomic, strong) UIColor *activityIndicatorViewColor; // iOS5 or more + +- (id)initInScrollView:(UIScrollView *)scrollView; + +// use custom activity indicator +- (id)initInScrollView:(UIScrollView *)scrollView activityIndicatorView:(UIView *)activity; + +// Tells the control that a refresh operation was started programmatically +- (void)beginRefreshing; + +// Tells the control the refresh operation has ended +- (void)endRefreshing; + +@end diff --git a/Vendors/ODRefreshControl/ODRefreshControl.m b/Vendors/ODRefreshControl/ODRefreshControl.m new file mode 100755 index 0000000..abb7cda --- /dev/null +++ b/Vendors/ODRefreshControl/ODRefreshControl.m @@ -0,0 +1,457 @@ +// +// ODRefreshControl.m +// ODRefreshControl +// +// Created by Fabio Ritrovato on 6/13/12. +// Copyright (c) 2012 orange in a day. All rights reserved. +// +// https://github.com/Sephiroth87/ODRefreshControl +// + +#import "ODRefreshControl.h" + +#define kTotalViewHeight 400 +#define kOpenedViewHeight 44 +#define kMinTopPadding 9 +#define kMaxTopPadding 5 +#define kMinTopRadius 12.5 +#define kMaxTopRadius 16 +#define kMinBottomRadius 3 +#define kMaxBottomRadius 16 +#define kMinBottomPadding 4 +#define kMaxBottomPadding 6 +#define kMinArrowSize 2 +#define kMaxArrowSize 3 +#define kMinArrowRadius 5 +#define kMaxArrowRadius 7 +#define kMaxDistance 53 + +@interface ODRefreshControl () + +@property (nonatomic, readwrite) BOOL refreshing; +@property (nonatomic, assign) UIScrollView *scrollView; +@property (nonatomic, assign) UIEdgeInsets originalContentInset; + +@end + +@implementation ODRefreshControl + +@synthesize refreshing = _refreshing; +@synthesize tintColor = _tintColor; + +@synthesize scrollView = _scrollView; +@synthesize originalContentInset = _originalContentInset; + +static inline CGFloat lerp(CGFloat a, CGFloat b, CGFloat p) +{ + return a + (b - a) * p; +} + +- (id)initInScrollView:(UIScrollView *)scrollView { + return [self initInScrollView:scrollView activityIndicatorView:nil]; +} + +- (id)initInScrollView:(UIScrollView *)scrollView activityIndicatorView:(UIView *)activity +{ + self = [super initWithFrame:CGRectMake(0, -(kTotalViewHeight + scrollView.contentInset.top), scrollView.frame.size.width, kTotalViewHeight)]; + + if (self) { + self.scrollView = scrollView; + self.originalContentInset = scrollView.contentInset; + + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [scrollView addSubview:self]; + [scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; + [scrollView addObserver:self forKeyPath:@"contentInset" options:NSKeyValueObservingOptionNew context:nil]; + + _activity = activity ? activity : [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activity.center = CGPointMake(floor(self.frame.size.width / 2), floor(self.frame.size.height / 2)); + _activity.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + _activity.alpha = 0; + if ([_activity respondsToSelector:@selector(startAnimating)]) { + [(UIActivityIndicatorView *)_activity startAnimating]; + } + [self addSubview:_activity]; + + _refreshing = NO; + _canRefresh = YES; + _ignoreInset = NO; + _ignoreOffset = NO; + _didSetInset = NO; + _hasSectionHeaders = NO; + _tintColor = [UIColor colorWithRed:155.0 / 255.0 green:162.0 / 255.0 blue:172.0 / 255.0 alpha:1.0]; + + _shapeLayer = [CAShapeLayer layer]; + _shapeLayer.fillColor = [_tintColor CGColor]; + _shapeLayer.strokeColor = [[[UIColor darkGrayColor] colorWithAlphaComponent:0.5] CGColor]; + _shapeLayer.lineWidth = 0.5; + _shapeLayer.shadowColor = [[UIColor blackColor] CGColor]; + _shapeLayer.shadowOffset = CGSizeMake(0, 0.5); + _shapeLayer.shadowOpacity = 0.2; + _shapeLayer.shadowRadius = 1.0; + [self.layer addSublayer:_shapeLayer]; + + _arrowLayer = [CAShapeLayer layer]; + _arrowLayer.strokeColor = [[[UIColor darkGrayColor] colorWithAlphaComponent:0.5] CGColor]; + _arrowLayer.lineWidth = 0.1; + _arrowLayer.fillColor = [[[UIColor darkGrayColor] colorWithAlphaComponent:0.3] CGColor]; + [_shapeLayer addSublayer:_arrowLayer]; + + _highlightLayer = [CAShapeLayer layer]; + _highlightLayer.fillColor = [[[UIColor whiteColor] colorWithAlphaComponent:0.2] CGColor]; + [_shapeLayer addSublayer:_highlightLayer]; + } + return self; +} + +- (void)dealloc +{ + [self.scrollView removeObserver:self forKeyPath:@"contentOffset"]; + [self.scrollView removeObserver:self forKeyPath:@"contentInset"]; + self.scrollView = nil; +} + +- (void)setEnabled:(BOOL)enabled +{ + super.enabled = enabled; + _shapeLayer.hidden = !self.enabled; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + [super willMoveToSuperview:newSuperview]; + if (!newSuperview) { + [self.scrollView removeObserver:self forKeyPath:@"contentOffset"]; + [self.scrollView removeObserver:self forKeyPath:@"contentInset"]; + self.scrollView = nil; + } +} + +- (void)setTintColor:(UIColor *)tintColor +{ + _tintColor = tintColor; + _shapeLayer.fillColor = [_tintColor CGColor]; +} + +- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle +{ + if ([_activity isKindOfClass:[UIActivityIndicatorView class]]) { + [(UIActivityIndicatorView *)_activity setActivityIndicatorViewStyle:activityIndicatorViewStyle]; + } +} + +- (UIActivityIndicatorViewStyle)activityIndicatorViewStyle +{ + if ([_activity isKindOfClass:[UIActivityIndicatorView class]]) { + return [(UIActivityIndicatorView *)_activity activityIndicatorViewStyle]; + } + return 0; +} + +- (void)setActivityIndicatorViewColor:(UIColor *)activityIndicatorViewColor +{ + if ([_activity isKindOfClass:[UIActivityIndicatorView class]] && [_activity respondsToSelector:@selector(setColor:)]) { + [(UIActivityIndicatorView *)_activity setColor:activityIndicatorViewColor]; + } +} + +- (UIColor *)activityIndicatorViewColor +{ + if ([_activity isKindOfClass:[UIActivityIndicatorView class]] && [_activity respondsToSelector:@selector(color)]) { + return [(UIActivityIndicatorView *)_activity color]; + } + return nil; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:@"contentInset"]) { + if (!_ignoreInset) { + self.originalContentInset = [[change objectForKey:@"new"] UIEdgeInsetsValue]; + self.frame = CGRectMake(0, -(kTotalViewHeight + self.scrollView.contentInset.top), self.scrollView.frame.size.width, kTotalViewHeight); + } + return; + } + + if (!self.enabled || _ignoreOffset) { + return; + } + + CGFloat offset = [[change objectForKey:@"new"] CGPointValue].y + self.originalContentInset.top; + + if (_refreshing) { + if (offset != 0) { + // Keep thing pinned at the top + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + _shapeLayer.position = CGPointMake(0, kMaxDistance + offset + kOpenedViewHeight); + [CATransaction commit]; + + _activity.center = CGPointMake(floor(self.frame.size.width / 2), MIN(offset + self.frame.size.height + floor(kOpenedViewHeight / 2), self.frame.size.height - kOpenedViewHeight/ 2)); + + _ignoreInset = YES; + _ignoreOffset = YES; + + if (offset < 0) { + // Set the inset depending on the situation + if (offset >= -kOpenedViewHeight) { + if (!self.scrollView.dragging) { + if (!_didSetInset) { + _didSetInset = YES; + _hasSectionHeaders = NO; + if([self.scrollView isKindOfClass:[UITableView class]]){ + for (int i = 0; i < [(UITableView *)self.scrollView numberOfSections]; ++i) { + if ([(UITableView *)self.scrollView rectForHeaderInSection:i].size.height) { + _hasSectionHeaders = YES; + break; + } + } + } + } + if (_hasSectionHeaders) { + [self.scrollView setContentInset:UIEdgeInsetsMake(MIN(-offset, kOpenedViewHeight) + self.originalContentInset.top, self.originalContentInset.left, self.originalContentInset.bottom, self.originalContentInset.right)]; + } else { + [self.scrollView setContentInset:UIEdgeInsetsMake(kOpenedViewHeight + self.originalContentInset.top, self.originalContentInset.left, self.originalContentInset.bottom, self.originalContentInset.right)]; + } + } else if (_didSetInset && _hasSectionHeaders) { + [self.scrollView setContentInset:UIEdgeInsetsMake(-offset + self.originalContentInset.top, self.originalContentInset.left, self.originalContentInset.bottom, self.originalContentInset.right)]; + } + } + } else if (_hasSectionHeaders) { + [self.scrollView setContentInset:self.originalContentInset]; + } + _ignoreInset = NO; + _ignoreOffset = NO; + } + return; + } else { + // Check if we can trigger a new refresh and if we can draw the control + BOOL dontDraw = NO; + if (!_canRefresh) { + if (offset >= 0) { + // We can refresh again after the control is scrolled out of view + _canRefresh = YES; + _didSetInset = NO; + } else { + dontDraw = YES; + } + } else { + if (offset >= 0) { + // Don't draw if the control is not visible + dontDraw = YES; + } + } + if (offset > 0 && _lastOffset > offset && !self.scrollView.isTracking) { + // If we are scrolling too fast, don't draw, and don't trigger unless the scrollView bounced back + _canRefresh = NO; + dontDraw = YES; + } + if (dontDraw) { + _shapeLayer.path = nil; + _shapeLayer.shadowPath = nil; + _arrowLayer.path = nil; + _highlightLayer.path = nil; + _lastOffset = offset; + return; + } + } + + _lastOffset = offset; + + BOOL triggered = NO; + + CGMutablePathRef path = CGPathCreateMutable(); + + //Calculate some useful points and values + CGFloat verticalShift = MAX(0, -((kMaxTopRadius + kMaxBottomRadius + kMaxTopPadding + kMaxBottomPadding) + offset)); + CGFloat distance = MIN(kMaxDistance, fabs(verticalShift)); + CGFloat percentage = 1 - (distance / kMaxDistance); + + CGFloat currentTopPadding = lerp(kMinTopPadding, kMaxTopPadding, percentage); + CGFloat currentTopRadius = lerp(kMinTopRadius, kMaxTopRadius, percentage); + CGFloat currentBottomRadius = lerp(kMinBottomRadius, kMaxBottomRadius, percentage); + CGFloat currentBottomPadding = lerp(kMinBottomPadding, kMaxBottomPadding, percentage); + + CGPoint bottomOrigin = CGPointMake(floor(self.bounds.size.width / 2), self.bounds.size.height - currentBottomPadding -currentBottomRadius); + CGPoint topOrigin = CGPointZero; + if (distance == 0) { + topOrigin = CGPointMake(floor(self.bounds.size.width / 2), bottomOrigin.y); + } else { + topOrigin = CGPointMake(floor(self.bounds.size.width / 2), self.bounds.size.height + offset + currentTopPadding + currentTopRadius); + if (percentage == 0) { + bottomOrigin.y -= (fabs(verticalShift) - kMaxDistance); + triggered = YES; + } + } + + //Top semicircle + CGPathAddArc(path, NULL, topOrigin.x, topOrigin.y, currentTopRadius, 0, M_PI, YES); + + //Left curve + CGPoint leftCp1 = CGPointMake(lerp((topOrigin.x - currentTopRadius), (bottomOrigin.x - currentBottomRadius), 0.1), lerp(topOrigin.y, bottomOrigin.y, 0.2)); + CGPoint leftCp2 = CGPointMake(lerp((topOrigin.x - currentTopRadius), (bottomOrigin.x - currentBottomRadius), 0.9), lerp(topOrigin.y, bottomOrigin.y, 0.2)); + CGPoint leftDestination = CGPointMake(bottomOrigin.x - currentBottomRadius, bottomOrigin.y); + + CGPathAddCurveToPoint(path, NULL, leftCp1.x, leftCp1.y, leftCp2.x, leftCp2.y, leftDestination.x, leftDestination.y); + + //Bottom semicircle + CGPathAddArc(path, NULL, bottomOrigin.x, bottomOrigin.y, currentBottomRadius, M_PI, 0, YES); + + //Right curve + CGPoint rightCp2 = CGPointMake(lerp((topOrigin.x + currentTopRadius), (bottomOrigin.x + currentBottomRadius), 0.1), lerp(topOrigin.y, bottomOrigin.y, 0.2)); + CGPoint rightCp1 = CGPointMake(lerp((topOrigin.x + currentTopRadius), (bottomOrigin.x + currentBottomRadius), 0.9), lerp(topOrigin.y, bottomOrigin.y, 0.2)); + CGPoint rightDestination = CGPointMake(topOrigin.x + currentTopRadius, topOrigin.y); + + CGPathAddCurveToPoint(path, NULL, rightCp1.x, rightCp1.y, rightCp2.x, rightCp2.y, rightDestination.x, rightDestination.y); + CGPathCloseSubpath(path); + + if (!triggered) { + // Set paths + + _shapeLayer.path = path; + _shapeLayer.shadowPath = path; + + // Add the arrow shape + + CGFloat currentArrowSize = lerp(kMinArrowSize, kMaxArrowSize, percentage); + CGFloat currentArrowRadius = lerp(kMinArrowRadius, kMaxArrowRadius, percentage); + CGFloat arrowBigRadius = currentArrowRadius + (currentArrowSize / 2); + CGFloat arrowSmallRadius = currentArrowRadius - (currentArrowSize / 2); + CGMutablePathRef arrowPath = CGPathCreateMutable(); + CGPathAddArc(arrowPath, NULL, topOrigin.x, topOrigin.y, arrowBigRadius, 0, 3 * M_PI_2, NO); + CGPathAddLineToPoint(arrowPath, NULL, topOrigin.x, topOrigin.y - arrowBigRadius - currentArrowSize); + CGPathAddLineToPoint(arrowPath, NULL, topOrigin.x + (2 * currentArrowSize), topOrigin.y - arrowBigRadius + (currentArrowSize / 2)); + CGPathAddLineToPoint(arrowPath, NULL, topOrigin.x, topOrigin.y - arrowBigRadius + (2 * currentArrowSize)); + CGPathAddLineToPoint(arrowPath, NULL, topOrigin.x, topOrigin.y - arrowBigRadius + currentArrowSize); + CGPathAddArc(arrowPath, NULL, topOrigin.x, topOrigin.y, arrowSmallRadius, 3 * M_PI_2, 0, YES); + CGPathCloseSubpath(arrowPath); + _arrowLayer.path = arrowPath; + [_arrowLayer setFillRule:kCAFillRuleEvenOdd]; + CGPathRelease(arrowPath); + + // Add the highlight shape + + CGMutablePathRef highlightPath = CGPathCreateMutable(); + CGPathAddArc(highlightPath, NULL, topOrigin.x, topOrigin.y, currentTopRadius, 0, M_PI, YES); + CGPathAddArc(highlightPath, NULL, topOrigin.x, topOrigin.y + 1.25, currentTopRadius, M_PI, 0, NO); + + _highlightLayer.path = highlightPath; + [_highlightLayer setFillRule:kCAFillRuleNonZero]; + CGPathRelease(highlightPath); + + } else { + // Start the shape disappearance animation + + CGFloat radius = lerp(kMinBottomRadius, kMaxBottomRadius, 0.2); + CABasicAnimation *pathMorph = [CABasicAnimation animationWithKeyPath:@"path"]; + pathMorph.duration = 0.15; + pathMorph.fillMode = kCAFillModeForwards; + pathMorph.removedOnCompletion = NO; + CGMutablePathRef toPath = CGPathCreateMutable(); + CGPathAddArc(toPath, NULL, topOrigin.x, topOrigin.y, radius, 0, M_PI, YES); + CGPathAddCurveToPoint(toPath, NULL, topOrigin.x - radius, topOrigin.y, topOrigin.x - radius, topOrigin.y, topOrigin.x - radius, topOrigin.y); + CGPathAddArc(toPath, NULL, topOrigin.x, topOrigin.y, radius, M_PI, 0, YES); + CGPathAddCurveToPoint(toPath, NULL, topOrigin.x + radius, topOrigin.y, topOrigin.x + radius, topOrigin.y, topOrigin.x + radius, topOrigin.y); + CGPathCloseSubpath(toPath); + pathMorph.toValue = (__bridge id)toPath; + [_shapeLayer addAnimation:pathMorph forKey:nil]; + CABasicAnimation *shadowPathMorph = [CABasicAnimation animationWithKeyPath:@"shadowPath"]; + shadowPathMorph.duration = 0.15; + shadowPathMorph.fillMode = kCAFillModeForwards; + shadowPathMorph.removedOnCompletion = NO; + shadowPathMorph.toValue = (__bridge id)toPath; + [_shapeLayer addAnimation:shadowPathMorph forKey:nil]; + CGPathRelease(toPath); + CABasicAnimation *shapeAlphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + shapeAlphaAnimation.duration = 0.1; + shapeAlphaAnimation.beginTime = CACurrentMediaTime() + 0.1; + shapeAlphaAnimation.toValue = [NSNumber numberWithFloat:0]; + shapeAlphaAnimation.fillMode = kCAFillModeForwards; + shapeAlphaAnimation.removedOnCompletion = NO; + [_shapeLayer addAnimation:shapeAlphaAnimation forKey:nil]; + CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + alphaAnimation.duration = 0.1; + alphaAnimation.toValue = [NSNumber numberWithFloat:0]; + alphaAnimation.fillMode = kCAFillModeForwards; + alphaAnimation.removedOnCompletion = NO; + [_arrowLayer addAnimation:alphaAnimation forKey:nil]; + [_highlightLayer addAnimation:alphaAnimation forKey:nil]; + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + _activity.layer.transform = CATransform3DMakeScale(0.1, 0.1, 1); + [CATransaction commit]; + [UIView animateWithDuration:0.2 delay:0.15 options:UIViewAnimationOptionCurveLinear animations:^{ + _activity.alpha = 1; + _activity.layer.transform = CATransform3DMakeScale(1, 1, 1); + } completion:nil]; + + self.refreshing = YES; + _canRefresh = NO; + [self sendActionsForControlEvents:UIControlEventValueChanged]; + } + + CGPathRelease(path); +} + +- (void)beginRefreshing +{ + if (!_refreshing) { + CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + alphaAnimation.duration = 0.0001; + alphaAnimation.toValue = [NSNumber numberWithFloat:0]; + alphaAnimation.fillMode = kCAFillModeForwards; + alphaAnimation.removedOnCompletion = NO; + [_shapeLayer addAnimation:alphaAnimation forKey:nil]; + [_arrowLayer addAnimation:alphaAnimation forKey:nil]; + [_highlightLayer addAnimation:alphaAnimation forKey:nil]; + + _activity.alpha = 1; + _activity.layer.transform = CATransform3DMakeScale(1, 1, 1); + + CGPoint offset = self.scrollView.contentOffset; + _ignoreInset = YES; + [self.scrollView setContentInset:UIEdgeInsetsMake(kOpenedViewHeight + self.originalContentInset.top, self.originalContentInset.left, self.originalContentInset.bottom, self.originalContentInset.right)]; + _ignoreInset = NO; + [self.scrollView setContentOffset:offset animated:NO]; + + self.refreshing = YES; + _canRefresh = NO; + } +} + +- (void)endRefreshing +{ + if (_refreshing) { + self.refreshing = NO; + // Create a temporary retain-cycle, so the scrollView won't be released + // halfway through the end animation. + // This allows for the refresh control to clean up the observer, + // in the case the scrollView is released while the animation is running + __block UIScrollView *blockScrollView = self.scrollView; + [UIView animateWithDuration:0.4 animations:^{ + _ignoreInset = YES; + [blockScrollView setContentInset:self.originalContentInset]; + _ignoreInset = NO; + _activity.alpha = 0; + _activity.layer.transform = CATransform3DMakeScale(0.1, 0.1, 1); + } completion:^(BOOL finished) { + [_shapeLayer removeAllAnimations]; + _shapeLayer.path = nil; + _shapeLayer.shadowPath = nil; + _shapeLayer.position = CGPointZero; + [_arrowLayer removeAllAnimations]; + _arrowLayer.path = nil; + [_highlightLayer removeAllAnimations]; + _highlightLayer.path = nil; + // We need to use the scrollView somehow in the end block, + // or it'll get released in the animation block. + _ignoreInset = YES; + [blockScrollView setContentInset:self.originalContentInset]; + _ignoreInset = NO; + }]; + } +} + +@end diff --git a/Vendors/REComposeViewController/DEComposeRuledView.h b/Vendors/REComposeViewController/DEComposeRuledView.h new file mode 100755 index 0000000..f9f6f9e --- /dev/null +++ b/Vendors/REComposeViewController/DEComposeRuledView.h @@ -0,0 +1,26 @@ +// +// DERuledView.h +// DEFacebooker +// +// Copyright (c) 2011 Double Encore, Inc. 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 Double Encore Inc. 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 + +@interface DEComposeRuledView : UIView + +@property (nonatomic) CGFloat rowHeight; +@property (nonatomic) CGFloat lineWidth; +@property (nonatomic, retain) UIColor *lineColor; + +@end diff --git a/Vendors/REComposeViewController/DEComposeRuledView.m b/Vendors/REComposeViewController/DEComposeRuledView.m new file mode 100755 index 0000000..06238c3 --- /dev/null +++ b/Vendors/REComposeViewController/DEComposeRuledView.m @@ -0,0 +1,96 @@ +// +// DERuledView.m +// DEFacebooker +// +// Copyright (c) 2011 Double Encore, Inc. 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 Double Encore Inc. 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 "DEComposeRuledView.h" + + +@interface DEComposeRuledView () + +- (void)tweetRuledViewInit; + +@end + + +@implementation DEComposeRuledView + +@synthesize rowHeight = _rowHeight; +@synthesize lineWidth = _lineWidth; +@synthesize lineColor = _lineColor; + + +#pragma mark - Setup & Teardown + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self tweetRuledViewInit]; + } + + return self; +} + + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self tweetRuledViewInit]; + } + + return self; +} + + +- (void)tweetRuledViewInit +{ + self.backgroundColor = [UIColor clearColor]; + self.contentMode = UIViewContentModeRedraw; + self.userInteractionEnabled = NO; + + _rowHeight = 20.0f; + _lineWidth = 1.0f; + _lineColor = [UIColor colorWithWhite:0.5f alpha:0.15f]; +} + +#pragma mark - Superclass Overrides + +- (void)drawRect:(CGRect)rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor); + CGContextSetLineWidth(context, self.lineWidth); + CGFloat strokeOffset = (self.lineWidth / 2); // Because lines are drawn between pixels. This moves it back onto the pixel. + + if (self.rowHeight > 0.0f) { + CGRect rowRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, self.rowHeight); + NSInteger rowNumber = 1; + while (rowRect.origin.y < self.frame.size.height + 100.0f) { + CGContextMoveToPoint(context, rowRect.origin.x + strokeOffset, rowRect.origin.y + strokeOffset); + CGContextAddLineToPoint(context, rowRect.origin.x + rowRect.size.width + strokeOffset, rowRect.origin.y + strokeOffset); + CGContextDrawPath(context, kCGPathStroke); + + rowRect.origin.y += self.rowHeight; + rowNumber++; + } + } +} + + +@end diff --git a/Vendors/REComposeViewController/DEComposeTextView.h b/Vendors/REComposeViewController/DEComposeTextView.h new file mode 100755 index 0000000..6e93087 --- /dev/null +++ b/Vendors/REComposeViewController/DEComposeTextView.h @@ -0,0 +1,34 @@ +// +// DEFacebookTextView.h +// DEFacebooker +// +// Copyright (c) 2011 Double Encore, Inc. 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 Double Encore Inc. 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 + +@protocol DEComposeTextViewDelegate; + +@interface DEComposeTextView : UITextView + +@property (nonatomic, copy) NSString *accountName; +@property (nonatomic, readonly) CGRect fromButtonFrame; // So the popover can be displayed from this rect. + +@end + + +@protocol DEComposeTextViewDelegate + +- (void)textViewAccountButtonWasTouched:(DEComposeTextView *)textView; + +@end \ No newline at end of file diff --git a/Vendors/REComposeViewController/DEComposeTextView.m b/Vendors/REComposeViewController/DEComposeTextView.m new file mode 100755 index 0000000..b80aaec --- /dev/null +++ b/Vendors/REComposeViewController/DEComposeTextView.m @@ -0,0 +1,219 @@ +// +// DEFacebookTextView.m +// DEFacebooker +// +// Copyright (c) 2011 Double Encore, Inc. 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 Double Encore Inc. 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 "DEComposeTextView.h" +#import "DEComposeRuledView.h" + + +@interface DEComposeTextView () + +@property (nonatomic, retain) DEComposeRuledView *ruledView; +@property (nonatomic, retain) UIButton *fromButton; +@property (nonatomic, retain) UIButton *accountButton; +@property (nonatomic, retain) UIImageView *accountLine; + +- (void)textViewInit; +- (CGRect)ruledViewFrame; +- (void)updateAccountsView; + +@end + + +@implementation DEComposeTextView + + // Public +@synthesize accountName = _accountName; +@dynamic fromButtonFrame; + + // Private +@synthesize ruledView = _ruledView; +@synthesize fromButton = _fromButton; +@synthesize accountButton = _accountButton; +@synthesize accountLine = _accountLine; + + +#pragma mark - Setup & Teardown + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self textViewInit]; + } + + return self; +} + + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self textViewInit]; + } + + return self; +} + + +- (void)textViewInit +{ + self.clipsToBounds = NO; + + _ruledView = [[DEComposeRuledView alloc] initWithFrame:[self ruledViewFrame]]; + _ruledView.lineColor = [UIColor colorWithWhite:0.5f alpha:0.15f]; + _ruledView.lineWidth = 1.0f; + _ruledView.rowHeight = self.font.lineHeight; + [self insertSubview:self.ruledView atIndex:0]; +} + + +#pragma mark - Superclass Overrides + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if ([self.accountName length] > 0) { + CGRect frame = self.fromButton.frame; + frame.origin = CGPointMake(12.0f, -21.0f); + self.fromButton.frame = frame; + + frame = self.accountButton.frame; + frame.origin.x = CGRectGetMaxX(self.fromButton.frame) + 3.0f; + frame.origin.y = self.fromButton.frame.origin.y; + self.accountButton.frame = frame; + + frame = self.accountLine.frame; + frame.origin = CGPointMake(0.0f, CGRectGetMaxY(self.fromButton.frame) + 2.0f); + self.accountLine.frame = frame; + + self.contentInset = UIEdgeInsetsMake(25.0f, 0.0f, 0.0f, 0.0f); + } + + self.ruledView.frame = [self ruledViewFrame]; +} + + +- (void)setContentSize:(CGSize)contentSize +{ + [super setContentSize:contentSize]; + self.ruledView.frame = [self ruledViewFrame]; +} + + +- (void)setFont:(UIFont *)font +{ + [super setFont:font]; + self.ruledView.rowHeight = self.font.lineHeight; +} + + +#pragma mark - Private + +- (CGRect)ruledViewFrame +{ + CGFloat extraForBounce = 200.0f; // Extra added to top and bottom so it's visible when the user drags past the bounds. + CGFloat width = 1024.0f; // Needs to be at least as wide as we might make the Tweet sheet. + CGFloat textAlignmentOffset = -2.0f; // To center the text between the lines. May want to find a way to determine this procedurally eventually. + + CGRect frame; + if ([self.accountName length] > 0) { + frame = CGRectMake(0.0f, 30.0f, width, self.contentSize.height + extraForBounce); + } + else { + frame = CGRectMake(0.0f, -extraForBounce + textAlignmentOffset, width, self.contentSize.height + (2 * extraForBounce)); + } + + return frame; +} + + +- (void)updateAccountsView +{ + if ([self.accountName length] > 0) { + if (self.fromButton == nil) { + self.fromButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.fromButton addTarget:self action:@selector(accountButtonTouched) forControlEvents:UIControlEventTouchUpInside]; + [self.fromButton setTitle:NSLocalizedString(@"From:", @"") forState:UIControlStateNormal]; + self.fromButton.titleLabel.font = [UIFont systemFontOfSize:17.0f]; + [self.fromButton setTitleColor:[UIColor colorWithWhite:0.58f alpha:1.0f] forState:UIControlStateNormal]; + [self.fromButton setTitleColor:[UIColor lightTextColor] forState:UIControlStateHighlighted]; + [self.fromButton sizeToFit]; + [self addSubview:self.fromButton]; + } + if (self.accountButton == nil) { + self.accountButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.accountButton addTarget:self action:@selector(accountButtonTouched) forControlEvents:UIControlEventTouchUpInside]; + [self.accountButton setTitleColor:[UIColor darkTextColor] forState:UIControlStateNormal]; + [self.accountButton setTitleColor:[UIColor lightTextColor] forState:UIControlStateHighlighted]; + self.accountButton.titleLabel.font = [UIFont systemFontOfSize:17.0f]; + [self addSubview:self.accountButton]; + + self.accountLine = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"DEFacebookCardAccountLine"]]; + [self addSubview:self.accountLine]; + } + [self.accountButton setTitle:self.accountName forState:UIControlStateNormal]; + [self.accountButton sizeToFit]; + [self setNeedsLayout]; + } + + else { + [self.fromButton removeFromSuperview]; + self.fromButton = nil; + [self.accountButton removeFromSuperview]; + self.accountButton = nil; + } +} + + +#pragma mark - Actions + +- (IBAction)accountButtonTouched +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + + SEL tweetTextViewAccountButtonWasTouched = sel_registerName("tweetTextViewAccountButtonWasTouched:"); + + if ([self.delegate respondsToSelector:tweetTextViewAccountButtonWasTouched]) { + [self.delegate performSelector:tweetTextViewAccountButtonWasTouched withObject:self]; + } +#pragma clang diagnostic pop + +} + + +#pragma mark - Accessors + +- (void)setAccountName:(NSString *)name +{ + if ([_accountName isEqualToString:name] == NO) { + _accountName = [name copy]; + [self updateAccountsView]; + } +} + + +- (CGRect)fromButtonFrame +{ + return self.fromButton.frame; +} + + +@end diff --git a/Vendors/REComposeViewController/REComposeBackgroundView.h b/Vendors/REComposeViewController/REComposeBackgroundView.h new file mode 100755 index 0000000..1474515 --- /dev/null +++ b/Vendors/REComposeViewController/REComposeBackgroundView.h @@ -0,0 +1,25 @@ +// +// REComposeBackgroundView.h +// REComposeViewController +// +// Copyright (c) 2011 Double Encore, Inc. 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 Double Encore Inc. 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 + +@interface REComposeBackgroundView : UIView + +@property (nonatomic) CGSize centerOffset; // If this is CGSizeZero, the view's center is used. + +@end diff --git a/Vendors/REComposeViewController/REComposeBackgroundView.m b/Vendors/REComposeViewController/REComposeBackgroundView.m new file mode 100755 index 0000000..5c2d251 --- /dev/null +++ b/Vendors/REComposeViewController/REComposeBackgroundView.m @@ -0,0 +1,66 @@ +// +// REComposeBackgroundView.m +// REComposeViewController +// +// Copyright (c) 2011 Double Encore, Inc. 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 Double Encore Inc. 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 "REComposeBackgroundView.h" + +@implementation REComposeBackgroundView + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.opaque = NO; + self.contentMode = UIViewContentModeRedraw; + } + return self; +} + +- (void)drawRect:(CGRect)rect +{ + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + if (CGSizeEqualToSize(self.centerOffset, CGSizeZero) == NO) { + center.x += self.centerOffset.width; + center.y += self.centerOffset.height; + } + + CGContextRef currentContext = UIGraphicsGetCurrentContext(); + + size_t num_locations = 2; + CGFloat locations[2] = { 0.0, 1.0 }; + CGFloat components[8] = { 0.0, 0.0, 0.0, 0.7, // Start color + 0.0, 0.0, 0.0, 0.85 }; // End color + + CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations); + CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + CGFloat endRadius = [UIApplication sharedApplication].keyWindow.bounds.size.height / 2; + CGContextDrawRadialGradient(currentContext, gradient, center, 20.0f, center, endRadius, options); + + CGGradientRelease(gradient); + CGColorSpaceRelease(rgbColorspace); +} + +- (void)setCenterOffset:(CGSize)offset +{ + if (CGSizeEqualToSize(_centerOffset, offset) == NO) { + _centerOffset = offset; + [self setNeedsDisplay]; + } +} + +@end diff --git a/Vendors/REComposeViewController/REComposeSheetView.h b/Vendors/REComposeViewController/REComposeSheetView.h new file mode 100755 index 0000000..d1f54e7 --- /dev/null +++ b/Vendors/REComposeViewController/REComposeSheetView.h @@ -0,0 +1,50 @@ +// +// REComposeSheetView.h +// REComposeViewController +// +// Copyright (c) 2012 Roman Efimov (https://github.com/romaonthego) +// +// 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 "DEComposeTextView.h" + +@protocol REComposeSheetViewDelegate; + +@interface REComposeSheetView : UIView { + UIImageView *_attachmentContainerView; +} + +@property (readonly, nonatomic) UIView *attachmentView; +@property (readonly, nonatomic) UIImageView *attachmentImageView; +@property (strong, readwrite, nonatomic) UIViewController *delegate; +@property (readonly, nonatomic) UINavigationItem *navigationItem; +@property (readonly, nonatomic) UINavigationBar *navigationBar; +@property (readonly, nonatomic) UIView *textViewContainer; +@property (readonly, nonatomic) DEComposeTextView *textView; + +@end + +@protocol REComposeSheetViewDelegate + +- (void)cancelButtonPressed; +- (void)postButtonPressed; + +@end \ No newline at end of file diff --git a/Vendors/REComposeViewController/REComposeSheetView.m b/Vendors/REComposeViewController/REComposeSheetView.m new file mode 100755 index 0000000..9679beb --- /dev/null +++ b/Vendors/REComposeViewController/REComposeSheetView.m @@ -0,0 +1,104 @@ +// +// REComposeSheetView.m +// REComposeViewController +// +// Copyright (c) 2012 Roman Efimov (https://github.com/romaonthego) +// +// 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 "REComposeSheetView.h" +#import + +@implementation REComposeSheetView + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor whiteColor]; + + _navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, 44)]; + _navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth + | UIViewAutoresizingFlexibleBottomMargin + | UIViewAutoresizingFlexibleRightMargin; + + _navigationItem = [[UINavigationItem alloc] initWithTitle:@""]; + _navigationBar.items = @[_navigationItem]; + + UIBarButtonItem *cancelButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"REComposeSheetView_Cancel", nil, [NSBundle mainBundle], @"Cancel", @"Cancel") style:UIBarButtonItemStyleBordered target:self action:@selector(cancelButtonPressed)]; + _navigationItem.leftBarButtonItem = cancelButtonItem; + + UIBarButtonItem *postButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"REComposeSheetView_Post", nil, [NSBundle mainBundle], @"Post", @"Post") style:UIBarButtonItemStyleBordered target:self action:@selector(postButtonPressed)]; + _navigationItem.rightBarButtonItem = postButtonItem; + + + _textViewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 44, frame.size.width, frame.size.height - 44)]; + _textViewContainer.clipsToBounds = YES; + _textViewContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _textView = [[DEComposeTextView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height - 47)]; + _textView.backgroundColor = [UIColor whiteColor]; + _textView.font = [UIFont systemFontOfSize:17]; + _textView.contentInset = UIEdgeInsetsMake(0, 0, 20, 0); + _textView.bounces = YES; + + [_textViewContainer addSubview:_textView]; + [self addSubview:_textViewContainer]; + + _attachmentView = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width - 84, 54, 84, 79)]; + [self addSubview:_attachmentView]; + + _attachmentImageView = [[UIImageView alloc] initWithFrame:CGRectMake(6, 2, 72, 72)]; + _attachmentImageView.layer.cornerRadius = 3.0f; + _attachmentImageView.layer.masksToBounds = YES; + [_attachmentView addSubview:_attachmentImageView]; + + _attachmentContainerView = [[UIImageView alloc] initWithFrame:_attachmentView.bounds]; + _attachmentContainerView.image = [UIImage imageNamed:@"REComposeViewController.bundle/AttachmentFrame"]; + [_attachmentView addSubview:_attachmentContainerView]; + _attachmentView.hidden = YES; + + [self addSubview:_navigationBar]; + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + if (_delegate) { + _navigationItem.title = _delegate.title; + } +} + +- (void)cancelButtonPressed +{ + id local_delegate = _delegate; + if ([local_delegate respondsToSelector:@selector(cancelButtonPressed)]) + [local_delegate cancelButtonPressed]; +} + +- (void)postButtonPressed +{ + id local_delegate = _delegate; + if ([local_delegate respondsToSelector:@selector(postButtonPressed)]) + [local_delegate postButtonPressed]; +} + +@end diff --git a/Vendors/REComposeViewController/REComposeViewController.bundle/AttachmentFrame.png b/Vendors/REComposeViewController/REComposeViewController.bundle/AttachmentFrame.png new file mode 100755 index 0000000..a34d202 Binary files /dev/null and b/Vendors/REComposeViewController/REComposeViewController.bundle/AttachmentFrame.png differ diff --git a/Vendors/REComposeViewController/REComposeViewController.bundle/AttachmentFrame@2x.png b/Vendors/REComposeViewController/REComposeViewController.bundle/AttachmentFrame@2x.png new file mode 100755 index 0000000..72097d7 Binary files /dev/null and b/Vendors/REComposeViewController/REComposeViewController.bundle/AttachmentFrame@2x.png differ diff --git a/Vendors/REComposeViewController/REComposeViewController.bundle/PaperClip.png b/Vendors/REComposeViewController/REComposeViewController.bundle/PaperClip.png new file mode 100755 index 0000000..5fa2e11 Binary files /dev/null and b/Vendors/REComposeViewController/REComposeViewController.bundle/PaperClip.png differ diff --git a/Vendors/REComposeViewController/REComposeViewController.bundle/PaperClip@2x.png b/Vendors/REComposeViewController/REComposeViewController.bundle/PaperClip@2x.png new file mode 100755 index 0000000..0b4e8f3 Binary files /dev/null and b/Vendors/REComposeViewController/REComposeViewController.bundle/PaperClip@2x.png differ diff --git a/Vendors/REComposeViewController/REComposeViewController.bundle/URLAttachment.png b/Vendors/REComposeViewController/REComposeViewController.bundle/URLAttachment.png new file mode 100755 index 0000000..4d617b9 Binary files /dev/null and b/Vendors/REComposeViewController/REComposeViewController.bundle/URLAttachment.png differ diff --git a/Vendors/REComposeViewController/REComposeViewController.bundle/URLAttachment@2x.png b/Vendors/REComposeViewController/REComposeViewController.bundle/URLAttachment@2x.png new file mode 100755 index 0000000..6b1da73 Binary files /dev/null and b/Vendors/REComposeViewController/REComposeViewController.bundle/URLAttachment@2x.png differ diff --git a/Vendors/REComposeViewController/REComposeViewController.h b/Vendors/REComposeViewController/REComposeViewController.h new file mode 100755 index 0000000..2f532a7 --- /dev/null +++ b/Vendors/REComposeViewController/REComposeViewController.h @@ -0,0 +1,73 @@ +// +// REComposeViewController.h +// REComposeViewController +// +// Copyright (c) 2012 Roman Efimov (https://github.com/romaonthego) +// +// 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 "REComposeSheetView.h" +#import "REComposeBackgroundView.h" + +enum REComposeResult { + REComposeResultCancelled, + REComposeResultPosted +}; +typedef enum REComposeResult REComposeResult; + +@class REComposeViewController; + +typedef void (^REComposeViewControllerCompletionHandler)(REComposeViewController *composeViewController, REComposeResult result); + +@protocol REComposeViewControllerDelegate; + +@interface REComposeViewController : UIViewController { + REComposeSheetView *_sheetView; + REComposeBackgroundView *_backgroundView; + UIView *_backView; + UIView *_containerView; + UIImageView *_paperclipView; + BOOL _hasAttachment; + UIImage *_attachmentImage; +} + +- (UINavigationItem *)navigationItem; +- (UINavigationBar *)navigationBar; +- (NSString *)text; +- (void)setText:(NSString *)text; + +- (BOOL)hasAttachment; +- (void)setHasAttachment:(BOOL)hasAttachment; + +- (UIImage *)attachmentImage; +- (void)setAttachmentImage:(UIImage *)attachmentImage; + +@property (copy, nonatomic) REComposeViewControllerCompletionHandler completionHandler; +@property (weak, nonatomic) id delegate; +@property (assign, readwrite, nonatomic) NSInteger cornerRadius; + +@end + +@protocol REComposeViewControllerDelegate + +- (void)composeViewController:(REComposeViewController *)composeViewController didFinishWithResult:(REComposeResult)result; + +@end diff --git a/Vendors/REComposeViewController/REComposeViewController.m b/Vendors/REComposeViewController/REComposeViewController.m new file mode 100755 index 0000000..8bef451 --- /dev/null +++ b/Vendors/REComposeViewController/REComposeViewController.m @@ -0,0 +1,289 @@ +// +// REComposeViewController.m +// REComposeViewController +// +// Copyright (c) 2012 Roman Efimov (https://github.com/romaonthego) +// +// 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 "REComposeViewController.h" +#import + +@interface REComposeViewController () + +@end + +@implementation REComposeViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + _cornerRadius = 10; + _sheetView = [[REComposeSheetView alloc] initWithFrame:CGRectMake(0, 0, self.currentWidth - 8, 202)]; + } + return self; +} + +- (int)currentWidth +{ + UIScreen *screen = [UIScreen mainScreen]; + return (!UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) ? screen.bounds.size.width : screen.bounds.size.height; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + _backgroundView = [[REComposeBackgroundView alloc] initWithFrame:self.view.bounds]; + _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _backgroundView.centerOffset = CGSizeMake(0, - self.view.frame.size.height / 2); + _backgroundView.alpha = 0; + + + _containerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 202)]; + _containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _backView = [[UIView alloc] initWithFrame:CGRectMake(4, 0, self.currentWidth - 8, 202)]; + _backView.layer.cornerRadius = _cornerRadius; + _backView.layer.shadowOpacity = 0.7; + _backView.layer.shadowColor = [UIColor blackColor].CGColor; + _backView.layer.shadowOffset = CGSizeMake(3, 5); + _backView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:_backView.bounds] CGPath]; + + _sheetView.frame = _backView.bounds; + _sheetView.layer.cornerRadius = _cornerRadius; + _sheetView.clipsToBounds = YES; + _sheetView.delegate = self; + + [self.view addSubview:_backgroundView]; + [_containerView addSubview:_backView]; + [self.view addSubview:_containerView]; + [_backView addSubview:_sheetView]; + + _paperclipView = [[UIImageView alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 77, 60, 79, 34)]; + _paperclipView.image = [UIImage imageNamed:@"REComposeViewController.bundle/PaperClip"]; + [_containerView addSubview:_paperclipView]; + [_paperclipView setHidden:YES]; + + if (!_attachmentImage) + _attachmentImage = [UIImage imageNamed:@"REComposeViewController.bundle/URLAttachment"]; + + _sheetView.attachmentImageView.image = _attachmentImage; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + [_sheetView.textView becomeFirstResponder]; + + [UIView animateWithDuration:0.4 animations:^{ + if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) { + [self layoutWithOrientation:self.interfaceOrientation width:self.view.frame.size.height height:self.view.frame.size.width]; + } else { + [self layoutWithOrientation:self.interfaceOrientation width:self.view.frame.size.width height:self.view.frame.size.height]; + } + }]; + + [UIView animateWithDuration:0.4 + delay:0.1 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + _backgroundView.alpha = 1; + } completion:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewOrientationDidChanged:) name:UIDeviceOrientationDidChangeNotification object:nil]; + +} + +- (void) viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear: animated]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; +} + +- (void)layoutWithOrientation:(UIInterfaceOrientation)interfaceOrientation width:(NSInteger)width height:(NSInteger)height +{ + NSInteger offset = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 60 : 4; + if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) { + CGRect frame = _containerView.frame; + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + offset *= 2; + } + + NSInteger verticalOffset = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 316 : 216; + + NSInteger containerWidth = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? _containerView.frame.size.height : _containerView.frame.size.width; + frame.origin.y = (width - verticalOffset - containerWidth) / 2; + if (frame.origin.y < 0) frame.origin.y = 0; + _containerView.frame = frame; + + _containerView.clipsToBounds = YES; + _backView.frame = CGRectMake(offset, 0, height - offset*2, UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 202 : 140); + _sheetView.frame = _backView.bounds; + + CGRect paperclipFrame = _paperclipView.frame; + paperclipFrame.origin.x = height - 73 - offset; + _paperclipView.frame = paperclipFrame; + } else { + CGRect frame = _containerView.frame; + frame.origin.y = (height - 216 - _containerView.frame.size.height) / 2; + if (frame.origin.y < 0) frame.origin.y = 0; + _containerView.frame = frame; + _backView.frame = CGRectMake(offset, 0, width - offset*2, 202); + _sheetView.frame = _backView.bounds; + + CGRect paperclipFrame = _paperclipView.frame; + paperclipFrame.origin.x = width - 73 - offset; + _paperclipView.frame = paperclipFrame; + } + + _paperclipView.hidden = !_hasAttachment; + _sheetView.attachmentView.hidden = !_hasAttachment; + + [_sheetView.navigationBar sizeToFit]; + + CGRect attachmentViewFrame = _sheetView.attachmentView.frame; + attachmentViewFrame.origin.x = _sheetView.frame.size.width - 84; + attachmentViewFrame.origin.y = _sheetView.navigationBar.frame.size.height + 10; + _sheetView.attachmentView.frame = attachmentViewFrame; + + CGRect textViewFrame = _sheetView.textView.frame; + textViewFrame.size.width = !_hasAttachment ? _sheetView.frame.size.width : _sheetView.frame.size.width - 84; + _sheetView.textView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, _hasAttachment ? -85 : 0); + textViewFrame.size.height = _sheetView.frame.size.height - _sheetView.navigationBar.frame.size.height - 3; + _sheetView.textView.frame = textViewFrame; + + CGRect textViewContainerFrame = _sheetView.textViewContainer.frame; + textViewContainerFrame.origin.y = _sheetView.navigationBar.frame.size.height; + textViewContainerFrame.size.height = _sheetView.frame.size.height - _sheetView.navigationBar.frame.size.height; + _sheetView.textViewContainer.frame = textViewContainerFrame; +} + +- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion +{ + [_sheetView.textView resignFirstResponder]; + [UIView animateWithDuration:0.4 animations:^{ + CGRect frame = _containerView.frame; + frame.origin.y = self.view.frame.size.height; + _containerView.frame = frame; + }]; + + [UIView animateWithDuration:0.4 + delay:0.1 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + _backgroundView.alpha = 0; + } completion:^(BOOL finished) { + [super dismissViewControllerAnimated:NO completion:nil]; + }]; +} + +#pragma mark - +#pragma mark Accessors + +- (UINavigationItem *)navigationItem +{ + return _sheetView.navigationItem; +} + +- (UINavigationBar *)navigationBar +{ + return _sheetView.navigationBar; +} + +- (UIImage *)attachmentImage +{ + return _attachmentImage; +} + +- (void)setAttachmentImage:(UIImage *)attachmentImage +{ + _attachmentImage = attachmentImage; + _sheetView.attachmentImageView.image = _attachmentImage; +} + +- (BOOL)hasAttachment +{ + return _hasAttachment; +} + +- (void)setHasAttachment:(BOOL)hasAttachment +{ + _hasAttachment = hasAttachment; +} + +- (NSString *)text +{ + return _sheetView.textView.text; +} + +- (void)setText:(NSString *)text +{ + _sheetView.textView.text = text; +} + +#pragma mark - +#pragma mark REComposeSheetViewDelegate + +- (void)cancelButtonPressed +{ + id localDelegate = _delegate; + if (localDelegate && [localDelegate respondsToSelector:@selector(composeViewController:didFinishWithResult:)]) { + [localDelegate composeViewController:self didFinishWithResult:REComposeResultCancelled]; + } + if (_completionHandler) + _completionHandler(self, REComposeResultCancelled); +} + +- (void)postButtonPressed +{ + id localDelegate = _delegate; + if (localDelegate && [localDelegate respondsToSelector:@selector(composeViewController:didFinishWithResult:)]) { + [localDelegate composeViewController:self didFinishWithResult:REComposeResultPosted]; + } + if (_completionHandler) + _completionHandler(self, REComposeResultPosted); +} + +#pragma mark - +#pragma mark Orientation + +- (NSUInteger)supportedInterfaceOrientations +{ + return UIInterfaceOrientationMaskAll; +} + +- (BOOL)shouldAutorotate +{ + return YES; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation +{ + return YES; +} + +- (void)viewOrientationDidChanged:(NSNotification *)notification +{ + [self layoutWithOrientation:self.interfaceOrientation width:self.view.frame.size.width height:self.view.frame.size.height]; +} + +@end diff --git a/Vendors/SVWebViewController/SVModalWebViewController.h b/Vendors/SVWebViewController/SVModalWebViewController.h new file mode 100755 index 0000000..2f7a072 --- /dev/null +++ b/Vendors/SVWebViewController/SVModalWebViewController.h @@ -0,0 +1,32 @@ +// +// SVModalWebViewController.h +// +// Created by Oliver Letterer on 13.08.11. +// Copyright 2011 Home. All rights reserved. +// +// https://github.com/samvermette/SVWebViewController + +#import + +enum { + SVWebViewControllerAvailableActionsNone = 0, + SVWebViewControllerAvailableActionsOpenInSafari = 1 << 0, + SVWebViewControllerAvailableActionsMailLink = 1 << 1, + SVWebViewControllerAvailableActionsCopyLink = 1 << 2, + SVWebViewControllerAvailableActionsOpenInChrome = 1 << 3 +}; + +typedef NSUInteger SVWebViewControllerAvailableActions; + + +@class SVWebViewController; + +@interface SVModalWebViewController : UINavigationController + +- (id)initWithAddress:(NSString*)urlString; +- (id)initWithURL:(NSURL *)URL; + +@property (nonatomic, strong) UIColor *barsTintColor; +@property (nonatomic, readwrite) SVWebViewControllerAvailableActions availableActions; + +@end diff --git a/Vendors/SVWebViewController/SVModalWebViewController.m b/Vendors/SVWebViewController/SVModalWebViewController.m new file mode 100755 index 0000000..94dc079 --- /dev/null +++ b/Vendors/SVWebViewController/SVModalWebViewController.m @@ -0,0 +1,48 @@ +// +// SVModalWebViewController.m +// +// Created by Oliver Letterer on 13.08.11. +// Copyright 2011 Home. All rights reserved. +// +// https://github.com/samvermette/SVWebViewController + +#import "SVModalWebViewController.h" +#import "SVWebViewController.h" + +@interface SVModalWebViewController () + +@property (nonatomic, strong) SVWebViewController *webViewController; + +@end + + +@implementation SVModalWebViewController + +@synthesize barsTintColor, availableActions, webViewController; + +#pragma mark - Initialization + + +- (id)initWithAddress:(NSString*)urlString { + return [self initWithURL:[NSURL URLWithString:urlString]]; +} + +- (id)initWithURL:(NSURL *)URL { + self.webViewController = [[SVWebViewController alloc] initWithURL:URL]; + if (self = [super initWithRootViewController:self.webViewController]) { + self.webViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:webViewController action:@selector(doneButtonClicked:)]; + } + return self; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:NO]; + + self.navigationBar.tintColor = self.barsTintColor; +} + +- (void)setAvailableActions:(SVWebViewControllerAvailableActions)newAvailableActions { + self.webViewController.availableActions = newAvailableActions; +} + +@end diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/action.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/action.png new file mode 100755 index 0000000..1060923 Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/action.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/back.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/back.png new file mode 100755 index 0000000..1045d2b Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/back.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/forward.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/forward.png new file mode 100755 index 0000000..7e916cd Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/forward.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/refresh.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/refresh.png new file mode 100755 index 0000000..ba96a2d Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/refresh.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/stop.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/stop.png new file mode 100755 index 0000000..050d039 Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPad/stop.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/back.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/back.png new file mode 100755 index 0000000..0443582 Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/back.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/back@2x.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/back@2x.png new file mode 100755 index 0000000..d7bab66 Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/back@2x.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/forward.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/forward.png new file mode 100755 index 0000000..8ab2282 Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/forward.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/forward@2x.png b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/forward@2x.png new file mode 100755 index 0000000..8adeb9d Binary files /dev/null and b/Vendors/SVWebViewController/SVWebViewController.bundle/iPhone/forward@2x.png differ diff --git a/Vendors/SVWebViewController/SVWebViewController.h b/Vendors/SVWebViewController/SVWebViewController.h new file mode 100755 index 0000000..78d8408 --- /dev/null +++ b/Vendors/SVWebViewController/SVWebViewController.h @@ -0,0 +1,20 @@ +// +// SVWebViewController.h +// +// Created by Sam Vermette on 08.11.10. +// Copyright 2010 Sam Vermette. All rights reserved. +// +// https://github.com/samvermette/SVWebViewController + +#import + +#import "SVModalWebViewController.h" + +@interface SVWebViewController : UIViewController + +- (id)initWithAddress:(NSString*)urlString; +- (id)initWithURL:(NSURL*)URL; + +@property (nonatomic, readwrite) SVWebViewControllerAvailableActions availableActions; + +@end diff --git a/Vendors/SVWebViewController/SVWebViewController.m b/Vendors/SVWebViewController/SVWebViewController.m new file mode 100755 index 0000000..9e71322 --- /dev/null +++ b/Vendors/SVWebViewController/SVWebViewController.m @@ -0,0 +1,418 @@ +// +// SVWebViewController.m +// +// Created by Sam Vermette on 08.11.10. +// Copyright 2010 Sam Vermette. All rights reserved. +// +// https://github.com/samvermette/SVWebViewController + +#import "SVWebViewController.h" + +@interface SVWebViewController () + +@property (nonatomic, strong, readonly) UIBarButtonItem *backBarButtonItem; +@property (nonatomic, strong, readonly) UIBarButtonItem *forwardBarButtonItem; +@property (nonatomic, strong, readonly) UIBarButtonItem *refreshBarButtonItem; +@property (nonatomic, strong, readonly) UIBarButtonItem *stopBarButtonItem; +@property (nonatomic, strong, readonly) UIBarButtonItem *actionBarButtonItem; +@property (nonatomic, strong, readonly) UIActionSheet *pageActionSheet; + +@property (nonatomic, strong) UIWebView *mainWebView; +@property (nonatomic, strong) NSURL *URL; + +- (id)initWithAddress:(NSString*)urlString; +- (id)initWithURL:(NSURL*)URL; +- (void)loadURL:(NSURL*)URL; + +- (void)updateToolbarItems; + +- (void)goBackClicked:(UIBarButtonItem *)sender; +- (void)goForwardClicked:(UIBarButtonItem *)sender; +- (void)reloadClicked:(UIBarButtonItem *)sender; +- (void)stopClicked:(UIBarButtonItem *)sender; +- (void)actionButtonClicked:(UIBarButtonItem *)sender; + +@end + + +@implementation SVWebViewController + +@synthesize availableActions; + +@synthesize URL, mainWebView; +@synthesize backBarButtonItem, forwardBarButtonItem, refreshBarButtonItem, stopBarButtonItem, actionBarButtonItem, pageActionSheet; + +#pragma mark - setters and getters + +- (UIBarButtonItem *)backBarButtonItem { + + if (!backBarButtonItem) { + backBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"SVWebViewController.bundle/iPhone/back"] style:UIBarButtonItemStylePlain target:self action:@selector(goBackClicked:)]; + backBarButtonItem.imageInsets = UIEdgeInsetsMake(2.0f, 0.0f, -2.0f, 0.0f); + backBarButtonItem.width = 18.0f; + } + return backBarButtonItem; +} + +- (UIBarButtonItem *)forwardBarButtonItem { + + if (!forwardBarButtonItem) { + forwardBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"SVWebViewController.bundle/iPhone/forward"] style:UIBarButtonItemStylePlain target:self action:@selector(goForwardClicked:)]; + forwardBarButtonItem.imageInsets = UIEdgeInsetsMake(2.0f, 0.0f, -2.0f, 0.0f); + forwardBarButtonItem.width = 18.0f; + } + return forwardBarButtonItem; +} + +- (UIBarButtonItem *)refreshBarButtonItem { + + if (!refreshBarButtonItem) { + refreshBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadClicked:)]; + } + + return refreshBarButtonItem; +} + +- (UIBarButtonItem *)stopBarButtonItem { + + if (!stopBarButtonItem) { + stopBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop target:self action:@selector(stopClicked:)]; + } + return stopBarButtonItem; +} + +- (UIBarButtonItem *)actionBarButtonItem { + + if (!actionBarButtonItem) { + actionBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionButtonClicked:)]; + } + return actionBarButtonItem; +} + +- (UIActionSheet *)pageActionSheet { + + if(!pageActionSheet) { + pageActionSheet = [[UIActionSheet alloc] + initWithTitle:self.mainWebView.request.URL.absoluteString + delegate:self + cancelButtonTitle:nil + destructiveButtonTitle:nil + otherButtonTitles:nil]; + + if((self.availableActions & SVWebViewControllerAvailableActionsCopyLink) == SVWebViewControllerAvailableActionsCopyLink) + [pageActionSheet addButtonWithTitle:NSLocalizedStringFromTable(@"Copy Link", @"SVWebViewController", @"")]; + + if((self.availableActions & SVWebViewControllerAvailableActionsOpenInSafari) == SVWebViewControllerAvailableActionsOpenInSafari) + [pageActionSheet addButtonWithTitle:NSLocalizedStringFromTable(@"Open in Safari", @"SVWebViewController", @"")]; + + if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"googlechrome://"]] && (self.availableActions & SVWebViewControllerAvailableActionsOpenInChrome) == SVWebViewControllerAvailableActionsOpenInChrome) + [pageActionSheet addButtonWithTitle:NSLocalizedStringFromTable(@"Open in Chrome", @"SVWebViewController", @"")]; + + if([MFMailComposeViewController canSendMail] && (self.availableActions & SVWebViewControllerAvailableActionsMailLink) == SVWebViewControllerAvailableActionsMailLink) + [pageActionSheet addButtonWithTitle:NSLocalizedStringFromTable(@"Mail Link to this Page", @"SVWebViewController", @"")]; + + [pageActionSheet addButtonWithTitle:NSLocalizedStringFromTable(@"Cancel", @"SVWebViewController", @"")]; + pageActionSheet.cancelButtonIndex = [self.pageActionSheet numberOfButtons]-1; + } + + return pageActionSheet; +} + +#pragma mark - Initialization + +- (id)initWithAddress:(NSString *)urlString { + return [self initWithURL:[NSURL URLWithString:urlString]]; +} + +- (id)initWithURL:(NSURL*)pageURL { + + if(self = [super init]) { + self.URL = pageURL; + self.availableActions = SVWebViewControllerAvailableActionsOpenInSafari | SVWebViewControllerAvailableActionsOpenInChrome | SVWebViewControllerAvailableActionsMailLink; + } + + return self; +} + +- (void)loadURL:(NSURL *)pageURL { + [mainWebView loadRequest:[NSURLRequest requestWithURL:pageURL]]; +} + +#pragma mark - View lifecycle + +- (void)loadView { + mainWebView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + mainWebView.delegate = self; + mainWebView.scalesPageToFit = YES; + [self loadURL:self.URL]; + self.view = mainWebView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + [self updateToolbarItems]; +} + +- (void)viewDidUnload { + [super viewDidUnload]; + mainWebView = nil; + backBarButtonItem = nil; + forwardBarButtonItem = nil; + refreshBarButtonItem = nil; + stopBarButtonItem = nil; + actionBarButtonItem = nil; + pageActionSheet = nil; +} + +- (void)viewWillAppear:(BOOL)animated { + NSAssert(self.navigationController, @"SVWebViewController needs to be contained in a UINavigationController. If you are presenting SVWebViewController modally, use SVModalWebViewController instead."); + + [super viewWillAppear:animated]; + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { + [self.navigationController setToolbarHidden:NO animated:animated]; + } +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { + [self.navigationController setToolbarHidden:YES animated:animated]; + } +} + +- (void)viewDidDisappear:(BOOL)animated { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + return YES; + + return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown; +} + +- (void)dealloc +{ + [mainWebView stopLoading]; + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + mainWebView.delegate = nil; +} + +#pragma mark - Toolbar + +- (void)updateToolbarItems { + self.backBarButtonItem.enabled = self.mainWebView.canGoBack; + self.forwardBarButtonItem.enabled = self.mainWebView.canGoForward; + self.actionBarButtonItem.enabled = !self.mainWebView.isLoading; + + UIBarButtonItem *refreshStopBarButtonItem = self.mainWebView.isLoading ? self.stopBarButtonItem : self.refreshBarButtonItem; + + UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; + fixedSpace.width = 5.0f; + UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + NSArray *items; + CGFloat toolbarWidth = 250.0f; + + if(self.availableActions == 0) { + toolbarWidth = 200.0f; + items = [NSArray arrayWithObjects: + fixedSpace, + refreshStopBarButtonItem, + flexibleSpace, + self.backBarButtonItem, + flexibleSpace, + self.forwardBarButtonItem, + fixedSpace, + nil]; + } else { + items = [NSArray arrayWithObjects: + fixedSpace, + refreshStopBarButtonItem, + flexibleSpace, + self.backBarButtonItem, + flexibleSpace, + self.forwardBarButtonItem, + flexibleSpace, + self.actionBarButtonItem, + fixedSpace, + nil]; + } + + UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, toolbarWidth, 44.0f)]; + toolbar.items = items; + toolbar.barStyle = self.navigationController.navigationBar.barStyle; + toolbar.tintColor = self.navigationController.navigationBar.tintColor; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:toolbar]; + } + + else { + NSArray *items; + + if(self.availableActions == 0) { + items = [NSArray arrayWithObjects: + flexibleSpace, + self.backBarButtonItem, + flexibleSpace, + self.forwardBarButtonItem, + flexibleSpace, + refreshStopBarButtonItem, + flexibleSpace, + nil]; + } else { + items = [NSArray arrayWithObjects: + fixedSpace, + self.backBarButtonItem, + flexibleSpace, + self.forwardBarButtonItem, + flexibleSpace, + refreshStopBarButtonItem, + flexibleSpace, + self.actionBarButtonItem, + fixedSpace, + nil]; + } + + self.navigationController.toolbar.barStyle = self.navigationController.navigationBar.barStyle; + self.navigationController.toolbar.tintColor = self.navigationController.navigationBar.tintColor; + self.toolbarItems = items; + } +} + +#pragma mark - +#pragma mark UIWebViewDelegate + +- (void)webViewDidStartLoad:(UIWebView *)webView { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + [self updateToolbarItems]; +} + + +- (void)webViewDidFinishLoad:(UIWebView *)webView { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + + self.navigationItem.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; + [self updateToolbarItems]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + [self updateToolbarItems]; +} + +#pragma mark - Target actions + +- (void)goBackClicked:(UIBarButtonItem *)sender { + [mainWebView goBack]; +} + +- (void)goForwardClicked:(UIBarButtonItem *)sender { + [mainWebView goForward]; +} + +- (void)reloadClicked:(UIBarButtonItem *)sender { + [mainWebView reload]; +} + +- (void)stopClicked:(UIBarButtonItem *)sender { + [mainWebView stopLoading]; + [self updateToolbarItems]; +} + +- (void)actionButtonClicked:(id)sender { + + if(pageActionSheet) + return; + + if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + [self.pageActionSheet showFromBarButtonItem:self.actionBarButtonItem animated:YES]; + else + [self.pageActionSheet showFromToolbar:self.navigationController.toolbar]; + +} + +- (void)doneButtonClicked:(id)sender { +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 + [self dismissModalViewControllerAnimated:YES]; +#else + [self dismissViewControllerAnimated:YES completion:NULL]; +#endif +} + +#pragma mark - +#pragma mark UIActionSheetDelegate + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + NSString *title = [actionSheet buttonTitleAtIndex:buttonIndex]; + + if([title localizedCompare:NSLocalizedStringFromTable(@"Open in Safari", @"SVWebViewController", @"")] == NSOrderedSame) + [[UIApplication sharedApplication] openURL:self.mainWebView.request.URL]; + + if([title localizedCompare:NSLocalizedStringFromTable(@"Open in Chrome", @"SVWebViewController", @"")] == NSOrderedSame) { + NSURL *inputURL = self.mainWebView.request.URL; + NSString *scheme = inputURL.scheme; + + NSString *chromeScheme = nil; + if ([scheme isEqualToString:@"http"]) { + chromeScheme = @"googlechrome"; + } else if ([scheme isEqualToString:@"https"]) { + chromeScheme = @"googlechromes"; + } + + if (chromeScheme) { + NSString *absoluteString = [inputURL absoluteString]; + NSRange rangeForScheme = [absoluteString rangeOfString:@":"]; + NSString *urlNoScheme = + [absoluteString substringFromIndex:rangeForScheme.location]; + NSString *chromeURLString = + [chromeScheme stringByAppendingString:urlNoScheme]; + NSURL *chromeURL = [NSURL URLWithString:chromeURLString]; + + [[UIApplication sharedApplication] openURL:chromeURL]; + } + } + + if([title localizedCompare:NSLocalizedStringFromTable(@"Copy Link", @"SVWebViewController", @"")] == NSOrderedSame) { + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + pasteboard.string = self.mainWebView.request.URL.absoluteString; + } + + else if([title localizedCompare:NSLocalizedStringFromTable(@"Mail Link to this Page", @"SVWebViewController", @"")] == NSOrderedSame) { + + MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init]; + + mailViewController.mailComposeDelegate = self; + [mailViewController setSubject:[self.mainWebView stringByEvaluatingJavaScriptFromString:@"document.title"]]; + [mailViewController setMessageBody:self.mainWebView.request.URL.absoluteString isHTML:NO]; + mailViewController.modalPresentationStyle = UIModalPresentationFormSheet; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 + [self presentModalViewController:mailViewController animated:YES]; +#else + [self presentViewController:mailViewController animated:YES completion:NULL]; +#endif + } + + pageActionSheet = nil; +} + +#pragma mark - +#pragma mark MFMailComposeViewControllerDelegate + +- (void)mailComposeController:(MFMailComposeViewController *)controller + didFinishWithResult:(MFMailComposeResult)result + error:(NSError *)error +{ + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 + [self dismissModalViewControllerAnimated:YES]; +#else + [self dismissViewControllerAnimated:YES completion:NULL]; +#endif +} + +@end diff --git a/Vendors/SVWebViewController/da.lproj/SVWebViewController.strings b/Vendors/SVWebViewController/da.lproj/SVWebViewController.strings new file mode 100755 index 0000000..56b673e --- /dev/null +++ b/Vendors/SVWebViewController/da.lproj/SVWebViewController.strings @@ -0,0 +1,5 @@ +// Copyright (c) 2013 Anders Fogh Eriksen +"Open in Safari" = "Åbn i Safari"; +"Open in Chrome" = "Åbn i Chrome"; +"Copy Link" = "Kopier Link"; +"Mail Link to this Page" = "Mail Link til denne side"; \ No newline at end of file diff --git a/Vendors/SVWebViewController/en.lproj/SVWebViewController.strings b/Vendors/SVWebViewController/en.lproj/SVWebViewController.strings new file mode 100755 index 0000000..6373eee --- /dev/null +++ b/Vendors/SVWebViewController/en.lproj/SVWebViewController.strings @@ -0,0 +1,5 @@ +// Copyright (c) 2013 Alex Ruperez +"Open in Safari" = "Open in Safari"; +"Open in Chrome" = "Open in Chrome"; +"Copy Link" = "Copy Link"; +"Mail Link to this Page" = "Mail Link to this Page"; \ No newline at end of file diff --git a/Vendors/SVWebViewController/es-ES.lproj/SVWebViewController.strings b/Vendors/SVWebViewController/es-ES.lproj/SVWebViewController.strings new file mode 100755 index 0000000..de049b2 --- /dev/null +++ b/Vendors/SVWebViewController/es-ES.lproj/SVWebViewController.strings @@ -0,0 +1,5 @@ +// Copyright (c) 2013 Alex Ruperez +"Open in Safari" = "Abrir en Safari"; +"Open in Chrome" = "Abrir en Chrome"; +"Copy Link" = "Copiar Enlace"; +"Mail Link to this Page" = "Enviar Enlace a esta Página"; \ No newline at end of file diff --git a/Vendors/SVWebViewController/es.lproj/SVWebViewController.strings b/Vendors/SVWebViewController/es.lproj/SVWebViewController.strings new file mode 100755 index 0000000..de049b2 --- /dev/null +++ b/Vendors/SVWebViewController/es.lproj/SVWebViewController.strings @@ -0,0 +1,5 @@ +// Copyright (c) 2013 Alex Ruperez +"Open in Safari" = "Abrir en Safari"; +"Open in Chrome" = "Abrir en Chrome"; +"Copy Link" = "Copiar Enlace"; +"Mail Link to this Page" = "Enviar Enlace a esta Página"; \ No newline at end of file diff --git a/Vendors/SVWebViewController/zh-Hans.lproj/SVWebViewController.strings b/Vendors/SVWebViewController/zh-Hans.lproj/SVWebViewController.strings new file mode 100755 index 0000000..f86e9fa --- /dev/null +++ b/Vendors/SVWebViewController/zh-Hans.lproj/SVWebViewController.strings @@ -0,0 +1,6 @@ +// Copyright (c) 2013 James Stout +"Open in Safari" = "打开Safari"; +"Open in Chrome" = "打开Chrome"; +"Copy Link" = "复制网页连结"; +"Mail Link to this Page" = "以电邮传送此页连结"; +"Cancel"="取消"; diff --git a/Vendors/SVWebViewController/zh-Hant.lproj/SVWebViewController.strings b/Vendors/SVWebViewController/zh-Hant.lproj/SVWebViewController.strings new file mode 100755 index 0000000..91556c3 --- /dev/null +++ b/Vendors/SVWebViewController/zh-Hant.lproj/SVWebViewController.strings @@ -0,0 +1,6 @@ +// Copyright (c) 2013 James Stout +"Open in Safari" = "打開Safari"; +"Open in Chrome" = "打開Chrome"; +"Copy Link" = "複製網頁連結"; +"Mail Link to this Page" = "以電郵傳送此頁連結"; +"Cancel"="取消"; \ No newline at end of file