first commit

This commit is contained in:
Sami Samhuri 2011-12-17 16:49:40 -08:00
commit 15c1744e66
51 changed files with 5151 additions and 0 deletions

View file

@ -0,0 +1,489 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
7B043E87149D6984004B52E8 /* back.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B043E83149D6984004B52E8 /* back.png */; };
7B043E88149D6984004B52E8 /* back@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B043E84149D6984004B52E8 /* back@2x.png */; };
7B043E89149D6984004B52E8 /* forw.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B043E85149D6984004B52E8 /* forw.png */; };
7B043E8A149D6984004B52E8 /* forw@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B043E86149D6984004B52E8 /* forw@2x.png */; };
7B043E8E149D6FA0004B52E8 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B043E8C149D6FA0004B52E8 /* icon.png */; };
7B043E8F149D6FA0004B52E8 /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B043E8D149D6FA0004B52E8 /* icon@2x.png */; };
7B1A6ECF149D140600FC5105 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B1A6ECE149D140600FC5105 /* UIKit.framework */; };
7B1A6ED1149D140600FC5105 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B1A6ED0149D140600FC5105 /* Foundation.framework */; };
7B1A6ED3149D140600FC5105 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B1A6ED2149D140600FC5105 /* CoreGraphics.framework */; };
7B1A6ED9149D140600FC5105 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7B1A6ED7149D140600FC5105 /* InfoPlist.strings */; };
7B1A6EDB149D140600FC5105 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B1A6EDA149D140600FC5105 /* main.m */; };
7B1A6EDF149D140600FC5105 /* SSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B1A6EDE149D140600FC5105 /* SSAppDelegate.m */; };
7B1A6EE2149D140600FC5105 /* SSMasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B1A6EE1149D140600FC5105 /* SSMasterViewController.m */; };
7B1A6EE5149D140600FC5105 /* SSDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B1A6EE4149D140600FC5105 /* SSDetailViewController.m */; };
7B1A6EE8149D140600FC5105 /* SSMasterViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B1A6EE6149D140600FC5105 /* SSMasterViewController_iPhone.xib */; };
7B1A6EEB149D140600FC5105 /* SSMasterViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B1A6EE9149D140600FC5105 /* SSMasterViewController_iPad.xib */; };
7B1A6EEE149D140600FC5105 /* SSDetailViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B1A6EEC149D140600FC5105 /* SSDetailViewController_iPhone.xib */; };
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 */; };
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 */; };
7B7302F5149D670A003547E5 /* NSDate+InternetDateTime.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B7302ED149D670A003547E5 /* NSDate+InternetDateTime.m */; };
7B7302F6149D670A003547E5 /* NSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302EE149D670A003547E5 /* NSString+HTML.h */; };
7B7302F7149D670A003547E5 /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B7302EF149D670A003547E5 /* NSString+HTML.m */; };
7B7302F8149D670A003547E5 /* NSString+XMLEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302F0149D670A003547E5 /* NSString+XMLEntities.h */; };
7B7302F9149D670A003547E5 /* NSString+XMLEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B7302F1149D670A003547E5 /* NSString+XMLEntities.m */; };
7B730301149D6714003547E5 /* MWFeedInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302FA149D6714003547E5 /* MWFeedInfo.h */; };
7B730302149D6714003547E5 /* MWFeedInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B7302FB149D6714003547E5 /* MWFeedInfo.m */; };
7B730303149D6714003547E5 /* MWFeedItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302FC149D6714003547E5 /* MWFeedItem.h */; };
7B730304149D6714003547E5 /* MWFeedItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B7302FD149D6714003547E5 /* MWFeedItem.m */; };
7B730305149D6714003547E5 /* MWFeedParser_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302FE149D6714003547E5 /* MWFeedParser_Private.h */; };
7B730306149D6714003547E5 /* MWFeedParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B7302FF149D6714003547E5 /* MWFeedParser.h */; };
7B730307149D6714003547E5 /* MWFeedParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B730300149D6714003547E5 /* MWFeedParser.m */; };
7B868946149D593C00F3A2C6 /* Episode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B868945149D593C00F3A2C6 /* Episode.m */; };
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 */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
7B043E83149D6984004B52E8 /* back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = back.png; sourceTree = "<group>"; };
7B043E84149D6984004B52E8 /* back@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back@2x.png"; sourceTree = "<group>"; };
7B043E85149D6984004B52E8 /* forw.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = forw.png; sourceTree = "<group>"; };
7B043E86149D6984004B52E8 /* forw@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "forw@2x.png"; sourceTree = "<group>"; };
7B043E8C149D6FA0004B52E8 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
7B043E8D149D6FA0004B52E8 /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = "<group>"; };
7B1A6ECA149D140600FC5105 /* 5by5Browser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 5by5Browser.app; sourceTree = BUILT_PRODUCTS_DIR; };
7B1A6ECE149D140600FC5105 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
7B1A6ED0149D140600FC5105 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
7B1A6ED2149D140600FC5105 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
7B1A6ED6149D140600FC5105 /* 5by5Browser-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "5by5Browser-Info.plist"; sourceTree = "<group>"; };
7B1A6ED8149D140600FC5105 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
7B1A6EDA149D140600FC5105 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
7B1A6EDC149D140600FC5105 /* 5by5Browser-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "5by5Browser-Prefix.pch"; sourceTree = "<group>"; };
7B1A6EDD149D140600FC5105 /* SSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSAppDelegate.h; sourceTree = "<group>"; };
7B1A6EDE149D140600FC5105 /* SSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSAppDelegate.m; sourceTree = "<group>"; };
7B1A6EE0149D140600FC5105 /* SSMasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSMasterViewController.h; sourceTree = "<group>"; };
7B1A6EE1149D140600FC5105 /* SSMasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSMasterViewController.m; sourceTree = "<group>"; };
7B1A6EE3149D140600FC5105 /* SSDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSDetailViewController.h; sourceTree = "<group>"; };
7B1A6EE4149D140600FC5105 /* SSDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSDetailViewController.m; sourceTree = "<group>"; };
7B1A6EE7149D140600FC5105 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SSMasterViewController_iPhone.xib; sourceTree = "<group>"; };
7B1A6EEA149D140600FC5105 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SSMasterViewController_iPad.xib; sourceTree = "<group>"; };
7B1A6EED149D140600FC5105 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SSDetailViewController_iPhone.xib; sourceTree = "<group>"; };
7B1A6EF0149D140600FC5105 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SSDetailViewController_iPad.xib; sourceTree = "<group>"; };
7B1A6EF7149D148800FC5105 /* FiveByFive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FiveByFive.h; sourceTree = "<group>"; };
7B1A6EF8149D148800FC5105 /* FiveByFive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FiveByFive.m; sourceTree = "<group>"; };
7B1A6EFA149D14C500FC5105 /* Show.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Show.h; sourceTree = "<group>"; };
7B1A6EFB149D14C500FC5105 /* Show.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Show.m; sourceTree = "<group>"; };
7B7302EA149D670A003547E5 /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = "<group>"; };
7B7302EB149D670A003547E5 /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = "<group>"; };
7B7302EC149D670A003547E5 /* NSDate+InternetDateTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+InternetDateTime.h"; sourceTree = "<group>"; };
7B7302ED149D670A003547E5 /* NSDate+InternetDateTime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+InternetDateTime.m"; sourceTree = "<group>"; };
7B7302EE149D670A003547E5 /* NSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTML.h"; sourceTree = "<group>"; };
7B7302EF149D670A003547E5 /* NSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTML.m"; sourceTree = "<group>"; };
7B7302F0149D670A003547E5 /* NSString+XMLEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+XMLEntities.h"; sourceTree = "<group>"; };
7B7302F1149D670A003547E5 /* NSString+XMLEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+XMLEntities.m"; sourceTree = "<group>"; };
7B7302FA149D6714003547E5 /* MWFeedInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWFeedInfo.h; sourceTree = "<group>"; };
7B7302FB149D6714003547E5 /* MWFeedInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWFeedInfo.m; sourceTree = "<group>"; };
7B7302FC149D6714003547E5 /* MWFeedItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWFeedItem.h; sourceTree = "<group>"; };
7B7302FD149D6714003547E5 /* MWFeedItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWFeedItem.m; sourceTree = "<group>"; };
7B7302FE149D6714003547E5 /* MWFeedParser_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWFeedParser_Private.h; sourceTree = "<group>"; };
7B7302FF149D6714003547E5 /* MWFeedParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWFeedParser.h; sourceTree = "<group>"; };
7B730300149D6714003547E5 /* MWFeedParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWFeedParser.m; sourceTree = "<group>"; };
7B868944149D593C00F3A2C6 /* Episode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Episode.h; sourceTree = "<group>"; };
7B868945149D593C00F3A2C6 /* Episode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Episode.m; sourceTree = "<group>"; };
7B868948149D5C0F00F3A2C6 /* ShowViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowViewController.h; sourceTree = "<group>"; };
7B868949149D5C0F00F3A2C6 /* ShowViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowViewController.m; sourceTree = "<group>"; };
7B86894E149D5C6F00F3A2C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ShowViewController_iPad.xib; sourceTree = "<group>"; };
7B868950149D5C6F00F3A2C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ShowViewController_iPhone.xib; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
7B1A6EC7149D140600FC5105 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7B1A6ECF149D140600FC5105 /* UIKit.framework in Frameworks */,
7B1A6ED1149D140600FC5105 /* Foundation.framework in Frameworks */,
7B1A6ED3149D140600FC5105 /* CoreGraphics.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7B1A6EBF149D140600FC5105 = {
isa = PBXGroup;
children = (
7B7302E9149D66ED003547E5 /* MWFeedParser */,
7B1A6ED4149D140600FC5105 /* 5by5Browser */,
7B1A6ECD149D140600FC5105 /* Frameworks */,
7B1A6ECB149D140600FC5105 /* Products */,
);
sourceTree = "<group>";
};
7B1A6ECB149D140600FC5105 /* Products */ = {
isa = PBXGroup;
children = (
7B1A6ECA149D140600FC5105 /* 5by5Browser.app */,
);
name = Products;
sourceTree = "<group>";
};
7B1A6ECD149D140600FC5105 /* Frameworks */ = {
isa = PBXGroup;
children = (
7B1A6ECE149D140600FC5105 /* UIKit.framework */,
7B1A6ED0149D140600FC5105 /* Foundation.framework */,
7B1A6ED2149D140600FC5105 /* CoreGraphics.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
7B1A6ED4149D140600FC5105 /* 5by5Browser */ = {
isa = PBXGroup;
children = (
7B1A6EDD149D140600FC5105 /* SSAppDelegate.h */,
7B1A6EDE149D140600FC5105 /* SSAppDelegate.m */,
7B868948149D5C0F00F3A2C6 /* ShowViewController.h */,
7B868949149D5C0F00F3A2C6 /* ShowViewController.m */,
7B1A6EE0149D140600FC5105 /* SSMasterViewController.h */,
7B1A6EE1149D140600FC5105 /* SSMasterViewController.m */,
7B1A6EE3149D140600FC5105 /* SSDetailViewController.h */,
7B1A6EE4149D140600FC5105 /* SSDetailViewController.m */,
7B868944149D593C00F3A2C6 /* Episode.h */,
7B868945149D593C00F3A2C6 /* Episode.m */,
7B1A6EF7149D148800FC5105 /* FiveByFive.h */,
7B1A6EF8149D148800FC5105 /* FiveByFive.m */,
7B1A6EFA149D14C500FC5105 /* Show.h */,
7B1A6EFB149D14C500FC5105 /* Show.m */,
7B86894D149D5C6F00F3A2C6 /* ShowViewController_iPad.xib */,
7B86894F149D5C6F00F3A2C6 /* ShowViewController_iPhone.xib */,
7B1A6EE6149D140600FC5105 /* SSMasterViewController_iPhone.xib */,
7B1A6EE9149D140600FC5105 /* SSMasterViewController_iPad.xib */,
7B1A6EEC149D140600FC5105 /* SSDetailViewController_iPhone.xib */,
7B1A6EEF149D140600FC5105 /* SSDetailViewController_iPad.xib */,
7B1A6ED5149D140600FC5105 /* Supporting Files */,
);
path = 5by5Browser;
sourceTree = "<group>";
};
7B1A6ED5149D140600FC5105 /* Supporting Files */ = {
isa = PBXGroup;
children = (
7B043E8C149D6FA0004B52E8 /* icon.png */,
7B043E8D149D6FA0004B52E8 /* icon@2x.png */,
7B043E83149D6984004B52E8 /* back.png */,
7B043E84149D6984004B52E8 /* back@2x.png */,
7B043E85149D6984004B52E8 /* forw.png */,
7B043E86149D6984004B52E8 /* forw@2x.png */,
7B1A6ED6149D140600FC5105 /* 5by5Browser-Info.plist */,
7B1A6ED7149D140600FC5105 /* InfoPlist.strings */,
7B1A6EDA149D140600FC5105 /* main.m */,
7B1A6EDC149D140600FC5105 /* 5by5Browser-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
7B7302E7149D66EA003547E5 /* Categories */ = {
isa = PBXGroup;
children = (
7B7302EA149D670A003547E5 /* GTMNSString+HTML.h */,
7B7302EB149D670A003547E5 /* GTMNSString+HTML.m */,
7B7302EC149D670A003547E5 /* NSDate+InternetDateTime.h */,
7B7302ED149D670A003547E5 /* NSDate+InternetDateTime.m */,
7B7302EE149D670A003547E5 /* NSString+HTML.h */,
7B7302EF149D670A003547E5 /* NSString+HTML.m */,
7B7302F0149D670A003547E5 /* NSString+XMLEntities.h */,
7B7302F1149D670A003547E5 /* NSString+XMLEntities.m */,
);
name = Categories;
sourceTree = "<group>";
};
7B7302E9149D66ED003547E5 /* MWFeedParser */ = {
isa = PBXGroup;
children = (
7B7302E7149D66EA003547E5 /* Categories */,
7B7302FA149D6714003547E5 /* MWFeedInfo.h */,
7B7302FB149D6714003547E5 /* MWFeedInfo.m */,
7B7302FC149D6714003547E5 /* MWFeedItem.h */,
7B7302FD149D6714003547E5 /* MWFeedItem.m */,
7B7302FE149D6714003547E5 /* MWFeedParser_Private.h */,
7B7302FF149D6714003547E5 /* MWFeedParser.h */,
7B730300149D6714003547E5 /* MWFeedParser.m */,
);
name = MWFeedParser;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
7B868976149D63B000F3A2C6 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7B7302F2149D670A003547E5 /* GTMNSString+HTML.h in Headers */,
7B7302F4149D670A003547E5 /* NSDate+InternetDateTime.h in Headers */,
7B7302F6149D670A003547E5 /* NSString+HTML.h in Headers */,
7B7302F8149D670A003547E5 /* NSString+XMLEntities.h in Headers */,
7B730301149D6714003547E5 /* MWFeedInfo.h in Headers */,
7B730303149D6714003547E5 /* MWFeedItem.h in Headers */,
7B730305149D6714003547E5 /* MWFeedParser_Private.h in Headers */,
7B730306149D6714003547E5 /* MWFeedParser.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
7B1A6EC9149D140600FC5105 /* 5by5Browser */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7B1A6EF4149D140600FC5105 /* Build configuration list for PBXNativeTarget "5by5Browser" */;
buildPhases = (
7B1A6EC6149D140600FC5105 /* Sources */,
7B1A6EC7149D140600FC5105 /* Frameworks */,
7B1A6EC8149D140600FC5105 /* Resources */,
7B868976149D63B000F3A2C6 /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = 5by5Browser;
productName = 5by5Browser;
productReference = 7B1A6ECA149D140600FC5105 /* 5by5Browser.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7B1A6EC1149D140600FC5105 /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = SS;
LastUpgradeCheck = 0430;
ORGANIZATIONNAME = "Guru Logic";
};
buildConfigurationList = 7B1A6EC4149D140600FC5105 /* Build configuration list for PBXProject "5by5Browser" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 7B1A6EBF149D140600FC5105;
productRefGroup = 7B1A6ECB149D140600FC5105 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7B1A6EC9149D140600FC5105 /* 5by5Browser */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7B1A6EC8149D140600FC5105 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7B1A6ED9149D140600FC5105 /* InfoPlist.strings in Resources */,
7B1A6EE8149D140600FC5105 /* SSMasterViewController_iPhone.xib in Resources */,
7B1A6EEB149D140600FC5105 /* SSMasterViewController_iPad.xib in Resources */,
7B1A6EEE149D140600FC5105 /* SSDetailViewController_iPhone.xib in Resources */,
7B1A6EF1149D140600FC5105 /* SSDetailViewController_iPad.xib in Resources */,
7B868951149D5C6F00F3A2C6 /* ShowViewController_iPad.xib in Resources */,
7B868952149D5C6F00F3A2C6 /* ShowViewController_iPhone.xib in Resources */,
7B043E87149D6984004B52E8 /* back.png in Resources */,
7B043E88149D6984004B52E8 /* back@2x.png in Resources */,
7B043E89149D6984004B52E8 /* forw.png in Resources */,
7B043E8A149D6984004B52E8 /* forw@2x.png in Resources */,
7B043E8E149D6FA0004B52E8 /* icon.png in Resources */,
7B043E8F149D6FA0004B52E8 /* icon@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7B1A6EC6149D140600FC5105 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7B1A6EDB149D140600FC5105 /* main.m in Sources */,
7B1A6EDF149D140600FC5105 /* SSAppDelegate.m in Sources */,
7B1A6EE2149D140600FC5105 /* SSMasterViewController.m in Sources */,
7B1A6EE5149D140600FC5105 /* SSDetailViewController.m in Sources */,
7B1A6EF9149D148800FC5105 /* FiveByFive.m in Sources */,
7B1A6EFC149D14C500FC5105 /* Show.m in Sources */,
7B868946149D593C00F3A2C6 /* Episode.m in Sources */,
7B86894B149D5C1000F3A2C6 /* ShowViewController.m in Sources */,
7B7302F3149D670A003547E5 /* GTMNSString+HTML.m in Sources */,
7B7302F5149D670A003547E5 /* NSDate+InternetDateTime.m in Sources */,
7B7302F7149D670A003547E5 /* NSString+HTML.m in Sources */,
7B7302F9149D670A003547E5 /* NSString+XMLEntities.m in Sources */,
7B730302149D6714003547E5 /* MWFeedInfo.m in Sources */,
7B730304149D6714003547E5 /* MWFeedItem.m in Sources */,
7B730307149D6714003547E5 /* MWFeedParser.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
7B1A6ED7149D140600FC5105 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
7B1A6ED8149D140600FC5105 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
7B1A6EE6149D140600FC5105 /* SSMasterViewController_iPhone.xib */ = {
isa = PBXVariantGroup;
children = (
7B1A6EE7149D140600FC5105 /* en */,
);
name = SSMasterViewController_iPhone.xib;
sourceTree = "<group>";
};
7B1A6EE9149D140600FC5105 /* SSMasterViewController_iPad.xib */ = {
isa = PBXVariantGroup;
children = (
7B1A6EEA149D140600FC5105 /* en */,
);
name = SSMasterViewController_iPad.xib;
sourceTree = "<group>";
};
7B1A6EEC149D140600FC5105 /* SSDetailViewController_iPhone.xib */ = {
isa = PBXVariantGroup;
children = (
7B1A6EED149D140600FC5105 /* en */,
);
name = SSDetailViewController_iPhone.xib;
sourceTree = "<group>";
};
7B1A6EEF149D140600FC5105 /* SSDetailViewController_iPad.xib */ = {
isa = PBXVariantGroup;
children = (
7B1A6EF0149D140600FC5105 /* en */,
);
name = SSDetailViewController_iPad.xib;
sourceTree = "<group>";
};
7B86894D149D5C6F00F3A2C6 /* ShowViewController_iPad.xib */ = {
isa = PBXVariantGroup;
children = (
7B86894E149D5C6F00F3A2C6 /* en */,
);
name = ShowViewController_iPad.xib;
sourceTree = "<group>";
};
7B86894F149D5C6F00F3A2C6 /* ShowViewController_iPhone.xib */ = {
isa = PBXVariantGroup;
children = (
7B868950149D5C6F00F3A2C6 /* en */,
);
name = ShowViewController_iPhone.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
7B1A6EF2149D140600FC5105 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
7B1A6EF3149D140600FC5105 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
7B1A6EF5149D140600FC5105 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "5by5Browser/5by5Browser-Prefix.pch";
INFOPLIST_FILE = "5by5Browser/5by5Browser-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
7B1A6EF6149D140600FC5105 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "5by5Browser/5by5Browser-Prefix.pch";
INFOPLIST_FILE = "5by5Browser/5by5Browser-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7B1A6EC4149D140600FC5105 /* Build configuration list for PBXProject "5by5Browser" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7B1A6EF2149D140600FC5105 /* Debug */,
7B1A6EF3149D140600FC5105 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7B1A6EF4149D140600FC5105 /* Build configuration list for PBXNativeTarget "5by5Browser" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7B1A6EF5149D140600FC5105 /* Debug */,
7B1A6EF6149D140600FC5105 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7B1A6EC1149D140600FC5105 /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:5by5Browser.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
<true/>
<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B1A6EC9149D140600FC5105"
BuildableName = "5by5Browser.app"
BlueprintName = "5by5Browser"
ReferencedContainer = "container:5by5Browser.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B1A6EC9149D140600FC5105"
BuildableName = "5by5Browser.app"
BlueprintName = "5by5Browser"
ReferencedContainer = "container:5by5Browser.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B1A6EC9149D140600FC5105"
BuildableName = "5by5Browser.app"
BlueprintName = "5by5Browser"
ReferencedContainer = "container:5by5Browser.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7B1A6EC9149D140600FC5105"
BuildableName = "5by5Browser.app"
BlueprintName = "5by5Browser"
ReferencedContainer = "container:5by5Browser.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>5by5Browser.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>7B1A6EC9149D140600FC5105</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>net.samhuri.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,14 @@
//
// Prefix header for all source files of the '5by5Browser' target in the '5by5Browser' project
//
#import <Availability.h>
#ifndef __IPHONE_4_0
#warning "This project uses features only available in iOS SDK 4.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif

20
5by5Browser/Episode.h Normal file
View file

@ -0,0 +1,20 @@
//
// Episode.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Episode : NSObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *number;
@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;
@end

33
5by5Browser/Episode.m Normal file
View file

@ -0,0 +1,33 @@
//
// Episode.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "Episode.h"
@implementation Episode
@synthesize name = _name;
@synthesize number = _number;
@synthesize url = _url;
+ (id) episodeWithName: (NSString *)name number: (NSString *)number url: (NSURL *)url
{
return [[self alloc] initWithName: name number: number url: url];
}
- (id) initWithName: (NSString *)name number: (NSString *)number url: (NSURL *)url
{
self = [super init];
if (self) {
self.name = name;
self.number = number;
self.url = url;
}
return self;
}
@end

21
5by5Browser/FiveByFive.h Normal file
View file

@ -0,0 +1,21 @@
//
// FiveByFive.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <Foundation/Foundation.h>
@class Show;
@interface FiveByFive : NSObject
@property (nonatomic, retain) NSString *baseURL;
@property (nonatomic, retain) NSMutableArray *shows;
- (id) initWithBaseURL: (NSString *)baseURL;
- (void) addShow: (Show *)show;
@end

33
5by5Browser/FiveByFive.m Normal file
View file

@ -0,0 +1,33 @@
//
// FiveByFive.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "FiveByFive.h"
#import "Show.h"
@implementation FiveByFive
@synthesize baseURL = _baseURL;
@synthesize shows = _shows;
- (id) initWithBaseURL: (NSString *)baseURL
{
self = [super init];
if (self) {
self.baseURL = baseURL;
self.shows = [NSMutableArray array];
}
return self;
}
- (void) addShow: (Show *)show
{
show.fiveByFive = self;
[self.shows addObject: show];
}
@end

View file

@ -0,0 +1,19 @@
//
// SSAppDelegate.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SSAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navigationController;
@property (strong, nonatomic) UISplitViewController *splitViewController;
@end

View file

@ -0,0 +1,85 @@
//
// SSAppDelegate.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "SSAppDelegate.h"
#import "SSMasterViewController.h"
#import "SSDetailViewController.h"
#import "FiveByFive.h"
#import "Show.h"
@implementation SSAppDelegate
@synthesize window = _window;
@synthesize navigationController = _navigationController;
@synthesize splitViewController = _splitViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
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: @"Geek Friday" path: @"GeekFriday"]];
[fiveByFive addShow: [Show showWithName: @"Hypercritical" path: @"hypercritical"]];
[fiveByFive addShow: [Show showWithName: @"The Talk Show" path: @"thetalkshow"]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
SSMasterViewController *masterViewController = [[SSMasterViewController alloc] initWithNibName:@"SSMasterViewController_iPhone" bundle:nil];
masterViewController.fiveByFive = fiveByFive;
self.navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
self.window.rootViewController = self.navigationController;
} else {
SSMasterViewController *masterViewController = [[SSMasterViewController alloc] initWithNibName:@"SSMasterViewController_iPad" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
SSDetailViewController *detailViewController = [[SSDetailViewController alloc] initWithNibName:@"SSDetailViewController_iPad" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
masterViewController.fiveByFive = fiveByFive;
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
self.window.rootViewController = self.splitViewController;
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

View file

@ -0,0 +1,22 @@
//
// SSDetailViewController.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "Episode.h"
@interface SSDetailViewController : UIViewController <UISplitViewControllerDelegate, UIWebViewDelegate>
@property (strong, nonatomic) Episode *episode;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (strong, nonatomic) IBOutlet UIWebView *webView;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *backButton;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *forwardButton;
- (IBAction) goHome;
@end

View file

@ -0,0 +1,107 @@
//
// SSDetailViewController.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "SSDetailViewController.h"
@interface SSDetailViewController ()
@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
@end
@implementation SSDetailViewController
@synthesize episode = _episode;
@synthesize detailDescriptionLabel = _detailDescriptionLabel;
@synthesize webView = _webView;
@synthesize backButton = _backButton;
@synthesize forwardButton = _forwardButton;
@synthesize masterPopoverController = _masterPopoverController;
#pragma mark - Managing the detail item
- (void) setEpisode: (Episode *)episode
{
if (_episode != episode) {
_episode = episode;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (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]];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)viewDidUnload
{
[self setWebView:nil];
[self setBackButton:nil];
[self setForwardButton:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
self.detailDescriptionLabel = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (void) webViewDidFinishLoad: (UIWebView *)webView
{
[self.backButton setEnabled: webView.canGoBack];
[self.forwardButton setEnabled: webView.canGoForward];
}
- (IBAction) goHome
{
while (self.webView.canGoBack) {
[self.webView goBack];
}
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = @"Shows";
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
@end

View file

@ -0,0 +1,17 @@
//
// SSMasterViewController.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "FiveByFive.h"
#import "Show.h"
@interface SSMasterViewController : UITableViewController <ShowDelegate>
@property (nonatomic, retain) FiveByFive *fiveByFive;
@end

View file

@ -0,0 +1,128 @@
//
// SSMasterViewController.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "SSMasterViewController.h"
#import "ShowViewController.h"
#import "Show.h"
@implementation SSMasterViewController
@synthesize fiveByFive = _fiveByFive;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = @"5by5 Shows";
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void) viewWillAppear: (BOOL)animated
{
[super viewWillAppear: animated];
for (int i = 0; i < [self tableView: self.tableView numberOfRowsInSection: 0]; ++i) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath: [NSIndexPath indexPathForRow: i inSection: 0]];
cell.accessoryView = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (void) setFiveByFive: (FiveByFive *)fiveByFive
{
_fiveByFive = fiveByFive;
[self.tableView reloadData];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.fiveByFive.shows.count;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
Show *show = [self.fiveByFive.shows objectAtIndex: indexPath.row];
show.delegate = self;
cell.textLabel.text = show.name;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self.fiveByFive.shows objectAtIndex: indexPath.row] getEpisodes];
UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray];
[indicatorView startAnimating];
[tableView cellForRowAtIndexPath: indexPath].accessoryView = indicatorView;
}
- (void) gotEpisodesForShow: (Show *)show
{
ShowViewController *vc;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
vc = [[ShowViewController alloc] initWithNibName: @"ShowViewController_iPhone" bundle: nil];
}
else {
vc = [[ShowViewController alloc] initWithNibName: @"ShowViewController_iPad" bundle: nil];
}
vc.show = show;
[self.navigationController pushViewController: vc animated:YES];
}
@end

31
5by5Browser/Show.h Normal file
View file

@ -0,0 +1,31 @@
//
// Show.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MWFeedParser.h"
#import "Episode.h"
#import "FiveByFive.h"
@protocol ShowDelegate <NSObject>
- (void) gotEpisodesForShow: (Show *)show;
@end
@interface Show : NSObject <MWFeedParserDelegate>
@property (nonatomic, assign) id<ShowDelegate> delegate;
@property (nonatomic, retain) FiveByFive *fiveByFive;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *path;
@property (nonatomic, retain) NSMutableArray *episodes;
+ (id) showWithName: (NSString *)name path: (NSString *)path;
- (id) initWithName: (NSString *)name path: (NSString *)path;
- (void) addEpisode: (Episode *)episode;
- (void) getEpisodes;
@end

95
5by5Browser/Show.m Normal file
View file

@ -0,0 +1,95 @@
//
// Show.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "Show.h"
#import "MWFeedParser.h"
// Private API
@interface Show ()
- (NSURL *) feedURL;
@end
@implementation Show
@synthesize delegate = _delegate;
@synthesize fiveByFive = _fiveByFive;
@synthesize name = _name;
@synthesize path = _path;
@synthesize episodes = _episodes;
+ (id) showWithName: (NSString *)name path: (NSString *)path
{
return [[self alloc] initWithName: name path: path];
}
- (id) initWithName: (NSString *)name path: (NSString *)path
{
self = [super init];
if (self) {
self.name = name;
self.path = path;
self.episodes = [NSMutableArray array];
}
return self;
}
- (void) getEpisodes
{
if (self.episodes.count == 0) {
MWFeedParser *parser = [[MWFeedParser alloc] initWithFeedURL: [self feedURL]];
parser.delegate = self;
parser.feedParseType = ParseTypeFull;
parser.connectionType = ConnectionTypeAsynchronously;
[parser parse];
}
else {
[self.delegate gotEpisodesForShow: self];
}
}
- (void) addEpisode: (Episode *)episode
{
[self.episodes addObject: episode];
}
- (NSURL *) feedURL
{
return [NSURL URLWithString: [[self.fiveByFive baseURL] stringByAppendingPathComponent: self.path]];
}
// MWFeedParserDelegate methods
- (void) feedParserDidStart: (MWFeedParser *)parser
{
NSLog(@"feed parser started");
}
- (void) feedParser: (MWFeedParser *)parser didParseFeedInfo: (MWFeedInfo *)info
{
NSLog(@"feed info: %@", info);
}
- (void) feedParser: (MWFeedParser *)parser didParseFeedItem: (MWFeedItem *)item
{
NSLog(@"feed item: %@", item);
[self addEpisode: [Episode episodeWithName: item.title number: @"<n>" url: [NSURL URLWithString: item.link]]];
}
- (void) feedParserDidFinish: (MWFeedParser *)parser
{
[self.delegate gotEpisodesForShow: self];
}
- (void) feedParser: (MWFeedParser *)parser didFailWithError: (NSError *)error
{
NSLog(@"feed parser error: %@", error);
}
@end

View file

@ -0,0 +1,18 @@
//
// ShowViewController.h
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SSDetailViewController.h"
#import "Show.h"
@interface ShowViewController : UITableViewController
@property (strong, nonatomic) SSDetailViewController *detailViewController;
@property (nonatomic, retain) Show *show;
@end

View file

@ -0,0 +1,101 @@
//
// ShowViewController.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import "ShowViewController.h"
#import "SSDetailViewController.h"
#import "Episode.h"
@interface ShowViewController ()
@end
@implementation ShowViewController
@synthesize detailViewController = _detailViewController;
@synthesize show = _show;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void) setShow: (Show *)show
{
_show = show;
self.title = self.show.name;
[self.tableView reloadData];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.show.episodes.count;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
Episode *episode = [self.show.episodes objectAtIndex: indexPath.row];
cell.textLabel.text = episode.name;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Episode *episode = [self.show.episodes objectAtIndex: indexPath.row];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if (!self.detailViewController) {
self.detailViewController = [[SSDetailViewController alloc] initWithNibName: @"SSDetailViewController_iPhone" bundle: nil];
}
self.detailViewController.episode = episode;
[self.navigationController pushViewController: self.detailViewController animated:YES];
} else {
self.detailViewController.episode = episode;
}
}
@end

BIN
5by5Browser/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

BIN
5by5Browser/back@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

View file

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C74</string>
<string key="IBDocument.InterfaceBuilderVersion">2148</string>
<string key="IBDocument.AppKitVersion">1138.23</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1145</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUIView</string>
<string>IBUIWebView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBProxyObject" id="606714003">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBUIView" id="647120888">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUIWebView" id="982214524">
<reference key="NSNextResponder" ref="647120888"/>
<int key="NSvFlags">274</int>
<string key="NSFrameSize">{768, 1004}</string>
<reference key="NSSuperview" ref="647120888"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:693</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIDataDetectorTypes">1</int>
<bool key="IBUIDetectsPhoneNumbers">YES</bool>
</object>
</array>
<string key="NSFrame">{{0, 20}, {768, 1004}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="982214524"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
<int key="IBUIStatusBarStyle">2</int>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="647120888"/>
</object>
<int key="connectionID">12</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">webView</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="982214524"/>
</object>
<int key="connectionID">69</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="982214524"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">68</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="606714003"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="647120888"/>
<array class="NSMutableArray" key="children">
<reference ref="982214524"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">67</int>
<reference key="object" ref="982214524"/>
<reference key="parent" ref="647120888"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">SSDetailViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="67.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="8.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">69</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">SSDetailViewController</string>
<string key="superclassName">UIViewController</string>
<dictionary class="NSMutableDictionary" key="outlets">
<string key="detailDescriptionLabel">UILabel</string>
<string key="webView">UIWebView</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<object class="IBToOneOutletInfo" key="detailDescriptionLabel">
<string key="name">detailDescriptionLabel</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo" key="webView">
<string key="name">webView</string>
<string key="candidateClassName">UIWebView</string>
</object>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/SSDetailViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">1145</string>
</data>
</archive>

View file

@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C74</string>
<string key="IBDocument.InterfaceBuilderVersion">2148</string>
<string key="IBDocument.AppKitVersion">1138.23</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1145</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBUIWebView</string>
<string>IBUIBarButtonItem</string>
<string>IBUIToolbar</string>
<string>IBUIView</string>
<string>IBProxyObject</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUIWebView" id="517643825">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">274</int>
<string key="NSFrameSize">{320, 416}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="592752272"/>
<string key="NSReuseIdentifierKey">_NS:693</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIScalesPageToFit">YES</bool>
<int key="IBUIDataDetectorTypes">1</int>
<bool key="IBUIDetectsPhoneNumbers">YES</bool>
</object>
<object class="IBUIToolbar" id="592752272">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">266</int>
<string key="NSFrame">{{0, 416}, {320, 44}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:371</string>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<array class="NSMutableArray" key="IBUIItems">
<object class="IBUIBarButtonItem" id="1003054208">
<bool key="IBUIEnabled">NO</bool>
<object class="NSCustomResource" key="IBUIImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">back.png</string>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="592752272"/>
</object>
<object class="IBUIBarButtonItem" id="703595657">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="592752272"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
<object class="IBUIBarButtonItem" id="362932883">
<string key="IBUITitle">Home</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="592752272"/>
</object>
<object class="IBUIBarButtonItem" id="8781673">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="592752272"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
<object class="IBUIBarButtonItem" id="604700786">
<bool key="IBUIEnabled">NO</bool>
<object class="NSCustomResource" key="IBUIImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">forw.png</string>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="592752272"/>
</object>
</array>
</object>
</array>
<string key="NSFrame">{{0, 20}, {320, 460}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="517643825"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace">
<int key="NSID">2</int>
</object>
</object>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="191373211"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">webView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="517643825"/>
</object>
<int key="connectionID">9</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">backButton</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="1003054208"/>
</object>
<int key="connectionID">18</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">forwardButton</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="604700786"/>
</object>
<int key="connectionID">19</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="517643825"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">8</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">goBack</string>
<reference key="source" ref="1003054208"/>
<reference key="destination" ref="517643825"/>
</object>
<int key="connectionID">16</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">goForward</string>
<reference key="source" ref="604700786"/>
<reference key="destination" ref="517643825"/>
</object>
<int key="connectionID">17</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">goHome</string>
<reference key="source" ref="362932883"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">20</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<array class="NSMutableArray" key="children">
<reference ref="517643825"/>
<reference ref="592752272"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">7</int>
<reference key="object" ref="517643825"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">10</int>
<reference key="object" ref="592752272"/>
<array class="NSMutableArray" key="children">
<reference ref="1003054208"/>
<reference ref="604700786"/>
<reference ref="703595657"/>
<reference ref="8781673"/>
<reference ref="362932883"/>
</array>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">11</int>
<reference key="object" ref="1003054208"/>
<reference key="parent" ref="592752272"/>
<string key="objectName">Bar Button Item - Back</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">12</int>
<reference key="object" ref="604700786"/>
<reference key="parent" ref="592752272"/>
<string key="objectName">Bar Button Item - Forward</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">13</int>
<reference key="object" ref="703595657"/>
<reference key="parent" ref="592752272"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">14</int>
<reference key="object" ref="8781673"/>
<reference key="parent" ref="592752272"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">15</int>
<reference key="object" ref="362932883"/>
<reference key="parent" ref="592752272"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">SSDetailViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="10.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="11.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="12.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="13.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="14.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="15.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="7.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">20</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">SSDetailViewController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">goHome</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">goHome</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">goHome</string>
<string key="candidateClassName">id</string>
</object>
</object>
<dictionary class="NSMutableDictionary" key="outlets">
<string key="backButton">UIBarButtonItem</string>
<string key="detailDescriptionLabel">UILabel</string>
<string key="forwardButton">UIBarButtonItem</string>
<string key="webView">UIWebView</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<object class="IBToOneOutletInfo" key="backButton">
<string key="name">backButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo" key="detailDescriptionLabel">
<string key="name">detailDescriptionLabel</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo" key="forwardButton">
<string key="name">forwardButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo" key="webView">
<string key="name">webView</string>
<string key="candidateClassName">UIWebView</string>
</object>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/SSDetailViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<string key="back.png">{15, 18}</string>
<string key="forw.png">{15, 18}</string>
</dictionary>
<string key="IBCocoaTouchPluginVersion">1145</string>
</data>
</archive>

View file

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C74</string>
<string key="IBDocument.InterfaceBuilderVersion">2148</string>
<string key="IBDocument.AppKitVersion">1138.23</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1145</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUITableView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBProxyObject" id="371349661">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBUITableView" id="286511803">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<string key="NSFrame">{{0, 20}, {320, 832}}</string>
<reference key="NSSuperview"/>
<reference key="NSNextKeyView"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
<int key="IBUIStatusBarStyle">2</int>
</object>
<object class="IBUISimulatedSizeMetrics" key="IBUISimulatedDestinationMetrics">
<string key="IBUISimulatedSizeMetricsClass">IBUISplitViewMasterSimulatedSizeMetrics</string>
<object class="NSMutableDictionary" key="IBUINormalizedOrientationToSizeMap">
<bool key="EncodedWithXMLCoder">YES</bool>
<array key="dict.sortedKeys">
<integer value="1"/>
<integer value="3"/>
</array>
<array key="dict.values">
<string>{320, 852}</string>
<string>{320, 768}</string>
</array>
</object>
<string key="IBUITargetRuntime">IBIPadFramework</string>
<string key="IBUIDisplayName">Master</string>
<string key="IBUIDestinationClass">IBUISplitViewController</string>
<object class="NSDictionary" key="IBUIDestinationContext">
<string key="NS.key.0">IBUISplitViewControllerContentSizeLocation</string>
<string key="NS.object.0">IBUISplitViewControllerContentSizeLocationMaster</string>
</object>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBUIAlwaysBounceVertical">YES</bool>
<int key="IBUISeparatorStyle">1</int>
<int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
<bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
<float key="IBUIRowHeight">44</float>
<float key="IBUISectionHeaderHeight">22</float>
<float key="IBUISectionFooterHeight">22</float>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="286511803"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="286511803"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">4</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="286511803"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">5</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="371349661"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="286511803"/>
<reference key="parent" ref="0"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">ShowViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">5</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">1145</string>
</data>
</archive>

View file

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C74</string>
<string key="IBDocument.InterfaceBuilderVersion">2148</string>
<string key="IBDocument.AppKitVersion">1138.23</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1145</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUITableView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="371349661">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUITableView" id="709618507">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<string key="NSFrame">{{0, 20}, {320, 460}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIAlwaysBounceVertical">YES</bool>
<int key="IBUISeparatorStyle">1</int>
<int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
<bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
<float key="IBUIRowHeight">44</float>
<float key="IBUISectionHeaderHeight">22</float>
<float key="IBUISectionFooterHeight">22</float>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="709618507"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="709618507"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">4</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="709618507"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">5</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="371349661"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="709618507"/>
<reference key="parent" ref="0"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">ShowViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">5</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">ShowViewController</string>
<string key="superclassName">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/ShowViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">1145</string>
</data>
</archive>

View file

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C25</string>
<string key="IBDocument.InterfaceBuilderVersion">1919</string>
<string key="IBDocument.AppKitVersion">1138.11</string>
<string key="IBDocument.HIToolboxVersion">566.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">916</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUITableView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBProxyObject" id="371349661">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBUITableView" id="286511803">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<string key="NSFrame">{{0, 20}, {320, 832}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
<int key="IBUIStatusBarStyle">2</int>
</object>
<object class="IBUISimulatedSizeMetrics" key="IBUISimulatedDestinationMetrics">
<string key="IBUISimulatedSizeMetricsClass">IBUISplitViewMasterSimulatedSizeMetrics</string>
<object class="NSMutableDictionary" key="IBUINormalizedOrientationToSizeMap">
<bool key="EncodedWithXMLCoder">YES</bool>
<array key="dict.sortedKeys">
<integer value="1"/>
<integer value="3"/>
</array>
<array class="NSMutableArray" key="dict.values">
<string>{320, 852}</string>
<string>{320, 768}</string>
</array>
</object>
<string key="IBUITargetRuntime">IBIPadFramework</string>
<string key="IBUIDisplayName">Master</string>
<string key="IBUIDestinationClass">IBUISplitViewController</string>
<object class="NSDictionary" key="IBUIDestinationContext">
<string key="NS.key.0">IBUISplitViewControllerContentSizeLocation</string>
<string key="NS.object.0">IBUISplitViewControllerContentSizeLocationMaster</string>
</object>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBUIAlwaysBounceVertical">YES</bool>
<int key="IBUISeparatorStyle">1</int>
<int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
<bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
<float key="IBUIRowHeight">44</float>
<float key="IBUISectionHeaderHeight">22</float>
<float key="IBUISectionFooterHeight">22</float>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="286511803"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="286511803"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">4</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="286511803"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">5</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="371349661"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="286511803"/>
<reference key="parent" ref="0"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">SSMasterViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">5</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">SSMasterViewController</string>
<string key="superclassName">UITableViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/SSMasterViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">916</string>
</data>
</archive>

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C25</string>
<string key="IBDocument.InterfaceBuilderVersion">1919</string>
<string key="IBDocument.AppKitVersion">1138.11</string>
<string key="IBDocument.HIToolboxVersion">566.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">916</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUITableView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="371349661">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUITableView" id="709618507">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<string key="NSFrame">{{0, 20}, {320, 460}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIAlwaysBounceVertical">YES</bool>
<int key="IBUISeparatorStyle">1</int>
<int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
<bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
<float key="IBUIRowHeight">44</float>
<float key="IBUISectionHeaderHeight">22</float>
<float key="IBUISectionFooterHeight">22</float>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="709618507"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="709618507"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">4</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="709618507"/>
<reference key="destination" ref="841351856"/>
</object>
<int key="connectionID">5</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="371349661"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="709618507"/>
<reference key="parent" ref="0"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">SSMasterViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">5</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">SSMasterViewController</string>
<string key="superclassName">UITableViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/SSMasterViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">916</string>
</data>
</archive>

BIN
5by5Browser/forw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
5by5Browser/forw@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

BIN
5by5Browser/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
5by5Browser/icon@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

18
5by5Browser/main.m Normal file
View file

@ -0,0 +1,18 @@
//
// main.m
// 5by5Browser
//
// Created by Sami Samhuri on 11-12-17.
// Copyright (c) 2011 Guru Logic. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SSAppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([SSAppDelegate class]));
}
}

66
GTMNSString+HTML.h Normal file
View file

@ -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 <Foundation/Foundation.h>
/// Utilities for NSStrings containing HTML
@interface NSString (GTMNSStringHTMLAdditions)
/// Get a string where internal characters that need escaping for HTML are escaped
//
/// For example, '&' become '&amp;'. 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 '&amp;'
/// 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, '&amp;' becomes '&'
/// Handles &#32; and &#x32; cases as well
///
// Returns:
// Autoreleased NSString
//
- (NSString *)gtm_stringByUnescapingFromHTML;
@end

522
GTMNSString+HTML.m Normal file
View file

@ -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 {
__unsafe_unretained 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
{ @"&quot;", 34 },
{ @"&amp;", 38 },
{ @"&apos;", 39 },
{ @"&lt;", 60 },
{ @"&gt;", 62 },
// A.2.1. Latin-1 characters
{ @"&nbsp;", 160 },
{ @"&iexcl;", 161 },
{ @"&cent;", 162 },
{ @"&pound;", 163 },
{ @"&curren;", 164 },
{ @"&yen;", 165 },
{ @"&brvbar;", 166 },
{ @"&sect;", 167 },
{ @"&uml;", 168 },
{ @"&copy;", 169 },
{ @"&ordf;", 170 },
{ @"&laquo;", 171 },
{ @"&not;", 172 },
{ @"&shy;", 173 },
{ @"&reg;", 174 },
{ @"&macr;", 175 },
{ @"&deg;", 176 },
{ @"&plusmn;", 177 },
{ @"&sup2;", 178 },
{ @"&sup3;", 179 },
{ @"&acute;", 180 },
{ @"&micro;", 181 },
{ @"&para;", 182 },
{ @"&middot;", 183 },
{ @"&cedil;", 184 },
{ @"&sup1;", 185 },
{ @"&ordm;", 186 },
{ @"&raquo;", 187 },
{ @"&frac14;", 188 },
{ @"&frac12;", 189 },
{ @"&frac34;", 190 },
{ @"&iquest;", 191 },
{ @"&Agrave;", 192 },
{ @"&Aacute;", 193 },
{ @"&Acirc;", 194 },
{ @"&Atilde;", 195 },
{ @"&Auml;", 196 },
{ @"&Aring;", 197 },
{ @"&AElig;", 198 },
{ @"&Ccedil;", 199 },
{ @"&Egrave;", 200 },
{ @"&Eacute;", 201 },
{ @"&Ecirc;", 202 },
{ @"&Euml;", 203 },
{ @"&Igrave;", 204 },
{ @"&Iacute;", 205 },
{ @"&Icirc;", 206 },
{ @"&Iuml;", 207 },
{ @"&ETH;", 208 },
{ @"&Ntilde;", 209 },
{ @"&Ograve;", 210 },
{ @"&Oacute;", 211 },
{ @"&Ocirc;", 212 },
{ @"&Otilde;", 213 },
{ @"&Ouml;", 214 },
{ @"&times;", 215 },
{ @"&Oslash;", 216 },
{ @"&Ugrave;", 217 },
{ @"&Uacute;", 218 },
{ @"&Ucirc;", 219 },
{ @"&Uuml;", 220 },
{ @"&Yacute;", 221 },
{ @"&THORN;", 222 },
{ @"&szlig;", 223 },
{ @"&agrave;", 224 },
{ @"&aacute;", 225 },
{ @"&acirc;", 226 },
{ @"&atilde;", 227 },
{ @"&auml;", 228 },
{ @"&aring;", 229 },
{ @"&aelig;", 230 },
{ @"&ccedil;", 231 },
{ @"&egrave;", 232 },
{ @"&eacute;", 233 },
{ @"&ecirc;", 234 },
{ @"&euml;", 235 },
{ @"&igrave;", 236 },
{ @"&iacute;", 237 },
{ @"&icirc;", 238 },
{ @"&iuml;", 239 },
{ @"&eth;", 240 },
{ @"&ntilde;", 241 },
{ @"&ograve;", 242 },
{ @"&oacute;", 243 },
{ @"&ocirc;", 244 },
{ @"&otilde;", 245 },
{ @"&ouml;", 246 },
{ @"&divide;", 247 },
{ @"&oslash;", 248 },
{ @"&ugrave;", 249 },
{ @"&uacute;", 250 },
{ @"&ucirc;", 251 },
{ @"&uuml;", 252 },
{ @"&yacute;", 253 },
{ @"&thorn;", 254 },
{ @"&yuml;", 255 },
// A.2.2. Special characters cont'd
{ @"&OElig;", 338 },
{ @"&oelig;", 339 },
{ @"&Scaron;", 352 },
{ @"&scaron;", 353 },
{ @"&Yuml;", 376 },
// A.2.3. Symbols
{ @"&fnof;", 402 },
// A.2.2. Special characters cont'd
{ @"&circ;", 710 },
{ @"&tilde;", 732 },
// A.2.3. Symbols cont'd
{ @"&Alpha;", 913 },
{ @"&Beta;", 914 },
{ @"&Gamma;", 915 },
{ @"&Delta;", 916 },
{ @"&Epsilon;", 917 },
{ @"&Zeta;", 918 },
{ @"&Eta;", 919 },
{ @"&Theta;", 920 },
{ @"&Iota;", 921 },
{ @"&Kappa;", 922 },
{ @"&Lambda;", 923 },
{ @"&Mu;", 924 },
{ @"&Nu;", 925 },
{ @"&Xi;", 926 },
{ @"&Omicron;", 927 },
{ @"&Pi;", 928 },
{ @"&Rho;", 929 },
{ @"&Sigma;", 931 },
{ @"&Tau;", 932 },
{ @"&Upsilon;", 933 },
{ @"&Phi;", 934 },
{ @"&Chi;", 935 },
{ @"&Psi;", 936 },
{ @"&Omega;", 937 },
{ @"&alpha;", 945 },
{ @"&beta;", 946 },
{ @"&gamma;", 947 },
{ @"&delta;", 948 },
{ @"&epsilon;", 949 },
{ @"&zeta;", 950 },
{ @"&eta;", 951 },
{ @"&theta;", 952 },
{ @"&iota;", 953 },
{ @"&kappa;", 954 },
{ @"&lambda;", 955 },
{ @"&mu;", 956 },
{ @"&nu;", 957 },
{ @"&xi;", 958 },
{ @"&omicron;", 959 },
{ @"&pi;", 960 },
{ @"&rho;", 961 },
{ @"&sigmaf;", 962 },
{ @"&sigma;", 963 },
{ @"&tau;", 964 },
{ @"&upsilon;", 965 },
{ @"&phi;", 966 },
{ @"&chi;", 967 },
{ @"&psi;", 968 },
{ @"&omega;", 969 },
{ @"&thetasym;", 977 },
{ @"&upsih;", 978 },
{ @"&piv;", 982 },
// A.2.2. Special characters cont'd
{ @"&ensp;", 8194 },
{ @"&emsp;", 8195 },
{ @"&thinsp;", 8201 },
{ @"&zwnj;", 8204 },
{ @"&zwj;", 8205 },
{ @"&lrm;", 8206 },
{ @"&rlm;", 8207 },
{ @"&ndash;", 8211 },
{ @"&mdash;", 8212 },
{ @"&lsquo;", 8216 },
{ @"&rsquo;", 8217 },
{ @"&sbquo;", 8218 },
{ @"&ldquo;", 8220 },
{ @"&rdquo;", 8221 },
{ @"&bdquo;", 8222 },
{ @"&dagger;", 8224 },
{ @"&Dagger;", 8225 },
// A.2.3. Symbols cont'd
{ @"&bull;", 8226 },
{ @"&hellip;", 8230 },
// A.2.2. Special characters cont'd
{ @"&permil;", 8240 },
// A.2.3. Symbols cont'd
{ @"&prime;", 8242 },
{ @"&Prime;", 8243 },
// A.2.2. Special characters cont'd
{ @"&lsaquo;", 8249 },
{ @"&rsaquo;", 8250 },
// A.2.3. Symbols cont'd
{ @"&oline;", 8254 },
{ @"&frasl;", 8260 },
// A.2.2. Special characters cont'd
{ @"&euro;", 8364 },
// A.2.3. Symbols cont'd
{ @"&image;", 8465 },
{ @"&weierp;", 8472 },
{ @"&real;", 8476 },
{ @"&trade;", 8482 },
{ @"&alefsym;", 8501 },
{ @"&larr;", 8592 },
{ @"&uarr;", 8593 },
{ @"&rarr;", 8594 },
{ @"&darr;", 8595 },
{ @"&harr;", 8596 },
{ @"&crarr;", 8629 },
{ @"&lArr;", 8656 },
{ @"&uArr;", 8657 },
{ @"&rArr;", 8658 },
{ @"&dArr;", 8659 },
{ @"&hArr;", 8660 },
{ @"&forall;", 8704 },
{ @"&part;", 8706 },
{ @"&exist;", 8707 },
{ @"&empty;", 8709 },
{ @"&nabla;", 8711 },
{ @"&isin;", 8712 },
{ @"&notin;", 8713 },
{ @"&ni;", 8715 },
{ @"&prod;", 8719 },
{ @"&sum;", 8721 },
{ @"&minus;", 8722 },
{ @"&lowast;", 8727 },
{ @"&radic;", 8730 },
{ @"&prop;", 8733 },
{ @"&infin;", 8734 },
{ @"&ang;", 8736 },
{ @"&and;", 8743 },
{ @"&or;", 8744 },
{ @"&cap;", 8745 },
{ @"&cup;", 8746 },
{ @"&int;", 8747 },
{ @"&there4;", 8756 },
{ @"&sim;", 8764 },
{ @"&cong;", 8773 },
{ @"&asymp;", 8776 },
{ @"&ne;", 8800 },
{ @"&equiv;", 8801 },
{ @"&le;", 8804 },
{ @"&ge;", 8805 },
{ @"&sub;", 8834 },
{ @"&sup;", 8835 },
{ @"&nsub;", 8836 },
{ @"&sube;", 8838 },
{ @"&supe;", 8839 },
{ @"&oplus;", 8853 },
{ @"&otimes;", 8855 },
{ @"&perp;", 8869 },
{ @"&sdot;", 8901 },
{ @"&lceil;", 8968 },
{ @"&rceil;", 8969 },
{ @"&lfloor;", 8970 },
{ @"&rfloor;", 8971 },
{ @"&lang;", 9001 },
{ @"&rang;", 9002 },
{ @"&loz;", 9674 },
{ @"&spades;", 9824 },
{ @"&clubs;", 9827 },
{ @"&hearts;", 9829 },
{ @"&diams;", 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
{ @"&quot;", 34 },
{ @"&amp;", 38 },
{ @"&apos;", 39 },
{ @"&lt;", 60 },
{ @"&gt;", 62 },
// Latin Extended-A
{ @"&OElig;", 338 },
{ @"&oelig;", 339 },
{ @"&Scaron;", 352 },
{ @"&scaron;", 353 },
{ @"&Yuml;", 376 },
// Spacing Modifier Letters
{ @"&circ;", 710 },
{ @"&tilde;", 732 },
// General Punctuation
{ @"&ensp;", 8194 },
{ @"&emsp;", 8195 },
{ @"&thinsp;", 8201 },
{ @"&zwnj;", 8204 },
{ @"&zwj;", 8205 },
{ @"&lrm;", 8206 },
{ @"&rlm;", 8207 },
{ @"&ndash;", 8211 },
{ @"&mdash;", 8212 },
{ @"&lsquo;", 8216 },
{ @"&rsquo;", 8217 },
{ @"&sbquo;", 8218 },
{ @"&ldquo;", 8220 },
{ @"&rdquo;", 8221 },
{ @"&bdquo;", 8222 },
{ @"&dagger;", 8224 },
{ @"&Dagger;", 8225 },
{ @"&permil;", 8240 },
{ @"&lsaquo;", 8249 },
{ @"&rsaquo;", 8250 },
{ @"&euro;", 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((__bridge_retained 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((__bridge_retained 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((__bridge_retained 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 (&lt;) and less than 11 (&thetasym;)
if (length > 3 && length < 11) {
if ([escapeString characterAtIndex:1] == '#') {
unichar char2 = [escapeString characterAtIndex:2];
if (char2 == 'x' || char2 == 'X') {
// Hex escape squences &#xa3;
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 = value;
NSString *charString = [NSString stringWithCharacters:&uchar length:1];
[finalString replaceCharactersInRange:escapeRange withString:charString];
}
} else {
// Decimal Sequences &#123;
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 = 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

44
MWFeedInfo.h Normal file
View file

@ -0,0 +1,44 @@
//
// MWFeedInfo.h
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 <Foundation/Foundation.h>
@interface MWFeedInfo : NSObject <NSCoding> {
NSString *title; // Feed title
NSString *link; // Feed link
NSString *summary; // Feed summary / description
}
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *link;
@property (nonatomic, copy) NSString *summary;
@end

66
MWFeedInfo.m Normal file
View file

@ -0,0 +1,66 @@
//
// MWFeedInfo.m
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 "MWFeedInfo.h"
#define EXCERPT(str, len) (([str length] > len) ? [[str substringToIndex:len-1] stringByAppendingString:@"…"] : str)
@implementation MWFeedInfo
@synthesize title, link, summary;
#pragma mark NSObject
- (NSString *)description {
NSMutableString *string = [[NSMutableString alloc] initWithString:@"MWFeedInfo: "];
if (title) [string appendFormat:@"“%@”", EXCERPT(title, 50)];
//if (link) [string appendFormat:@" (%@)", link];
//if (summary) [string appendFormat:@", %@", MWExcerpt(summary, 50)];
return string;
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
title = [decoder decodeObjectForKey:@"title"];
link = [decoder decodeObjectForKey:@"link"];
summary = [decoder decodeObjectForKey:@"summary"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
if (title) [encoder encodeObject:title forKey:@"title"];
if (link) [encoder encodeObject:link forKey:@"link"];
if (summary) [encoder encodeObject:summary forKey:@"summary"];
}
@end

60
MWFeedItem.h Normal file
View file

@ -0,0 +1,60 @@
//
// MWFeedItem.h
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 <Foundation/Foundation.h>
@interface MWFeedItem : NSObject <NSCoding> {
NSString *identifier; // Item identifier
NSString *title; // Item title
NSString *link; // Item URL
NSDate *date; // Date the item was published
NSDate *updated; // Date the item was updated if available
NSString *summary; // Description of item
NSString *content; // More detailed content (if available)
// Enclosures: Holds 1 or more item enclosures (i.e. podcasts, mp3. pdf, etc)
// - NSArray of NSDictionaries with the following keys:
// url: where the enclosure is located (NSString)
// length: how big it is in bytes (NSNumber)
// type: what its type is, a standard MIME type (NSString)
NSArray *enclosures;
}
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *link;
@property (nonatomic, copy) NSDate *date;
@property (nonatomic, copy) NSDate *updated;
@property (nonatomic, copy) NSString *summary;
@property (nonatomic, copy) NSString *content;
@property (nonatomic, copy) NSArray *enclosures;
@end

76
MWFeedItem.m Normal file
View file

@ -0,0 +1,76 @@
//
// MWFeedItem.m
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 "MWFeedItem.h"
#define EXCERPT(str, len) (([str length] > len) ? [[str substringToIndex:len-1] stringByAppendingString:@"…"] : str)
@implementation MWFeedItem
@synthesize identifier, title, link, date, updated, summary, content, enclosures;
#pragma mark NSObject
- (NSString *)description {
NSMutableString *string = [[NSMutableString alloc] initWithString:@"MWFeedItem: "];
if (title) [string appendFormat:@"“%@”", EXCERPT(title, 50)];
if (date) [string appendFormat:@" - %@", date];
//if (link) [string appendFormat:@" (%@)", link];
//if (summary) [string appendFormat:@", %@", EXCERPT(summary, 50)];
return string ;
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
identifier = [decoder decodeObjectForKey:@"identifier"];
title = [decoder decodeObjectForKey:@"title"];
link = [decoder decodeObjectForKey:@"link"];
date = [decoder decodeObjectForKey:@"date"];
updated = [decoder decodeObjectForKey:@"updated"];
summary = [decoder decodeObjectForKey:@"summary"];
content = [decoder decodeObjectForKey:@"content"];
enclosures = [decoder decodeObjectForKey:@"enclosures"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
if (identifier) [encoder encodeObject:identifier forKey:@"identifier"];
if (title) [encoder encodeObject:title forKey:@"title"];
if (link) [encoder encodeObject:link forKey:@"link"];
if (date) [encoder encodeObject:date forKey:@"date"];
if (updated) [encoder encodeObject:updated forKey:@"updated"];
if (summary) [encoder encodeObject:summary forKey:@"summary"];
if (content) [encoder encodeObject:content forKey:@"content"];
if (enclosures) [encoder encodeObject:enclosures forKey:@"enclosures"];
}
@end

143
MWFeedParser.h Normal file
View file

@ -0,0 +1,143 @@
//
// MWFeedParser.h
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 <Foundation/Foundation.h>
#import "MWFeedInfo.h"
#import "MWFeedItem.h"
// Debug Logging
#if 0 // Set to 1 to enable debug logging
#define MWLog(x, ...) NSLog(x, ## __VA_ARGS__);
#else
#define MWLog(x, ...)
#endif
// Errors & codes
#define MWErrorDomain @"MWFeedParser"
#define MWErrorCodeNotInitiated 1 /* MWFeedParser not initialised correctly */
#define MWErrorCodeConnectionFailed 2 /* Connection to the URL failed */
#define MWErrorCodeFeedParsingError 3 /* NSXMLParser encountered a parsing error */
#define MWErrorCodeFeedValidationError 4 /* NSXMLParser encountered a validation error */
#define MWErrorCodeGeneral 5 /* MWFeedParser general error */
// Class
@class MWFeedParser;
// Types
typedef enum { ConnectionTypeAsynchronously, ConnectionTypeSynchronously } ConnectionType;
typedef enum { ParseTypeFull, ParseTypeItemsOnly, ParseTypeInfoOnly } ParseType;
typedef enum { FeedTypeUnknown, FeedTypeRSS, FeedTypeRSS1, FeedTypeAtom } FeedType;
// Delegate
@protocol MWFeedParserDelegate <NSObject>
@optional
- (void)feedParserDidStart:(MWFeedParser *)parser;
- (void)feedParser:(MWFeedParser *)parser didParseFeedInfo:(MWFeedInfo *)info;
- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item;
- (void)feedParserDidFinish:(MWFeedParser *)parser;
- (void)feedParser:(MWFeedParser *)parser didFailWithError:(NSError *)error;
@end
// MWFeedParser
@interface MWFeedParser : NSObject <NSXMLParserDelegate> {
@private
// Required
//id <MWFeedParserDelegate> delegate;
// Connection
NSURLConnection *urlConnection;
NSMutableData *asyncData;
NSString *asyncTextEncodingName;
ConnectionType connectionType;
// Parsing
ParseType feedParseType;
NSXMLParser *feedParser;
FeedType feedType;
NSDateFormatter *dateFormatterRFC822, *dateFormatterRFC3339;
// Parsing State
NSURL *url;
BOOL aborted; // Whether parse stopped due to abort
BOOL parsing; // Whether the MWFeedParser has started parsing
BOOL stopped; // Whether the parse was stopped
BOOL failed; // Whether the parse failed
BOOL parsingComplete; // Whether NSXMLParser parsing has completed
BOOL hasEncounteredItems; // Whether the parser has started parsing items
// Parsing of XML structure as content
NSString *pathOfElementWithXHTMLType; // Hold the path of the element who's type="xhtml" so we can stop parsing when it's ended
BOOL parseStructureAsContent; // For atom feeds when element type="xhtml"
// Parsing Data
NSString *currentPath;
NSMutableString *currentText;
NSDictionary *currentElementAttributes;
MWFeedItem *item;
MWFeedInfo *info;
}
#pragma mark Public Properties
// Delegate to recieve data as it is parsed
@property (nonatomic, assign) id <MWFeedParserDelegate> delegate;
// Whether to parse feed info & all items, just feed info, or just feed items
@property (nonatomic) ParseType feedParseType;
// Set whether to download asynchronously or synchronously
@property (nonatomic) ConnectionType connectionType;
// Whether parsing was stopped
@property (nonatomic, readonly, getter=isStopped) BOOL stopped;
// Whether parsing failed
@property (nonatomic, readonly, getter=didFail) BOOL failed;
// Whether parsing is in progress
@property (nonatomic, readonly, getter=isParsing) BOOL parsing;
#pragma mark Public Methods
// Init MWFeedParser with a URL string
- (id)initWithFeedURL:(NSURL *)feedURL;
// Begin parsing
- (BOOL)parse;
// Stop parsing
- (void)stopParsing;
// Returns the URL
- (NSURL *)url;
@end

935
MWFeedParser.m Normal file
View file

@ -0,0 +1,935 @@
//
// MWFeedParser.m
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 "MWFeedParser.h"
#import "MWFeedParser_Private.h"
#import "NSString+HTML.h"
#import "NSDate+InternetDateTime.h"
// NSXMLParser Logging
#if 0 // Set to 1 to enable XML parsing logs
#define MWXMLLog(x, ...) NSLog(x, ## __VA_ARGS__);
#else
#define MWXMLLog(x, ...)
#endif
// Empty XHTML elements ( <!ELEMENT br EMPTY> in http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd )
#define ELEMENT_IS_EMPTY(e) ([e isEqualToString:@"br"] || [e isEqualToString:@"img"] || [e isEqualToString:@"input"] || \
[e isEqualToString:@"hr"] || [e isEqualToString:@"link"] || [e isEqualToString:@"base"] || \
[e isEqualToString:@"basefont"] || [e isEqualToString:@"frame"] || [e isEqualToString:@"meta"] || \
[e isEqualToString:@"area"] || [e isEqualToString:@"col"] || [e isEqualToString:@"param"])
// Implementation
@implementation MWFeedParser
// Properties
@synthesize url, delegate;
@synthesize urlConnection, asyncData, asyncTextEncodingName, connectionType;
@synthesize feedParseType, feedParser, currentPath, currentText, currentElementAttributes, item, info;
@synthesize pathOfElementWithXHTMLType;
@synthesize stopped, failed, parsing;
#pragma mark -
#pragma mark NSObject
- (id)init {
if ((self = [super init])) {
// Defaults
feedParseType = ParseTypeFull;
connectionType = ConnectionTypeSynchronously;
// Date Formatters
// Good info on internet dates here: http://developer.apple.com/iphone/library/qa/qa2010/qa1480.html
NSLocale *en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
dateFormatterRFC822 = [[NSDateFormatter alloc] init];
dateFormatterRFC3339 = [[NSDateFormatter alloc] init];
[dateFormatterRFC822 setLocale:en_US_POSIX];
[dateFormatterRFC3339 setLocale:en_US_POSIX];
[dateFormatterRFC822 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[dateFormatterRFC3339 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}
return self;
}
// Initialise with a URL
// Mainly for historic reasons before -parseURL:
- (id)initWithFeedURL:(NSURL *)feedURL {
if ((self = [self init])) {
// Check if an string was passed as old init asked for NSString not NSURL
if ([feedURL isKindOfClass:[NSString class]]) {
feedURL = [NSURL URLWithString:(NSString *)feedURL];
}
// Remember url
self.url = feedURL;
}
return self;
}
#pragma mark -
#pragma mark Parsing
// Reset data variables before processing
// Exclude parse state variables as they are needed after parse
- (void)reset {
self.asyncData = nil;
self.asyncTextEncodingName = nil;
self.urlConnection = nil;
feedType = FeedTypeUnknown;
self.currentPath = @"/";
self.currentText = [[NSMutableString alloc] init];
self.item = nil;
self.info = nil;
self.currentElementAttributes = nil;
parseStructureAsContent = NO;
self.pathOfElementWithXHTMLType = nil;
hasEncounteredItems = NO;
}
// Parse using URL for backwards compatibility
- (BOOL)parse {
// Reset
[self reset];
// Perform checks before parsing
if (!url || !delegate) { [self parsingFailedWithErrorCode:MWErrorCodeNotInitiated
andDescription:@"Delegate or URL not specified"]; return NO; }
if (parsing) { [self parsingFailedWithErrorCode:MWErrorCodeGeneral
andDescription:@"Cannot start parsing as parsing is already in progress"]; return NO; }
// Reset state for next parse
parsing = YES;
aborted = NO;
stopped = NO;
failed = NO;
parsingComplete = NO;
// Start
BOOL success = YES;
// Request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:60];
[request setValue:@"MWFeedParser" forHTTPHeaderField:@"User-Agent"];
// Debug Log
MWLog(@"MWFeedParser: Connecting & downloading feed data");
// Connection
if (connectionType == ConnectionTypeAsynchronously) {
// Async
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (urlConnection) {
asyncData = [[NSMutableData alloc] init];// Create data
} else {
[self parsingFailedWithErrorCode:MWErrorCodeConnectionFailed
andDescription:[NSString stringWithFormat:@"Asynchronous connection failed to URL: %@", url]];
success = NO;
}
} else {
// Sync
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (data && !error) {
[self startParsingData:data textEncodingName:[response textEncodingName]]; // Process
} else {
[self parsingFailedWithErrorCode:MWErrorCodeConnectionFailed
andDescription:[NSString stringWithFormat:@"Synchronous connection failed to URL: %@", url]];
success = NO;
}
}
// return
return success;
}
// Begin XML parsing
- (void)startParsingData:(NSData *)data textEncodingName:(NSString *)textEncodingName {
if (data && !feedParser) {
// Create feed info
MWFeedInfo *i = [[MWFeedInfo alloc] init];
self.info = i;
// Check whether it's UTF-8
if (![[textEncodingName lowercaseString] isEqualToString:@"utf-8"]) {
// Not UTF-8 so convert
MWLog(@"MWFeedParser: XML document was not UTF-8 so we're converting it");
NSString *string = nil;
// Attempt to detect encoding from response header
NSStringEncoding nsEncoding = 0;
if (textEncodingName) {
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge_retained CFStringRef)textEncodingName);
if (cfEncoding != kCFStringEncodingInvalidId) {
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
if (nsEncoding != 0) string = [[NSString alloc] initWithData:data encoding:nsEncoding];
}
}
// If that failed then make our own attempts
if (!string) {
// http://www.mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html
string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (!string) string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
if (!string) string = [[NSString alloc] initWithData:data encoding:NSMacOSRomanStringEncoding];
}
// Nil data
data = nil;
// Parse
if (string) {
// Set XML encoding to UTF-8
if ([string hasPrefix:@"<?xml"]) {
NSRange a = [string rangeOfString:@"?>"];
if (a.location != NSNotFound) {
NSString *xmlDec = [string substringToIndex:a.location];
if ([xmlDec rangeOfString:@"encoding=\"UTF-8\""
options:NSCaseInsensitiveSearch].location == NSNotFound) {
NSRange b = [xmlDec rangeOfString:@"encoding=\""];
if (b.location != NSNotFound) {
NSUInteger s = b.location+b.length;
NSRange c = [xmlDec rangeOfString:@"\"" options:0 range:NSMakeRange(s, [xmlDec length] - s)];
if (c.location != NSNotFound) {
NSString *temp = [string stringByReplacingCharactersInRange:NSMakeRange(b.location,c.location+c.length-b.location)
withString:@"encoding=\"UTF-8\""];
string = temp;
}
}
}
}
}
// Convert string to UTF-8 data
if (string) {
data = [string dataUsingEncoding:NSUTF8StringEncoding];
}
}
}
// Create NSXMLParser
if (data) {
NSXMLParser *newFeedParser = [[NSXMLParser alloc] initWithData:data];
self.feedParser = newFeedParser;
if (feedParser) {
// Parse!
feedParser.delegate = self;
[feedParser setShouldProcessNamespaces:YES];
[feedParser parse];
self.feedParser = nil; // Release after parse
} else {
[self parsingFailedWithErrorCode:MWErrorCodeFeedParsingError andDescription:@"Feed not a valid XML document"];
}
} else {
[self parsingFailedWithErrorCode:MWErrorCodeFeedParsingError andDescription:@"Error with feed encoding"];
}
}
}
// Abort parsing early if we're ignoring feed items
- (void)abortParsingEarly {
// Abort
aborted = YES; [feedParser abortParsing];
[self parsingFinished];
}
// Stop parsing
- (void)stopParsing {
// Only if we're parsing
if (parsing && !parsingComplete) {
// Debug Log
MWLog(@"MWFeedParser: Parsing stopped");
// Stop
stopped = YES;
// Stop downloading
[urlConnection cancel];
self.urlConnection = nil;
self.asyncData = nil;
self.asyncTextEncodingName = nil;
// Abort
aborted = YES;
[feedParser abortParsing];
// Finished
[self parsingFinished];
}
}
// Finished parsing document successfully
- (void)parsingFinished {
// Finish
if (!parsingComplete) {
// Set state and notify delegate
parsing = NO;
parsingComplete = YES;
if ([delegate respondsToSelector:@selector(feedParserDidFinish:)])
[delegate feedParserDidFinish:self];
// Reset
[self reset];
}
}
// If an error occurs, create NSError and inform delegate
- (void)parsingFailedWithErrorCode:(int)code andDescription:(NSString *)description {
// Finish & create error
if (!parsingComplete) {
// State
failed = YES;
parsing = NO;
parsingComplete = YES;
// Create error
NSError *error = [NSError errorWithDomain:MWErrorDomain
code:code
userInfo:[NSDictionary dictionaryWithObject:description
forKey:NSLocalizedDescriptionKey]];
MWLog(@"%@", error);
// Abort parsing
if (feedParser) {
aborted = YES;
[feedParser abortParsing];
}
// Reset
[self reset];
// Inform delegate
if ([delegate respondsToSelector:@selector(feedParser:didFailWithError:)])
[delegate feedParser:self didFailWithError:error];
}
}
#pragma mark -
#pragma mark NSURLConnection Delegate (Async)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[asyncData setLength:0];
self.asyncTextEncodingName = [response textEncodingName];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[asyncData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Failed
self.urlConnection = nil;
self.asyncData = nil;
self.asyncTextEncodingName = nil;
// Error
[self parsingFailedWithErrorCode:MWErrorCodeConnectionFailed andDescription:[error localizedDescription]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Succeed
MWLog(@"MWFeedParser: Connection successful... received %d bytes of data", [asyncData length]);
// Parse
if (!stopped) [self startParsingData:asyncData textEncodingName:self.asyncTextEncodingName];
// Cleanup
self.urlConnection = nil;
self.asyncData = nil;
self.asyncTextEncodingName = nil;
}
-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil; // Don't cache
}
#pragma mark -
#pragma mark XML Parsing
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
MWXMLLog(@"NSXMLParser: didStartElement: %@", qualifiedName);
@autoreleasepool {
// Adjust path
self.currentPath = [currentPath stringByAppendingPathComponent:qualifiedName];
self.currentElementAttributes = attributeDict;
// Parse content as structure (Atom feeds with element type="xhtml")
// - Use elementName not qualifiedName to ignore XML namespaces for XHTML entities
if (parseStructureAsContent) {
// Open XHTML tag
[currentText appendFormat:@"<%@", elementName];
// Add attributes
for (NSString *key in attributeDict) {
[currentText appendFormat:@" %@=\"%@\"", key,
[[attributeDict objectForKey:key] stringByEncodingHTMLEntities]];
}
// End tag or close
if (ELEMENT_IS_EMPTY(elementName)) {
[currentText appendFormat:@" />", elementName];
} else {
[currentText appendFormat:@">", elementName];
}
return;
}
// Reset
[self.currentText setString:@""];
// Determine feed type
if (feedType == FeedTypeUnknown) {
if ([qualifiedName isEqualToString:@"rss"]) feedType = FeedTypeRSS;
else if ([qualifiedName isEqualToString:@"rdf:RDF"]) feedType = FeedTypeRSS1;
else if ([qualifiedName isEqualToString:@"feed"]) feedType = FeedTypeAtom;
else {
// Invalid format so fail
[self parsingFailedWithErrorCode:MWErrorCodeFeedParsingError
andDescription:@"XML document is not a valid web feed document."];
}
return;
}
// Entering new feed element
if (feedParseType != ParseTypeItemsOnly) {
if ((feedType == FeedTypeRSS && [currentPath isEqualToString:@"/rss/channel"]) ||
(feedType == FeedTypeRSS1 && [currentPath isEqualToString:@"/rdf:RDF/channel"]) ||
(feedType == FeedTypeAtom && [currentPath isEqualToString:@"/feed"])) {
return;
}
}
// Entering new item element
if ((feedType == FeedTypeRSS && [currentPath isEqualToString:@"/rss/channel/item"]) ||
(feedType == FeedTypeRSS1 && [currentPath isEqualToString:@"/rdf:RDF/item"]) ||
(feedType == FeedTypeAtom && [currentPath isEqualToString:@"/feed/entry"])) {
// Send off feed info to delegate
if (!hasEncounteredItems) {
hasEncounteredItems = YES;
if (feedParseType != ParseTypeItemsOnly) { // Check whether to ignore feed info
// Dispatch feed info to delegate
[self dispatchFeedInfoToDelegate];
// Stop parsing if only requiring meta data
if (feedParseType == ParseTypeInfoOnly) {
// Debug log
MWLog(@"MWFeedParser: Parse type is ParseTypeInfoOnly so finishing here");
// Finish
[self abortParsingEarly];
return;
}
} else {
// Ignoring feed info so debug log
MWLog(@"MWFeedParser: Parse type is ParseTypeItemsOnly so ignoring feed info");
}
}
// New item
MWFeedItem *newItem = [[MWFeedItem alloc] init];
self.item = newItem;
// Return
return;
}
// Check if entering into an Atom content tag with type "xhtml"
// If type is "xhtml" then it can contain child elements and structure needs
// to be parsed as content
// See: http://www.atomenabled.org/developers/syndication/atom-format-spec.php#rfc.section.3.1.1
if (feedType == FeedTypeAtom) {
// Check type attribute
NSString *typeAttribute = [attributeDict objectForKey:@"type"];
if (typeAttribute && [typeAttribute isEqualToString:@"xhtml"]) {
// Start parsing structure as content
parseStructureAsContent = YES;
// Remember path so we can stop parsing structure when element ends
self.pathOfElementWithXHTMLType = currentPath;
}
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
MWXMLLog(@"NSXMLParser: didEndElement: %@", qName);
@autoreleasepool {
// Parse content as structure (Atom feeds with element type="xhtml")
// - Use elementName not qualifiedName to ignore XML namespaces for XHTML entities
if (parseStructureAsContent) {
// Check for finishing parsing structure as content
if (currentPath.length > pathOfElementWithXHTMLType.length) {
// Close XHTML tag unless it is an empty element
if (!ELEMENT_IS_EMPTY(elementName)) [currentText appendFormat:@"</%@>", elementName];
// Adjust path & don't continue
self.currentPath = [currentPath stringByDeletingLastPathComponent];
// Return
return;
}
// Finish
parseStructureAsContent = NO;
self.pathOfElementWithXHTMLType = nil;
// Continue...
}
// Store data
BOOL processed = NO;
if (currentText) {
// Remove newlines and whitespace from currentText
NSString *processedText = [currentText stringByRemovingNewLinesAndWhitespace];
// Process
switch (feedType) {
case FeedTypeRSS: {
// Item
if (!processed) {
if ([currentPath isEqualToString:@"/rss/channel/item/title"]) { if (processedText.length > 0) item.title = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/link"]) { if (processedText.length > 0) item.link = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/guid"]) { if (processedText.length > 0) item.identifier = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/description"]) { if (processedText.length > 0) item.summary = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/content:encoded"]) { if (processedText.length > 0) item.content = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/pubDate"]) { if (processedText.length > 0) item.date = [NSDate dateFromInternetDateTimeString:processedText formatHint:DateFormatHintRFC822]; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/enclosure"]) { [self createEnclosureFromAttributes:currentElementAttributes andAddToItem:item]; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/item/dc:date"]) { if (processedText.length > 0) item.date = [NSDate dateFromInternetDateTimeString:processedText formatHint:DateFormatHintRFC3339]; processed = YES; }
}
// Info
if (!processed && feedParseType != ParseTypeItemsOnly) {
if ([currentPath isEqualToString:@"/rss/channel/title"]) { if (processedText.length > 0) info.title = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/description"]) { if (processedText.length > 0) info.summary = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rss/channel/link"]) { if (processedText.length > 0) info.link = processedText; processed = YES; }
}
break;
}
case FeedTypeRSS1: {
// Item
if (!processed) {
if ([currentPath isEqualToString:@"/rdf:RDF/item/title"]) { if (processedText.length > 0) item.title = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/item/link"]) { if (processedText.length > 0) item.link = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/item/dc:identifier"]) { if (processedText.length > 0) item.identifier = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/item/description"]) { if (processedText.length > 0) item.summary = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/item/content:encoded"]) { if (processedText.length > 0) item.content = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/item/dc:date"]) { if (processedText.length > 0) item.date = [NSDate dateFromInternetDateTimeString:processedText formatHint:DateFormatHintRFC3339]; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/item/enc:enclosure"]) { [self createEnclosureFromAttributes:currentElementAttributes andAddToItem:item]; processed = YES; }
}
// Info
if (!processed && feedParseType != ParseTypeItemsOnly) {
if ([currentPath isEqualToString:@"/rdf:RDF/channel/title"]) { if (processedText.length > 0) info.title = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/channel/description"]) { if (processedText.length > 0) info.summary = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/rdf:RDF/channel/link"]) { if (processedText.length > 0) info.link = processedText; processed = YES; }
}
break;
}
case FeedTypeAtom: {
// Item
if (!processed) {
if ([currentPath isEqualToString:@"/feed/entry/title"]) { if (processedText.length > 0) item.title = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/entry/link"]) { [self processAtomLink:currentElementAttributes andAddToMWObject:item]; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/entry/id"]) { if (processedText.length > 0) item.identifier = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/entry/summary"]) { if (processedText.length > 0) item.summary = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/entry/content"]) { if (processedText.length > 0) item.content = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/entry/published"]) { if (processedText.length > 0) item.date = [NSDate dateFromInternetDateTimeString:processedText formatHint:DateFormatHintRFC3339]; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/entry/updated"]) { if (processedText.length > 0) item.updated = [NSDate dateFromInternetDateTimeString:processedText formatHint:DateFormatHintRFC3339]; processed = YES; }
}
// Info
if (!processed && feedParseType != ParseTypeItemsOnly) {
if ([currentPath isEqualToString:@"/feed/title"]) { if (processedText.length > 0) info.title = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/description"]) { if (processedText.length > 0) info.summary = processedText; processed = YES; }
else if ([currentPath isEqualToString:@"/feed/link"]) { [self processAtomLink:currentElementAttributes andAddToMWObject:info]; processed = YES;}
}
break;
}
default: break;
}
}
// Adjust path
self.currentPath = [currentPath stringByDeletingLastPathComponent];
// If end of an item then tell delegate
if (!processed) {
if (((feedType == FeedTypeRSS || feedType == FeedTypeRSS1) && [qName isEqualToString:@"item"]) ||
(feedType == FeedTypeAtom && [qName isEqualToString:@"entry"])) {
// Dispatch item to delegate
[self dispatchFeedItemToDelegate];
}
}
// Check if the document has finished parsing and send off info if needed (i.e. there were no items)
if (!processed) {
if ((feedType == FeedTypeRSS && [qName isEqualToString:@"rss"]) ||
(feedType == FeedTypeRSS1 && [qName isEqualToString:@"rdf:RDF"]) ||
(feedType == FeedTypeAtom && [qName isEqualToString:@"feed"])) {
// Document ending so if we havent sent off feed info yet, do so
if (info && feedParseType != ParseTypeItemsOnly) [self dispatchFeedInfoToDelegate];
}
}
}
}
//- (void)parser:(NSXMLParser *)parser foundAttributeDeclarationWithName:(NSString *)attributeName
// forElement:(NSString *)elementName type:(NSString *)type defaultValue:(NSString *)defaultValue {
// MWXMLLog(@"NSXMLParser: foundAttributeDeclarationWithName: %@", attributeName);
//}
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {
MWXMLLog(@"NSXMLParser: foundCDATA (%d bytes)", CDATABlock.length);
// Remember characters
NSString *string = nil;
@try {
// Try decoding with NSUTF8StringEncoding & NSISOLatin1StringEncoding
string = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
if (!string) string = [[NSString alloc] initWithData:CDATABlock encoding:NSISOLatin1StringEncoding];
// Add - No need to encode as CDATA should not be encoded as it's ignored by the parser
if (string) [currentText appendString:string];
} @catch (NSException * e) {
} @finally {
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
MWXMLLog(@"NSXMLParser: foundCharacters: %@", string);
// Remember characters
if (!parseStructureAsContent) {
// Add characters normally
[currentText appendString:string];
} else {
// If parsing structure as content then we should encode characters
[currentText appendString:[string stringByEncodingHTMLEntities]];
}
}
- (void)parserDidStartDocument:(NSXMLParser *)parser {
MWXMLLog(@"NSXMLParser: parserDidStartDocument");
// Debug Log
MWLog(@"MWFeedParser: Parsing started");
// Inform delegate
if ([delegate respondsToSelector:@selector(feedParserDidStart:)])
[delegate feedParserDidStart:self];
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
MWXMLLog(@"NSXMLParser: parserDidEndDocument");
// Debug Log
MWLog(@"MWFeedParser: Parsing finished");
// Inform delegate
[self parsingFinished];
}
// Call if parsing error occured or parse was aborted
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
MWXMLLog(@"NSXMLParser: parseErrorOccurred: %@", parseError);
// Fail with error
if (!aborted) {
// This method is called when legimitaly aboring the parser so ignore if this is the case
[self parsingFailedWithErrorCode:MWErrorCodeFeedParsingError andDescription:[parseError localizedDescription]];
}
}
- (void)parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validError {
MWXMLLog(@"NSXMLParser: validationErrorOccurred: %@", validError);
// Fail with error
[self parsingFailedWithErrorCode:MWErrorCodeFeedValidationError andDescription:[validError localizedDescription]];
}
#pragma mark -
#pragma mark Send Items to Delegate
- (void)dispatchFeedInfoToDelegate {
if (info) {
// Inform delegate
if ([delegate respondsToSelector:@selector(feedParser:didParseFeedInfo:)])
[delegate feedParser:self didParseFeedInfo:info];
// Debug log
MWLog(@"MWFeedParser: Feed info for \"%@\" successfully parsed", info.title);
// Finish
self.info = nil;
}
}
- (void)dispatchFeedItemToDelegate {
if (item) {
// Process before hand
if (!item.summary) { item.summary = item.content; item.content = nil; }
if (!item.date && item.updated) { item.date = item.updated; }
// Debug log
MWLog(@"MWFeedParser: Feed item \"%@\" successfully parsed", item.title);
// Inform delegate
if ([delegate respondsToSelector:@selector(feedParser:didParseFeedItem:)])
[delegate feedParser:self didParseFeedItem:item];
// Finish
self.item = nil;
}
}
#pragma mark -
#pragma mark Helpers & Properties
// Set URL to parse and removing feed: uri scheme info
// http://en.wikipedia.org/wiki/Feed:_URI_scheme
- (void)setUrl:(NSURL *)value {
// Check if an string was passed as old init asked for NSString not NSURL
if ([value isKindOfClass:[NSString class]]) {
value = [NSURL URLWithString:(NSString *)value];
}
// Create new instance of NSURL and check URL scheme
NSURL *newURL = nil;
if (value) {
if ([value.scheme isEqualToString:@"feed"]) {
// Remove feed URL scheme
newURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",
([value.resourceSpecifier hasPrefix:@"//"] ? @"http:" : @""),
value.resourceSpecifier]];
} else {
// Copy
newURL = [value copy];
}
}
// Set new url
if (url) url = nil;
url = newURL;
}
#pragma mark -
#pragma mark Misc
// Create an enclosure NSDictionary from enclosure (or link) attributes
- (BOOL)createEnclosureFromAttributes:(NSDictionary *)attributes andAddToItem:(MWFeedItem *)currentItem {
// Create enclosure
NSDictionary *enclosure = nil;
NSString *encURL = nil, *encType = nil;
NSNumber *encLength = nil;
if (attributes) {
switch (feedType) {
case FeedTypeRSS: { // http://cyber.law.harvard.edu/rss/rss.html#ltenclosuregtSubelementOfLtitemgt
// <enclosure>
encURL = [attributes objectForKey:@"url"];
encType = [attributes objectForKey:@"type"];
encLength = [NSNumber numberWithLongLong:[((NSString *)[attributes objectForKey:@"length"]) longLongValue]];
break;
}
case FeedTypeRSS1: { // http://www.xs4all.nl/~foz/mod_enclosure.html
// <enc:enclosure>
encURL = [attributes objectForKey:@"rdf:resource"];
encType = [attributes objectForKey:@"enc:type"];
encLength = [NSNumber numberWithLongLong:[((NSString *)[attributes objectForKey:@"enc:length"]) longLongValue]];
break;
}
case FeedTypeAtom: { // http://www.atomenabled.org/developers/syndication/atom-format-spec.php#rel_attribute
// <link rel="enclosure" href=...
if ([[attributes objectForKey:@"rel"] isEqualToString:@"enclosure"]) {
encURL = [attributes objectForKey:@"href"];
encType = [attributes objectForKey:@"type"];
encLength = [NSNumber numberWithLongLong:[((NSString *)[attributes objectForKey:@"length"]) longLongValue]];
}
break;
}
default: break;
}
}
if (encURL) {
NSMutableDictionary *e = [[NSMutableDictionary alloc] initWithCapacity:3];
[e setObject:encURL forKey:@"url"];
if (encType) [e setObject:encType forKey:@"type"];
if (encLength) [e setObject:encLength forKey:@"length"];
enclosure = [NSDictionary dictionaryWithDictionary:e];
}
// Add to item
if (enclosure) {
if (currentItem.enclosures) {
currentItem.enclosures = [currentItem.enclosures arrayByAddingObject:enclosure];
} else {
currentItem.enclosures = [NSArray arrayWithObject:enclosure];
}
return YES;
} else {
return NO;
}
}
// Process ATOM link and determine whether to ignore it, add it as the link element or add as enclosure
// Links can be added to MWObject (info or item)
- (BOOL)processAtomLink:(NSDictionary *)attributes andAddToMWObject:(id)MWObject {
if (attributes && [attributes objectForKey:@"rel"]) {
// Use as link if rel == alternate
if ([[attributes objectForKey:@"rel"] isEqualToString:@"alternate"]) {
[MWObject setLink:[attributes objectForKey:@"href"]]; // Can be added to MWFeedItem or MWFeedInfo
return YES;
}
// Use as enclosure if rel == enclosure
if ([[attributes objectForKey:@"rel"] isEqualToString:@"enclosure"]) {
if ([MWObject isMemberOfClass:[MWFeedItem class]]) { // Enclosures can only be added to MWFeedItem
[self createEnclosureFromAttributes:attributes andAddToItem:(MWFeedItem *)MWObject];
return YES;
}
}
}
return NO;
}
@end

68
MWFeedParser_Private.h Normal file
View file

@ -0,0 +1,68 @@
//
// MWFeedParser_Private.h
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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.
//
@interface MWFeedParser ()
#pragma mark Private Properties
// Feed Downloading Properties
@property (nonatomic, copy) NSURL *url;
@property (nonatomic, retain) NSURLConnection *urlConnection;
@property (nonatomic, retain) NSMutableData *asyncData;
@property (nonatomic, retain) NSString *asyncTextEncodingName;
// Parsing Properties
@property (nonatomic, retain) NSXMLParser *feedParser;
@property (nonatomic, retain) NSString *currentPath;
@property (nonatomic, retain) NSMutableString *currentText;
@property (nonatomic, retain) NSDictionary *currentElementAttributes;
@property (nonatomic, retain) MWFeedItem *item;
@property (nonatomic, retain) MWFeedInfo *info;
@property (nonatomic, copy) NSString *pathOfElementWithXHTMLType;
#pragma mark Private Methods
// Parsing Methods
- (void)reset;
- (void)abortParsingEarly;
- (void)parsingFinished;
- (void)parsingFailedWithErrorCode:(int)code andDescription:(NSString *)description;
- (void)startParsingData:(NSData *)data textEncodingName:(NSString *)textEncodingName;
// Dispatching to Delegate
- (void)dispatchFeedInfoToDelegate;
- (void)dispatchFeedItemToDelegate;
// Error Handling
// Misc
- (BOOL)createEnclosureFromAttributes:(NSDictionary *)attributes andAddToItem:(MWFeedItem *)currentItem;
- (BOOL)processAtomLink:(NSDictionary *)attributes andAddToMWObject:(id)MWObject;
@end

31
NSDate+InternetDateTime.h Normal file
View file

@ -0,0 +1,31 @@
//
// NSDate+InternetDateTime.h
// MWFeedParser
//
// Created by Michael Waterfall on 07/10/2010.
// Copyright 2010 Michael Waterfall. All rights reserved.
//
#import <Foundation/Foundation.h>
// Formatting hints
typedef enum {
DateFormatHintNone,
DateFormatHintRFC822,
DateFormatHintRFC3339
} DateFormatHint;
// A category to parse internet date & time strings
@interface NSDate (InternetDateTime)
// Get date from RFC3339 or RFC822 string
// - A format/specification hint can be used to speed up,
// otherwise both will be attempted in order to get a date
+ (NSDate *)dateFromInternetDateTimeString:(NSString *)dateString
formatHint:(DateFormatHint)hint;
// Get date from a string using a specific date specification
+ (NSDate *)dateFromRFC3339String:(NSString *)dateString;
+ (NSDate *)dateFromRFC822String:(NSString *)dateString;
@end

136
NSDate+InternetDateTime.m Normal file
View file

@ -0,0 +1,136 @@
//
// NSDate+InternetDateTime.m
// MWFeedParser
//
// Created by Michael Waterfall on 07/10/2010.
// Copyright 2010 Michael Waterfall. All rights reserved.
//
#import "NSDate+InternetDateTime.h"
// Always keep the formatter around as they're expensive to instantiate
static NSDateFormatter *_internetDateTimeFormatter = nil;
// Good info on internet dates here:
// http://developer.apple.com/iphone/library/qa/qa2010/qa1480.html
@implementation NSDate (InternetDateTime)
// Instantiate single date formatter
+ (NSDateFormatter *)internetDateTimeFormatter {
@synchronized(self) {
if (!_internetDateTimeFormatter) {
NSLocale *en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
_internetDateTimeFormatter = [[NSDateFormatter alloc] init];
[_internetDateTimeFormatter setLocale:en_US_POSIX];
[_internetDateTimeFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}
}
return _internetDateTimeFormatter;
}
// Get a date from a string - hint can be used to speed up
+ (NSDate *)dateFromInternetDateTimeString:(NSString *)dateString formatHint:(DateFormatHint)hint {
NSDate *date = nil;
if (dateString) {
if (hint != DateFormatHintRFC3339) {
// Try RFC822 first
date = [NSDate dateFromRFC822String:dateString];
if (!date) date = [NSDate dateFromRFC3339String:dateString];
} else {
// Try RFC3339 first
date = [NSDate dateFromRFC3339String:dateString];
if (!date) date = [NSDate dateFromRFC822String:dateString];
}
}
return date;
}
// See http://www.faqs.org/rfcs/rfc822.html
+ (NSDate *)dateFromRFC822String:(NSString *)dateString {
NSDate *date = nil;
if (dateString) {
NSDateFormatter *dateFormatter = [NSDate internetDateTimeFormatter];
@synchronized(dateFormatter) {
// Process
NSString *RFC822String = [[NSString stringWithString:dateString] uppercaseString];
if ([RFC822String rangeOfString:@","].location != NSNotFound) {
if (!date) { // Sun, 19 May 2002 15:21:36 GMT
[dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss zzz"];
date = [dateFormatter dateFromString:RFC822String];
}
if (!date) { // Sun, 19 May 2002 15:21 GMT
[dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm zzz"];
date = [dateFormatter dateFromString:RFC822String];
}
if (!date) { // Sun, 19 May 2002 15:21:36
[dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss"];
date = [dateFormatter dateFromString:RFC822String];
}
if (!date) { // Sun, 19 May 2002 15:21
[dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm"];
date = [dateFormatter dateFromString:RFC822String];
}
} else {
if (!date) { // 19 May 2002 15:21:36 GMT
[dateFormatter setDateFormat:@"d MMM yyyy HH:mm:ss zzz"];
date = [dateFormatter dateFromString:RFC822String];
}
if (!date) { // 19 May 2002 15:21 GMT
[dateFormatter setDateFormat:@"d MMM yyyy HH:mm zzz"];
date = [dateFormatter dateFromString:RFC822String];
}
if (!date) { // 19 May 2002 15:21:36
[dateFormatter setDateFormat:@"d MMM yyyy HH:mm:ss"];
date = [dateFormatter dateFromString:RFC822String];
}
if (!date) { // 19 May 2002 15:21
[dateFormatter setDateFormat:@"d MMM yyyy HH:mm"];
date = [dateFormatter dateFromString:RFC822String];
}
}
if (!date) NSLog(@"Could not parse RFC822 date: \"%@\" Possible invalid format.", dateString);
}
}
return date;
}
// See http://www.faqs.org/rfcs/rfc3339.html
+ (NSDate *)dateFromRFC3339String:(NSString *)dateString {
NSDate *date = nil;
if (dateString) {
NSDateFormatter *dateFormatter = [NSDate internetDateTimeFormatter];
@synchronized(dateFormatter) {
// Process date
NSString *RFC3339String = [[NSString stringWithString:dateString] uppercaseString];
RFC3339String = [RFC3339String stringByReplacingOccurrencesOfString:@"Z" withString:@"-0000"];
// Remove colon in timezone as it breaks NSDateFormatter in iOS 4+.
// - see https://devforums.apple.com/thread/45837
if (RFC3339String.length > 20) {
RFC3339String = [RFC3339String stringByReplacingOccurrencesOfString:@":"
withString:@""
options:0
range:NSMakeRange(20, RFC3339String.length-20)];
}
if (!date) { // 1996-12-19T16:39:57-0800
[dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"];
date = [dateFormatter dateFromString:RFC3339String];
}
if (!date) { // 1937-01-01T12:00:27.87+0020
[dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZZZ"];
date = [dateFormatter dateFromString:RFC3339String];
}
if (!date) { // 1937-01-01T12:00:27
[dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss"];
date = [dateFormatter dateFromString:RFC3339String];
}
if (!date) NSLog(@"Could not parse RFC3339 date: \"%@\" Possible invalid format.", dateString);
}
}
return date;
}
@end

67
NSString+HTML.h Normal file
View file

@ -0,0 +1,67 @@
//
// NSString+HTML.h
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 <Foundation/Foundation.h>
// Dependant upon GTMNSString+HTML
@interface NSString (HTML)
// Strips HTML tags & comments, removes extra whitespace and decodes HTML character entities.
- (NSString *)stringByConvertingHTMLToPlainText;
// Decode all HTML entities using GTM.
- (NSString *)stringByDecodingHTMLEntities;
// Encode all HTML entities using GTM.
- (NSString *)stringByEncodingHTMLEntities;
// Minimal unicode encoding 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.
- (NSString *)stringByEncodingHTMLEntities:(BOOL)isUnicode;
// Replace newlines with <br /> tags.
- (NSString *)stringWithNewLinesAsBRs;
// Remove newlines and white space from string.
- (NSString *)stringByRemovingNewLinesAndWhitespace;
// Wrap plain URLs in <a href="..." class="linkified">...</a>
// - Ignores URLs inside tags (any URL beginning with =")
// - HTTP & HTTPS schemes only
// - Only works in iOS 4+ as we use NSRegularExpression (returns self if not supported so be careful with NSMutableStrings)
// - Expression: (?<!=")\b((http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)
// - Adapted from http://regexlib.com/REDetails.aspx?regexp_id=96
- (NSString *)stringByLinkifyingURLs;
// DEPRECIATED - Please use NSString stringByConvertingHTMLToPlainText
- (NSString *)stringByStrippingTags __attribute__((deprecated));
@end

318
NSString+HTML.m Normal file
View file

@ -0,0 +1,318 @@
//
// NSString+HTML.m
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 "NSString+HTML.h"
#import "GTMNSString+HTML.h"
@implementation NSString (HTML)
#pragma mark - Instance Methods
- (NSString *)stringByConvertingHTMLToPlainText {
@autoreleasepool {
// Character sets
NSCharacterSet *stopCharacters = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"< \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]];
NSCharacterSet *newLineAndWhitespaceCharacters = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@" \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]];
NSCharacterSet *tagNameCharacters = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
// Scan and find all tags
NSMutableString *result = [[NSMutableString alloc] initWithCapacity:self.length];
NSScanner *scanner = [[NSScanner alloc] initWithString:self];
[scanner setCharactersToBeSkipped:nil];
[scanner setCaseSensitive:YES];
NSString *str = nil, *tagName = nil;
BOOL dontReplaceTagWithSpace = NO;
do {
// Scan up to the start of a tag or whitespace
if ([scanner scanUpToCharactersFromSet:stopCharacters intoString:&str]) {
[result appendString:str];
str = nil; // reset
}
// Check if we've stopped at a tag/comment or whitespace
if ([scanner scanString:@"<" intoString:NULL]) {
// Stopped at a comment or tag
if ([scanner scanString:@"!--" intoString:NULL]) {
// Comment
[scanner scanUpToString:@"-->" intoString:NULL];
[scanner scanString:@"-->" intoString:NULL];
} else {
// Tag - remove and replace with space unless it's
// a closing inline tag then dont replace with a space
if ([scanner scanString:@"/" intoString:NULL]) {
// Closing tag - replace with space unless it's inline
tagName = nil; dontReplaceTagWithSpace = NO;
if ([scanner scanCharactersFromSet:tagNameCharacters intoString:&tagName]) {
tagName = [tagName lowercaseString];
dontReplaceTagWithSpace = ([tagName isEqualToString:@"a"] ||
[tagName isEqualToString:@"b"] ||
[tagName isEqualToString:@"i"] ||
[tagName isEqualToString:@"q"] ||
[tagName isEqualToString:@"span"] ||
[tagName isEqualToString:@"em"] ||
[tagName isEqualToString:@"strong"] ||
[tagName isEqualToString:@"cite"] ||
[tagName isEqualToString:@"abbr"] ||
[tagName isEqualToString:@"acronym"] ||
[tagName isEqualToString:@"label"]);
}
// Replace tag with string unless it was an inline
if (!dontReplaceTagWithSpace && result.length > 0 && ![scanner isAtEnd]) [result appendString:@" "];
}
// Scan past tag
[scanner scanUpToString:@">" intoString:NULL];
[scanner scanString:@">" intoString:NULL];
}
} else {
// Stopped at whitespace - replace all whitespace and newlines with a space
if ([scanner scanCharactersFromSet:newLineAndWhitespaceCharacters intoString:NULL]) {
if (result.length > 0 && ![scanner isAtEnd]) [result appendString:@" "]; // Dont append space to beginning or end of result
}
}
} while (![scanner isAtEnd]);
// Decode HTML entities and return
NSString *retString = [result stringByDecodingHTMLEntities] ;
// Return
return retString;
}
}
- (NSString *)stringByDecodingHTMLEntities {
// Can return self so create new string if we're a mutable string
return [NSString stringWithString:[self gtm_stringByUnescapingFromHTML]];
}
- (NSString *)stringByEncodingHTMLEntities {
// Can return self so create new string if we're a mutable string
return [NSString stringWithString:[self gtm_stringByEscapingForAsciiHTML]];
}
- (NSString *)stringByEncodingHTMLEntities:(BOOL)isUnicode {
// Can return self so create new string if we're a mutable string
return [NSString stringWithString:(isUnicode ? [self gtm_stringByEscapingForHTML] : [self gtm_stringByEscapingForAsciiHTML])];
}
- (NSString *)stringWithNewLinesAsBRs {
@autoreleasepool {
// Strange New lines:
// Next Line, U+0085
// Form Feed, U+000C
// Line Separator, U+2028
// Paragraph Separator, U+2029
// Scanner
NSScanner *scanner = [[NSScanner alloc] initWithString:self];
[scanner setCharactersToBeSkipped:nil];
NSMutableString *result = [[NSMutableString alloc] init];
NSString *temp;
NSCharacterSet *newLineCharacters = [NSCharacterSet characterSetWithCharactersInString:
[NSString stringWithFormat:@"\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]];
// Scan
do {
// Get non new line characters
temp = nil;
[scanner scanUpToCharactersFromSet:newLineCharacters intoString:&temp];
if (temp) [result appendString:temp];
temp = nil;
// Add <br /> s
if ([scanner scanString:@"\r\n" intoString:nil]) {
// Combine \r\n into just 1 <br />
[result appendString:@"<br />"];
} else if ([scanner scanCharactersFromSet:newLineCharacters intoString:&temp]) {
// Scan other new line characters and add <br /> s
if (temp) {
for (int i = 0; i < temp.length; i++) {
[result appendString:@"<br />"];
}
}
}
} while (![scanner isAtEnd]);
NSString *retString = [NSString stringWithString:result] ;
// Return
return retString ;
}
}
- (NSString *)stringByRemovingNewLinesAndWhitespace {
@autoreleasepool {
// Strange New lines:
// Next Line, U+0085
// Form Feed, U+000C
// Line Separator, U+2028
// Paragraph Separator, U+2029
// Scanner
NSScanner *scanner = [[NSScanner alloc] initWithString:self];
[scanner setCharactersToBeSkipped:nil];
NSMutableString *result = [[NSMutableString alloc] init];
NSString *temp;
NSCharacterSet *newLineAndWhitespaceCharacters = [NSCharacterSet characterSetWithCharactersInString:
[NSString stringWithFormat:@" \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]];
// Scan
while (![scanner isAtEnd]) {
// Get non new line or whitespace characters
temp = nil;
[scanner scanUpToCharactersFromSet:newLineAndWhitespaceCharacters intoString:&temp];
if (temp) [result appendString:temp];
// Replace with a space
if ([scanner scanCharactersFromSet:newLineAndWhitespaceCharacters intoString:NULL]) {
if (result.length > 0 && ![scanner isAtEnd]) // Dont append space to beginning or end of result
[result appendString:@" "];
}
}
NSString *retString = [NSString stringWithString:result] ;
// Return
return retString ;
}
}
- (NSString *)stringByLinkifyingURLs {
if (!NSClassFromString(@"NSRegularExpression")) return self;
@autoreleasepool {
NSString *pattern = @"(?<!=\")\\b((http|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%%&amp;:/~\\+#]*[\\w\\-\\@?^=%%&amp;/~\\+#])?)";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
NSString *modifiedString = [regex stringByReplacingMatchesInString:self options:0 range:NSMakeRange(0, [self length])
withTemplate:@"<a href=\"$1\" class=\"linkified\">$1</a>"];
return modifiedString ;
}
}
- (NSString *)stringByStrippingTags {
@autoreleasepool {
// Find first & and short-cut if we can
NSUInteger ampIndex = [self rangeOfString:@"<" options:NSLiteralSearch].location;
if (ampIndex == NSNotFound) {
return [NSString stringWithString:self]; // return copy of string as no tags found
}
// Scan and find all tags
NSScanner *scanner = [NSScanner scannerWithString:self];
[scanner setCharactersToBeSkipped:nil];
NSMutableSet *tags = [[NSMutableSet alloc] init];
NSString *tag;
do {
// Scan up to <
tag = nil;
[scanner scanUpToString:@"<" intoString:NULL];
[scanner scanUpToString:@">" intoString:&tag];
// Add to set
if (tag) {
NSString *t = [[NSString alloc] initWithFormat:@"%@>", tag];
[tags addObject:t];
}
} while (![scanner isAtEnd]);
// Strings
NSMutableString *result = [[NSMutableString alloc] initWithString:self];
NSString *finalString;
// Replace tags
NSString *replacement;
for (NSString *t in tags) {
// Replace tag with space unless it's an inline element
replacement = @" ";
if ([t isEqualToString:@"<a>"] ||
[t isEqualToString:@"</a>"] ||
[t isEqualToString:@"<span>"] ||
[t isEqualToString:@"</span>"] ||
[t isEqualToString:@"<strong>"] ||
[t isEqualToString:@"</strong>"] ||
[t isEqualToString:@"<em>"] ||
[t isEqualToString:@"</em>"]) {
replacement = @"";
}
// Replace
[result replaceOccurrencesOfString:t
withString:replacement
options:NSLiteralSearch
range:NSMakeRange(0, result.length)];
}
// Remove multi-spaces and line breaks
finalString = [result stringByRemovingNewLinesAndWhitespace];
// Return
return finalString ;
}
}
@end

44
NSString+XMLEntities.h Normal file
View file

@ -0,0 +1,44 @@
//
// NSString+XMLEntities.h
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 <Foundation/Foundation.h>
// Import new HTML category
#import "NSString+HTML.h"
// THIS CLASS IS DEPRECIATED 03/08/2010
// REPLACED BY NSString+HTML
@interface NSString (XMLEntities)
// Old Instance Methods
- (NSString *)stringByDecodingXMLEntities;
- (NSString *)stringByEncodingXMLEntities;
@end

45
NSString+XMLEntities.m Normal file
View file

@ -0,0 +1,45 @@
//
// NSString+XMLEntities.m
// MWFeedParser
//
// Copyright (c) 2010 Michael Waterfall
//
// 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:
//
// 1. The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 2. This Software cannot be used to archive or collect data such as (but not
// limited to) that of events, news, experiences and activities, for the
// purpose of any concept relating to diary/journal keeping.
//
// 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 "NSString+XMLEntities.h"
// THIS CLASS IS DEPRECIATED 03/08/2010
// REPLACED BY NSString+HTML
@implementation NSString (XMLEntities)
- (NSString *)stringByDecodingXMLEntities {
return [self stringByDecodingHTMLEntities];
}
- (NSString *)stringByEncodingXMLEntities {
return [self stringByEncodingHTMLEntities];
}
@end