From ab538612df382c3f78b27536fed8aeed25f6a186 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Mon, 19 Dec 2011 17:12:08 -0500 Subject: [PATCH] flesh out, add basic Instapaper support --- 5by5Browser.xcodeproj/project.pbxproj | 55 ++++ .../UserInterfaceState.xcuserstate | Bin 36958 -> 46625 bytes 5by5Browser/5by5Browser-Info.plist | 4 +- 5by5Browser/Episode.h | 8 +- 5by5Browser/Episode.m | 17 +- 5by5Browser/MMHTTPClient.h | 54 ++++ 5by5Browser/MMHTTPClient.m | 252 ++++++++++++++++++ 5by5Browser/MMHTTPRequest.h | 43 +++ 5by5Browser/MMHTTPRequest.m | 147 ++++++++++ 5by5Browser/NSString+marshmallows.h | 19 ++ 5by5Browser/NSString+marshmallows.m | 76 ++++++ 5by5Browser/SSAppDelegate.m | 5 +- 5by5Browser/SSDetailViewController.h | 6 +- 5by5Browser/SSDetailViewController.m | 55 +++- 5by5Browser/SSMasterViewController.m | 2 +- 5by5Browser/Show.h | 2 + 5by5Browser/Show.m | 3 +- 5by5Browser/UIAlertView+marshmallows.h | 17 ++ 5by5Browser/UIAlertView+marshmallows.m | 32 +++ 5by5Browser/UIAlertViewDelegate.h | 20 ++ 5by5Browser/UIAlertViewDelegate.m | 33 +++ .../SSDetailViewController_iPhone.xib | 148 +++++++++- 22 files changed, 967 insertions(+), 31 deletions(-) create mode 100644 5by5Browser/MMHTTPClient.h create mode 100644 5by5Browser/MMHTTPClient.m create mode 100644 5by5Browser/MMHTTPRequest.h create mode 100644 5by5Browser/MMHTTPRequest.m create mode 100644 5by5Browser/NSString+marshmallows.h create mode 100644 5by5Browser/NSString+marshmallows.m create mode 100644 5by5Browser/UIAlertView+marshmallows.h create mode 100644 5by5Browser/UIAlertView+marshmallows.m create mode 100644 5by5Browser/UIAlertViewDelegate.h create mode 100644 5by5Browser/UIAlertViewDelegate.m diff --git a/5by5Browser.xcodeproj/project.pbxproj b/5by5Browser.xcodeproj/project.pbxproj index 5b1aea5..8f6b285 100644 --- a/5by5Browser.xcodeproj/project.pbxproj +++ b/5by5Browser.xcodeproj/project.pbxproj @@ -27,6 +27,11 @@ 7B1A6EF1149D140600FC5105 /* SSDetailViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B1A6EEF149D140600FC5105 /* SSDetailViewController_iPad.xib */; }; 7B1A6EF9149D148800FC5105 /* FiveByFive.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B1A6EF8149D148800FC5105 /* FiveByFive.m */; }; 7B1A6EFC149D14C500FC5105 /* Show.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B1A6EFB149D14C500FC5105 /* Show.m */; }; + 7B4CADE9149FE797007E7941 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B4CADE8149FE797007E7941 /* Security.framework */; }; + 7B4CADF0149FE944007E7941 /* UIAlertView+marshmallows.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B4CADEE149FE944007E7941 /* UIAlertView+marshmallows.h */; }; + 7B4CADF1149FE944007E7941 /* UIAlertView+marshmallows.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B4CADEF149FE944007E7941 /* UIAlertView+marshmallows.m */; }; + 7B4CADF5149FEAE5007E7941 /* UIAlertViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B4CADF3149FEAE5007E7941 /* UIAlertViewDelegate.h */; }; + 7B4CADF6149FEAE5007E7941 /* UIAlertViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B4CADF4149FEAE5007E7941 /* UIAlertViewDelegate.m */; }; 7B7302F2149D670A003547E5 /* GTMNSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302EA149D670A003547E5 /* GTMNSString+HTML.h */; }; 7B7302F3149D670A003547E5 /* GTMNSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B7302EB149D670A003547E5 /* GTMNSString+HTML.m */; }; 7B7302F4149D670A003547E5 /* NSDate+InternetDateTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302EC149D670A003547E5 /* NSDate+InternetDateTime.h */; }; @@ -46,6 +51,12 @@ 7B86894B149D5C1000F3A2C6 /* ShowViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B868949149D5C0F00F3A2C6 /* ShowViewController.m */; }; 7B868951149D5C6F00F3A2C6 /* ShowViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B86894D149D5C6F00F3A2C6 /* ShowViewController_iPad.xib */; }; 7B868952149D5C6F00F3A2C6 /* ShowViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B86894F149D5C6F00F3A2C6 /* ShowViewController_iPhone.xib */; }; + 7BDCFA03149E695E00DDF1B3 /* MMHTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BDCFA01149E695E00DDF1B3 /* MMHTTPRequest.h */; }; + 7BDCFA04149E695E00DDF1B3 /* MMHTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFA02149E695E00DDF1B3 /* MMHTTPRequest.m */; }; + 7BDCFA07149E69E700DDF1B3 /* MMHTTPClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BDCFA05149E69E700DDF1B3 /* MMHTTPClient.h */; }; + 7BDCFA08149E69E700DDF1B3 /* MMHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFA06149E69E700DDF1B3 /* MMHTTPClient.m */; }; + 7BDCFA0B149E6A2B00DDF1B3 /* NSString+marshmallows.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BDCFA09149E6A2A00DDF1B3 /* NSString+marshmallows.h */; }; + 7BDCFA0C149E6A2B00DDF1B3 /* NSString+marshmallows.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFA0A149E6A2B00DDF1B3 /* NSString+marshmallows.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -77,6 +88,11 @@ 7B1A6EF8149D148800FC5105 /* FiveByFive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FiveByFive.m; sourceTree = ""; }; 7B1A6EFA149D14C500FC5105 /* Show.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Show.h; sourceTree = ""; }; 7B1A6EFB149D14C500FC5105 /* Show.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Show.m; sourceTree = ""; }; + 7B4CADE8149FE797007E7941 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 7B4CADEE149FE944007E7941 /* UIAlertView+marshmallows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+marshmallows.h"; sourceTree = ""; }; + 7B4CADEF149FE944007E7941 /* UIAlertView+marshmallows.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+marshmallows.m"; sourceTree = ""; }; + 7B4CADF3149FEAE5007E7941 /* UIAlertViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAlertViewDelegate.h; sourceTree = ""; }; + 7B4CADF4149FEAE5007E7941 /* UIAlertViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAlertViewDelegate.m; sourceTree = ""; }; 7B7302EA149D670A003547E5 /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = ""; }; 7B7302EB149D670A003547E5 /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = ""; }; 7B7302EC149D670A003547E5 /* NSDate+InternetDateTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+InternetDateTime.h"; sourceTree = ""; }; @@ -98,6 +114,13 @@ 7B868949149D5C0F00F3A2C6 /* ShowViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowViewController.m; sourceTree = ""; }; 7B86894E149D5C6F00F3A2C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ShowViewController_iPad.xib; sourceTree = ""; }; 7B868950149D5C6F00F3A2C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ShowViewController_iPhone.xib; sourceTree = ""; }; + 7BDCFA00149E688600DDF1B3 /* InstapaperCredentials.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InstapaperCredentials.h; sourceTree = ""; }; + 7BDCFA01149E695E00DDF1B3 /* MMHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMHTTPRequest.h; sourceTree = ""; }; + 7BDCFA02149E695E00DDF1B3 /* MMHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMHTTPRequest.m; sourceTree = ""; }; + 7BDCFA05149E69E700DDF1B3 /* MMHTTPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMHTTPClient.h; sourceTree = ""; }; + 7BDCFA06149E69E700DDF1B3 /* MMHTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMHTTPClient.m; sourceTree = ""; }; + 7BDCFA09149E6A2A00DDF1B3 /* NSString+marshmallows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+marshmallows.h"; sourceTree = ""; }; + 7BDCFA0A149E6A2B00DDF1B3 /* NSString+marshmallows.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+marshmallows.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -105,6 +128,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7B4CADE9149FE797007E7941 /* Security.framework in Frameworks */, 7B1A6ECF149D140600FC5105 /* UIKit.framework in Frameworks */, 7B1A6ED1149D140600FC5105 /* Foundation.framework in Frameworks */, 7B1A6ED3149D140600FC5105 /* CoreGraphics.framework in Frameworks */, @@ -118,6 +142,7 @@ isa = PBXGroup; children = ( 7B7302E9149D66ED003547E5 /* MWFeedParser */, + 7B4CADF2149FE954007E7941 /* Marshmallows */, 7B1A6ED4149D140600FC5105 /* 5by5Browser */, 7B1A6ECD149D140600FC5105 /* Frameworks */, 7B1A6ECB149D140600FC5105 /* Products */, @@ -135,6 +160,7 @@ 7B1A6ECD149D140600FC5105 /* Frameworks */ = { isa = PBXGroup; children = ( + 7B4CADE8149FE797007E7941 /* Security.framework */, 7B1A6ECE149D140600FC5105 /* UIKit.framework */, 7B1A6ED0149D140600FC5105 /* Foundation.framework */, 7B1A6ED2149D140600FC5105 /* CoreGraphics.framework */, @@ -145,6 +171,7 @@ 7B1A6ED4149D140600FC5105 /* 5by5Browser */ = { isa = PBXGroup; children = ( + 7BDCFA00149E688600DDF1B3 /* InstapaperCredentials.h */, 7B1A6EDD149D140600FC5105 /* SSAppDelegate.h */, 7B1A6EDE149D140600FC5105 /* SSAppDelegate.m */, 7B868948149D5C0F00F3A2C6 /* ShowViewController.h */, @@ -187,6 +214,24 @@ name = "Supporting Files"; sourceTree = ""; }; + 7B4CADF2149FE954007E7941 /* Marshmallows */ = { + isa = PBXGroup; + children = ( + 7BDCFA05149E69E700DDF1B3 /* MMHTTPClient.h */, + 7BDCFA06149E69E700DDF1B3 /* MMHTTPClient.m */, + 7BDCFA01149E695E00DDF1B3 /* MMHTTPRequest.h */, + 7BDCFA02149E695E00DDF1B3 /* MMHTTPRequest.m */, + 7BDCFA09149E6A2A00DDF1B3 /* NSString+marshmallows.h */, + 7BDCFA0A149E6A2B00DDF1B3 /* NSString+marshmallows.m */, + 7B4CADF3149FEAE5007E7941 /* UIAlertViewDelegate.h */, + 7B4CADF4149FEAE5007E7941 /* UIAlertViewDelegate.m */, + 7B4CADEE149FE944007E7941 /* UIAlertView+marshmallows.h */, + 7B4CADEF149FE944007E7941 /* UIAlertView+marshmallows.m */, + ); + name = Marshmallows; + path = 5by5Browser; + sourceTree = ""; + }; 7B7302E7149D66EA003547E5 /* Categories */ = { isa = PBXGroup; children = ( @@ -232,6 +277,11 @@ 7B730303149D6714003547E5 /* MWFeedItem.h in Headers */, 7B730305149D6714003547E5 /* MWFeedParser_Private.h in Headers */, 7B730306149D6714003547E5 /* MWFeedParser.h in Headers */, + 7BDCFA03149E695E00DDF1B3 /* MMHTTPRequest.h in Headers */, + 7BDCFA07149E69E700DDF1B3 /* MMHTTPClient.h in Headers */, + 7BDCFA0B149E6A2B00DDF1B3 /* NSString+marshmallows.h in Headers */, + 7B4CADF0149FE944007E7941 /* UIAlertView+marshmallows.h in Headers */, + 7B4CADF5149FEAE5007E7941 /* UIAlertViewDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -326,6 +376,11 @@ 7B730302149D6714003547E5 /* MWFeedInfo.m in Sources */, 7B730304149D6714003547E5 /* MWFeedItem.m in Sources */, 7B730307149D6714003547E5 /* MWFeedParser.m in Sources */, + 7BDCFA04149E695E00DDF1B3 /* MMHTTPRequest.m in Sources */, + 7BDCFA08149E69E700DDF1B3 /* MMHTTPClient.m in Sources */, + 7BDCFA0C149E6A2B00DDF1B3 /* NSString+marshmallows.m in Sources */, + 7B4CADF1149FE944007E7941 /* UIAlertView+marshmallows.m in Sources */, + 7B4CADF6149FEAE5007E7941 /* UIAlertViewDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/5by5Browser.xcodeproj/project.xcworkspace/xcuserdata/sjs.xcuserdatad/UserInterfaceState.xcuserstate b/5by5Browser.xcodeproj/project.xcworkspace/xcuserdata/sjs.xcuserdatad/UserInterfaceState.xcuserstate index 1261e914cf18af3d8416d4c0985de1c33f611b13..686b48d8682fc86df457d6a626b2d2d91314ee38 100644 GIT binary patch literal 46625 zcmeEv2Y6IP*Z-ZFd$--POUkB@NC^vvh<5ZVh)o_W<`G_Yn6m z_XzhW_Zas$_agTa_bT@$_ZGK<+s*CaKHxs&KH@&(4seIKZ@C}1FFXLD6 zcKj-S4Zn`xz;EImcn^LLe}MPmgZL2s8Xv~r5KIW+i9mEjPYlFJOhhCSF_U0oC6OeK zq>uq5m86jz;vh~kh>Rqo$Y?T#j3s4cHmN2xq>eO_MPwPdkX%ZxB-fB@$qnR2auZok z?j-k+E#!Xk5P6t9LLMcLk;lnXAAM$=}1@&p*sR&OgCF z!#~Tv$ZzLg@w@pw{CoWS{0ICe{Ac`^{8#)>{89dA{uuuYe_X(VNf3o#Aw)CCm}3g&M&l)CmiO2H{*` zxp1DaLQn-NtQ0O1E*7p3ZV+x1ZW3-5)(P#xqrzjtLYMLHJQPCj2h^p_{L3)HUlC>6Yr2>r~w;-G#bK zbyw)F(Os{*S$CUmt*%42Nq3j-KHUSlM|6+tp4L65dr9}Q?ls+;x_5QEbnojv(tWD? zT=%8!knUUE5#3SUFSDo{Y3p_{SE>mSlTrhiiZto{Z4HvM+}8~V5PJN0|?pX{ucu=@CL!4GXxoe4Ize5gVhjb2scC+`WuoA$%Yhz!{9XJ8eE1#Ly@7_ zFu^dO%#He6%4)^MF+tzn&^ z-Oyp!Vz}S%fZ;*Ivxesk&l_Gayl8mIu+^~5@UCHpVW(l2VV~hM!{>%C4Bs1$7=AGP zXgFawY2=K^C>s@{*=R9F8KaFc##m#TG2NJ9bQ|-H1C3`HM;J#MM;S*Orx>Rirx~Xk z=Nji3YmIfrHsd1WV&nP7RmKa9tBuzfuQgt0yxzFZ*lz4Ft~cIqe8Bji@gd`L#^;SM z7+*BLWqjNCj`3aNC&sI%!binkb=_}Jg(^1pUremgGL?ZH{AnL>*F<1-{L&aDzK}-}=!~tT4m?=6$ zr#Mg?A`TbFh^68ru|k|7&KBp2XNwJDvv`Skg?P1iy?B#&n|O!VA#N1!67Lls5FZvF z7oQTJ6JHcx7GD+L6yFg)7C#a9il2)6#Qov{@q6)z_=EVPcuf3V{6jKICP|beNtP7J zELo%wDO`$@QltS=s+1k`OX)}H zC+Vp4vvf@QUFKy$*2#KVl+Ch5j*(;KI5}SKCnv~>a({V%oFQk*c6qQoLLMWR$`j;D zdA3|7d*nKKfxJ{cU%pVjRK85UMZQ(OO{YO$x&R&Fr`=-sf<#l zD$|td$}DA$GFNF(8kHtxq0+1@QI;y_D3>T#C|4@iD%UADDK{&(D{GW?r9-(}xktHI zxlh@mJf=LZJfS?Pyr{gSY*n@?Zz^voZ!7O9?<*fDpDCX!Unu*P1Ii)gC*`Q}vvN#1 zZq}O(W~13;7R{1bHiwzR%@O8EbE3JwImtZ0>^A3{2b#|^55hs_!R7*UvAM)N++1#+ zYOXL>n&+73n(NF9%uVK2^J4Qd^Lgf#<_pXhPfBcPtFPb5895UtauO$VmdT0L^;In` zQ^u8L&i5>BnGXN$3~1ESc242UDppB5*M|#Id5WSdEs3@gcVTgkBPYXEoR^*9aOF5M zhS^+>3`c>(k(-^DS5jP9QfUbpSyViwv3Y(=Q&qL6sIj_jfv2I>hiv7-xHvWOPA;5_ z;3B!cTof10#c;8zpz2h;YEX@;NflM;PA;D7$0cxyz(^#Q%q6O_s;Cxq95AINs* zF_gN7n#RQ?%~cCLm6mvTFuu9Q)9k6~LNBkHQ`p$h+T2)Q4>4P%WpE&C-CkA}mbhIx zc}0#4r`uJW;cynZGV+QFiZcodiybakp0hBw$WdvD=t3RYPyzyNsc!ao8pbx(00ZfP z#5LCZ;FB8a7Pfgt)_^GL=GJ+dD=o25d{}iWgpED38wgh3*jPWOs(EC~2cZi<>_(DvUBs;Z(DZm^5r`~`68FYIaKpHE>hL+;9OiD=jQUK zYlhGOVw|e|^f0R!*4$jRv>gfzoI0*-yf@VKYjH3)oM~|gHx=I5m(HWsD0ER zHCPQ%LshF9riS0ijR4(^;zo00xUukSycz+z8>X&T@204aI)$RBnb45pn!47;=3&jA zs!EFm{$5twxOiOEqPlri@VL@q9oEtUv3ONOt5&dOOx4oHw$@5Z1`~qM(z>a0Qfpm( zUF%XtvJ3+G*2#6A#TxA{x*A+JrFA1wEI_p}RdYODz01l? z=W5i%&0GaHgPY0C;wrh>TopHmt5ze`zG{>jt;VRaYMdIc_S?*PxVa!1CZ)5v`CL7> zKuu8lgOr$@rmBtVV)e#KOI(p>ZdF@-Ybl$0w6ua1P4qN1ds^T}RV&Ozz_zVjg7lhW z8OW=qs<{SQ%@{87%xRl9&*K#`6FGaPNprgPAI*k?;Z-M;w}vL9KEB!kvShtHpyqH- zLtR_T^m?ym%Ar&EO=T%}j+(f3t!8fLa?2|%!Q;xtwzXD)As5v_4`^(tYF^sGoy(oa z4V*Eq%>T?!gL3DqiR-wPYSKDxm6}{>>91jTT0(DP@e{a+yMz(ASWQ{SU8)W!A6r$| z;Cm}^S8~^?r5)T=+|}GQYO0#1rgw1HfktjnGe8TIDlG%L!GUaBJ!N2^4fDJ|yEq1e zsI-K3FYBp?Iid&j(Ea&vPvZhlYx7cHue^=*%G=dUA5&ItEqAAyxQScGwR0WZdTs-^ zk=vwZsWvrRwW~R*Lv?QAHgk7zcXRh}_j31fThv_Dr4Cmor~}oBYT1mEI*@NwYgPHu zCgw1PdFQB1=-o}HLSxt4kV;D*t$#qNk*%Hu#IJd zvD$0ov;3q>t6FQlB^H1l{g0W?Y^d=p89&!o*Z)o}P~B>tTA~iCv}8@K2A7=)Cfuz_ zMU9MHW|vDTYi(|;Zf$G!_>{CwHFt0?bFXmQ)qHi3I)pucjeDKDcX|NMS?WL#cUf7H zr?slCo_QYsFp#-G>jrOg?^If%0N<|_00VpSvR_(N*WAv%%K@FgEaQXUrJkh@u5iv- z>MUq(T-@Sm_Pux?UJMOlS!gTB{Z`Rn&A2)SxSsQ;XF? zM&ont3vT~32n<@h%&A3{mh!n^R{2?3^`Mmc+QydFtVsaalGSo{OIB%fqc*YQ zN=wSAn)j=rd*^89jxcinc6nd#mAId{U)9p}+)?gl?ilxrIzk<(j#5Xj=YHdUhoI(9 zb&NU|f|>DPDW%PI3%~*TT2k&)2?SiHyy_hN3u~*ItH7r;PpoQ~=b1U8uEsO0zJ6p& zOB-`P{ZFY>*6Dz{*Gd@n@EzYy*i^f3#S{{r8tPhV$23;eSX19<^L8R3UK%u*ib?Od3}Prr3^xxPjVFW{(iQpipp> z$f`~Thw2SHE{!qLeVG&#iBTMnmS#rP-mz!H=uqf z0VTqpN$M=MQmukNtHJoR@uUR$LJJ!Zr%kSEu4680xPxr!>~-8I=6WK#NHSsilv7sS*yJBFQ7+13wOs0)&RTss;lZba-Wj?d zVh|d_3J+Fm)}f&)xcWZOFS?jvZoI`SJ5-2@)P{Ccj7rqG>USbWXQA%h0)Mo4N?B!>8`fI4WUi zv)Zk$6;ZTGO>9RiAyQeQCbgpr&}wz5dd^I*I!YT`>R7bXZwS=#;aSn8=n7WnG6+tV zsVVK~N_3Tau1cp3W2$a|NW)iDLf4_2PK{I0&D}fCRK&Wn!gV@UGDTax5Ku4xv!pt?nYZq1H2yqA5>QY;Pt*f`3UQi zkE-YUDq7JK=!Jh3yr37+OK25W!`%E#Z(;WE6L@%OMO#aAR$W7NJ#+zI>CfQB<&~D8y3*Rl22WYt zf;Mn&jm^%??11qWed!%puVj^AWJQPAczWY$ybF6fP?oeOfu-FSDg)epWz>vO1gWQ>BEyK|k~~ zlA#~bPh2AUS-o1l21c@L*@$wu@5l|WP4iAaT`uSHX)yoH0l#cx7NvQA^d+|!JpGl- z(`)|xB-jq-)Ej#6=U!vng$d?4BNniZ+lvj@2t#I*He_mD*ejSNt>eHcR$9X5c_$)0 zya{S^kwf48`XFbEH>v?Tl+2<>Sa21||t8oqXsQ0M%s`sf|)cZH!dAJtW z;j{64^#OIOxcPJstpp{J`xnFmO+oFEW~KG{1(Hp40_9LwgvZ7p5Zy8wgO z>&_5bOM*d+&C(>@z`bYzW2xN745W$tTH8nLhw}L?9prsyPT!`<1cmUt5KI^M`KkF9&%PRU6{s?}CDg06V7=9c- zfuF=r;iuJY>dWdY>UQ;2^)>bNP24C@^#uF^ei6Tfw?c?84!Xk|>YM7@Dol7;pY5{E zE-jW-v;3?tIMS5XLn%mDwTy4-Om;C_IlTGFIb7Nam%XIJ9S_!fK(p~s1{5F0E2Am-8 zzwbhhb&RKBUTZC6z`XOi!p63SR!GYGX13~Enxou_cfnIHV_qBD%?%9ryM^0<--eO( zL;Sw_u2$hg{1MCR_)>1ZnjQFK{0Z}bktNc~v- zMBS@?s_xqi7WzB>1OEyCJ%LYhi3EX_ex`n|?uY*#pvbI_qsXGQr?|PfvH4V|aGK}p z1MTQiwYOt|RN+~di||g(SvA($mV%{PN-mT==yWYwZ(uV`t!fuJRurDGg$K=(oyrYF zo$#)!yk=?L0(;FFxg4EK07NE=`i166K%@v^GZ-W)LYy;bs*v3 zDoBL-Wv8nkQQ&1rG>IXx>R0Mv^*f(RB+`%chp~|)kVN&MdZ?Y-MUugVeLb5J$yw_66e+$ERx*S@*s-1r zCBsMoDO8WBKd3*dKdmRlq=XD7Bh;hn9~4Ox$)I#!py})HEz?@s=G1vDr@R%6pkgkp zH;sjERW;9-k0j$EMkJ+V0-31(tp29{{sAc`lemw`WHN;_lBwJ$WEz>yeMlmc(;Epw^873euJ*`wCT*`T$0zt${+L2y;-uj;X$iZ_vk-r_AjCHK*_S(2fK zjn(k=)g>^jlcnSw^-qd;zm+a0D|@uk^T{f50a>jcrw9@om?GjO#O08SAxt5cs3&@2 zh1uI>UG{bbnMDx?Oq}#-`|AHA+Sc6N^=iXf=p4f)7RD7;c}J(XiEUml?S{N}TxI2) z=DM1Bp19V=IGFXp7QoiHn!09)XX}^7r8L09T9%`Yt80jBT-*S8^4hwl0e;EfLhk5M z@@vUD(oQ-k5-2iKWTHs)iGCv!{U(ZZy~%!am+bFmvNx!|QUq(gT35U8|CI0_>{a;1 zfx@qBZLVr)nd@n0TIl-SCGWV#xp7r-Y1whfiyG@{;!=}A8&D8b+OuL+rf62FFs`W) ziZZ(3fBn^=@|=3e3!UA8fP|8~=?}Yde;@pq|2xRAbO{2xT zfdf*sFf)zpTsPNJPi#8rX(?%JhD8hr-@>#NbQnyvjUfe|T39}2n;Sy3Js=a?8eqc# zM8DpZ&KcUnvg%q7+u9P{jkGtFSyp$Br;EDTH@nq{1Dpj4ne|cP^SOCi{ zHC_nVBLR~pm=c1pweV77-$$^Mdx*WfYa$~X#=_E1S6hJlZhwcsovo2{X@|G)R!DmA zefS_gm=6IlqEw2~C`zX&gQCn0d>9|jNAQt+Uy8CQa#Qp?CE=80LiVUz7NR>>nWb!R zw)U*j;?hcb|IF!Jg6y2=p1K^>t<1l@boDQo8Phn=C$37%S>4z!Y^-jqDsOD7u4U7k zdCgwsp30QJkY8{Ke1GWPJXou39S_!OhgVtxl^?(os1%l!AR_TPbv}bR^@(%OU|lvK z&dwOwFt-u%#~@%oJ2u_{b3Zs6ef=>8(A=>cp{KtG`xO} z%i#y9W|+ai7)@k;D0s#mvd7N69RE^=l`rB)ur?I)CH!!T@+mrtqQM>fNPZMQnxdf; z&8O&EznI5qVjk!h^F$`*5&w>u$4)8nK(DK6hL!QGz`tZJVfzl6V3|D2k2Fa`8stZs z#*j+>9i#xo1unGrh%-aO8REy8&2T210Zz}De!HpWYazbjYj_Vom!C(`Fp3H&Dx|1r zJzvM4&CloSDJrIDJVi4pIvW<+yY*15hi7WLM8<$cX11`+T#H~45;B236s^>r6|_Ol zy|fm*T#L_1r1wzTH~04SUSAjw{k`$lt`@%-=#$DMb?~ znn+O@Mc{fSZRBs`Z|B$WckpZZbreme2zK&KrDz&O6%@^Yu_U`F*XGR2E3y^YoJB=B zMK)V*K}mtr;eS=q>npeI^jDqAK25PjbuHEPjWC+Aok}gEJxlGGHa|x3>4cKz_hKTx zEF=VcK*pyHD}NV%H(NRNKe&(I(*41M{6p-4&l^3eezA^!jH2lf62QcFep~?z>di~z zrqs38#*L|)(+pb*JOMWI6#q1+f*aTf$;(B4%&(Iem#u+Xb3RLGwM-zIPZ zqNtgou0TQ(ctIaH5x^4!qq>QrmebvqAPbgLiUzTwZKwMz!79YUW@I5u2p1xRNTII~ zB}5Al`!A+w2}MgOI)|cV6oIE*PSJUH3UNZb&`(GZ5{3Rk67ygyC{ih+6s@G_e2Olh zXtn0YTm^Z#d2Uy3w!>YNn^R!3*-CPT*$Z=fx9$?}4jphI;D%fU*~4;+3tg;sw!@YS zwF{luHmCoU`}9};Ls!>{k#{;FAH0|O?(qLpFDTfB9M;iFyDVLB39v;ez%L2|g|oE2 z=ncw*A>alDh^kiodu~uD5{kRi8zGGBqQ~V3W1#m7V=20*%OMKm`E9}k0fwbZyl(JP ziY{jM2Ps8i(%%?Cv5gIfMjVdt?`{#M2{XYT3e$xOVFpE)QFJ*)S9A!oxLpFcYQFJw+QR z+DH-j*gGlOOwnBw-F>HUA24^n@PP23@R0B@^OaB*IK7vm`zYE%(SsB{^#6TjK*T6K zFTmEfK+m{U*w+2QcHz}7*G6RF4fTt5;Z5N!iteZA0hq}a!UAzzL0dyjy(ey>N1Oh@ zq$F62tl%t26RfLv8ma^1NMWb2t2@ej!uwq);6PdUBtZD|h~I(k1qZrM_>3Y5S0Rpk zoT5knzC=R69F#?r^ZMZ%GJJi&Dtsw??TsjfgTf(iMEL|oPj(20g>Qs!DSC>crzv{o z?+7JN+c5#6ILbuvGeyt(`T14&jR~vE2b>nQ`imadK{8~EC095hoYZkTq{BL*<8^{g zr_<{UI-|~{6LpeK)+su(&Z6t13(^JaLUf@zt1e6zu8Yt`>iX)UbkVvPU92un7q9E5 zOVB0i`sv5{htVwqwy#eFCarZ|-1Fp486?n`kr#jzB}Q=C9?e~OdU>nI)o z^3XYTxjL6FPv_R<>jvu1(hbrL)(z1O)eX}X=n8d3x?)|4Zn$oQZlrFMZnSQUZme#c zZoIBkH$gX1SEei1P0~%)P0>x&P18-+Rp@5uX6k0?Ds{7URk}I4YF&-aqnoRnr>oV~ z>CUD&jbb~+xfJJ9TtION#iJ-5OYsDXCs90&;wp+g6xUI_fZ{famr}f(VoLD^6kkm7 zrnp#kWzsmf{YIH&J{y#ak$Th~md6ev0B}Dc(l$8x-%P_&th0qWDvazo7U_ ziod4#JBoj#_!z~%Q+%8fM2UeCi4qGXAz&|B!cMn9%e%bTm3PrCWYNj2zbWt1Ex}X? zoBtO<<~fs1k~;BQHTO3wrm5Heu!ai z4eV<_*l`9{b|zpW8(_~_Qx%+qDTGTOAS|e>s&5JCD|$`?{iX|4&!4dtJq?-l-UU3C z9`LS1cOH&qDSd5t*6TIg@BFwT!yR`fjXl+N1FS}`Xn05bctH%W;7ssX{|Lkg)i8ea zV?;2FGqM&fq?-u4U|{P*AXt~}wylY#w{PBj2MPecFHkA4_T*Kg1uF^7m zNyZYkvmv9Vt_2pvJqza4FNHe-z&%&bubj*7=NSS%vl=!pk1QGl7YI4+Nv+=F0PxrB zByR!s%jtKa(=_TjM!oodLf!3h_DDO&PkWX|+rVfK{nxa;?2V{usU4J^?Q+;{4u{>5 z>$bt^9%pur%}=&kBWwB}v+Q)|*m~kQfOf4$Tk?5+o<6we!Lcj_YVx$ z0am+61GV@;&pG4166l7tMP={)rU2CC8fuUql`_;Rf0y$wD}&v0JtRr=t2E#cKk!BW zAaHj#Yxh>5$prrH z8ELJC6y--+&yfDcA*RQBWCGN=N#h{K&%xbiEa6^KPywj-X{d32)CU>rKQluLzW7Vf?I?+H2d?$vJ*uXOCrY>-YiyAmyi_zp|1gM zr0m&V4Dp73(3C`q3n?yQO(|xJnUw?2xxTexnZsrW31#QF*-aO2H;5}hhlW%`R<9DW z8M1*aC~-K2O9+7MNJcjFj`Jqp>g6;$&&fD-=5{CPHVp2S(;s(=5y|)-Vn@E2S8i8qYW@WhU|MgpF_f((S4!|4mU{9nUb? zP|+)UGYm6H#cA??2Ugdqc;Rfwoyr0^~&PihI@eIeH6oT?tDhFp0PMnx~gEKmt;;hGYPxRcY}=P z%nT13o&b`DM+}b|9y2^naRbGT6gN@4aJ}J4!&5-=8H$@JZeb+Z!IuA;d#_)zf1gKd zir4el^G;FVD~2~%Q+hPUHw|w=Q{JI?5ygvHQbarm81C$67 zI;Gd_F?`gk)ITQ3m0f_2Mk{W z+0%W-VZ%2-@;i#pqj&`)sWRI*jAy_2tGD&;T;_M(j#IqZQNwRNlKb882ar2X@k)x% zXXI8fa$mYmy#7bUvfMnlo-W&#o5MJ?yStm4kr)j9;%%1aA%KjczI+u zfn*2tFk`$i3F!5xbYrqHMRVd;P<$oO^g8i1RYxE0h?&C1cNmPjx~ehD2pds)9=D86 zV=ge4NAcAZ!}UxQUkl9L?%Dd;;Y}6G?Ci3<>|7`F0NM8J?pihuHWv2EUy-pG$ikp? zJ;gULvNrczU`n!Odv@|eYB+o9h~ zU6z-73dQ-xrd|cQ(AcbL24cr{psEe8RdKs2pVx+0dtSisYFuhOuU8?iFsi^H825UL zH!ub_G9lI-`t8FbT7w;Mk0I+Xr?7XC@$z2TyTW)S&;(`QN%3Yz^Ddxy`^<4uB8GUo zpu@&mY`1&wcJ!OjjmFy;n?3nbP<1ZAyOz|reZ>RXx^~T?fziX{{jpEn;>#Z2j@#^!( zy?V$F3>r;>$pkGh=}dZ)!34YGz$D(H_-%^cS#J_elGcKEDcq?OqIfs70PkVFvS$Cix$8BX%=7w=JQ%+10VI=5X+W~a zam^Lh>lHoA=yrXthGUWtw}jRvA)Dc(o%XN>6QjA&KRqcg+3M04`I zj5?gBFj{7s3N(A{n5Jo_>6$_8r}zNS)JBTvRWHo>!P|C6PQXB6sxr;(Rf_XWwZI)j z$6rx=ka2g2NpaqiKfipTVp$Fwx9x7QQ-}cF&hB2=)MSE_h`s20k!dlIg$HfWMDh0&A7LbaU?k_?@yC8` zKI{*O{&dk~dNe*^Z-(B4uzlByxL1HFNXygdG_u>i|HQI13i*^(Daa|6Ucd$ ze8-_#Tb5n!C70t2@Z+W@P0#g6<$2Q!z!W&}6BM5WDuiQ9ef{I2uR2&P&J^r$u zo0m7tX)7+VXFw)vScW4n&y`V7V9(1ab`|B8P*pcgW zW)wJ`_6)}`dr5}dk&~NISW;AME6jGe;p;JeLTMTySiU%oP+?wSvAf8bn^BydSCru> zcEjgb;1pg4d~0Tydzf<=9LZx##+|~oiFP${otRBY+&bO~*Al?3X}uot_E&R>?yhQL zJ|+D+Pg{v_#lSjoFeM4QxsS!6;xGV$O=*aTMPjj-1UHv>yw^{bv&(0yo9midr@?K2 zo+a#ZVU>f8;E9wZQ4$46;s|l1I0^@eqv5zv=iPm*n0DbGPzreFy)loH{ua3L2Trw$ zW5scBTe|nsK31h&9M265e4v$@AWqc201|K+h3}kghgc?-vqP`!y7C_0_k7ybFQ6&n zG$x>_l%%W^r&BWEujJ|z-Yh1(N=i~s6<#$6FU>2TG_QPOn0)4eghq*VDBXMie^+DK zUNR5`CFwv`oG;dk3)pS(HQvV=1tRRJU?OT1n`U<1VaMupz@vrSz{%QfG=EE4#T9De zU1FQKNL(x~5toYRh|9!t#pU98lw?r?mXkw?lM)vtZb}AHGKi8PlnlE|R7EPT6wen| zi5G~g#S6uY#EU5@q+|vqZIrB}@5C23*?nOBP&ZRRF*de|L zahteP+$HW7_fS$qNiiiQlnh@lzAt_t?h!wtWCSIn;0YxY0>-nkuyxJT?EmTooVtu1 zSukQLyPmD<*4gfx)?rB3q71l^15OSvs)LUeu(7tFu&QBE6@0NRa3{ZSc>J6Vk6%zS zass>Asgv!m#KSCmJe16(WZpkxRnkDodnbgSnIY29o~=qMl1715NySo$ zG+Y`XjijWGlCvq9Pf0x`3pPljr7_Z2X`BR|pn;M`N}4EXp``VHi&afumf?g6drnTk zYO^#=s({#80_$J6PJ&>uc|tanv)jP?+uPb006REbiul#+8OS>`XkO1hvs zGZ#r0_oBwjCCKRfm234`lI4^@;Jtzpl@i+3JEv>gS8}ogvSiZj(i#Tn zYt_2b`{o8|V|N}mOZW9Eur1R4ATWs5&!=RSS6~`p0_B(2zSHHkUpmm8%Y)LlATQ~V^tE(Y`i7D#DY=T0t0}o=z4V>* zy>vwSfs$(}xsHeTB%Rl4XWTOm=0s&oAmSv@9JUiP~0OA@{XE!dc(7O{FH{uMLl#xxmYfdA&%Qj$z7D( z?Jqt`9^D;yoIJi41(nH=@2)2;;- z+1*4`E!T93h|7`L9X{QCy*yv8@6KnV+{(IQ&tZkUNXnAoS}O=E9`cUokNI1?Oojx< zX=A;t%2ZxS$-|U9Ldm24;upxPy8~Y=U($Ol`a8Ihox%RhXQH;0O5Qf!%m*g07Cwll9wx%Wn|~E?JBT;%g^XO z`Lpg4+Akjh3CRcKFXgY~gA}fVCa+QQIwfyFWGWw)56a(q;bG7z2N z3wOvr%g5wjD0z#Lw<&q2L;g+vUH*f@)zoAMB|HBa=dB zpdNalGFTa+45j2VN;GqmxM)abmF$(N2`U@Yflqu!PBxSNPg^~l5 zd`Zbylt9-w6wm{`JEyyap2`emCIj>}Y4&OUTd7ez-Fd84=BtVAN}U4pm&25N)2`Gj z3n=-PlJ6i}09knWgkN2?$Csdo8=tzKc|SAP3f~WauMSsQuB%zpTH`I7QP;%s94)>t z3+8A2=f8)Np(S$5>vHngLXb9>P+AqZR&JfrM#+(N%3?}>V7I}u>sYl*6aw#G@>e=n zIUjDBRhBE~DJv9Jq2e-1exl@OO28BRMhW!)fg6=o$_2`5l*g3UQQky( znerAOre)Zb&I~)87XDk$-Sm_<4mhN&T&-ok;MDLLo&-7Vc(DH@h;oAl0>^{T_|Rxi z>9fH0+^Rvr;ovho3)b7QZbdsQMc@GN8J=b7I-KscK}p%5+zD6UDH|17gE>LT$#!M4 zau?+}%A-k(S)yxlt*35YZEM2NmCt)~KwUq4CMhWoC=W9l4+0Gy<_?J^OP4f z8eMg|e)uattvtsnKf@~Py_KI=UZA{z^2UE%`4#20Q!2l%yg_-9@=_O6!caQf%ef!( zU1irP)pjd;D6df798hhg<&pc%YeTot5x+b1}6;lYLMe5#7**EOV`x| zv)K$O%hQ_>Vh%N1DW5>$0|I>igrS=HxQY33J$hqvlsT>^GV$hqW}uNw`4q|z=o%%f zxw!tAivw$5OS0w^N;1ax!87J~zZqb&?LwIFf^OhhQxoBHIP4>EQxhBKc)trjEwQQ@ zzK05r%M)80n}RsBdhNY>gV_?0MFU51^;0m*8rfoCG}PFT2zCoPZ*k{ z>7b7cABO8QrmecJrfN7W{KJv=g2wuq9ZAV41Ey;nHUN#O+E3tG!)i}S1$tVNdZ#Hp zBQwhejq}2nHNh7PftM+@klR*G7E)m2fSf(Y$6vcW75?tbg$hM=)$IF+&`;W(>|vf8 zzR?vRB(_8WpBQP+Pwnx!cX1tVCWdc+bgJRqL4(WLXK_`;8Mr=NB$vQtaCTT!FXl#b z6S+!mF4xE{xOe4csPfGj~7tIQIg#oqL;mpZgGQDnGy- z>DP!>816`>L|0*ywcXez2evk`oi8?~WJ(B-HdJ&YbhPiw-(sMFBd z_pt;13Am@xoMwh>_D1Nm8RkqgHrveE-maTQ`E=;Ll+U1i=0W_ygjIk4%`iz#pSZhSBonv1}|7)ZOhknQlWlsDE?Esd*cjdSMP za^p(JTE6ad+UAjJ!(HZ4=F#Rc=CNqIdAzyQJVBRfE~9)d@FG3RYObylTN3CM06VGIUGhm&y`?pRy zTUw)G`ak?(u4S12=^Ya0`5GVo&w`j686RhKJ~!YBsvd46FgI(E{x5`>7yToU{v*NW zr5fZgzlN7H$baS-xj(Kps~T>hA9vM1ha1q|3pLzgKklUr_a8XE4y%pkab?}zz4;3B zm6N=mrt*6H2(BNO#yPosZiwM+!*0XphC_xQ41XFjd87Ui*ctZ#AGv7nkekVecZGI zc6okc+GqOQbl7yvbW+rdk{BxX6B9+J2pjLjf#M)>h&W6vgk52i#i`l(AlX8o4 zo3ch(tF$ZYm4{%;@R;(1@)S%2o>N{>-ck-JKbwu_{@|&{o9CNV^R?!7^CRYW%{$Gz z&F`5%Fn?r`EmljoCDsyeNwD;{WLn&oftEp*sg^2>YPrC&-g2*Hi{$~!GnSVv+byqI z_E_Gx9I$+E`NMMDapGAEx>T_kEoBFKl^H`s!`|RlRQIIZ349X2E z3@Q#97gQQFF{nIfa!_SZRZw-1Cum;K%An0bj|F`ibTH`Wpku*YurXK(P7W>&o*7&h zyfC;WxGi{buo}EN_|o93gKr7GE%=V$b-_;rKNl?LI#Em3KL=#Cjh0 zUk`mJbVul}&<{dC3jHMXtI#8%$3stAd8^K9u*z1mwU0Hynr?Ml2U>?(3#>)fk=D`H zvDOOfZ0iDRqqWt#$hyS3+`7U_t(RJFvfg4{XYH_Vux_&6XT9J0p!H$vGuG#Zc`iEtP*}`(doM8oFMPX%O zlftHiO$)0Cn;BLaRu$G1)*RLvwkT{#*g0Y6hMgC7RoI5GO<|kE?hd;*Y)ja_utQ;o z!+s1q8g?w~*Kib0!i8`-+!`Jp9vPk#o)w-Qo)bPId{X$7@M+-{;WgoNBe)1T!WA|oOzB0C~Kq9|fqL`B5Rh{}knh_fT=BN`(XMqC_mOT;4)Z%6Ep z_%h;P#McquL<*59k&eiLk;5ZPBPT|dM^28M8aX|(F>+yKOJrN*;>e|uD7TjZL^O_7@;?~Z&p^3ll0BezDr8@V%bcjSAKM(S0Q3Im}MU_OAMNNvD9W^JaCTea}ZPeLOZBdJ( zmPRd$S{}6`sy%93)c&Ynqr;>7M<++;M(0K6N1qivIC^MwL3B}cS@fjnDbdrSE23vc zS4LMwH$`6`{ZjN-F}j$(G1)OAVkX3t#gxa)jH!;PjcJHk9J4g$+?exXE{wT2=Bk)$ zVy=t1A*Ma%?wEUH9*%i5=JA*(V_u5c7V}2TTQTp%?1(uK^Igo5n4e;PjyVy_#R{?d zSYxa;Has>tc0g=eY({KWY<6r;tTVPKwj_2$?5Nl=vEyP(V<*NgiM=iMh1f6R;lrnseXtK+VYyEg8&xJ_~Q#61@GMBIyUTjSo0`z-EY+@JCK_>g#Od~|$le0+RD zd}@4pd}h2Yesuht_?7V+;vbBEIR2IRSL0uge>48=_;=%X#_x`QFaCr0Z{oj;KNA0A z{L%Ph@xR9Z5r4cN*ROBC)P95eP3%|IZ)v~F`)%&`Y`@+8_V(M?@3Vei_xq*ai3C1D zPB14}5+V}%Cd4GfB@9SNORy(65^@vr5{eUsCyYrLmr$B8DPc;&w1gQ6l?hb|4GB#N zZ3&ALmL{B+peC$L3`>kiOiD~i%t*{i%udWr%u5`YI3jUWVp-y(#3_l>5-SpG66Yq? zCeBZ6NL-nCS>n}+w4v16l5R5Zh_ zN$(|noU}LTv!w5mjwJn-^heV1q?5@g*_13Lo0Ef*Lz82YQcNjgN^D9+N^Q#7DJ>~&DT`BnU%gyqEI9fR+L04!B^zg##`caNB_W1Aa|imbyH3b?QZ_7pGp6dR^-EsqLxj zQ#YkKOuIepuC#m7UP^m8?bWn5(%w$n zk+wVS{j`tL_NIN7wmzATbu$qLGf$coB}&FYsmAS*p9E6bi$merbdW7gWN z_N8QS)XL>%labg%dBs+j%59mb<9R=g3Vwv*@A7Mw%N7?wnkgC zt<|>Nw!*gEw%hie?L*tgwga}WvIl05$R3qlmOVLpTK0_W%Iw>t`cwSR6uVE@kkhy8etDM!w+ zISX?x%Xu%sHbRF^*VAoFl`L1?O(F9r=#4977xhj!H+pqruVWXmTue zsE(D63mg|YZgzAyHaa#t?s05!{O%-9i!;a>;f!*|I{P^XIMbb3PWXJgbEvc0x!ie) z^IGTi&YPUKI`4Ge<-FT@kMj}dZs*6&KXZd}({tUq19OXVhv$yU9h+O4J1uucZe{MA zTu<&*xzFZ)>xyy>aHY92T{c&aE7vu|HOy7w8tEG2n&zr-&34Uk)wt?h^IZ+D#jaJZ z)viljm%FZXUFW*Nb+fD8wZ-*->qXZ#*LK(IuD4w8x^}tVbA9MK?E232gX^g47uWBu z<9S>j$&1MwmNz4Bao&c!=kq>t^X@+GV0VZ+)NOSqxcj@4+{tc-JJ;=U=edWu3*9B| zk?y(f7I&L_k$bUwm3y`OLia`PtKHYSZ*br2zRP{D`+oOB?nm9vxnIaPZG#-3Nog zXk$0^v}sdsJQ;Xf3TuE-yNJ-Lm442^!#odehuwjK_Wo2c>?aMiz^Zn(0|A+T^ z&w19xZHU_#w<)eJ?t0vfxcaz;xMy*VaZPc}ai8P9#kYxXAKxjyOMHC1GQKMQSo~{B zSIPj&Aj(k62+C;6I7&Pvk&;YFr({yHDME^vqM)cLT8fEMOev!fl;0^!DH|!9DYcYc zl)aQgl%tdrlnazQl)IEBN;9Q}@{aP6@`c)p+LqdZ+K)PrI)pl$I*K}$I)OTw`ZJY9 z)l+@c71Ybrm$Z(wX*4R0PGisjS|*J{%ckYfL^KIaN|VuaGy|=eRz|bYBDB@CTG~$9 zF4}I|3EC-I9qlyj3hf&02JJSjk=8_OrnS)C(KG1zbSYg<*U}AiGrffFpd)mYPS6AN zP4qhYHTqrpeR@5;f&QA_LVrVl%jm%v!WhN~Ga`&C#_x>zj75y4j1`PkjJ1pnjLnQ~ zj2(>KjD3uQj3bQWj8lv=jPs03jH`^l7`GUIGww6$84ZkQj7CNiqnXjdc*pq2_yV*7 z+5#PbAAqhvcc2&W6VM+R1Pld60Hc9%z(im&@H4O&SO)wFtOnKr8-Xprc3>y42iOlB z0*(SFfI8qTZ~?dsTmxB#UxH@r5;-p^-mj-HY%+w z4Nvo=`O+%VVrkWBEv(k8wyX}UPOP4+-Ygx<#&WO_mYd~g1z0y(4XmfE7p#}8H>`K* zN$EN1dFh4e+;nNWJbiEasr1w7=h82v-$=ie@k<6h1IS3sNY2R0$j(@vu{mRFMs3E< zj6)em*d5rt**~%Svj?(AvB$8B*={z*_OMCzTy~UQ&wj;jX1B24vcF}v&J<>9GxeFK zOiQLC6V5!Fc{B5N=H1Nuna?s`WTj;lWEExcv%oB6mO5)+R$bPatn*nHvu6P#0=OPnj&{jx`AkIkNtJt@018_OoL$!vf2?JTsYU8 z>&v~L`!csFw>kH}+|RjR^NR8md8#}pPnTDkSDtq{?_S=6yhnKrc};o$=F{@i^V#{F z{G5D#KA2ydel8h{LA@Q3x*aBk}_yTId(t?czn+vuT)D|2lI8@lT za8%)#!tsR@3uhGmS{N=|P`IdYY2ot1^@SUY+7|UF>Q(epQNN<$MI(!fi`+$6k*A0( znp+etx>5AF=tfJI zN&ZFtP5wiE6aQcS2mWXNH$fXgdqForPeC6+U%>#uNWoOWZ-OL2ihv`?6%+`#0)apw zPz$sIy}%@}2{1uGFi$XFutKm(uvV}^uvxHMuupJMa9D6ma8B@-;E|v~&?NX*@LJF! zcq{k@wgKCN9l_3EZ*Vv`5u6Rq0hwSDmWSg7zC@q-@!k? z1>hoZ6}T4M0B!=ef_uT^;05pwco%#EJ_lcdufW&fC-AGVwXm(QgK&f}RahWIgrqPc zoG)B0Tr1or+#%d8+$TIJJS99MJTJT`ydu0Kd@O7deiD8Wbr5wDbry9Mbrbay4HOL# z4HJzNO%lx%QAI3KhA3ZDB;t#NB8dnR=|v`yMN}$6M5Ji0Xpv}%XpLySXp?BGs8)19 zbXasubV5`odL-^F?k{GD)5JMqzE~}W#Kqz=u}$n0yTm?mKpYm&701L2#cRde#fQa5 z#b?DA#Fxd_#5cqb#E-;J#LvWy;uc9ONoPqv$pFbn$!N(~$#}^`$qdOX$s9?%geFOm za3oxbQlgfaCB>3bNx8%(!6Y7uPf{TXNopj^B^xArB>N=CB_}0yk~5NXlIxP2k~@;S zk_VFKlDE=U(jTSWrTwJ?rGuqIrNgBYq?4sTOQ%a`N~zLhDMt!QMN+j?D>X>X(h?~w zbxU!nN9vPCq<=_PN!LiXN^7ONqC$;QaW%cjd{vQ$~VtWYMFNo8`GQl^%fWhJt5nN8-Dd1RHcd9szV)v_(J zZL(U~PT6kR5!rFsDcNb+IoWmDL)i=2JJ|<$8+m(qCwUinH+et#K=}~)F!@ONB>As$ zraV#3mUHB}@&Y+mF8$7o)N)9!mzT-ia=*MrK2N?({-=Dke4Tuwe5ZVme82pl{D}OF z{HpwK`Ez-ryhZ*_{!#u#(Ms`yqN}32qNk#dVu)g_;%5a#K~tnCSPHg+qsUbV6e5N6 zJCag;*K|1*UPVk%tyrvBrub8_TCq;CU9nTKN3ls}rsQ5?mR`EgcS@BKT zM%hK#P1#e~TiI7ROgUaTT}e|ilxfNgWtK8WnXeQoB}%#S`x33RC>=_lvO*bC)+ql_ zE>td2u2HU6Zc=VhZdYDVzEQPRO;XKN0jf+DN5xeMR3epBrBE4EW>tx*Ol4D5srIQ( ztD03G)os;1)V>UsK;zKTtnX zKT$tdzgE9dzgK@$f6;W%bl3FPjM0qKOw-KJ%+k!!P&7%JR86{ut>I|68mUIBv1;rZ zT;tXFH9<|KW}aq&X0c|eW`$;hW~b(`=Dg;j=BDP3=AP!E=CS4<&A*!eG;cKTq1I3r zs1Gy(8U;;;eujR5euaL604NbkfzqH1C?66+T1XF-K{m(oL+7DO&{gO!=oVBDH9*gx7tlY@Tj-m%llCWVKkabsDD7D71np$)uiD?V@mi{u zp-t1~X!%;T7Sa}L%d|GFQ|r?Dv;l2cJ69XiF4V5nZqn}49?+iD)@jdZ&uK4cZ)k68 z?`rRB>$Q#A7VQ^ZXI)p_Pr81(0lGoDA-b`;3A)L;sk-U9I9-B{qs!6pb)Zh9ljvl+ z@A3zoMOUIL*SU2;U5##qZl!LMZmX_Vw@bHIcT9IucUpH=cR}}D-$OrCpRCW)7wHvx zmENSc=u7lvdaEAQ6M9nb*9Z00`o;Ry`da-?{UQBP{Rw@Y{;d9*{)Yax{%`$#{WE>D z{-dFz;Ri!6Lmxw5Lx00S!zja8!vw=5!&Jj;17Juu6d8C1nL%mL7<2}s!D?_A5QE!* z8-j*v!(zjF!$w1`VV7aA;eg?|;gsQw;k@Ct;cvrz!$V_V<0#`8<9Oplqv$(NH5kpt zV&fX)4&yH4UgH7dVdF95ed9}Gld;*@Vti-(XliR}Z|Y>~Vj6CmVWOJoCWa}&lx#{f z6__9sY;v10Q^54QX`yM2X{+gs>9XmCxs~}xb9Zwu^H1jf=0WD!W`>z*PBN#OL9@s# zHp|RPv&LLz_L&3bu=#iM67zENO7kZ3e)BQ&8S@SEEpwB(+1z4&XZ~pZVrgY*Yw2M5 z!P3<-!ZO-2&N9(5#WKw@!!pY<$3n5tEg6lUKZyU zON-^js$xyCrMRT{dGXug_r;%zzm#++=~Ut<@s(7Rgi0z)=9SDZ{jqdl>EO~~r6Wow zmQF4WmCi3+Sh}QiS?Rjc4Q0K{hL?>j8&fu}Y+BhbW%J8cm#r<^P`0UTSJ|HOq2-gx zr<6}C|D`;>oLauE{80Ik@-yY<%P*B*E&r?hetCU)L;17vMr%jw1S{2AU=>*hH8?3LbE!H>Ix3;#n_O=eTje)a+O zf%ZZ6@%D-K8TMKBId+PjZcnwR+cWLi_B^}E9<{HsAF`jbU$9@d-?ZPc-?KN^pV=Gj zO^!~EVUBo5s)OZ7cVsxY4!%R+03Aw4v7^-CcHj=L!|w<>DjoA3iyTWGD;%pFYaO+Y z!;YuU*3R+H=}w9>&6)1ZbY?sAoP|!VQ|{C|jZTZR)QLF>r^o4a#+=p8#m;5UKb@zv!2JDq!+`<;iJb3HO3~!^7c` z@F;jRJOz$}DR2tRg4r+!&V>tL2`qMSK#aLP52J{7=8-Bfd7I2g+IWb;IBw)q#M!$>4ywNh9JX{amYku3Nj6$ zB6Ng-07xdnL9&q?gpa5Z2q{IZhyy_o6d{mGB#Klc^NWE_t5reH*_#M0iA?SMyH^&(BIH_l!h`;HkyTU&}>wQ zict-!Lyf2fEkzL&MG2HdE70H3KhUM<3Un2^7TtnwM|Yxo(35B#dKx{0-av1mx6wQ3 zW3(Cl5B-X@#@b;WvCi0!SbuB~HWV9yjmE}d(=a-gfn{P@7zg8Gd`yT*Fau`7%$Nmp zVJKFCg|N9;46DJGV9T+U*cxm-wj0}v9l?%cr?4~FW$YSu1G|kq#hzm?utw}X_7VGp zea744z3@KxFnlCF1|N@4!l&YMa0*VxnRpVOif7|uT#p-Z6K=-sxD$8b7+#4-@F*U` zm*UIu_4p=yD_)E5!Vlxe@RRsy{2YD@zk}D~4fr#>5r2)p!QbPbh)zUjq6^WL7(fgn z1`|VwvBV7GSAsz#5XnRukwIh;JOU)dgp5!U8p1-rL{;G zDw$4Zl6(>*<)n&)NIh9XmXmf8CM(Dw86v~v0&)?#m|Q}xA$O3w$fM*5vW`4UULY@% zf0Osgda{9hMmCZylES{^|ai{@MOGf2u#vFY(L#CV#QN%y08M{VqS|uk=U#)&6<@1^&hU zW&Tb6E&lEP9Toj6##D@}m{>8n0@J_7V$!dJt8h3mtw!p-6L;ZNbOm8~l~Rd%WDR@t+1VC9I)iIr0# zp~(Em!pPdlhREi~w#bgi?#RAKUF2-!LgaGfTI5FLcH~=hbTl(+jiS+tXf(PYx;VNz zx+%IndNNuUy&Sz7eHLwuHbtAGEz!5p57E!jZ?QJ9_OV{EKCynW0kM&>(J@EN7psVc zVwJIZvH7uAvCpxuRjsSqR&}lFR#jd_RC%lXRe`Fis+y`7Rqv`kRDG`cTHUewhib4I zs@7E-tIgH+YG?KJ>iX)()laLRSHG@qsbSafY6LZ+8c7XQqpLYubFJo9&HewqaN4x_ OUw5DTzyANMdH8>MTi7K4 literal 36958 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 en CFBundleDisplayName - ${PRODUCT_NAME} + 5by5 Browser CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier @@ -24,6 +24,8 @@ 1.0 LSRequiresIPhoneOS + UIStatusBarStyle + UIStatusBarStyleBlackTranslucent UIRequiredDeviceCapabilities armv7 diff --git a/5by5Browser/Episode.h b/5by5Browser/Episode.h index 84b4ab6..c6d96f2 100644 --- a/5by5Browser/Episode.h +++ b/5by5Browser/Episode.h @@ -7,14 +7,18 @@ // #import +#import "Show.h" + +@class Show; @interface Episode : NSObject @property (nonatomic, retain) NSString *name; @property (nonatomic, retain) NSString *number; +@property (nonatomic, retain) Show *show; @property (nonatomic, retain) NSURL *url; -+ (id) episodeWithName: (NSString *)name number: (NSString *)number url: (NSURL *)url; -- (id) initWithName: (NSString *)name number: (NSString *)number url: (NSURL *)url; ++ (id) episodeWithShow: (Show *)show name: (NSString *)name number: (NSString *)number url: (NSURL *)url; +- (id) initWithShow: (Show *)show name: (NSString *)name number: (NSString *)number url: (NSURL *)url; @end diff --git a/5by5Browser/Episode.m b/5by5Browser/Episode.m index 0b076ef..7f046d2 100644 --- a/5by5Browser/Episode.m +++ b/5by5Browser/Episode.m @@ -12,17 +12,19 @@ @synthesize name = _name; @synthesize number = _number; +@synthesize show = _show; @synthesize url = _url; -+ (id) episodeWithName: (NSString *)name number: (NSString *)number url: (NSURL *)url ++ (id) episodeWithShow: (Show *)show name: (NSString *)name number: (NSString *)number url: (NSURL *)url { - return [[self alloc] initWithName: name number: number url: url]; + return [[self alloc] initWithShow: show name: name number: number url: url]; } -- (id) initWithName: (NSString *)name number: (NSString *)number url: (NSURL *)url +- (id) initWithShow: (Show *)show name: (NSString *)name number: (NSString *)number url: (NSURL *)url { self = [super init]; if (self) { + self.show = show; self.name = name; self.number = number; self.url = url; @@ -30,4 +32,13 @@ return self; } +- (void) setName: (NSString *)name +{ + if (self.show && [name hasPrefix: self.show.name]) { + NSString *showName = [self.show.name stringByAppendingString: @" "]; + name = [name stringByReplacingOccurrencesOfString: showName withString: @""]; + } + _name = name; +} + @end diff --git a/5by5Browser/MMHTTPClient.h b/5by5Browser/MMHTTPClient.h new file mode 100644 index 0000000..9afd176 --- /dev/null +++ b/5by5Browser/MMHTTPClient.h @@ -0,0 +1,54 @@ +// +// MMHTTPClient.h +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import +#import "MMHTTPRequest.h" + +@interface MMHTTPClient : NSObject +{ + NSString *_baseURL; + NSUInteger _timeout; +} + ++ (MMHTTPClient *) sharedClient; ++ (id) client; ++ (id) clientWithBaseURL: (NSString *)baseURL; ++ (id) clientWithBaseURL: (NSString *)baseURL timeout: (NSUInteger)timeout; + ++ (NSString *) pathFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; ++ (NSString *) urlFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; ++ (NSString *) urlWithPath: (NSString *)path; ++ (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback; ++ (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback; ++ (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback; + +@property (nonatomic, retain) NSString *baseURL; +@property NSUInteger timeout; + +- (id) initWithBaseURL: (NSString *)baseURl; +- (id) initWithBaseURL: (NSString *)baseURl timeout: (NSUInteger)timeout; +- (NSString *) pathFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; +- (NSString *) urlFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; +- (NSString *) urlWithPath: (NSString *)path; +- (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback; +- (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback; +- (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback; + +@end diff --git a/5by5Browser/MMHTTPClient.m b/5by5Browser/MMHTTPClient.m new file mode 100644 index 0000000..9f03308 --- /dev/null +++ b/5by5Browser/MMHTTPClient.m @@ -0,0 +1,252 @@ +// +// MMHTTPClient.m +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import "MMHTTPClient.h" +#import "NSString+marshmallows.h" + +MMHTTPClient *_client; + +NSString *JoinURLComponents(NSString *first, va_list args) +{ + NSMutableString *url = [NSMutableString string]; + NSString *slash = @""; + for (NSString *arg = first; arg != nil; arg = va_arg(args, NSString *)) { + [url appendFormat: @"%@%@", slash, [arg stringByURLEncoding]]; + slash = @"/"; + } + return [NSString stringWithString: url]; +} + +@implementation MMHTTPClient + +@synthesize baseURL = _baseURL; +@synthesize timeout = _timeout; + ++ (MMHTTPClient *) sharedClient +{ + if (!_client) { + _client = [[self alloc] init]; + } + return _client; +} + ++ (id) client +{ + return [[self alloc] init]; +} + ++ (id) clientWithBaseURL: (NSString *)baseURL +{ + return [[self alloc] initWithBaseURL: baseURL]; +} + ++ (id) clientWithBaseURL: (NSString *)baseURL timeout: (NSUInteger)timeout +{ + return [[self alloc] initWithBaseURL: baseURL timeout: timeout]; +} + ++ (NSString *) pathFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = JoinURLComponents(first, args); + va_end(args); + return url; +} + ++ (NSString *) urlFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = [[[self sharedClient] baseURL] stringByAppendingString: JoinURLComponents(first, args)]; + va_end(args); + return url; +} + ++ (NSString *) urlWithPath: (NSString *)path +{ + return [[[self sharedClient] baseURL] stringByAppendingPathComponent: path]; +} + ++ (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback +{ + return [[self sharedClient] request: options then: callback]; +} + ++ (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback +{ + return [[self sharedClient] get: url then: callback]; +} + ++ (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback +{ + return [[self sharedClient] getImage: url then: callback]; +} + ++ (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback +{ + return [[self sharedClient] getText: url then: callback]; +} + ++ (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback +{ + return [[self sharedClient] post: url then: callback]; +} + ++ (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback +{ + return [[self sharedClient] post: url fields: fields then: callback]; +} + ++ (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + return [[self sharedClient] post: url data: data then: callback]; +} + ++ (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + return [[self sharedClient] put: url data: data then: callback]; +} + ++ (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback +{ + return [[self sharedClient] delete: url then: callback]; +} + +- (id) init +{ + return [self initWithBaseURL: nil timeout: MMHTTPRequestDefaultTimeout]; +} + +- (id) initWithBaseURL: (NSString *)baseURL +{ + return [self initWithBaseURL: baseURL timeout: MMHTTPRequestDefaultTimeout]; +} + +- (id) initWithBaseURL: (NSString *)baseURL timeout: (NSUInteger)timeout +{ + self = [super init]; + if (self) { + _baseURL = [baseURL copy]; + _timeout = timeout; + } + return self; +} + +- (NSString *) pathFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = JoinURLComponents(first, args); + va_end(args); + return url; +} + +- (NSString *) urlFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = [_baseURL stringByAppendingString: JoinURLComponents(first, args)]; + va_end(args); + return url; +} + +- (NSString *) urlWithPath: (NSString *)path +{ + return [_baseURL stringByAppendingPathComponent: path]; +} + +- (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback +{ + return [self request: [NSDictionary dictionary] then: (MMHTTPCallback)callback]; +} + +- (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + url, @"url", + @"text", @"type", + nil]; + return [self request: options then: (MMHTTPCallback)callback]; +} + +- (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + url, @"url", + @"image", @"type", + nil]; + return [self request: options then: (MMHTTPCallback)callback]; +} + +- (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"POST", @"method", + url, @"url", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback +{ + NSMutableArray *parts = [NSMutableArray array]; + NSString *value; + for (NSString *key in [fields keyEnumerator]) { + value = [fields objectForKey: key]; + [parts addObject: [NSString stringWithFormat: @"%@=%@", [key stringByURLEncoding], [value stringByURLEncoding]]]; + } + NSString *body = [parts componentsJoinedByString: @"&"]; + return [self post: url data: [body dataUsingEncoding: NSUTF8StringEncoding] then: callback]; +} + +- (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"POST", @"method", + url, @"url", + data, @"data", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"PUT", @"method", + url, @"url", + data, @"data", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"DELETE", @"method", + url, @"url", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback +{ + NSString *url = [options objectForKey: @"url"]; + NSMutableDictionary *mutableOptions = [options mutableCopy]; + if (_baseURL && !([url hasPrefix: @"http:"] || [url hasPrefix: @"https:"])) { + [mutableOptions setObject: [self urlWithPath: url] forKey: @"url"]; + } + NSUInteger timeout = [[options objectForKey: @"timeout"] unsignedIntValue]; + if (timeout == 0) { + [mutableOptions setValue: [NSNumber numberWithUnsignedInt: self.timeout] forKey: @"timeout"]; + } + options = [NSDictionary dictionaryWithDictionary: mutableOptions]; + return [MMHTTPRequest requestWithOptions: options callback: callback]; +} + +@end diff --git a/5by5Browser/MMHTTPRequest.h b/5by5Browser/MMHTTPRequest.h new file mode 100644 index 0000000..7448024 --- /dev/null +++ b/5by5Browser/MMHTTPRequest.h @@ -0,0 +1,43 @@ +// +// MMHTTPRequest.h +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import +#import + +#define MMHTTPRequestStatusError -1 +#define MMHTTPRequestDefaultTimeout 120 + +typedef void (^MMHTTPCallback)(NSInteger status, id data); +typedef void (^MMHTTPTextCallback)(NSInteger status, NSString *text); +typedef void (^MMHTTPImageCallback)(NSInteger status, UIImage *image); + +@interface MMHTTPRequest : NSObject +{ + NSMutableData *_responseData; +} + +@property (nonatomic, retain) NSURLConnection *connection; +@property (nonatomic, retain) NSMutableURLRequest *request; +@property (nonatomic, retain) NSString *method; +@property (nonatomic, retain) NSString *url; +@property (nonatomic, retain) NSMutableDictionary *headers; +@property (nonatomic, retain) NSData *data; +@property (nonatomic, retain) NSString *type; +@property (nonatomic, copy) MMHTTPCallback callback; +@property NSUInteger timeout; +@property (readonly) NSInteger statusCode; +@property (readonly) NSDictionary *responseHeaders; +@property (readonly) NSData *responseData; +@property (readonly) NSString *responseText; +@property (readonly) UIImage *responseImage; + ++ (id) requestWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback; +- (id) initWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback; +- (void) cancel; + +@end diff --git a/5by5Browser/MMHTTPRequest.m b/5by5Browser/MMHTTPRequest.m new file mode 100644 index 0000000..d79b9c6 --- /dev/null +++ b/5by5Browser/MMHTTPRequest.m @@ -0,0 +1,147 @@ +// +// MMHTTPRequest.m +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import "MMHTTPRequest.h" + +@interface MMHTTPRequest () +- (void) _start; +@end + + +@implementation MMHTTPRequest + +@synthesize connection = _connection; +@synthesize request = _request; +@synthesize method = _method; +@synthesize url = _url; +@synthesize headers = _headers; +@synthesize data = _data; +@synthesize type = _type; +@synthesize callback = _callback; +@synthesize timeout = _timeout; +@synthesize statusCode = _statusCode; +@synthesize responseHeaders = _responseHeaders; + ++ (id) requestWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback +{ + return [[self alloc] initWithOptions: options callback: callback]; +} + +- (id) initWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback +{ + self = [super init]; + if (self) { + self.callback = [callback copy]; + self.timeout = MMHTTPRequestDefaultTimeout; + self.method = [options objectForKey: @"method"]; + self.url = [options objectForKey: @"url"]; + self.headers = [options objectForKey: @"headers"]; + self.data = [options objectForKey: @"data"]; + self.type = [options objectForKey: @"type"]; + if (!self.method) self.method = @"GET"; + [self _start]; + } + return self; +} + +- (void) cancel +{ + [_connection cancel]; +} + +- (NSData *) responseData +{ + return [NSData dataWithData: _responseData]; +} + +- (NSString *) responseText +{ + return [[NSString alloc] initWithBytes: _responseData.bytes + length: _responseData.length + encoding: NSUTF8StringEncoding]; +} + +- (UIImage *) responseImage +{ + return [UIImage imageWithData: _responseData]; +} + +- (void) _start +{ + self.request = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: self.url] + cachePolicy: NSURLRequestUseProtocolCachePolicy + timeoutInterval: self.timeout]; + [self.request setHTTPMethod: self.method]; + + if (self.data && ([self.method isEqualToString: @"POST"] || [self.method isEqualToString: @"PUT"])) { + [self.request setHTTPBody: self.data]; + } + + if (self.headers) { + for (NSString *key in self.headers) { + [self.request setValue: [self.headers objectForKey: key] forHTTPHeaderField: key]; + } + } + + self.connection = [NSURLConnection connectionWithRequest: self.request delegate: self]; + [self.connection start]; +} + +#pragma mark - NSURLConnection delegate methods + +- (void) connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response +{ +// NSLog(@"didReceiveResponse: %@",response); + if ([response respondsToSelector: @selector(statusCode)]) + { + _statusCode = [(NSHTTPURLResponse *)response statusCode]; + _responseHeaders = [(NSHTTPURLResponse *)response allHeaderFields]; + } + else { + NSLog(@"Not an HTTP response? connection: %@ response: %@", connection, response); + _statusCode = 500; + _responseHeaders = [[NSDictionary alloc] init]; + } + + _responseData = [[NSMutableData alloc] init]; +} + +- (void) connection: (NSURLConnection *)connection didReceiveData: (NSData *)data +{ +// NSLog(@"didReceiveData: %@", data); + [_responseData appendData: data]; +} + +- (void) connection: (NSURLConnection *)connection didFailWithError: (NSError *)error +{ + NSLog(@"didFailWithError: %@", error); + _responseData = nil; + _statusCode = MMHTTPRequestStatusError; + self.callback(self.statusCode, error); +} + +- (void) connectionDidFinishLoading: (NSURLConnection *)connection +{ +// NSLog(@"didFinishLoading: %d", self.statusCode); + id data = nil; + if (self.statusCode == 200) { + if ([self.type isEqualToString: @"text"]) { + data = self.responseText; + } + else if ([self.type isEqualToString: @"image"]) { + data = self.responseImage; + } + else { + data = self.responseData; + } + } + + self.callback(self.statusCode, data); +} + +@end diff --git a/5by5Browser/NSString+marshmallows.h b/5by5Browser/NSString+marshmallows.h new file mode 100644 index 0000000..57b2f10 --- /dev/null +++ b/5by5Browser/NSString+marshmallows.h @@ -0,0 +1,19 @@ +// +// NSString+marshmallows.h +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import + +@interface NSString (NSString_marshmallows) + +- (NSString *) firstMatch: (NSString *)pattern; +- (NSString *) stringByReplacing: (NSString *)pattern with: (NSString *)replacement; +- (NSString *) stringByReplacingFirst: (NSString *)pattern with: (NSString *)replacement; +- (NSString *) stringByTrimmingWhitespace; +- (NSString *) stringByURLEncoding; + +@end diff --git a/5by5Browser/NSString+marshmallows.m b/5by5Browser/NSString+marshmallows.m new file mode 100644 index 0000000..5267c83 --- /dev/null +++ b/5by5Browser/NSString+marshmallows.m @@ -0,0 +1,76 @@ +// +// NSString+marshmallows.m +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import "NSString+marshmallows.h" + +// Encode a string to embed in an URL. +NSString* URLEncode(NSString *string) { + return (__bridge NSString *) + CFURLCreateStringByAddingPercentEscapes(NULL, + (__bridge CFStringRef) string, + NULL, + (CFStringRef) @"!*'();:@&=+$,/?%#[]", + kCFStringEncodingUTF8); +} + + +@implementation NSString (NSString_marshmallows) + +- (NSString *) stringByTrimmingWhitespace +{ + return [self stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; +} + +- (NSString *) firstMatch: (NSString *)pattern +{ + NSRegularExpression *regex = [NSRegularExpression + regularExpressionWithPattern: pattern + options: 0 + error: NULL]; + NSRange match = [regex rangeOfFirstMatchInString: self + options: NSMatchingCompleted + range: NSMakeRange(0, self.length)]; + return match.location == NSNotFound ? nil : [self substringWithRange: match]; +} + +- (NSString *) stringByReplacing: (NSString *)pattern with: (NSString *)replacement +{ + NSRegularExpression *regex = [NSRegularExpression + regularExpressionWithPattern: pattern + options: NSRegularExpressionCaseInsensitive + error: NULL]; + return [regex stringByReplacingMatchesInString: self + options: NSMatchingCompleted + range: NSMakeRange(0, [self length]) + withTemplate: @""]; +} + +- (NSString *) stringByReplacingFirst: (NSString *)pattern with: (NSString *)replacement +{ + NSRegularExpression *regex = [NSRegularExpression + regularExpressionWithPattern: pattern + options: 0 + error: NULL]; + NSRange match = [regex rangeOfFirstMatchInString: self + options: NSMatchingCompleted + range: NSMakeRange(0, self.length)]; + if (match.location != NSNotFound) { + NSString *rest = [self substringFromIndex: match.location + match.length]; + return [[[self substringToIndex: match.location] + stringByAppendingString: replacement] + stringByAppendingString: rest]; + } + return [self copy]; +} + +- (NSString *) stringByURLEncoding +{ + return URLEncode(self); +} + +@end diff --git a/5by5Browser/SSAppDelegate.m b/5by5Browser/SSAppDelegate.m index 8105000..bec0a9d 100644 --- a/5by5Browser/SSAppDelegate.m +++ b/5by5Browser/SSAppDelegate.m @@ -25,7 +25,7 @@ FiveByFive *fiveByFive = [[FiveByFive alloc] initWithBaseURL: @"http:/feeds.feedburner.com/"]; [fiveByFive addShow: [Show showWithName: @"Back to Work" path: @"back2work"]]; [fiveByFive addShow: [Show showWithName: @"Build and Analyze" path: @"buildanalyze"]]; - [fiveByFive addShow: [Show showWithName: @"Critical Path" path: @"criticalpath"]]; + [fiveByFive addShow: [Show showWithName: @"The Critical Path" path: @"criticalpath"]]; [fiveByFive addShow: [Show showWithName: @"Geek Friday" path: @"GeekFriday"]]; [fiveByFive addShow: [Show showWithName: @"Hypercritical" path: @"hypercritical"]]; [fiveByFive addShow: [Show showWithName: @"The Talk Show" path: @"thetalkshow"]]; @@ -35,13 +35,16 @@ SSMasterViewController *masterViewController = [[SSMasterViewController alloc] initWithNibName:@"SSMasterViewController_iPhone" bundle:nil]; masterViewController.fiveByFive = fiveByFive; self.navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController]; + [self.navigationController.navigationBar setTintColor: [UIColor colorWithWhite: 0.3 alpha: 1.0]]; self.window.rootViewController = self.navigationController; } else { SSMasterViewController *masterViewController = [[SSMasterViewController alloc] initWithNibName:@"SSMasterViewController_iPad" bundle:nil]; UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController]; + [masterNavigationController.navigationBar setTintColor: [UIColor colorWithWhite: 0.3 alpha: 1.0]]; SSDetailViewController *detailViewController = [[SSDetailViewController alloc] initWithNibName:@"SSDetailViewController_iPad" bundle:nil]; UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController]; + [detailNavigationController.navigationBar setTintColor: [UIColor colorWithWhite: 0.3 alpha: 1.0]]; masterViewController.fiveByFive = fiveByFive; diff --git a/5by5Browser/SSDetailViewController.h b/5by5Browser/SSDetailViewController.h index 4bdc17c..2464b0b 100644 --- a/5by5Browser/SSDetailViewController.h +++ b/5by5Browser/SSDetailViewController.h @@ -14,9 +14,13 @@ @property (strong, nonatomic) Episode *episode; @property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel; @property (strong, nonatomic) IBOutlet UIWebView *webView; +@property (strong, nonatomic) IBOutlet UIToolbar *toolbar; @property (strong, nonatomic) IBOutlet UIBarButtonItem *backButton; @property (strong, nonatomic) IBOutlet UIBarButtonItem *forwardButton; +@property (strong, nonatomic) IBOutlet UIBarButtonItem *instapaperButton; +@property (strong, nonatomic) IBOutlet UIView *loadingView; -- (IBAction) goHome; +- (IBAction) goHome: (id)sender; +- (IBAction)sendToInstapaper: (id)sender; @end diff --git a/5by5Browser/SSDetailViewController.m b/5by5Browser/SSDetailViewController.m index cc3bdbe..233afc4 100644 --- a/5by5Browser/SSDetailViewController.m +++ b/5by5Browser/SSDetailViewController.m @@ -7,6 +7,9 @@ // #import "SSDetailViewController.h" +#import "MMHTTPClient.h" +#import "InstapaperCredentials.h" +#import "UIAlertView+marshmallows.h" @interface SSDetailViewController () @property (strong, nonatomic) UIPopoverController *masterPopoverController; @@ -18,8 +21,11 @@ @synthesize episode = _episode; @synthesize detailDescriptionLabel = _detailDescriptionLabel; @synthesize webView = _webView; +@synthesize toolbar = _toolbar; @synthesize backButton = _backButton; @synthesize forwardButton = _forwardButton; +@synthesize instapaperButton = _instapaperButton; +@synthesize loadingView = _loadingView; @synthesize masterPopoverController = _masterPopoverController; #pragma mark - Managing the detail item @@ -40,19 +46,16 @@ - (void)configureView { - // Update the user interface for the detail item. - if (self.episode) { self.title = self.episode.name; self.detailDescriptionLabel.text = self.episode.name; - [self.webView loadRequest: [NSURLRequest requestWithURL: self.episode.url]]; + [self goHome: nil]; } } - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. [self configureView]; } @@ -61,6 +64,9 @@ [self setWebView:nil]; [self setBackButton:nil]; [self setForwardButton:nil]; + [self setInstapaperButton:nil]; + [self setToolbar:nil]; + [self setLoadingView:nil]; [super viewDidUnload]; // Release any retained subviews of the main view. self.detailDescriptionLabel = nil; @@ -75,17 +81,50 @@ } } +- (void) webViewDidStartLoad: (UIWebView *)webView +{ + [UIView animateWithDuration: 1.0 animations: ^{ + self.loadingView.alpha = 1.0; + }]; +} + - (void) webViewDidFinishLoad: (UIWebView *)webView { + [UIView animateWithDuration: 0.3 animations: ^{ + self.loadingView.alpha = 0.0; + }]; [self.backButton setEnabled: webView.canGoBack]; [self.forwardButton setEnabled: webView.canGoForward]; } -- (IBAction) goHome +- (IBAction) goHome: (id)sender { - while (self.webView.canGoBack) { - [self.webView goBack]; - } + [self.webView loadRequest: [NSURLRequest requestWithURL: self.episode.url]]; +} + +- (IBAction) sendToInstapaper: (id)sender +{ + NSString *url = self.webView.request.URL.absoluteString; + NSDictionary *fields = [NSDictionary dictionaryWithObjectsAndKeys: + kInstapaperUser, @"username", + kInstapaperPassword, @"password", + url, @"url", + nil]; + + UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite]; + [indicatorView startAnimating]; + UIBarButtonItem *indicatorItem = [[UIBarButtonItem alloc] initWithCustomView: indicatorView]; + NSMutableArray *items = [[self.toolbar items] mutableCopy]; + NSInteger i = [items indexOfObject: self.instapaperButton]; + [items replaceObjectAtIndex: i withObject: indicatorItem]; + [self.toolbar setItems: items]; + [MMHTTPClient post: @"https://www.instapaper.com/api/add" fields: fields then: ^(NSInteger status, id data) { + [items replaceObjectAtIndex: i withObject: self.instapaperButton]; + [self.toolbar setItems: items]; + if (status != 201) { + [UIAlertView showAlertWithTitle: @"Error" message: @"Failed to send to Instapaper. Try again later."]; + } + }]; } #pragma mark - Split view diff --git a/5by5Browser/SSMasterViewController.m b/5by5Browser/SSMasterViewController.m index 823ef6b..6ade02b 100644 --- a/5by5Browser/SSMasterViewController.m +++ b/5by5Browser/SSMasterViewController.m @@ -107,7 +107,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [[self.fiveByFive.shows objectAtIndex: indexPath.row] getEpisodes]; - UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray]; + UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite]; [indicatorView startAnimating]; [tableView cellForRowAtIndexPath: indexPath].accessoryView = indicatorView; } diff --git a/5by5Browser/Show.h b/5by5Browser/Show.h index effdcfc..bcb3381 100644 --- a/5by5Browser/Show.h +++ b/5by5Browser/Show.h @@ -11,6 +11,8 @@ #import "Episode.h" #import "FiveByFive.h" +@class Episode; + @protocol ShowDelegate - (void) gotEpisodesForShow: (Show *)show; @end diff --git a/5by5Browser/Show.m b/5by5Browser/Show.m index 8037b14..0b738c2 100644 --- a/5by5Browser/Show.m +++ b/5by5Browser/Show.m @@ -79,7 +79,8 @@ - (void) feedParser: (MWFeedParser *)parser didParseFeedItem: (MWFeedItem *)item { NSLog(@"feed item: %@", item); - [self addEpisode: [Episode episodeWithName: item.title number: @"" url: [NSURL URLWithString: item.link]]]; + NSLog(@"show name: %@", self.name); + [self addEpisode: [Episode episodeWithShow: self name: item.title number: @"" url: [NSURL URLWithString: item.link]]]; } - (void) feedParserDidFinish: (MWFeedParser *)parser diff --git a/5by5Browser/UIAlertView+marshmallows.h b/5by5Browser/UIAlertView+marshmallows.h new file mode 100644 index 0000000..b24b882 --- /dev/null +++ b/5by5Browser/UIAlertView+marshmallows.h @@ -0,0 +1,17 @@ +// +// UIAlertView+marshmallows.h +// DatingX +// +// Created by Sami Samhuri on 11-08-24. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import + +typedef void (^UIAlertViewCallback)(BOOL ok); + +@interface UIAlertView (UIAlertView_marshmallows) + ++ (void) showAlertWithTitle: (NSString *)title message: (NSString *)message; + +@end diff --git a/5by5Browser/UIAlertView+marshmallows.m b/5by5Browser/UIAlertView+marshmallows.m new file mode 100644 index 0000000..b2dac01 --- /dev/null +++ b/5by5Browser/UIAlertView+marshmallows.m @@ -0,0 +1,32 @@ +// +// UIAlertView+marshmallows.m +// DatingX +// +// Created by Sami Samhuri on 11-08-24. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import "UIAlertView+marshmallows.h" +#import "UIAlertViewDelegate.h" + +@implementation UIAlertView (UIAlertView_marshmallows) + ++ (void) showAlertWithTitle: (NSString *)title message: (NSString *)message +{ + [[[self alloc] initWithTitle: title + message: message + delegate: nil + cancelButtonTitle: @"OK" + otherButtonTitles: nil] show]; +} + ++ (void) confirmWithTitle: (NSString *)title message: (NSString *)message then: (UIAlertViewCallback)callback +{ + [[[self alloc] initWithTitle: title + message: message + delegate: [UIAlertViewDelegate alertViewDelegateWithCallback: callback] + cancelButtonTitle: @"Cancel" + otherButtonTitles: @"OK", nil] show]; +} + +@end diff --git a/5by5Browser/UIAlertViewDelegate.h b/5by5Browser/UIAlertViewDelegate.h new file mode 100644 index 0000000..997526a --- /dev/null +++ b/5by5Browser/UIAlertViewDelegate.h @@ -0,0 +1,20 @@ +// +// UIAlertViewDelegate.h +// Marshmallows +// +// Created by Sami Samhuri on 11-09-05. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import +#import "UIAlertView+marshmallows.h" + +@interface UIAlertViewDelegate : NSObject +{ + UIAlertViewCallback _callback; +} + ++ (id) alertViewDelegateWithCallback: (UIAlertViewCallback)callback; +- (id) initWithCallback: (UIAlertViewCallback)callback; + +@end diff --git a/5by5Browser/UIAlertViewDelegate.m b/5by5Browser/UIAlertViewDelegate.m new file mode 100644 index 0000000..ee77b8b --- /dev/null +++ b/5by5Browser/UIAlertViewDelegate.m @@ -0,0 +1,33 @@ +// +// UIAlertViewDelegate.m +// Marshmallows +// +// Created by Sami Samhuri on 11-09-05. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import "UIAlertViewDelegate.h" + +@implementation UIAlertViewDelegate + ++ (id) alertViewDelegateWithCallback: (UIAlertViewCallback)callback +{ + return [[self alloc] initWithCallback: callback]; +} + +- (id) initWithCallback: (UIAlertViewCallback)callback +{ + self = [super init]; + if (self) { + _callback = callback; + } + return self; +} + +- (void) alertView: (UIAlertView *)alertView clickedButtonAtIndex: (NSInteger)buttonIndex +{ + BOOL ok = (buttonIndex == 1); + _callback(ok); +} + +@end diff --git a/5by5Browser/en.lproj/SSDetailViewController_iPhone.xib b/5by5Browser/en.lproj/SSDetailViewController_iPhone.xib index bec91ce..716bd44 100644 --- a/5by5Browser/en.lproj/SSDetailViewController_iPhone.xib +++ b/5by5Browser/en.lproj/SSDetailViewController_iPhone.xib @@ -14,6 +14,7 @@ IBUIWebView IBUIBarButtonItem IBUIToolbar + IBUIActivityIndicatorView IBUIView IBProxyObject @@ -43,7 +44,7 @@ {320, 416} - + _NS:693 1 @@ -85,6 +86,20 @@ IBCocoaTouchFramework 1 + + 1 + MC4zMjE1Njg2Mjc1IDAgMAA + + + + Instapaper + IBCocoaTouchFramework + 1 + + + 1 + MC4zMjE1Njg2Mjc1IDAgMAA + IBCocoaTouchFramework @@ -101,6 +116,40 @@ + + 3 + MC4zMzMzMzMzMzMzAA + + + + + 274 + + + + 301 + {{142, 189}, {37, 37}} + + + + _NS:1030 + NO + IBCocoaTouchFramework + NO + YES + 0 + + + {320, 416} + + + + _NS:196 + + 1 + MCAwIDAgMC42AA + + IBCocoaTouchFramework {{0, 20}, {320, 460}} @@ -152,6 +201,30 @@ 19 + + + instapaperButton + + + + 23 + + + + toolbar + + + + 24 + + + + loadingView + + + + 28 + delegate @@ -178,11 +251,19 @@ - goHome + goHome: - 20 + 25 + + + + sendToInstapaper: + + + + 22 @@ -198,6 +279,7 @@ + @@ -227,6 +309,7 @@ + @@ -257,6 +340,24 @@ + + 21 + + + + + 26 + + + + + + + + 27 + + + @@ -271,34 +372,43 @@ com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 20 + 28 SSDetailViewController UIViewController - - goHome - id - - - goHome - - goHome + + id + id + + + + goHome: id - + + sendToInstapaper: + id + + UIBarButtonItem UILabel UIBarButtonItem + UIBarButtonItem + UIView + UIToolbar UIWebView @@ -314,6 +424,18 @@ forwardButton UIBarButtonItem + + instapaperButton + UIBarButtonItem + + + loadingView + UIView + + + toolbar + UIToolbar + webView UIWebView