diff --git a/samples/Style/TTCSSStyleSheets/Classes/SampleCSSStyleSheet.h b/samples/Style/TTCSSStyleSheets/Classes/SampleCSSStyleSheet.h new file mode 100644 index 0000000000..494a0adff9 --- /dev/null +++ b/samples/Style/TTCSSStyleSheets/Classes/SampleCSSStyleSheet.h @@ -0,0 +1,18 @@ +// +// Copyright 2009-2011 Facebook +// +// 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. +// + +@interface SampleCSSStyleSheet : TTDefaultCSSStyleSheet {} +@end diff --git a/samples/Style/TTCSSStyleSheets/Classes/SampleCSSStyleSheet.m b/samples/Style/TTCSSStyleSheets/Classes/SampleCSSStyleSheet.m new file mode 100644 index 0000000000..ffb01b6960 --- /dev/null +++ b/samples/Style/TTCSSStyleSheets/Classes/SampleCSSStyleSheet.m @@ -0,0 +1,36 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "SampleCSSStyleSheet.h" + +@implementation SampleCSSStyleSheet + +- (TTStyle *)h3:(UIControlState)state { + return + [TTSolidFillStyle styleWithColor:TTCSSSTATE(@"h3", backgroundColor, state) next: + [TTTextStyle styleWithCssSelector:@"h3" forState:state next: + nil]]; +} + +- (TTStyle *)h4:(UIControlState)state { + return + [TTSolidFillStyle styleWithColor:TTCSSSTATE(@"h4text", backgroundColor, state) next: + [TTShadowStyle styleWithCssSelector:@"h4shadow" forState:state next: + [TTTextStyle styleWithCssSelector:@"h4text" forState:state next: + nil]]]; +} + +@end diff --git a/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.h b/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.h index 6dc1d236e6..98e6f5c517 100644 --- a/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.h +++ b/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.h @@ -16,8 +16,6 @@ @interface StyleSheetViewController : TTViewController { @private - TTCSSStyleSheet* _styleSheet; - BOOL _loadedSuccessfully; } diff --git a/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.m b/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.m index 15efd3a913..41c37ab804 100644 --- a/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.m +++ b/samples/Style/TTCSSStyleSheets/Classes/StyleSheetViewController.m @@ -16,6 +16,8 @@ #import "StyleSheetViewController.h" +#import "SampleCSSStyleSheet.h" + /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,25 +27,18 @@ @implementation StyleSheetViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { - _styleSheet = [[TTCSSStyleSheet alloc] init]; - + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + SampleCSSStyleSheet *_styleSheet = [[[SampleCSSStyleSheet alloc] init] autorelease]; _loadedSuccessfully = [_styleSheet - loadFromFilename:TTPathForBundleResource(@"stylesheet.css")]; + addStyleSheetFromDisk:TTPathForBundleResource(@"stylesheet.css")]; + [TTStyleSheet setGlobalStyleSheet:_styleSheet]; } return self; } -/////////////////////////////////////////////////////////////////////////////////////////////////// -- (void)dealloc { - TT_RELEASE_SAFELY(_styleSheet); - - [super dealloc]; -} - - /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)loadView { [super loadView]; @@ -55,23 +50,50 @@ - (void)loadView { } self.title = @"Three20 CSS extension"; + self.view.backgroundColor = TTCSS(@"body", backgroundColor); - self.view.backgroundColor = [_styleSheet backgroundColorWithCssSelector: @"body" - forState: UIControlStateNormal]; - + // Using helper macro UILabel* headerLabel = [[UILabel alloc] initWithFrame:CGRectZero]; headerLabel.text = @"Header text"; - headerLabel.font = [_styleSheet fontWithCssSelector:@"h1" forState:UIControlStateNormal]; - headerLabel.textColor = [_styleSheet colorWithCssSelector:@"h1" forState:UIControlStateNormal]; - headerLabel.backgroundColor = [_styleSheet backgroundColorWithCssSelector: @"h1" - forState: UIControlStateNormal]; - headerLabel.shadowColor = [_styleSheet textShadowColorWithCssSelector: @"h1" - forState: UIControlStateNormal]; - headerLabel.shadowOffset = [_styleSheet textShadowOffsetWithCssSelector: @"h1" - forState: UIControlStateNormal]; + headerLabel.font = TTCSS(@"h1", font); + headerLabel.textColor = TTCSS(@"h1", color); + headerLabel.backgroundColor = TTCSS(@"h1", backgroundColor); + headerLabel.shadowColor = TTCSS(@"h1", shadowColor); + headerLabel.shadowOffset = TTCSS(@"h1", shadowOffset); [headerLabel sizeToFit]; [self.view addSubview:headerLabel]; + + // Using UILabel addition + UILabel* headerLabel2 = [[UILabel alloc] initWithFrame:CGRectZero]; + headerLabel2.text = @"Header 2 text"; + [headerLabel2 applyCssSelector:@"h2"]; + [headerLabel2 sizeToFit]; + CGFloat top = headerLabel.frame.size.height; + CGRect frame = headerLabel2.frame; + frame.origin.y = top; + headerLabel2.frame = frame; + [self.view addSubview:headerLabel2]; + + // Using TTTextStyle addition + TTButton* headerLabel3 = [TTButton buttonWithStyle:@"h3:" title:@"Header 3 text"]; + [headerLabel3 sizeToFit]; + top += headerLabel2.frame.size.height; + frame = headerLabel3.frame; + frame.origin.y = top; + headerLabel3.frame = frame; + [self.view addSubview:headerLabel3]; + + // Using TTTextStyle + TTShadowStyle addition + TTButton* headerLabel4 = [TTButton buttonWithStyle:@"h4:" title:@"Header 4 text"]; + [headerLabel4 sizeToFit]; + top += headerLabel2.frame.size.height; + frame = headerLabel4.frame; + frame.origin.y = top; + headerLabel4.frame = frame; + [self.view addSubview:headerLabel4]; + TT_RELEASE_SAFELY(headerLabel); + TT_RELEASE_SAFELY(headerLabel2); } diff --git a/samples/Style/TTCSSStyleSheets/Headers/TTCSSStyleSheets_Prefix.pch b/samples/Style/TTCSSStyleSheets/Headers/TTCSSStyleSheets_Prefix.pch index 50fceb5312..ba95f7894a 100644 --- a/samples/Style/TTCSSStyleSheets/Headers/TTCSSStyleSheets_Prefix.pch +++ b/samples/Style/TTCSSStyleSheets/Headers/TTCSSStyleSheets_Prefix.pch @@ -6,6 +6,6 @@ #import #import #import "Three20/Three20.h" - #import "extThree20CSSStyle/extThree20CSSStyle.h" + #import "extThree20CSSStyle/extThree20CSSStyle+Additions.h" #import "Atlas.h" #endif diff --git a/samples/Style/TTCSSStyleSheets/Resources/stylesheet.css b/samples/Style/TTCSSStyleSheets/Resources/stylesheet.css index f9f859b9e9..0228f6492b 100644 --- a/samples/Style/TTCSSStyleSheets/Resources/stylesheet.css +++ b/samples/Style/TTCSSStyleSheets/Resources/stylesheet.css @@ -15,13 +15,44 @@ */ body { - background-color: #111; + background-color: #114; } h1 { font-weight: bold; font-size: 50pt; - color: #666; + color: #FF6; background-color: transparent; - text-shadow: 1px 1px 1px #999; /* blur amount (3rd value) doesn't do anything */ + text-shadow: 2px 2px 0px #F99; } + +h2 { + font-size: 45pt; + color: white; + background-color: transparent; + text-shadow: 0px 1px 3px #9BF; +} + +h3, h3:hover { + font-size: 35pt; + font-weight: bold; + color: white; + background-color: transparent; + text-shadow: 3px -3px 3px #99F; +} + +h3:hover { + color: #99F; + text-shadow: 3px 3px 3px white; +} + +h4text, h4text:hover { + color: white; + font-size: 35pt; + background-color: transparent; +} + +h4text:hover { color: gray; } + +h4shadow { text-shadow: 3px 3px 4px rgba(0, 255, 0, .5); } +h4shadow:hover { text-shadow: 0px 0px 4px green; } diff --git a/samples/Style/TTCSSStyleSheets/TTCSSStyleSheets.xcodeproj/project.pbxproj b/samples/Style/TTCSSStyleSheets/TTCSSStyleSheets.xcodeproj/project.pbxproj index 51020ecff5..4f6cae27ef 100755 --- a/samples/Style/TTCSSStyleSheets/TTCSSStyleSheets.xcodeproj/project.pbxproj +++ b/samples/Style/TTCSSStyleSheets/TTCSSStyleSheets.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 6E850FAE11B176F10071A4FD /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E850FAC11B176F10071A4FD /* Default.png */; }; 6E850FAF11B176F10071A4FD /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 6E850FAD11B176F10071A4FD /* Icon.png */; }; 6E850FB811B1795F0071A4FD /* Atlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E850FB711B1795F0071A4FD /* Atlas.m */; }; + 90C3A1C1132BF66B00AC06A2 /* SampleCSSStyleSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 90C3A1C0132BF66B00AC06A2 /* SampleCSSStyleSheet.m */; }; EB383B6510BBF62B0000B2D2 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB383B6410BBF62B0000B2D2 /* QuartzCore.framework */; }; /* End PBXBuildFile section */ @@ -234,6 +235,8 @@ 6E850FB711B1795F0071A4FD /* Atlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Atlas.m; sourceTree = ""; }; 6E8513D111B19B080071A4FD /* TTCSSStyleSheets_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTCSSStyleSheets_Prefix.pch; path = Headers/TTCSSStyleSheets_Prefix.pch; sourceTree = ""; }; 6E8513D211B19B0E0071A4FD /* TTCSSStyleSheets-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TTCSSStyleSheets-Info.plist"; sourceTree = ""; }; + 90C3A1BF132BF66B00AC06A2 /* SampleCSSStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleCSSStyleSheet.h; sourceTree = ""; }; + 90C3A1C0132BF66B00AC06A2 /* SampleCSSStyleSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SampleCSSStyleSheet.m; sourceTree = ""; }; EB383B6410BBF62B0000B2D2 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -268,6 +271,8 @@ 1D3623250D0F684500981E51 /* AppDelegate.m */, 6E850FB611B1795F0071A4FD /* Atlas.h */, 6E850FB711B1795F0071A4FD /* Atlas.m */, + 90C3A1BF132BF66B00AC06A2 /* SampleCSSStyleSheet.h */, + 90C3A1C0132BF66B00AC06A2 /* SampleCSSStyleSheet.m */, ); path = Classes; sourceTree = ""; @@ -680,6 +685,7 @@ 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */, 6E850FB811B1795F0071A4FD /* Atlas.m in Sources */, 6E036BF811B38F3C0025E8EE /* StyleSheetViewController.m in Sources */, + 90C3A1C1132BF66B00AC06A2 /* SampleCSSStyleSheet.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/samples/TTCatalog/Classes/ImageTest1Controller.m b/samples/TTCatalog/Classes/ImageTest1Controller.m index 54ef62e94c..bdac129313 100644 --- a/samples/TTCatalog/Classes/ImageTest1Controller.m +++ b/samples/TTCatalog/Classes/ImageTest1Controller.m @@ -3,6 +3,8 @@ @implementation ImageTest1Controller +static NSString * kDataUrlImage = @"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADkAAAA5CAYAAACMGIOFAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAEGxJREFUeNqMW2lvXOd1ft737sPhIpEURVHW5kiyC1uW5SZKa9d10cZJgKD9kqT9Bf0d/VSgRX9C+7GFmxZFUbSpYRSolyaO18iSFSlKTVILxU2khpzl7vf2w/A5c+5Qrj1AQGvmLmd5znOW98T85Rs/rz3PQ1VVqOsanuehLEuUZQnf9+G6LoqiAAAURSHXOY6Duq7hui7qukZZlnBdF2VZwnEcGGNQFIX87jgOkiSB67rgp6oqBEEg16VpKu+z1sIYgzzPEQQByrKEtRZ1XTdkTdMUxhjUdQ1jDADAdV1UVTV6ZhRFyLIM1lp5sbVWlE2SBMYYeJ4Ha60owuvqupa/AOTFnueJslTYWivXUrAsy+B5HoqiQF3XYjgKaa1FWZYN2QDI9zSa4zjI81ye77oujDHDe+gxXmitRVVV6PV6Yi1rLdI0BQB5MJUoy1IU1YJ6nicvyfP8kCA0VF3XKIpCDM17iI6qqmCMEU/yfUEQiOJVVYnsdESWZYIUa4xBv9+Xi3kjocqXuK4r1qFQFBAAfN+H4zgAgCAIkGUZ+v2+/M4PEQJA4EWhaQx6Oc9zQQLfy+cRYfxfVVViFMKe39nxB9MSVMAYIxBhjFBZz/Mkpvg949kYg1ar1YgRKsVnEQ2e54mntQEcxxHj89n8S+QYYxAEgcQ7w4ae9TwP1vM8tFotwXGapnAcR15I6xE+jDlCgl6mpTWkrbUIgkCQQQIiuWiPUXB6mrHFe6y1aLfbYryqqiSWB4NBgyv4O8nSpUuJe9/3GyTB+NCs6vs+8jyH7/tiZe31qqoaBgAgMa2FJjMS5jRQGIYSu77vI45jIRtrLZIkQavVQhzHDY/zXYSyxKkWREOAghK+QRBI2qAV8zxHVVXiwTzPUde1eE3HRxAEYKqiwq7rChnpVKXRxPsJxTRN4fs+9vb2hBSttQjDUOSmMSX8+B+aBIwxyLLsUCxRcHpS50lCzfM8YTZCTyPBGAPf92GtFfIKgqCR78IwlN/IDbw3iiLJnby33+83oM3QYny6zD2MKcYd45TQpVK0PF+kPU9FKACVJ/TIhFRApxL+Rm+laSrPobeIHM/zkCSJQHxqagrdblfu1zkcACzzGKHkOA4GgwHyPJe4ovcYyHVdI4oiSR3aiiQmxgOZTsdcq9WSeGWIaOQQfpSH7w3DUGKezzfGYDAYiEFoBBotz/MhuzJXBkEgcOLDdTrxPA9hGMp39C7hodHAf5OMNMGQ7gk/YwwmJiYakKYR+HwiIEkSFEUhZEMGJ0n5vi8Gs9YOjcGHEHo6DkjLVJxkxCRN6NJQjFvtIZ07dY6loAwHGoepizFFcmLdTOQQjmVZIssyuZ9Vjo5pSwswffBmPpR/SSaEH/HOPKqLcx3nYk1FbvQSDUh2p9CEueYDGoeK01ssRHRjkSSJyOF53jAmi6KQqkHXnjqeKBBJQqcLphNanIigVygchWDhrmtPGpBxOY4kPk93GlQ0DEMkSYIwDIUv+L48z0dlXRzH8hB6Nk1TUYq45ws9z4Pv+42yj6TA+NLe18lc51atnCYq8oKOSRqbBDOe/OM4lqoNAKIoGl6nE+qTekVdDelg5oPoRZIWez9d6zJ+mW5IYlRcQ6uuayRJIuUgiYUlJr/XSZ+lImObn36/PwwHem8wGEgK4EsEtlWBc4tHMN3yReH13R7WO4nEZpZlQk6MaxqA1l2an0bkD2Nr83EfcVY02i0qSgOQYEgsk60Qc1Mjdl/d3BOPUi6mNYaPtRYuH8Aqo6oqRFGENE0RBR5+95njeO70LELPwfin009xfWUb799aQ6vVktRAKydJgoXZaXzr/ALOn5hBMPaMzc4Any1v4eM7D8WoFJTsa4zBqflJXL24iNPHpg7JcG15C+/fWsP+IEVZlpIldDFg/uZfPq51ycZkPhEF+NErF3D8yAS+6nNvu4uffnIX3X4ssHQcBwszLfzolQuHlHuSoP/50RfSmqVpKqx6+ekFfP+3z/2/9yd5iZ+8exsPd7rCJ4z3Ayi7jeRsjEEYhvjjq+e+loIAcGp+ElfOzeGdG/cldmfaIX6oFHz38we4fX8HvbRE4Fo8fXwKr106hdB3cfncMeRljfdursk4pKoqnD8511DwnRv3cX31EdK8xPREgKsXF3HpzBxCz8GPX30Gf/vmdfSTrMH21lo4r//pn/8FcyTjY+loCy//1lJDkfduruE/PlrB2zfuY3ljD4FrMTfdkt+XZtv41f1d1GZIWq88u4il2TYA4L+u3cOnX2yhqIaIKaoaO70M97a7uLB0BK5jsTTbxvXlTdTGETlev3IGMxPDMcdPP7mL6yvbKMoh2ydZgZXNfVgDnJybhOtYJHmJ3cFo/sTnWOKX+ctai2dOHj2k4Id3NrDfj1GWJbb2Yvz7RyvY7Awa111cOiIjjAsHz1jd3MMvv9hsxBvT0PpuDx/+el3uP7MwLcXAVORJDN7b7uLG8oZkAbJ/mqb49IttJNmQUV84O9+ot8kNlmUTGbKqKpxZmG4Iv7zZbUzsmLdu399pXMecNhM5QlT3truSKpjraAgqyk/oj6Z0506MDL2y0REIk205OhmkubxjeiLA7NRoKsDc7jIx04tZlqHTT9Hpp/KS9UcdyWG6ZYoCr6FkWgyfNTkxMtLWXtyY3TCBM5+xANDVVZZlDTbf6WWN6R2nF8zj67s9XFg6AgBohy62O6PRZZ7ncNk/Mi6DIMC/frAiRUCaplIu6QnAyflpXDo731ByZauHMAyxsdvDP757G8YYbDzuSxJnBaUTt0bNdqcn12rSW9/poqoqab90h+I4Djq9UW+5eLSN1c09gWwURcNigDfqDpzQoBeqqsLVi4uw1mJuKjwUt29+vIx+PMxV8Dzcf9QTa2tSoyeSJEE7CvD8mTlJA6ube3Bddzg/ckce7idZo8piapAxTVk3QoZwlUqNNzOJ0oOMPRqhLEu8+tzJJ+aof/vF/2J5/bH0hhJjYSjPowCk91bo44cvnxdYfnD7ISpYGFM1Gm5CmgW5niLSm7o+ppOIFN/3h/0kyy7dz+nmVw+Cxz+h5+D1K2fwwtOL0pmQrVk4p2naUHAydPHj37uIhQNIXlvewi9uP5QSkPSvY5VlJ40WRdET45rf0UFFUcCN47hRAunaj0TBCdl7N9fgui6OTng4vTAtXpiZCPD9l06jF6dY2x3IHJTxq88zvnXhOK5eXEToD3//4NfrePv6vcZ79aEQ4RmGIXS6I8OOG4STPdbSaZoO4Up46OETO3DWtQDw8189EIh4jsGf/f6zWJgZFQSvXzmDv3vrc/EYW66qqjDb9vHdl87K9Xv9FG9+soKHj2OZsxJJlGNcUXqSnT+rK86fdEzqTsrVk2d6ZTIaDYKSAtjY2RvF0sFQNy4q3Li701ByuuVjfirEg+0hgbDV+Z1nlxrx/O7nD3Bj9RGSfOgRQlLHWJqXI2S5Vv6tOYJw9B1zKF8zJquqGubJPM/FmudPzOAPXjjdEOjh9mMp3rvdLjzPg+u6DermJwo8SdztKMB3XzqL8ydmAAB3t/bx1qer2O70JJlzXsuinF3M9n6CZw6eeWw6wtruoHF8x/41z3NMH5R+zMv6vKSqKljGAr/MyyZMLp48OrpYHY0ZY+A55kuLdmMMXn3+KVHwrV/exT/89010+mlj4ifwPyg2WPLFad7IfXp8wrEGIT0/FTZyLZHJowyrWyNrLTbG6tGFmRbOLEw3xpKE9uVzx57YdhljcOnsPC4d5MC/f/s2PlveQhAEkgfpOQpDBZMkgbUW9x71GjWtTiW6fm2FPk4f/L7XT7E/yISU5PiDFQ1zylZngNXNvYbgf/Ltb+DKN45jamJI28ePTOAH3zx7qIm9sfpIqPvbFxcF7huP+wI1xhILAh1D+rdHnR7ubu1LK3f53DHJf7r2fe35p4Tlrx0YkvrIsR9LNtKtMQYf/WazUW6FvovvvHga33nx9Jc3rlmB926uwfM8zLZ9iZPdbowTR6LGkGrEgJOiGAA83OmiqCAc8f6tNTHkH77wFFwL3Fobjjxmp1p48exso2L6/O6OjG44f6rrGuav//nDmglbD48unzuGP7p86ms1zUlW4Cf/cwdbnQGMMXjp6Xm8dunr3as/b7xzCw92+jJWTJIEz506iu991WQgK/DGO7ewvZ8I7Mkxxpghu7ItYeFbFAWuLW9hez/By88u4tT85JeOHe482MXPbq1jkOZS/4ZfMe74sk8URbA2RlVVMgO+s9FD72e/wTfPLzxxxvPZ8hY+vLOBnf1BY+eB8ej7Psxf/dMHtT531/NRstjc9AQWj7Yx1fIlT23vJ1jZ6DRqXj171eWWzlks0MmAURRBV116mE2Dl2U5LCyKDItH25LOVjf3RDEeyHLQrNdjXF1N6LKOlY4xBnuDDJ3+TiM9sPrQR9l68i5LCWPrL2TToigEkvxLpXQy1yNT13Vxd2v/UHcURZHkXb1FIn85GdCTaha5LIqZfFkC6lUV/Z2+V6+46CMG1peTk5OHRv+sTWmMKIpEkfHdHhqAh748E2F30mi1yHj8US838KLxcxAKw38T3nyxPr/QU219DY/J9Zif6YFN/PgpGBXVewq63qXD9LTedd2hJ7mMQO3HT680HLmoNL7rQyQQshREjzoZCrqB1utl+khAG47HcDru9VKFPi7US1AyF6KnWNVTUVpVn0FQsMFggFarJafRjDl9eMtD0/GDHX2mwvmSNANJIt5jQ8CuY7xk5JCKwzcSGxXV0w3Ll+j1M7qZ1Qcfxu94zMf4ohdIYBSQ7Ron42RVKipz0YM45PXa84Q2PUtiI7J4ukw5nrRcZakgj9o0YZBd9VkioUSFOarUnbgWins39Cxzsj6n1HtzFJYhRMgzFvkMhobuogh9HjtySCd7PGEYynk7lYrjWAhg/Py/UeWr7RFeo09+uZ6mSY6Eow9n9Fal67oYDAaNNRjdDPNaQp+hoxc3xIDMK+Mw0h+9kah38ZhK9IGrHk9QGL1nwN84p9H7cONLjIxN5mNNSoQs107ZyZDxdTNuuapC4tHn+iwKNLMyyBkPVVUhjmOZ2Wqo6a0Qvcs6fmKsv6fSvIdQ1yfRugvhkSPRqA+DhV0Z+CQHMqweIWp21Ce52vuEMK0/fnyuaZ+LgCwmAGB/f7+x6ka5KDQV1ovDOh0RKeSNoigQx/EohbBSIDPq0ouC0qt6g5k1pC4iaAxOyxiTTA1JkjRGnfRmq9UShVmi6V6ThKjnr3r0yOdQJl11WU0mevFIT6iptC4EiHm9dEiP8vs4jmUaIEfbiup1bqNAei1cI4YG57aXhrXePdBFhkw89ChQz06oNB9Ab+gNRz1w1mWYtj4F1MbUTEmvdrtdISl6YXxvnXHHTkh3PYSwXnhkXevStYSiLr9IyXpfTZdhegOLrKmtqPOn3qUlUehFJSKErZuuUfU6GmdAmpH1vJgcMRgMRrGt2yYKzhwGAK1Wq1EJ6RqSFQqR0G63G2cpOi/SMLolYw/IOGMhwUqLYUGo9/v90TbyAex1V8TNEaYVWTQkG3Hxln0docYlfH0Or9sdHc+a8fQyoe4YaBAqQ+VZsWgj6oTOooMTcyrCgoX/9wudooi+/xsAy+4QmddLQNUAAAAASUVORK5CYII="; + - (void)loadView { self.view = [[[UIView alloc] init] autorelease]; self.view.backgroundColor = [UIColor whiteColor]; @@ -12,6 +14,13 @@ - (void)loadView { imageView.autoresizesToImage = YES; imageView.urlPath = @"http://farm4.static.flickr.com/3163/3110335722_7a906f9d8b_m.jpg"; [self.view addSubview:imageView]; + + TTImageView* dataImageView = [[[TTImageView alloc] initWithFrame:CGRectMake(130, 200, 0, 0)] + autorelease]; + dataImageView.autoresizesToImage = YES; + dataImageView.urlPath = kDataUrlImage; + [self.view addSubview:dataImageView]; + } - (void)dealloc { diff --git a/samples/TTCatalog/Classes/StyleTestController.m b/samples/TTCatalog/Classes/StyleTestController.m index 7b7a654cb9..bc9839e950 100644 --- a/samples/TTCatalog/Classes/StyleTestController.m +++ b/samples/TTCatalog/Classes/StyleTestController.m @@ -45,20 +45,49 @@ - (void)loadView { [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], - // SpeechBubble - [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 pointLocation:60 - pointAngle:90 - pointSize:CGSizeMake(20,10)] next: - [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: - [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], - - // SpeechBubble - [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 pointLocation:290 - pointAngle:270 - pointSize:CGSizeMake(20,10)] next: - [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: - [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], - + // SpeechBubble with pointer left of the centre on the top edge + // Locations for top edge are 45 on the left, 90 in the centre, 134.999 on the right + [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 + pointLocation:60 + pointAngle:90 + pointSize:CGSizeMake(20,10)] next: + [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: + [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], + + // SpeechBubble with pointer on the extreme left on the bottom edge + // Locations for bottom edge are 225 on the left, 270 in the centre, 314.999 on the left + [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 + pointLocation:314 + pointAngle:270 + pointSize:CGSizeMake(20,10)] next: + [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: + [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], + + // SpeechBubble with pointer on the bottom of the left edge + // Locations for left edge are 315 on the bottom, 0 in the centre, 44.999 on top + [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 + pointLocation:315 + pointAngle:0 + pointSize:CGSizeMake(10,20)] next: + [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: + [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], + + // SpeechBubble with pointer on the centre of the left edge + // Locations for left edge are 315 on the bottom, 0 in the centre, 44.999 on top + [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 pointLocation:0 + pointAngle:0 + pointSize:CGSizeMake(20,10)] next: + [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: + [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], + + // SpeechBubble with pointer on the bottom of the right hand edge + // Locations for right edge are 135 on top, 180 in the middle, 314.999 on the bottom + [TTShapeStyle styleWithShape:[TTSpeechBubbleShape shapeWithRadius:5 pointLocation:224 + pointAngle:180 + pointSize:CGSizeMake(15,15)] next: + [TTSolidFillStyle styleWithColor:[UIColor whiteColor] next: + [TTSolidBorderStyle styleWithColor:black width:1 next:nil]]], + // Drop shadow [TTShapeStyle styleWithShape:[TTRoundedRectangleShape shapeWithRadius:10] next: [TTShadowStyle styleWithColor:RGBACOLOR(0,0,0,0.5) blur:5 offset:CGSizeMake(2, 2) next: @@ -121,6 +150,11 @@ - (void)loadView { [TTLinearGradientFillStyle styleWithColor1:RGBCOLOR(0, 180, 231) color2:RGBCOLOR(0, 0, 255) next:nil]], + // simple bottom only border + [TTShapeStyle styleWithShape:[TTRectangleShape shape] next: + [TTSolidFillStyle styleWithColor:RGBCOLOR(255, 255, 255) next: + [TTFourBorderStyle styleWithTop:nil right:nil bottom:black left:nil width:5 next:nil]]], + nil]; CGFloat padding = 10; diff --git a/samples/TTCatalog/Classes/StyledTextTestController.m b/samples/TTCatalog/Classes/StyledTextTestController.m index 88f9a61fbc..bf6fd1a775 100644 --- a/samples/TTCatalog/Classes/StyledTextTestController.m +++ b/samples/TTCatalog/Classes/StyledTextTestController.m @@ -114,6 +114,7 @@ - (void)loadView { label1.text = [TTStyledText textFromXHTML:kText lineBreaks:YES URLs:YES]; label1.contentInset = UIEdgeInsetsMake(10, 10, 10, 10); //label1.backgroundColor = [UIColor grayColor]; + //label1.textAlignment = UITextAlignmentCenter; [label1 sizeToFit]; [self.view addSubview:label1]; } diff --git a/src/Three20.bundle/cs.lproj/Localizable.strings b/src/Three20.bundle/cs.lproj/Localizable.strings new file mode 100644 index 0000000000..1a7262a07e Binary files /dev/null and b/src/Three20.bundle/cs.lproj/Localizable.strings differ diff --git a/src/Three20.bundle/he.lproj/Localizable.strings b/src/Three20.bundle/he.lproj/Localizable.strings new file mode 100755 index 0000000000..46c325b934 Binary files /dev/null and b/src/Three20.bundle/he.lproj/Localizable.strings differ diff --git a/src/Three20/Headers/Three20.h b/src/Three20/Headers/Three20.h index d41cc8507f..db1a872adc 100644 --- a/src/Three20/Headers/Three20.h +++ b/src/Three20/Headers/Three20.h @@ -14,12 +14,6 @@ // limitations under the License. // -/*! \mainpage Three20 API Documentation - * - * Generated from Three20 Release 1.0.5. - * - */ - // Core #import "Three20Core/Three20Core.h" diff --git a/src/Three20/Three20.xcodeproj/project.pbxproj b/src/Three20/Three20.xcodeproj/project.pbxproj index e4dacfb71f..baf543d89b 100755 --- a/src/Three20/Three20.xcodeproj/project.pbxproj +++ b/src/Three20/Three20.xcodeproj/project.pbxproj @@ -828,7 +828,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; PRODUCT_NAME = "$(BASE_PRODUCT_NAME)"; @@ -846,7 +845,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; PREBINDING = NO; PRODUCT_NAME = "$(BASE_PRODUCT_NAME)"; SDKROOT = iphoneos; diff --git a/src/Three20Core/Headers/NSDataAdditions.h b/src/Three20Core/Headers/NSDataAdditions.h index 274534a4bb..b0a98f30c8 100644 --- a/src/Three20Core/Headers/NSDataAdditions.h +++ b/src/Three20Core/Headers/NSDataAdditions.h @@ -32,4 +32,19 @@ */ @property (nonatomic, readonly) NSString* sha1Hash; + +/** + * Create an NSData from a base64 encoded representation + * + * @return the NSData object + */ ++ (id)dataWithBase64EncodedString:(NSString *)string; // Padding '=' characters are optional. Whitespace is ignored. + +/** + * Marshal the data into a base64 encoded representation + * + * @return the base64 encoded string + */ +- (NSString *)base64Encoding; + @end diff --git a/src/Three20Core/Headers/Three20.h b/src/Three20Core/Headers/Three20.h new file mode 100644 index 0000000000..948b026b81 --- /dev/null +++ b/src/Three20Core/Headers/Three20.h @@ -0,0 +1,75 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 + +/** + * General-purpose information about the Three20 ecosystem. + * + * This information is present in the core because Three20 versioning is done across all of + * the primary Three20 modules. + * + * This is the only object in the Three20 ecosystem that doesn't follow the standard TT* prefix. + * This is by design. [Three20 version] is much clearer than [TTVersion version]. + */ +@interface Three20 : NSObject + + +#pragma mark - +#pragma mark General Purpose Version Information + +/** + * @see Three20Version + */ ++ (NSString*)version; + + +#pragma mark - +#pragma mark Version Breakdown + +/** + * Major release number. + * + * Major releases involve large structural changes that will break compatibility + * with older versions. + */ ++ (NSInteger)majorVersion; + +/** + * Minor release number. + * + * Minor releases involve minimal structural changes that might break compatibility + * with older versions but should only involve minimal effort to transition to. + */ ++ (NSInteger)minorVersion; + +/** + * Bugfix release number. + * + * Bugfix releases involve no structural modifications, but may introduce new code and + * fix existing bugs. + */ ++ (NSInteger)bugfixVersion; + +/** + * Hotfix release number. + * + * Hotfix releases fix crashing bugs and compilation errors that may have slipped through the + * release process. + */ ++ (NSInteger)hotfixVersion; + +@end diff --git a/src/Three20Core/Headers/Three20Core.h b/src/Three20Core/Headers/Three20Core.h index ae4c933690..7853cd18a0 100644 --- a/src/Three20Core/Headers/Three20Core.h +++ b/src/Three20Core/Headers/Three20Core.h @@ -19,6 +19,8 @@ // - Global #import "Three20Core/TTCorePreprocessorMacros.h" +#import "Three20Core/Three20.h" +#import "Three20Core/Three20Version.h" #import "Three20Core/TTGlobalCore.h" #import "Three20Core/TTGlobalCoreLocale.h" #import "Three20Core/TTGlobalCorePaths.h" diff --git a/src/Three20Core/Headers/Three20Version.h b/src/Three20Core/Headers/Three20Version.h new file mode 100644 index 0000000000..c8194feb00 --- /dev/null +++ b/src/Three20Core/Headers/Three20Version.h @@ -0,0 +1,33 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 + +/** + * Expressed in MAJOR.MINOR.BUGFIX(.HOTFIX) notation. + * + * For example, 1.0.5.1 is: + * - the first major release, + * - with no minor updates, + * - with 5 bugfix patches, + * - and 1 hotfix patch. + * + * The .HOTFIX version will only be present if hotfixVersion is > 0. + * + * Check out the versionStringCompare: addition to NSString if you need to compare Three20 + * versions. You will need to import Three20+Additions.h in order to use it. + */ +extern NSString* const Three20Version; diff --git a/src/Three20Core/Sources/NSDataAdditions.m b/src/Three20Core/Sources/NSDataAdditions.m index cd1126ea1b..a638251503 100644 --- a/src/Three20Core/Sources/NSDataAdditions.m +++ b/src/Three20Core/Sources/NSDataAdditions.m @@ -59,4 +59,108 @@ - (NSString*)sha1Hash { ]; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +// base64 code found on http://www.cocoadev.com/index.pl?BaseSixtyFour +// where the poster released it to public domain +// style not exactly congruous with normal three20 style, but kept mostly intact with the original +static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + ++ (NSData*)dataWithBase64EncodedString:(NSString *)string; +{ + if ([string length] == 0) + return [NSData data]; + + static char *decodingTable = NULL; + if (decodingTable == NULL) + { + decodingTable = malloc(256); + if (decodingTable == NULL) + return nil; + memset(decodingTable, CHAR_MAX, 256); + NSUInteger i; + for (i = 0; i < 64; i++) + decodingTable[(short)encodingTable[i]] = i; + } + + const char *characters = [string cStringUsingEncoding:NSASCIIStringEncoding]; + if (characters == NULL) // Not an ASCII string! + return nil; + char *bytes = malloc((([string length] + 3) / 4) * 3); + if (bytes == NULL) + return nil; + NSUInteger length = 0; + + NSUInteger i = 0; + while (YES) + { + char buffer[4]; + short bufferLength; + for (bufferLength = 0; bufferLength < 4; i++) + { + if (characters[i] == '\0') + break; + if (isspace(characters[i]) || characters[i] == '=') + continue; + buffer[bufferLength] = decodingTable[(short)characters[i]]; + if (buffer[bufferLength++] == CHAR_MAX) // Illegal character! + { + free(bytes); + return nil; + } + } + + if (bufferLength == 0) + break; + if (bufferLength == 1) // At least two characters are needed to produce one byte! + { + free(bytes); + return nil; + } + + // Decode the characters in the buffer to bytes. + bytes[length++] = (buffer[0] << 2) | (buffer[1] >> 4); + if (bufferLength > 2) + bytes[length++] = (buffer[1] << 4) | (buffer[2] >> 2); + if (bufferLength > 3) + bytes[length++] = (buffer[2] << 6) | buffer[3]; + } + + realloc(bytes, length); + return [NSData dataWithBytesNoCopy:bytes length:length]; +} + +- (NSString *)base64Encoding; +{ + if ([self length] == 0) + return @""; + + char *characters = malloc((([self length] + 2) / 3) * 4); + if (characters == NULL) + return nil; + NSUInteger length = 0; + + NSUInteger i = 0; + while (i < [self length]) + { + char buffer[3] = {0,0,0}; + short bufferLength = 0; + while (bufferLength < 3 && i < [self length]) + buffer[bufferLength++] = ((char *)[self bytes])[i++]; + + // Encode the bytes in the buffer to four characters, including padding "=" characters if necessary. + characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2]; + characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; + if (bufferLength > 1) + characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; + else characters[length++] = '='; + if (bufferLength > 2) + characters[length++] = encodingTable[buffer[2] & 0x3F]; + else characters[length++] = '='; + } + + return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease]; +} +// end recycled base64 code +/////////////////////////////////////////////////////////////////////////////////////////////////// + @end diff --git a/src/Three20Core/Sources/NSDateAdditions.m b/src/Three20Core/Sources/NSDateAdditions.m index d0ec60c2f1..d229496119 100644 --- a/src/Three20Core/Sources/NSDateAdditions.m +++ b/src/Three20Core/Sources/NSDateAdditions.m @@ -40,14 +40,7 @@ @implementation NSDate (TTCategory) /////////////////////////////////////////////////////////////////////////////////////////////////// + (NSDate*)dateWithToday { - NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; - formatter.dateFormat = @"yyyy-d-M"; - - NSString* formattedTime = [formatter stringFromDate:[NSDate date]]; - NSDate* date = [formatter dateFromString:formattedTime]; - TT_RELEASE_SAFELY(formatter); - - return date; + return [[NSDate date] dateAtMidnight]; } @@ -59,14 +52,11 @@ + (NSDate*)dateWithToday { /////////////////////////////////////////////////////////////////////////////////////////////////// - (NSDate*)dateAtMidnight { - NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; - formatter.dateFormat = @"yyyy-d-M"; - - NSString* formattedTime = [formatter stringFromDate:self]; - NSDate* date = [formatter dateFromString:formattedTime]; - TT_RELEASE_SAFELY(formatter); - - return date; + NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; + NSDateComponents *comps = [gregorian components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:[NSDate date]]; + NSDate *midnight = [gregorian dateFromComponents:comps]; + [gregorian release]; + return midnight; } @@ -152,30 +142,59 @@ - (NSString*)formatDateTime { /////////////////////////////////////////////////////////////////////////////////////////////////// - (NSString*)formatRelativeTime { - NSTimeInterval elapsed = abs([self timeIntervalSinceNow]); - if (elapsed <= 1) { - return TTLocalizedString(@"just a moment ago", @""); + NSTimeInterval elapsed = [self timeIntervalSinceNow]; + if (elapsed > 0) { + if (elapsed <= 1) { + return TTLocalizedString(@"in just a moment", @""); + + } else if (elapsed < TT_MINUTE) { + int seconds = (int)(elapsed); + return [NSString stringWithFormat:TTLocalizedString(@"in %d seconds", @""), seconds]; + + } else if (elapsed < 2*TT_MINUTE) { + return TTLocalizedString(@"in about a minute", @""); + + } else if (elapsed < TT_HOUR) { + int mins = (int)(elapsed/TT_MINUTE); + return [NSString stringWithFormat:TTLocalizedString(@"in %d minutes", @""), mins]; + + } else if (elapsed < TT_HOUR*1.5) { + return TTLocalizedString(@"in about an hour", @""); + + } else if (elapsed < TT_DAY) { + int hours = (int)((elapsed+TT_HOUR/2)/TT_HOUR); + return [NSString stringWithFormat:TTLocalizedString(@"in %d hours", @""), hours]; + + } else { + return [self formatDateTime]; + } + } else { + elapsed = -elapsed; + + if (elapsed <= 1) { + return TTLocalizedString(@"just a moment ago", @""); - } else if (elapsed < TT_MINUTE) { - int seconds = (int)(elapsed); - return [NSString stringWithFormat:TTLocalizedString(@"%d seconds ago", @""), seconds]; + } else if (elapsed < TT_MINUTE) { + int seconds = (int)(elapsed); + return [NSString stringWithFormat:TTLocalizedString(@"%d seconds ago", @""), seconds]; - } else if (elapsed < 2*TT_MINUTE) { - return TTLocalizedString(@"about a minute ago", @""); + } else if (elapsed < 2*TT_MINUTE) { + return TTLocalizedString(@"about a minute ago", @""); - } else if (elapsed < TT_HOUR) { - int mins = (int)(elapsed/TT_MINUTE); - return [NSString stringWithFormat:TTLocalizedString(@"%d minutes ago", @""), mins]; + } else if (elapsed < TT_HOUR) { + int mins = (int)(elapsed/TT_MINUTE); + return [NSString stringWithFormat:TTLocalizedString(@"%d minutes ago", @""), mins]; - } else if (elapsed < TT_HOUR*1.5) { - return TTLocalizedString(@"about an hour ago", @""); + } else if (elapsed < TT_HOUR*1.5) { + return TTLocalizedString(@"about an hour ago", @""); - } else if (elapsed < TT_DAY) { - int hours = (int)((elapsed+TT_HOUR/2)/TT_HOUR); - return [NSString stringWithFormat:TTLocalizedString(@"%d hours ago", @""), hours]; + } else if (elapsed < TT_DAY) { + int hours = (int)((elapsed+TT_HOUR/2)/TT_HOUR); + return [NSString stringWithFormat:TTLocalizedString(@"%d hours ago", @""), hours]; - } else { - return [self formatDateTime]; + } else { + return [self formatDateTime]; + } } } diff --git a/src/Three20Core/Sources/TTExtensionAuthor.m b/src/Three20Core/Sources/TTExtensionAuthor.m index 821023147f..b1144a3835 100644 --- a/src/Three20Core/Sources/TTExtensionAuthor.m +++ b/src/Three20Core/Sources/TTExtensionAuthor.m @@ -57,7 +57,8 @@ - (id)initWithName: (NSString*)name github: (NSString*)github twitter: (NSString*)twitter website: (NSString*)website { - if (self = [super init]) { + self = [super init]; + if (self) { self.name = name; self.github = github; self.twitter = twitter; diff --git a/src/Three20Core/Sources/TTExtensionInfo.m b/src/Three20Core/Sources/TTExtensionInfo.m index bf5ff60ee4..85bd5b488d 100644 --- a/src/Three20Core/Sources/TTExtensionInfo.m +++ b/src/Three20Core/Sources/TTExtensionInfo.m @@ -39,7 +39,8 @@ @implementation TTExtensionInfo /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { self.version = @"No version provided."; self.description = @"No description provided."; self.copyright = @"No copyright provided."; diff --git a/src/Three20Core/Sources/Three20.m b/src/Three20Core/Sources/Three20.m new file mode 100644 index 0000000000..e8da57f20c --- /dev/null +++ b/src/Three20Core/Sources/Three20.m @@ -0,0 +1,64 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "Three20Core/Three20.h" + +#import "Three20Core/Three20Version.h" + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +@implementation Three20 + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (NSInteger)majorVersion { + return [[[Three20Version componentsSeparatedByString:@"."] objectAtIndex:0] intValue]; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (NSInteger)minorVersion { + return [[[Three20Version componentsSeparatedByString:@"."] objectAtIndex:1] intValue]; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (NSInteger)bugfixVersion { + return [[[Three20Version componentsSeparatedByString:@"."] objectAtIndex:2] intValue]; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (NSInteger)hotfixVersion { + NSArray* components = [Three20Version componentsSeparatedByString:@"."]; + if ([components count] > 3) { + return [[components objectAtIndex:3] intValue]; + + } else { + return 0; + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (NSString*)version { + return Three20Version; +} + + +@end diff --git a/src/Three20Core/Sources/Three20Version.m b/src/Three20Core/Sources/Three20Version.m new file mode 100644 index 0000000000..86a457754b --- /dev/null +++ b/src/Three20Core/Sources/Three20Version.m @@ -0,0 +1,23 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "Three20Core/Three20Version.h" + +/*! \mainpage Three20 API Documentation + * + * Generated from Three20 Release 1.0.5. + */ +NSString* const Three20Version = @"1.0.5"; diff --git a/src/Three20Core/Three20Core.xcodeproj/project.pbxproj b/src/Three20Core/Three20Core.xcodeproj/project.pbxproj index 072523fd79..d74bde33bb 100755 --- a/src/Three20Core/Three20Core.xcodeproj/project.pbxproj +++ b/src/Three20Core/Three20Core.xcodeproj/project.pbxproj @@ -7,11 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + 660E7375135A560A00531398 /* Three20Version.h in Headers */ = {isa = PBXBuildFile; fileRef = 660E7374135A560A00531398 /* Three20Version.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 66A0530A132EB47900132434 /* Three20.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A05309132EB47900132434 /* Three20.m */; }; + 66A0530C132EB47F00132434 /* Three20.h in Headers */ = {isa = PBXBuildFile; fileRef = 66A0530B132EB47F00132434 /* Three20.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66ADC9091290B23A00855386 /* TTExtensionInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 66ADC9071290B23A00855386 /* TTExtensionInfo.m */; }; 66ADC90F1290B24D00855386 /* TTExtensionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 66ADC90E1290B24D00855386 /* TTExtensionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66ADCE681291BC1E00855386 /* TTExtensionInfoPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 66ADCE661291BC1E00855386 /* TTExtensionInfoPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; 66ADCE871291BEAE00855386 /* TTExtensionAuthor.m in Sources */ = {isa = PBXBuildFile; fileRef = 66ADCE811291BEAE00855386 /* TTExtensionAuthor.m */; }; 66ADCE8B1291BEBB00855386 /* TTExtensionAuthor.h in Headers */ = {isa = PBXBuildFile; fileRef = 66ADCE881291BEBB00855386 /* TTExtensionAuthor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 66CE68FB135B320F00E127E2 /* Three20Version.m in Sources */ = {isa = PBXBuildFile; fileRef = 66CE68FA135B320F00E127E2 /* Three20Version.m */; }; 66ECA257128DFE28006C78C2 /* TTExtensionLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 66ECA251128DFE28006C78C2 /* TTExtensionLoader.m */; }; 66ECA25B128DFE34006C78C2 /* TTExtensionLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 66ECA258128DFE34006C78C2 /* TTExtensionLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E178E811183C8E5003B099E /* TTGlobalCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E178E7B1183C8E5003B099E /* TTGlobalCore.m */; }; @@ -63,11 +67,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 660E7374135A560A00531398 /* Three20Version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Three20Version.h; path = Headers/Three20Version.h; sourceTree = ""; }; + 66A05309132EB47900132434 /* Three20.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Three20.m; path = Sources/Three20.m; sourceTree = ""; }; + 66A0530B132EB47F00132434 /* Three20.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Three20.h; path = Headers/Three20.h; sourceTree = ""; }; 66ADC9071290B23A00855386 /* TTExtensionInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTExtensionInfo.m; path = Sources/TTExtensionInfo.m; sourceTree = ""; }; 66ADC90E1290B24D00855386 /* TTExtensionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTExtensionInfo.h; path = Headers/TTExtensionInfo.h; sourceTree = ""; }; 66ADCE661291BC1E00855386 /* TTExtensionInfoPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTExtensionInfoPrivate.h; path = Headers/TTExtensionInfoPrivate.h; sourceTree = ""; }; 66ADCE811291BEAE00855386 /* TTExtensionAuthor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTExtensionAuthor.m; path = Sources/TTExtensionAuthor.m; sourceTree = ""; }; 66ADCE881291BEBB00855386 /* TTExtensionAuthor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTExtensionAuthor.h; path = Headers/TTExtensionAuthor.h; sourceTree = ""; }; + 66CE68FA135B320F00E127E2 /* Three20Version.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Three20Version.m; path = Sources/Three20Version.m; sourceTree = ""; }; 66ECA251128DFE28006C78C2 /* TTExtensionLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTExtensionLoader.m; path = Sources/TTExtensionLoader.m; sourceTree = ""; }; 66ECA258128DFE34006C78C2 /* TTExtensionLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTExtensionLoader.h; path = Headers/TTExtensionLoader.h; sourceTree = ""; }; 6E178E751183C8AE003B099E /* Three20Core_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Three20Core_Prefix.pch; path = Headers/Three20Core_Prefix.pch; sourceTree = ""; }; @@ -224,6 +232,10 @@ 6ED117D21183BDBF0096AEBF /* Global */ = { isa = PBXGroup; children = ( + 660E7374135A560A00531398 /* Three20Version.h */, + 66CE68FA135B320F00E127E2 /* Three20Version.m */, + 66A0530B132EB47F00132434 /* Three20.h */, + 66A05309132EB47900132434 /* Three20.m */, 6E178E771183C8D3003B099E /* TTCorePreprocessorMacros.h */, 6E178E781183C8DA003B099E /* TTGlobalCore.h */, 6E178E7B1183C8E5003B099E /* TTGlobalCore.m */, @@ -334,6 +346,8 @@ 66ADC90F1290B24D00855386 /* TTExtensionInfo.h in Headers */, 66ADCE681291BC1E00855386 /* TTExtensionInfoPrivate.h in Headers */, 66ADCE8B1291BEBB00855386 /* TTExtensionAuthor.h in Headers */, + 66A0530C132EB47F00132434 /* Three20.h in Headers */, + 660E7375135A560A00531398 /* Three20Version.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -483,6 +497,8 @@ 66ECA257128DFE28006C78C2 /* TTExtensionLoader.m in Sources */, 66ADC9091290B23A00855386 /* TTExtensionInfo.m in Sources */, 66ADCE871291BEAE00855386 /* TTExtensionAuthor.m in Sources */, + 66A0530A132EB47900132434 /* Three20.m in Sources */, + 66CE68FB135B320F00E127E2 /* Three20Version.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -546,7 +562,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = ""; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -616,7 +631,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -632,7 +646,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; PREBINDING = NO; SDKROOT = iphoneos; ZERO_LINK = NO; diff --git a/src/Three20Core/UnitTests/CoreGlobalTests.m b/src/Three20Core/UnitTests/CoreGlobalTests.m index 5a92203bc9..bb86ecca6e 100644 --- a/src/Three20Core/UnitTests/CoreGlobalTests.m +++ b/src/Three20Core/UnitTests/CoreGlobalTests.m @@ -19,6 +19,8 @@ #import // Core +#import "Three20Core/Three20.h" +#import "Three20Core/Three20Version.h" #import "Three20Core/TTCorePreprocessorMacros.h" #import "Three20Core/TTGlobalCorePaths.h" #import "Three20Core/TTGlobalCore.h" @@ -47,6 +49,27 @@ - (void)testSuccess { } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Versioning + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)testThree20Versioning { + STAssertTrue([[Three20 version] isEqualToString:Three20Version], + @"The latest version has not been updated correctly."); + STAssertEquals([Three20 majorVersion], 1, + @"The latest major version has not been updated correctly."); + STAssertEquals([Three20 minorVersion], 0, + @"The latest minor version has not been updated correctly."); + STAssertEquals([Three20 bugfixVersion], 5, + @"The latest bugfix version has not been updated correctly."); + STAssertEquals([Three20 hotfixVersion], 0, + @"The latest hotfix version has not been updated correctly."); +} + + /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark - diff --git a/src/Three20Network/Headers/TTErrorCodes.h b/src/Three20Network/Headers/TTErrorCodes.h index e197bafea1..3e47c3a89e 100644 --- a/src/Three20Network/Headers/TTErrorCodes.h +++ b/src/Three20Network/Headers/TTErrorCodes.h @@ -18,4 +18,4 @@ extern NSString* const kTTNetworkErrorDomain; extern NSInteger const kTTNetworkErrorCodeInvalidImage; - +extern NSString* const kTTErrorResponseDataKey; diff --git a/src/Three20Network/Headers/TTURLRequest.h b/src/Three20Network/Headers/TTURLRequest.h index b8341050ce..9d3e7eb3db 100644 --- a/src/Three20Network/Headers/TTURLRequest.h +++ b/src/Three20Network/Headers/TTURLRequest.h @@ -22,6 +22,14 @@ // Core #import "Three20Core/TTCorePreprocessorMacros.h" // For __TTDEPRECATED_METHOD +/** + * A constant to improve code readabillity, when using negative numbers for + * timeoutInterval. + * + * @see timeoutInterval + */ +extern const NSTimeInterval TTURLRequestUseQueueTimeout; + @protocol TTURLRequestDelegate; @protocol TTURLResponse; @@ -54,6 +62,8 @@ NSInteger _totalBytesLoaded; NSInteger _totalBytesExpected; + + NSTimeInterval _timeoutInterval; NSInteger _totalBytesDownloaded; NSInteger _totalContentLength; @@ -175,12 +185,12 @@ @property (nonatomic) BOOL shouldHandleCookies; /** - * The number of bytes loaded by this request. + * The number of request body bytes already uploaded by this request. */ @property (nonatomic) NSInteger totalBytesLoaded; /** - * The number of expected bytes from this request. + * The total number of request body bytes expected to be uploaded for this request. */ @property (nonatomic) NSInteger totalBytesExpected; @@ -194,6 +204,35 @@ */ @property (nonatomic) NSInteger totalContentLength; +/** + * The timeout to use for the request. + * + * If a negative value is set the request uses + * the defaultTimeout of the TTURLRequestQueue. This differs from behaviour of + * NSURLRequest. Given a negative timeoutInterval NSURLRequest always fails. + * + * You should use the TTURLRequestUseQueueTimeout constant to improve + * code readabillity, instead of negative numbers. + * + * The default value is TTURLRequestUseQueueTimeout + * + * @par from NSURLRequest.h: + * + * The timeout interval specifies the limit on the idle + * interval alloted to a request in the process of loading. The "idle + * interval" is defined as the period of time that has passed since the + * last instance of load activity occurred for a request that is in the + * process of loading. Hence, when an instance of load activity occurs + * (e.g. bytes are received from the network for a request), the idle + * interval for a request is reset to 0. If the idle interval ever + * becomes greater than or equal to the timeout interval, the request + * is considered to have timed out. This timeout interval is measured + * in seconds. + * + * @see TTURLRequestQueue::defaultTimeout + */ +@property (nonatomic) NSTimeInterval timeoutInterval; + /** * Whether or not the request was loaded from the cache. * diff --git a/src/Three20Network/Headers/TTURLRequestQueue.h b/src/Three20Network/Headers/TTURLRequestQueue.h index 01f9bee59f..9d73a9b609 100644 --- a/src/Three20Network/Headers/TTURLRequestQueue.h +++ b/src/Three20Network/Headers/TTURLRequestQueue.h @@ -32,6 +32,8 @@ NSString* _userAgent; CGFloat _imageCompressionQuality; + + NSTimeInterval _defaultTimeout; BOOL _suspended; } @@ -72,6 +74,18 @@ */ @property (nonatomic) CGFloat imageCompressionQuality; + +/** + * The default Timeout used for all TTURLRequests. + * + * This timeout is applied to all requests that have a negative timeout set. + * + * The default value is defined as kTimeout in TTURLRequestQueue.m + * + * @see TTURLRequest::timeoutInterval + */ +@property (nonatomic) NSTimeInterval defaultTimeout; + /** * Get the shared cache singleton used across the application. */ diff --git a/src/Three20Network/NetworkRequestTimeoutTests.m b/src/Three20Network/NetworkRequestTimeoutTests.m new file mode 100644 index 0000000000..f4bb84a1eb --- /dev/null +++ b/src/Three20Network/NetworkRequestTimeoutTests.m @@ -0,0 +1,127 @@ +// +// Copyright 2009-2011 Facebook +// +// 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. +// + +// See: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/905-A-Unit-Test_Result_Macro_Reference/unit-test_results.html#//apple_ref/doc/uid/TP40007959-CH21-SW2 +// for unit test macros. + +// See Also: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html + +#import + +// Network +#import "Three20Network/TTURLRequest.h" +#import "Three20Network/TTURLRequestQueue.h" + +// Core +#import "Three20Core/TTGlobalCorePaths.h" + +// duplicate constant for testing declared in Three20Network/TTURLRequestQueue.h +static const NSTimeInterval kTimeout = 300.0; + +/** + * Unit tests for configurable request timeouts. + * + */ + +@interface NetworkRequestTimeout : SenTestCase { +} + +@end + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +@implementation NetworkRequestTimeout + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark TTURLRequest + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)testTTURLRequest_timeoutIntervalAccess { + STAssertEqualsWithAccuracy([[[[TTURLRequest alloc] init] autorelease] timeoutInterval], + (NSTimeInterval)TTURLRequestUseDefaultTimeout, + 0.1, + @"default timeout should be set on initialization"); + + TTURLRequest * request = [[TTURLRequest alloc] init]; + request.timeoutInterval = 20.0; + STAssertEqualsWithAccuracy(request.timeoutInterval,(NSTimeInterval)20.0,0.1, + @"should return the previously set timeout"); + + TT_RELEASE_SAFELY(request); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)testTTURLRequestQueue_timeoutIntervalAccess { + STAssertEqualsWithAccuracy([[[[TTURLRequestQueue alloc] init] autorelease] defaultTimeout], + (NSTimeInterval)kTimeout, + 0.1, + @"default timeout should be set on initialization"); + + TTURLRequestQueue * queue = [[TTURLRequestQueue alloc] init]; + queue.defaultTimeout = 20.0; + STAssertEqualsWithAccuracy(queue.defaultTimeout,(NSTimeInterval)20.0,0.1, + @"should return the previously set timeout"); + + TT_RELEASE_SAFELY(queue); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)testTTURLRequestQueue_timeoutIntervalUsage { + + TTURLRequestQueue* queue = [[TTURLRequestQueue alloc] init]; + + TTURLRequest* request = [[TTURLRequest alloc] init]; + request.urlPath = @"http://www.three20.info"; + + NSURL* url = [NSURL URLWithString:request.urlPath]; + + NSURLRequest* urlRequest = nil; + + urlRequest = [queue createNSURLRequest:request URL:url]; + + STAssertNotNil(urlRequest,@"request queue didn't return an NSURLRequest"); + STAssertEqualsWithAccuracy([urlRequest timeoutInterval],kTimeout,0.1,@"wrong timeoutInterval set"); + + queue.defaultTimeout = 48.5; + + urlRequest = [queue createNSURLRequest:request URL:url]; + + STAssertNotNil(urlRequest,@"request queue didn't return an NSURLRequest"); + STAssertEqualsWithAccuracy([urlRequest timeoutInterval],48.5,0.1,@"wrong timeoutInterval set"); + + request.timeoutInterval = 5.3; + + urlRequest = [queue createNSURLRequest:request URL:url]; + + STAssertNotNil(urlRequest,@"request queue didn't return an NSURLRequest"); + STAssertEqualsWithAccuracy([urlRequest timeoutInterval],5.3,0.1,@"wrong timeoutInterval set"); + + request.timeoutInterval = -17; + + urlRequest = [queue createNSURLRequest:request URL:url]; + + STAssertNotNil(urlRequest,@"request queue didn't return an NSURLRequest"); + STAssertEqualsWithAccuracy([urlRequest timeoutInterval],48.5,0.1,@"wrong timeoutInterval set"); +} + +@end diff --git a/src/Three20Network/Sources/TTErrorCodes.m b/src/Three20Network/Sources/TTErrorCodes.m index a9eac0a3d0..9c1f36f832 100644 --- a/src/Three20Network/Sources/TTErrorCodes.m +++ b/src/Three20Network/Sources/TTErrorCodes.m @@ -18,4 +18,4 @@ NSString* const kTTNetworkErrorDomain = @"three20.network"; NSInteger const kTTNetworkErrorCodeInvalidImage = 100; - +NSString* const kTTErrorResponseDataKey = @"responsedata"; diff --git a/src/Three20Network/Sources/TTRequestLoader.m b/src/Three20Network/Sources/TTRequestLoader.m index b750598ea9..a1d421d4dd 100644 --- a/src/Three20Network/Sources/TTRequestLoader.m +++ b/src/Three20Network/Sources/TTRequestLoader.m @@ -16,6 +16,9 @@ #import "Three20Network/private/TTRequestLoader.h" +//Global +#import "Three20Network/TTErrorCodes.h" + // Network #import "Three20Network/TTGlobalNetwork.h" #import "Three20Network/TTURLRequest.h" @@ -27,6 +30,7 @@ #import "Three20Network/private/TTURLRequestQueueInternal.h" // Core +#import "Three20Core/NSDataAdditions.h" #import "Three20Core/NSObjectAdditions.h" #import "Three20Core/TTDebug.h" #import "Three20Core/TTDebugFlags.h" @@ -48,7 +52,8 @@ @implementation TTRequestLoader /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initForRequest:(TTURLRequest*)request queue:(TTURLRequestQueue*)queue { - if (self = [super init]) { + self = [super init]; + if (self) { _urlPath = [request.urlPath copy]; _queue = queue; _cacheKey = [request.cacheKey retain]; @@ -81,10 +86,37 @@ - (void)dealloc { #pragma mark - #pragma mark Private +////////////////////////////////////////////////////////////////////////////////////////////////// +// This method not called from outside, +// used as a separate entry point for performSelector outside connectToURL below +- (void)deliverDataResponse:(NSURL*)URL { + // http://tools.ietf.org/html/rfc2397 + NSArray * dataSplit = [[URL resourceSpecifier] componentsSeparatedByString:@","]; + if([dataSplit count]!=2) { + TTDCONDITIONLOG(TTDFLAG_URLREQUEST, @"UNRECOGNIZED data: URL %@", self.urlPath); + return; + } + if([[dataSplit objectAtIndex:0] rangeOfString:@"base64"].location == NSNotFound) { + // Strictly speaking, to be really conformant need to interpret %xx hex encoded entities. + // The [NSString dataUsingEncoding] doesn't do that correctly, but most documents don't use that. + // Skip for now. + _responseData = [[[dataSplit objectAtIndex:1] dataUsingEncoding:NSASCIIStringEncoding] retain]; + } else { + _responseData = [[NSData dataWithBase64EncodedString:[dataSplit objectAtIndex:1]] retain]; + } + + [_queue performSelector:@selector(loader:didLoadResponse:data:) withObject:self + withObject:_response withObject:_responseData]; +} /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)connectToURL:(NSURL*)URL { TTDCONDITIONLOG(TTDFLAG_URLREQUEST, @"Connecting to %@", _urlPath); + // If this is a data: url, we can decode right here ... after a delay to get out of calling thread + if([[URL scheme] isEqualToString:@"data"]) { + [self performSelector:@selector(deliverDataResponse:) withObject:URL afterDelay:0.1]; + return; + } TTNetworkRequestStarted(); TTURLRequest* request = _requests.count == 1 ? [_requests objectAtIndex:0] : nil; @@ -344,8 +376,13 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)connection { } else { TTDCONDITIONLOG(TTDFLAG_URLREQUEST, @" FAILED LOADING (%d) %@", _response.statusCode, _urlPath); - NSError* error = [NSError errorWithDomain:NSURLErrorDomain code:_response.statusCode - userInfo:nil]; + NSDictionary* userInfo = [NSDictionary dictionaryWithObject:_responseData forKey:kTTErrorResponseDataKey]; + NSError* error = [NSError errorWithDomain:NSURLErrorDomain code:_response.statusCode + userInfo:userInfo]; + /* + NSError* error = [NSError errorWithDomain:NSURLErrorDomain code:_response.statusCode + userInfo:nil]; + */ [_queue loader:self didFailLoadWithError:error]; } diff --git a/src/Three20Network/Sources/TTURLCache.m b/src/Three20Network/Sources/TTURLCache.m index 0c8cba5025..7458bfe1ac 100644 --- a/src/Three20Network/Sources/TTURLCache.m +++ b/src/Three20Network/Sources/TTURLCache.m @@ -62,7 +62,8 @@ @implementation TTURLCache /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithName:(NSString*)name { - if (self == [super init]) { + self = [super init]; + if (self) { _name = [name copy]; _cachePath = [[TTURLCache cachePathWithName:name] retain]; _invalidationAge = TT_DEFAULT_CACHE_INVALIDATION_AGE; @@ -85,7 +86,8 @@ - (id)initWithName:(NSString*)name { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithName:kDefaultCacheName]) { + self = [self initWithName:kDefaultCacheName]; + if (self) { } return self; diff --git a/src/Three20Network/Sources/TTURLRequest.m b/src/Three20Network/Sources/TTURLRequest.m index 48de3b7fe5..ac86c93f2a 100644 --- a/src/Three20Network/Sources/TTURLRequest.m +++ b/src/Three20Network/Sources/TTURLRequest.m @@ -16,6 +16,7 @@ #import "Three20Network/TTURLRequest.h" + // Network #import "Three20Network/TTGlobalNetwork.h" #import "Three20Network/TTURLResponse.h" @@ -28,7 +29,7 @@ #import "Three20Core/NSStringAdditions.h" static NSString* kStringBoundary = @"3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f"; - +const NSTimeInterval TTURLRequestUseDefaultTimeout = -1.0; /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,6 +59,8 @@ @implementation TTURLRequest @synthesize totalBytesDownloaded = _totalBytesDownloaded; @synthesize totalContentLength = _totalContentLength; +@synthesize timeoutInterval = _timeoutInterval; + @synthesize userInfo = _userInfo; @synthesize isLoading = _isLoading; @@ -83,7 +86,8 @@ + (TTURLRequest*)requestWithURL:(NSString*)URL delegate:(id /**/)delegate { - if (self = [self init]) { + self = [self init]; + if (self) { _urlPath = [URL retain]; if (nil != delegate) { [_delegates addObject:delegate]; @@ -95,13 +99,15 @@ - (id)initWithURL:(NSString*)URL delegate:(id /**/)delegat /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _delegates = TTCreateNonRetainingArray(); _cachePolicy = TTURLRequestCachePolicyDefault; _cacheExpirationAge = TT_DEFAULT_CACHE_EXPIRATION_AGE; _shouldHandleCookies = YES; _charsetForMultipart = NSUTF8StringEncoding; _multiPartForm = YES; + _timeoutInterval = TTURLRequestUseDefaultTimeout; } return self; } @@ -173,7 +179,8 @@ - (NSData *)generateNonMultipartPostBody { - (void)appendImageData:(NSData*)data withName:(NSString*)name toBody:(NSMutableData*)body { - NSString *beginLine = [NSString stringWithFormat:@"\r\n--%@\r\n", kStringBoundary]; + NSString *beginLine = [NSString stringWithFormat:@"--%@\r\n", kStringBoundary]; + NSString *endLine = @"\r\n"; [body appendData:[beginLine dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat: @@ -187,17 +194,17 @@ - (void)appendImageData:(NSData*)data stringWithString:@"Content-Type: image/jpeg\r\n\r\n"] dataUsingEncoding:_charsetForMultipart]]; [body appendData:data]; + [body appendData:[endLine dataUsingEncoding:NSUTF8StringEncoding]]; } /////////////////////////////////////////////////////////////////////////////////////////////////// - (NSData*)generatePostBody { - NSMutableData* body = [NSMutableData data]; - NSString* beginLine = [NSString stringWithFormat:@"\r\n--%@\r\n", kStringBoundary]; - - [body appendData:[[NSString stringWithFormat:@"--%@\r\n", kStringBoundary] - dataUsingEncoding:NSUTF8StringEncoding]]; + NSMutableData* body = [NSMutableData data]; + NSString* beginLine = [NSString stringWithFormat:@"--%@\r\n", kStringBoundary]; + NSString *endLine = @"\r\n"; + for (id key in [_parameters keyEnumerator]) { NSString* value = [_parameters valueForKey:key]; // Really, this can only be an NSString. We're cheating here. @@ -208,6 +215,7 @@ - (NSData*)generatePostBody { stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:_charsetForMultipart]]; [body appendData:[value dataUsingEncoding:_charsetForMultipart]]; + [body appendData:[endLine dataUsingEncoding:NSUTF8StringEncoding]]; } } @@ -243,9 +251,10 @@ - (NSData*)generatePostBody { [body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:_charsetForMultipart]]; [body appendData:data]; + [body appendData:[endLine dataUsingEncoding:NSUTF8StringEncoding]]; } - [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", kStringBoundary] + [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", kStringBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; // If an image was found, remove it from the dictionary to save memory while we @@ -268,6 +277,15 @@ - (NSMutableDictionary*)parameters { } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (NSMutableDictionary*)headers { + if (!_headers) { + _headers = [[NSMutableDictionary alloc] init]; + } + return _headers; +} + + /////////////////////////////////////////////////////////////////////////////////////////////////// - (NSData*)httpBody { if (_httpBody) { diff --git a/src/Three20Network/Sources/TTURLRequestQueue.m b/src/Three20Network/Sources/TTURLRequestQueue.m index 1b09a2e667..fb5785f261 100644 --- a/src/Three20Network/Sources/TTURLRequestQueue.m +++ b/src/Three20Network/Sources/TTURLRequestQueue.m @@ -50,6 +50,7 @@ @implementation TTURLRequestQueue @synthesize userAgent = _userAgent; @synthesize suspended = _suspended; @synthesize imageCompressionQuality = _imageCompressionQuality; +@synthesize defaultTimeout = _defaultTimeout; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -72,11 +73,13 @@ + (void)setMainQueue:(TTURLRequestQueue*)queue { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self == [super init]) { + self = [super init]; + if (self) { _loaders = [[NSMutableDictionary alloc] init]; _loaderQueue = [[NSMutableArray alloc] init]; _maxContentLength = kDefaultMaxContentLength; _imageCompressionQuality = 0.75; + _defaultTimeout = kTimeout; } return self; } @@ -493,10 +496,16 @@ - (NSURLRequest*)createNSURLRequest:(TTURLRequest*)request URL:(NSURL*)URL { if (!URL) { URL = [NSURL URLWithString:request.urlPath]; } - + + NSTimeInterval usedTimeout = request.timeoutInterval; + + if (usedTimeout < 0.0) { + usedTimeout = self.defaultTimeout; + } + NSMutableURLRequest* URLRequest = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData - timeoutInterval:kTimeout]; + timeoutInterval:usedTimeout]; if (self.userAgent) { [URLRequest setValue:self.userAgent forHTTPHeaderField:@"User-Agent"]; diff --git a/src/Three20Network/Sources/TTUserInfo.m b/src/Three20Network/Sources/TTUserInfo.m index 2e1882cc38..19b16282bf 100644 --- a/src/Three20Network/Sources/TTUserInfo.m +++ b/src/Three20Network/Sources/TTUserInfo.m @@ -50,7 +50,8 @@ + (id)weakRef:(id)weakRef { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTopic:(NSString*)topic strongRef:(id)strongRef weakRef:(id)weakRef { - if (self = [super init]) { + self = [super init]; + if (self) { self.topic = topic; self.strongRef = strongRef; self.weakRef = weakRef; diff --git a/src/Three20Network/Three20Network.xcodeproj/project.pbxproj b/src/Three20Network/Three20Network.xcodeproj/project.pbxproj index e2666a012d..e0763eeb32 100755 --- a/src/Three20Network/Three20Network.xcodeproj/project.pbxproj +++ b/src/Three20Network/Three20Network.xcodeproj/project.pbxproj @@ -7,12 +7,50 @@ objects = { /* Begin PBXBuildFile section */ + 662D819D126304DE005851C2 /* Xcode324iOS41TestSuiteWorkaround.m in Sources */ = {isa = PBXBuildFile; fileRef = 662D819C126304DE005851C2 /* Xcode324iOS41TestSuiteWorkaround.m */; }; + 662D81A7126304EB005851C2 /* MockModelDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7363911849A9800A35176 /* MockModelDelegate.m */; }; + 662D81A8126304EB005851C2 /* NetworkModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7363B11849AA100A35176 /* NetworkModelTests.m */; }; + 662D81CD12630516005851C2 /* Three20Network.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE735F7118499D300A35176 /* Three20Network.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81CE12630516005851C2 /* TTGlobalNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE735F9118499FB00A35176 /* TTGlobalNetwork.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81CF12630516005851C2 /* TTUserInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360111849A1F00A35176 /* TTUserInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D012630516005851C2 /* TTModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360811849A3500A35176 /* TTModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D112630516005851C2 /* TTModelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360911849A3500A35176 /* TTModelDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D212630516005851C2 /* TTRequestLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360A11849A3500A35176 /* TTRequestLoader.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 662D81D312630516005851C2 /* TTURLCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360B11849A3500A35176 /* TTURLCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D412630516005851C2 /* TTURLDataResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360C11849A3500A35176 /* TTURLDataResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D512630516005851C2 /* TTURLImageResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360D11849A3500A35176 /* TTURLImageResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D612630516005851C2 /* TTURLRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360E11849A3500A35176 /* TTURLRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D712630516005851C2 /* TTURLRequestCachePolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7360F11849A3500A35176 /* TTURLRequestCachePolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D812630516005851C2 /* TTURLRequestDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7361011849A3500A35176 /* TTURLRequestDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81D912630516005851C2 /* TTURLRequestModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7361111849A3500A35176 /* TTURLRequestModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81DA12630516005851C2 /* TTURLRequestQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7361211849A3500A35176 /* TTURLRequestQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81DB12630516005851C2 /* TTURLResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7361311849A3500A35176 /* TTURLResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 662D81DC12630516005851C2 /* Three20Network_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 6EE7364811849B1D00A35176 /* Three20Network_Prefix.pch */; }; + 662D81DD12630516005851C2 /* TTURLRequestQueueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EF96404118EFCF0003902E7 /* TTURLRequestQueueInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 662D81E012630516005851C2 /* TTGlobalNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE735FB11849A0C00A35176 /* TTGlobalNetwork.m */; }; + 662D81E112630516005851C2 /* TTUserInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE735FE11849A1600A35176 /* TTUserInfo.m */; }; + 662D81E212630516005851C2 /* TTModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362011849A4000A35176 /* TTModel.m */; }; + 662D81E312630516005851C2 /* TTRequestLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362111849A4000A35176 /* TTRequestLoader.m */; }; + 662D81E412630516005851C2 /* TTURLCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362211849A4000A35176 /* TTURLCache.m */; }; + 662D81E512630516005851C2 /* TTURLDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362311849A4000A35176 /* TTURLDataResponse.m */; }; + 662D81E612630516005851C2 /* TTURLImageResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362411849A4000A35176 /* TTURLImageResponse.m */; }; + 662D81E712630516005851C2 /* TTURLRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362511849A4000A35176 /* TTURLRequest.m */; }; + 662D81E812630516005851C2 /* TTURLRequestModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362611849A4000A35176 /* TTURLRequestModel.m */; }; + 662D81E912630516005851C2 /* TTURLRequestQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE7362711849A4000A35176 /* TTURLRequestQueue.m */; }; + 664B29BE12848AA50008D569 /* TTErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 664B29BC12848AA50008D569 /* TTErrorCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 664B29BF12848AA50008D569 /* TTErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 664B29BC12848AA50008D569 /* TTErrorCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 664B29C212848AAD0008D569 /* TTErrorCodes.m in Sources */ = {isa = PBXBuildFile; fileRef = 664B29C012848AAD0008D569 /* TTErrorCodes.m */; }; 664B29C312848AAD0008D569 /* TTErrorCodes.m in Sources */ = {isa = PBXBuildFile; fileRef = 664B29C012848AAD0008D569 /* TTErrorCodes.m */; }; + 66C16B1C1263059A00A7825A /* libThree20Network-Xcode3.2.5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 662D81EF12630516005851C2 /* libThree20Network-Xcode3.2.5.a */; }; + 66C16B21126305AB00A7825A /* libThree20Core-Xcode3.2.5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 66C16AC31263027400A7825A /* libThree20Core-Xcode3.2.5.a */; }; 66F955B0126662E300BEF6F0 /* NetworkURLCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 66F955AF126662E300BEF6F0 /* NetworkURLCacheTests.m */; }; + 66F955B1126662E300BEF6F0 /* NetworkURLCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 66F955AF126662E300BEF6F0 /* NetworkURLCacheTests.m */; }; 66F955BA1266647A00BEF6F0 /* both.png in Resources */ = {isa = PBXBuildFile; fileRef = 66F955B71266647A00BEF6F0 /* both.png */; }; 66F955BB1266647A00BEF6F0 /* both@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 66F955B81266647A00BEF6F0 /* both@2x.png */; }; 66F955BC1266647A00BEF6F0 /* only@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 66F955B91266647A00BEF6F0 /* only@2x.png */; }; + 66F955BD1266647A00BEF6F0 /* both.png in Resources */ = {isa = PBXBuildFile; fileRef = 66F955B71266647A00BEF6F0 /* both.png */; }; + 66F955BE1266647A00BEF6F0 /* both@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 66F955B81266647A00BEF6F0 /* both@2x.png */; }; + 66F955BF1266647A00BEF6F0 /* only@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 66F955B91266647A00BEF6F0 /* only@2x.png */; }; 6EE735F8118499D300A35176 /* Three20Network.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE735F7118499D300A35176 /* Three20Network.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6EE735FA118499FB00A35176 /* TTGlobalNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE735F9118499FB00A35176 /* TTGlobalNetwork.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6EE735FC11849A0C00A35176 /* TTGlobalNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE735FB11849A0C00A35176 /* TTGlobalNetwork.m */; }; @@ -44,9 +82,46 @@ 6EE736C811849FAB00A35176 /* libThree20Core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EE7366C11849C5800A35176 /* libThree20Core.a */; }; 6EE739CD1184BAF600A35176 /* libThree20Network.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF31F3A0F352DF5000DE5D2 /* libThree20Network.a */; }; 6EF96405118EFCF0003902E7 /* TTURLRequestQueueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EF96404118EFCF0003902E7 /* TTURLRequestQueueInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E1F076A1130521F7007CA6F1 /* NetworkRequestTimeoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E1F076A0130521F7007CA6F1 /* NetworkRequestTimeoutTests.m */; }; + E1F076A2130521F7007CA6F1 /* NetworkRequestTimeoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E1F076A0130521F7007CA6F1 /* NetworkRequestTimeoutTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 662D824C12630805005851C2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6EE7366611849C5800A35176 /* Three20Core.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 664961541262EE5000C2C80E; + remoteInfo = "UnitTests-Xcode3.2.5"; + }; + 662D82E012639FCA005851C2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6EE7366611849C5800A35176 /* Three20Core.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 664961541262EE5000C2C80E; + remoteInfo = "UnitTests-Xcode3.2.5"; + }; + 66C16AC21263027400A7825A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6EE7366611849C5800A35176 /* Three20Core.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6650CAA21262F6E2003FF804; + remoteInfo = "Three20Core-Xcode3.2.5"; + }; + 66C16AC61263027400A7825A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6EE7366611849C5800A35176 /* Three20Core.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 664961641262EE5000C2C80E; + remoteInfo = "UnitTests-Xcode3.2.5"; + }; + 66C16B26126305F500A7825A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 662D81C912630516005851C2; + remoteInfo = "Three20Network-Xcode3.2.5"; + }; 6EE7366B11849C5800A35176 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6EE7366611849C5800A35176 /* Three20Core.xcodeproj */; @@ -85,6 +160,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 662D819C126304DE005851C2 /* Xcode324iOS41TestSuiteWorkaround.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Xcode324iOS41TestSuiteWorkaround.m; path = ../UnitTests/Xcode324iOS41TestSuiteWorkaround.m; sourceTree = SOURCE_ROOT; }; + 662D81B2126304EB005851C2 /* NetworkUnitTests-Xcode3.2.5.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetworkUnitTests-Xcode3.2.5.octest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 662D81EF12630516005851C2 /* libThree20Network-Xcode3.2.5.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libThree20Network-Xcode3.2.5.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 664B29BC12848AA50008D569 /* TTErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTErrorCodes.h; path = Headers/TTErrorCodes.h; sourceTree = ""; }; 664B29C012848AAD0008D569 /* TTErrorCodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTErrorCodes.m; path = Sources/TTErrorCodes.m; sourceTree = ""; }; 66F955AF126662E300BEF6F0 /* NetworkURLCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NetworkURLCacheTests.m; path = UnitTests/NetworkURLCacheTests.m; sourceTree = ""; }; @@ -127,10 +205,27 @@ 6EE7366611849C5800A35176 /* Three20Core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Three20Core.xcodeproj; path = ../Three20Core/Three20Core.xcodeproj; sourceTree = SOURCE_ROOT; }; 6EF96404118EFCF0003902E7 /* TTURLRequestQueueInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTURLRequestQueueInternal.h; path = Headers/TTURLRequestQueueInternal.h; sourceTree = ""; }; BEF31F3A0F352DF5000DE5D2 /* libThree20Network.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libThree20Network.a; sourceTree = BUILT_PRODUCTS_DIR; }; + E1F076A0130521F7007CA6F1 /* NetworkRequestTimeoutTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkRequestTimeoutTests.m; sourceTree = ""; }; EB9E6C6210B6A8F800DE563C /* NetworkUnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NetworkUnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 662D81AA126304EB005851C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 66C16B21126305AB00A7825A /* libThree20Core-Xcode3.2.5.a in Frameworks */, + 66C16B1C1263059A00A7825A /* libThree20Network-Xcode3.2.5.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 662D81EA12630516005851C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEF31F380F352DF5000DE5D2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -154,7 +249,9 @@ isa = PBXGroup; children = ( BEF31F3A0F352DF5000DE5D2 /* libThree20Network.a */, + 662D81EF12630516005851C2 /* libThree20Network-Xcode3.2.5.a */, EB9E6C6210B6A8F800DE563C /* NetworkUnitTests.octest */, + 662D81B2126304EB005851C2 /* NetworkUnitTests-Xcode3.2.5.octest */, ); name = Products; sourceTree = ""; @@ -173,13 +270,23 @@ name = CustomTemplate; sourceTree = ""; }; + 662D819B126304DE005851C2 /* Xcode 3.2.4 Workaround */ = { + isa = PBXGroup; + children = ( + 662D819C126304DE005851C2 /* Xcode324iOS41TestSuiteWorkaround.m */, + ); + name = "Xcode 3.2.4 Workaround"; + sourceTree = ""; + }; 6E08B274118282F700DA1579 /* Tests */ = { isa = PBXGroup; children = ( 6EE7364611849AF200A35176 /* Resources */, 6E08B27D118282F700DA1579 /* Mocks */, + 662D819B126304DE005851C2 /* Xcode 3.2.4 Workaround */, 6EE7363B11849AA100A35176 /* NetworkModelTests.m */, 66F955AF126662E300BEF6F0 /* NetworkURLCacheTests.m */, + E1F076A0130521F7007CA6F1 /* NetworkRequestTimeoutTests.m */, ); name = Tests; sourceTree = ""; @@ -332,7 +439,9 @@ isa = PBXGroup; children = ( 6EE7366C11849C5800A35176 /* libThree20Core.a */, + 66C16AC31263027400A7825A /* libThree20Core-Xcode3.2.5.a */, 6EE7366E11849C5800A35176 /* CoreUnitTests.octest */, + 66C16AC71263027400A7825A /* CoreUnitTests-Xcode3.2.5.octest */, ); name = Products; sourceTree = ""; @@ -348,6 +457,31 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 662D81CC12630516005851C2 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 662D81CD12630516005851C2 /* Three20Network.h in Headers */, + 662D81CE12630516005851C2 /* TTGlobalNetwork.h in Headers */, + 662D81CF12630516005851C2 /* TTUserInfo.h in Headers */, + 662D81D012630516005851C2 /* TTModel.h in Headers */, + 662D81D112630516005851C2 /* TTModelDelegate.h in Headers */, + 662D81D212630516005851C2 /* TTRequestLoader.h in Headers */, + 662D81D312630516005851C2 /* TTURLCache.h in Headers */, + 662D81D412630516005851C2 /* TTURLDataResponse.h in Headers */, + 662D81D512630516005851C2 /* TTURLImageResponse.h in Headers */, + 662D81D612630516005851C2 /* TTURLRequest.h in Headers */, + 662D81D712630516005851C2 /* TTURLRequestCachePolicy.h in Headers */, + 662D81D812630516005851C2 /* TTURLRequestDelegate.h in Headers */, + 662D81D912630516005851C2 /* TTURLRequestModel.h in Headers */, + 662D81DA12630516005851C2 /* TTURLRequestQueue.h in Headers */, + 662D81DB12630516005851C2 /* TTURLResponse.h in Headers */, + 662D81DC12630516005851C2 /* Three20Network_Prefix.pch in Headers */, + 662D81DD12630516005851C2 /* TTURLRequestQueueInternal.h in Headers */, + 664B29BE12848AA50008D569 /* TTErrorCodes.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEF31F360F352DF5000DE5D2 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -376,6 +510,45 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 662D81A0126304EB005851C2 /* Three20NetworkUnitTests-Xcode3.2.5 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 662D81AE126304EB005851C2 /* Build configuration list for PBXNativeTarget "Three20NetworkUnitTests-Xcode3.2.5" */; + buildPhases = ( + 662D81A5126304EB005851C2 /* Resources */, + 662D81A6126304EB005851C2 /* Sources */, + 662D81AA126304EB005851C2 /* Frameworks */, + 662D81AD126304EB005851C2 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 662D824D12630805005851C2 /* PBXTargetDependency */, + 66C16B27126305F500A7825A /* PBXTargetDependency */, + ); + name = "Three20NetworkUnitTests-Xcode3.2.5"; + productName = CoreUnitTests; + productReference = 662D81B2126304EB005851C2 /* NetworkUnitTests-Xcode3.2.5.octest */; + productType = "com.apple.product-type.bundle"; + }; + 662D81C912630516005851C2 /* Three20Network-Xcode3.2.5 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 662D81EB12630516005851C2 /* Build configuration list for PBXNativeTarget "Three20Network-Xcode3.2.5" */; + buildPhases = ( + 662D81CC12630516005851C2 /* Headers */, + 662D81DE12630516005851C2 /* Protect Copied Headers */, + 662D81DF12630516005851C2 /* Sources */, + 662D81EA12630516005851C2 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 662D82E112639FCA005851C2 /* PBXTargetDependency */, + ); + name = "Three20Network-Xcode3.2.5"; + productName = Three20; + productReference = 662D81EF12630516005851C2 /* libThree20Network-Xcode3.2.5.a */; + productType = "com.apple.product-type.library.static"; + }; BEF31F390F352DF5000DE5D2 /* Three20Network */ = { isa = PBXNativeTarget; buildConfigurationList = BEF31F410F352E14000DE5D2 /* Build configuration list for PBXNativeTarget "Three20Network" */; @@ -383,7 +556,6 @@ BEF31F360F352DF5000DE5D2 /* Headers */, 6E645F091187AD0E00F08CB1 /* Protect Copied Headers */, BEF31F370F352DF5000DE5D2 /* Sources */, - 668E65A0131ADF9B00ACE4AB /* Lint */, BEF31F380F352DF5000DE5D2 /* Frameworks */, ); buildRules = ( @@ -445,12 +617,28 @@ projectRoot = ""; targets = ( BEF31F390F352DF5000DE5D2 /* Three20Network */, + 662D81C912630516005851C2 /* Three20Network-Xcode3.2.5 */, EB9E6C6110B6A8F800DE563C /* Three20NetworkUnitTests */, + 662D81A0126304EB005851C2 /* Three20NetworkUnitTests-Xcode3.2.5 */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ + 66C16AC31263027400A7825A /* libThree20Core-Xcode3.2.5.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libThree20Core-Xcode3.2.5.a"; + remoteRef = 66C16AC21263027400A7825A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 66C16AC71263027400A7825A /* CoreUnitTests-Xcode3.2.5.octest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "CoreUnitTests-Xcode3.2.5.octest"; + remoteRef = 66C16AC61263027400A7825A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 6EE7366C11849C5800A35176 /* libThree20Core.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -468,6 +656,16 @@ /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ + 662D81A5126304EB005851C2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 66F955BD1266647A00BEF6F0 /* both.png in Resources */, + 66F955BE1266647A00BEF6F0 /* both@2x.png in Resources */, + 66F955BF1266647A00BEF6F0 /* only@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; EB9E6C5D10B6A8F800DE563C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -481,20 +679,32 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 668E65A0131ADF9B00ACE4AB /* Lint */ = { + 662D81AD126304EB005851C2 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = Lint; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; - shellPath = "/usr/bin/env python ../scripts/lint"; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; + }; + 662D81DE12630516005851C2 /* Protect Copied Headers */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Protect Copied Headers"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/sh ../scripts/Protect.command"; shellScript = ""; - showEnvVarsInLog = 0; }; 6E645F091187AD0E00F08CB1 /* Protect Copied Headers */ = { isa = PBXShellScriptBuildPhase; @@ -526,6 +736,35 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 662D81A6126304EB005851C2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 662D81A7126304EB005851C2 /* MockModelDelegate.m in Sources */, + 662D81A8126304EB005851C2 /* NetworkModelTests.m in Sources */, + 66F955B1126662E300BEF6F0 /* NetworkURLCacheTests.m in Sources */, + E1F076A2130521F7007CA6F1 /* NetworkRequestTimeoutTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 662D81DF12630516005851C2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 662D81E012630516005851C2 /* TTGlobalNetwork.m in Sources */, + 662D81E112630516005851C2 /* TTUserInfo.m in Sources */, + 662D81E212630516005851C2 /* TTModel.m in Sources */, + 662D81E312630516005851C2 /* TTRequestLoader.m in Sources */, + 662D81E412630516005851C2 /* TTURLCache.m in Sources */, + 662D81E512630516005851C2 /* TTURLDataResponse.m in Sources */, + 662D81E612630516005851C2 /* TTURLImageResponse.m in Sources */, + 662D81E712630516005851C2 /* TTURLRequest.m in Sources */, + 662D81E812630516005851C2 /* TTURLRequestModel.m in Sources */, + 662D81E912630516005851C2 /* TTURLRequestQueue.m in Sources */, + 664B29C212848AAD0008D569 /* TTErrorCodes.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEF31F370F352DF5000DE5D2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -550,13 +789,30 @@ files = ( 6EE7363A11849A9800A35176 /* MockModelDelegate.m in Sources */, 6EE7363C11849AA100A35176 /* NetworkModelTests.m in Sources */, + 662D819D126304DE005851C2 /* Xcode324iOS41TestSuiteWorkaround.m in Sources */, 66F955B0126662E300BEF6F0 /* NetworkURLCacheTests.m in Sources */, + E1F076A1130521F7007CA6F1 /* NetworkRequestTimeoutTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 662D824D12630805005851C2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "UnitTests-Xcode3.2.5"; + targetProxy = 662D824C12630805005851C2 /* PBXContainerItemProxy */; + }; + 662D82E112639FCA005851C2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "UnitTests-Xcode3.2.5"; + targetProxy = 662D82E012639FCA005851C2 /* PBXContainerItemProxy */; + }; + 66C16B27126305F500A7825A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 662D81C912630516005851C2 /* Three20Network-Xcode3.2.5 */; + targetProxy = 66C16B26126305F500A7825A /* PBXContainerItemProxy */; + }; 6EE7367111849C6300A35176 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Three20Core; @@ -575,6 +831,97 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 662D81AF126304EB005851C2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EE7364C11849B3D00A35176 /* UnitTests.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + PREBINDING = NO; + PRODUCT_NAME = "$(BASE_PRODUCT_NAME)-Xcode3.2.5"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 662D81B0126304EB005851C2 /* Internal */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EE7364C11849B3D00A35176 /* UnitTests.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + PREBINDING = NO; + PRODUCT_NAME = "$(BASE_PRODUCT_NAME)-Xcode3.2.5"; + SDKROOT = iphoneos; + }; + name = Internal; + }; + 662D81B1126304EB005851C2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EE7364C11849B3D00A35176 /* UnitTests.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + PREBINDING = NO; + PRODUCT_NAME = "$(BASE_PRODUCT_NAME)-Xcode3.2.5"; + SDKROOT = iphoneos; + ZERO_LINK = NO; + }; + name = Release; + }; + 662D81EC12630516005851C2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EE7364B11849B3D00A35176 /* Library.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + PREBINDING = NO; + PRODUCT_NAME = "$(BASE_PRODUCT_NAME)-Xcode3.2.5"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 662D81ED12630516005851C2 /* Internal */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EE7364B11849B3D00A35176 /* Library.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + PREBINDING = NO; + PRODUCT_NAME = "$(BASE_PRODUCT_NAME)-Xcode3.2.5"; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = iphoneos; + }; + name = Internal; + }; + 662D81EE12630516005851C2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EE7364B11849B3D00A35176 /* Library.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PREBINDING = NO; + PRODUCT_NAME = "$(BASE_PRODUCT_NAME)-Xcode3.2.5"; + SDKROOT = iphoneos; + ZERO_LINK = NO; + }; + name = Release; + }; 6E92493F112C6F4000531F03 /* Internal */ = { isa = XCBuildConfiguration; baseConfigurationReference = 6E64541F1184BDF900F08CB1 /* Project.xcconfig */; @@ -595,8 +942,7 @@ COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = ""; + GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; RUN_CLANG_STATIC_ANALYZER = YES; @@ -613,8 +959,8 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = ""; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -709,6 +1055,26 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 662D81AE126304EB005851C2 /* Build configuration list for PBXNativeTarget "Three20NetworkUnitTests-Xcode3.2.5" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 662D81AF126304EB005851C2 /* Debug */, + 662D81B0126304EB005851C2 /* Internal */, + 662D81B1126304EB005851C2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 662D81EB12630516005851C2 /* Build configuration list for PBXNativeTarget "Three20Network-Xcode3.2.5" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 662D81EC12630516005851C2 /* Debug */, + 662D81ED12630516005851C2 /* Internal */, + 662D81EE12630516005851C2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; BEF31F410F352E14000DE5D2 /* Build configuration list for PBXNativeTarget "Three20Network" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/src/Three20Style/Headers/TTDefaultStyleSheet.h b/src/Three20Style/Headers/TTDefaultStyleSheet.h index e570a8a679..0cf8941351 100644 --- a/src/Three20Style/Headers/TTDefaultStyleSheet.h +++ b/src/Three20Style/Headers/TTDefaultStyleSheet.h @@ -24,6 +24,7 @@ // Common styles @property (nonatomic, readonly) UIColor* textColor; @property (nonatomic, readonly) UIColor* highlightedTextColor; +@property (nonatomic, readonly) UIColor* backgroundTextColor; @property (nonatomic, readonly) UIFont* font; @property (nonatomic, readonly) UIColor* backgroundColor; @property (nonatomic, readonly) UIColor* navigationBarTintColor; @@ -32,7 +33,11 @@ // Tables @property (nonatomic, readonly) UIColor* tablePlainBackgroundColor; +@property (nonatomic, readonly) UIColor* tablePlainCellSeparatorColor; +@property (nonatomic, readonly) UITableViewCellSeparatorStyle tablePlainCellSeparatorStyle; @property (nonatomic, readonly) UIColor* tableGroupedBackgroundColor; +@property (nonatomic, readonly) UIColor* tableGroupedCellSeparatorColor; +@property (nonatomic, readonly) UITableViewCellSeparatorStyle tableGroupedCellSeparatorStyle; @property (nonatomic, readonly) UIColor* searchTableBackgroundColor; @property (nonatomic, readonly) UIColor* searchTableSeparatorColor; diff --git a/src/Three20Style/Headers/TTSpeechBubbleShape.h b/src/Three20Style/Headers/TTSpeechBubbleShape.h index 02aae556d1..96a5f53b5a 100644 --- a/src/Three20Style/Headers/TTSpeechBubbleShape.h +++ b/src/Three20Style/Headers/TTSpeechBubbleShape.h @@ -17,6 +17,10 @@ // Style #import "Three20Style/TTShape.h" +/** + * The shape that defines a rectangular shape with a pointer. + * + */ @interface TTSpeechBubbleShape : TTShape { CGFloat _radius; CGFloat _pointLocation; @@ -29,7 +33,25 @@ @property (nonatomic) CGFloat pointAngle; @property (nonatomic) CGSize pointSize; -+ (TTSpeechBubbleShape*)shapeWithRadius:(CGFloat)radius pointLocation:(CGFloat)pointLocation - pointAngle:(CGFloat)pointAngle pointSize:(CGSize)pointSize; +/** + * The shape that defines a rectangular shape with a pointer. + * Radius - number of pixels for the rounded corners + * pointLocation - location of the point where the top edge starts at 45, the right edge at 135, + * the bottom edge at 225 and the left edge at 315. + * pointAngle - not fgunctional yet. Make this equal to pointLocation in order to point it in the + * right direction. + * pointSize - the square in which the pointer will be defined, should be narrower or less high than + * the shape minus the radiuses. + * + * Pointers are not placed on the rounded corners. + * + * pointSize should be less wide or high than the edge that it is placed on minus 2 * radius. + * radius should be smaller than the length of the edge / 2. + * + */ ++ (TTSpeechBubbleShape*)shapeWithRadius:(CGFloat)radius + pointLocation:(CGFloat)pointLocation + pointAngle:(CGFloat)pointAngle + pointSize:(CGSize)pointSize; @end diff --git a/src/Three20Style/Headers/TTStyledLayout.h b/src/Three20Style/Headers/TTStyledLayout.h index 37bb17b2c8..e92c67e9fb 100644 --- a/src/Three20Style/Headers/TTStyledLayout.h +++ b/src/Three20Style/Headers/TTStyledLayout.h @@ -45,6 +45,8 @@ UIFont* _boldFont; UIFont* _italicFont; + UITextAlignment _textAlignment; + TTStyle* _linkStyle; TTStyledNode* _rootNode; TTStyledNode* _lastNode; @@ -55,6 +57,7 @@ @property (nonatomic) CGFloat width; @property (nonatomic) CGFloat height; @property (nonatomic, retain) UIFont* font; +@property (nonatomic) UITextAlignment textAlignment; @property (nonatomic, readonly) TTStyledFrame* rootFrame; @property (nonatomic, retain) NSMutableArray* invalidImages; diff --git a/src/Three20Style/Headers/TTStyledText.h b/src/Three20Style/Headers/TTStyledText.h index 9921ebdc18..09c7f8309f 100644 --- a/src/Three20Style/Headers/TTStyledText.h +++ b/src/Three20Style/Headers/TTStyledText.h @@ -28,6 +28,7 @@ TTStyledNode* _rootNode; TTStyledFrame* _rootFrame; UIFont* _font; + UITextAlignment _textAlignment; CGFloat _width; CGFloat _height; NSMutableArray* _invalidImages; @@ -53,6 +54,11 @@ */ @property (nonatomic, retain) UIFont* font; +/** + * The text alignment used for all text. + */ +@property (nonatomic) UITextAlignment textAlignment; + /** * The width that the text should be constrained to fit within. */ diff --git a/src/Three20Style/Sources/TTBevelBorderStyle.m b/src/Three20Style/Sources/TTBevelBorderStyle.m index de152dfbb7..e5ff7c06a4 100644 --- a/src/Three20Style/Sources/TTBevelBorderStyle.m +++ b/src/Three20Style/Sources/TTBevelBorderStyle.m @@ -41,7 +41,8 @@ @implementation TTBevelBorderStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _width = 1; _lightSource = kDefaultLightSource; } diff --git a/src/Three20Style/Sources/TTBlendStyle.m b/src/Three20Style/Sources/TTBlendStyle.m index b45a29a8b6..d643646e72 100644 --- a/src/Three20Style/Sources/TTBlendStyle.m +++ b/src/Three20Style/Sources/TTBlendStyle.m @@ -27,7 +27,8 @@ @implementation TTBlendStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _blendMode = kCGBlendModeNormal; } diff --git a/src/Three20Style/Sources/TTBoxStyle.m b/src/Three20Style/Sources/TTBoxStyle.m index 2d75cb9d17..0eb0608150 100644 --- a/src/Three20Style/Sources/TTBoxStyle.m +++ b/src/Three20Style/Sources/TTBoxStyle.m @@ -36,7 +36,8 @@ @implementation TTBoxStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _margin = UIEdgeInsetsZero; _padding = UIEdgeInsetsZero; _minSize = CGSizeZero; diff --git a/src/Three20Style/Sources/TTDefaultStyleSheet.m b/src/Three20Style/Sources/TTDefaultStyleSheet.m index 052d11cf9c..829d1ee475 100644 --- a/src/Three20Style/Sources/TTDefaultStyleSheet.m +++ b/src/Three20Style/Sources/TTDefaultStyleSheet.m @@ -741,6 +741,11 @@ - (UIColor*)highlightedTextColor { return [UIColor whiteColor]; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (UIColor*)backgroundTextColor { + return [UIColor whiteColor]; +} + /////////////////////////////////////////////////////////////////////////////////////////////////// - (UIFont*)font { @@ -783,12 +788,32 @@ - (UIColor*)tablePlainBackgroundColor { return nil; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (UIColor*)tablePlainCellSeparatorColor { + return nil; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (UITableViewCellSeparatorStyle)tablePlainCellSeparatorStyle { + return UITableViewCellSeparatorStyleSingleLine; +} /////////////////////////////////////////////////////////////////////////////////////////////////// - (UIColor*)tableGroupedBackgroundColor { return [UIColor groupTableViewBackgroundColor]; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (UIColor*)tableGroupedCellSeparatorColor { + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (UITableViewCellSeparatorStyle)tableGroupedCellSeparatorStyle { + return [self tablePlainCellSeparatorStyle]; +} + /////////////////////////////////////////////////////////////////////////////////////////////////// - (UIColor*)searchTableBackgroundColor { diff --git a/src/Three20Style/Sources/TTFourBorderStyle.m b/src/Three20Style/Sources/TTFourBorderStyle.m index 7ced93b285..ea7fc233e5 100644 --- a/src/Three20Style/Sources/TTFourBorderStyle.m +++ b/src/Three20Style/Sources/TTFourBorderStyle.m @@ -25,6 +25,7 @@ // Core #import "Three20Core/TTCorePreprocessorMacros.h" +#import "Three20Core/TTGlobalCoreRects.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -41,7 +42,8 @@ @implementation TTFourBorderStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _width = 1; } @@ -124,7 +126,11 @@ + (TTFourBorderStyle*)styleWithLeft:(UIColor*)left width:(CGFloat)width next:(TT /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)draw:(TTStyleContext*)context { CGRect rect = context.frame; - CGRect strokeRect = CGRectInset(rect, _width/2, _width/2); + UIEdgeInsets insets = UIEdgeInsetsMake(_top ? _width/2. : 0, + _left ? _width/2 : 0, + _bottom ? _width/2 : 0, + _right ? _width/2 : 0); + CGRect strokeRect = TTRectInset(rect, insets); [context.shape openPath:strokeRect]; CGContextRef ctx = UIGraphicsGetCurrentContext(); diff --git a/src/Three20Style/Sources/TTGridLayout.m b/src/Three20Style/Sources/TTGridLayout.m index 724c0cd847..64db4806fd 100644 --- a/src/Three20Style/Sources/TTGridLayout.m +++ b/src/Three20Style/Sources/TTGridLayout.m @@ -29,7 +29,8 @@ @implementation TTGridLayout /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _columnCount = 1; } diff --git a/src/Three20Style/Sources/TTHighlightBorderStyle.m b/src/Three20Style/Sources/TTHighlightBorderStyle.m index 05099b2ce7..d5f7f15253 100644 --- a/src/Three20Style/Sources/TTHighlightBorderStyle.m +++ b/src/Three20Style/Sources/TTHighlightBorderStyle.m @@ -36,7 +36,8 @@ @implementation TTHighlightBorderStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _width = 1; } diff --git a/src/Three20Style/Sources/TTImageStyle.m b/src/Three20Style/Sources/TTImageStyle.m index d4e7c5c129..a9ede0c5cd 100644 --- a/src/Three20Style/Sources/TTImageStyle.m +++ b/src/Three20Style/Sources/TTImageStyle.m @@ -43,7 +43,8 @@ @implementation TTImageStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _contentMode = UIViewContentModeScaleToFill; _size = CGSizeZero; } diff --git a/src/Three20Style/Sources/TTInsetStyle.m b/src/Three20Style/Sources/TTInsetStyle.m index 842159e40f..4735a9d94e 100644 --- a/src/Three20Style/Sources/TTInsetStyle.m +++ b/src/Three20Style/Sources/TTInsetStyle.m @@ -30,7 +30,8 @@ @implementation TTInsetStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _inset = UIEdgeInsetsZero; } diff --git a/src/Three20Style/Sources/TTLinearGradientBorderStyle.m b/src/Three20Style/Sources/TTLinearGradientBorderStyle.m index f8c7d10f2a..dbe75d7888 100644 --- a/src/Three20Style/Sources/TTLinearGradientBorderStyle.m +++ b/src/Three20Style/Sources/TTLinearGradientBorderStyle.m @@ -41,7 +41,8 @@ @implementation TTLinearGradientBorderStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _location2 = 1; _width = 1; } diff --git a/src/Three20Style/Sources/TTShadowStyle.m b/src/Three20Style/Sources/TTShadowStyle.m index 2b45c260c6..08626f1e44 100644 --- a/src/Three20Style/Sources/TTShadowStyle.m +++ b/src/Three20Style/Sources/TTShadowStyle.m @@ -38,7 +38,8 @@ @implementation TTShadowStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _offset = CGSizeZero; } diff --git a/src/Three20Style/Sources/TTSolidBorderStyle.m b/src/Three20Style/Sources/TTSolidBorderStyle.m index 94ef337a4b..b4ab395172 100644 --- a/src/Three20Style/Sources/TTSolidBorderStyle.m +++ b/src/Three20Style/Sources/TTSolidBorderStyle.m @@ -35,7 +35,8 @@ @implementation TTSolidBorderStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _width = 1; } diff --git a/src/Three20Style/Sources/TTSpeechBubbleShape.m b/src/Three20Style/Sources/TTSpeechBubbleShape.m index e697dd3e4f..f8493d3064 100644 --- a/src/Three20Style/Sources/TTSpeechBubbleShape.m +++ b/src/Three20Style/Sources/TTSpeechBubbleShape.m @@ -66,24 +66,24 @@ - (CGRect)subtractPointFromRect:(CGRect)rect { if ((_pointLocation >= 0 && _pointLocation < 45) || (_pointLocation >= 315 && _pointLocation < 360)) { - if ((_pointAngle >= 270 && _pointAngle < 360) || (_pointAngle >= 0 && _pointAngle < 90)) { + if ((_pointAngle >= 270 && _pointAngle <= 360) || (_pointAngle >= 0 && _pointAngle <= 90)) { x += _pointSize.width; w -= _pointSize.width; } } else if (_pointLocation >= 45 && _pointLocation < 135) { - if (_pointAngle >= 0 && _pointAngle < 180) { + if (_pointAngle >= 0 && _pointAngle <= 180) { y += _pointSize.height; h -= _pointSize.height; } } else if (_pointLocation >= 135 && _pointLocation < 225) { - if (_pointAngle >= 90 && _pointAngle < 270) { + if (_pointAngle >= 90 && _pointAngle <= 270) { w -= _pointSize.width; } - } else if (_pointLocation >= 225 && _pointLocation <= 315) { - if (_pointAngle >= 180 && _pointAngle < 360) { + } else if (_pointLocation >= 225 && _pointLocation < 315) { + if (_pointAngle >= 180 && _pointAngle <= 360) { h -= _pointSize.height; } } @@ -98,29 +98,31 @@ - (void)addTopEdge:(CGSize)size lightSource:(NSInteger)lightSource toPath:(CGMut CGFloat fw = size.width; CGFloat fh = size.height; CGFloat pointX = 0; + CGFloat radius = RD(_radius); if (lightSource >= 0 && lightSource <= 90) { if (reset) { - CGPathMoveToPoint(path, nil, RD(_radius), 0); + CGPathMoveToPoint(path, nil, radius, 0); } } else { if (reset) { - CGPathMoveToPoint(path, nil, 0, RD(_radius)); + CGPathMoveToPoint(path, nil, 0, radius); } - CGPathAddArcToPoint(path, nil, 0, 0, RD(_radius), 0, RD(_radius)); + CGPathAddArcToPoint(path, nil, 0, 0, radius, 0, radius); } - if (_pointLocation >= 45 && _pointLocation <= 135) { - CGFloat ph = _pointAngle >= 0 && _pointAngle < 180 ? _pointSize.height : -_pointSize.height; - pointX = ((_pointLocation-45)/90) * fw; + if (_pointLocation >= 45 && _pointLocation < 135) { + CGFloat ph = _pointAngle >= 0 && _pointAngle <= 180 ? _pointSize.height : -_pointSize.height; + pointX = (((_pointLocation-45)/90) * (fw - 2 *radius - _pointSize.width) + + radius + floor(_pointSize.width/2)); CGPathAddLineToPoint(path, nil, pointX-floor(_pointSize.width/2), 0); CGPathAddLineToPoint(path, nil, pointX, -ph); CGPathAddLineToPoint(path, nil, pointX+floor(_pointSize.width/2), 0); } - CGPathAddArcToPoint(path, nil, fw, 0, fw, RD(_radius), RD(_radius)); + CGPathAddArcToPoint(path, nil, fw, 0, fw, radius, radius); } @@ -129,12 +131,24 @@ - (void)addRightEdge:(CGSize)size lightSource:(NSInteger)lightSource toPath:(CGM reset:(BOOL)reset { CGFloat fw = size.width; CGFloat fh = size.height; + CGFloat pointY = 0; + CGFloat radius = RD(_radius); if (reset) { - CGPathMoveToPoint(path, nil, fw, RD(_radius)); + CGPathMoveToPoint(path, nil, fw, radius); } - CGPathAddArcToPoint(path, nil, fw, fh, fw-RD(_radius), fh, RD(_radius)); + if (_pointLocation >= 135 && _pointLocation < 225) { + CGFloat pw = _pointAngle >= 90 && _pointAngle <= 270 ? _pointSize.width : -_pointSize.width; + pointY = (((_pointLocation-135)/90) * (fh - 2 * radius - _pointSize.height) + + radius + floor(_pointSize.height/2)); + + CGPathAddLineToPoint(path, nil, fw, pointY-floor(_pointSize.height/2)); + CGPathAddLineToPoint(path, nil, fw+pw, pointY); + CGPathAddLineToPoint(path, nil, fw, pointY+floor(_pointSize.height/2)); + } + + CGPathAddArcToPoint(path, nil, fw, fh, fw-radius, fh, radius); } @@ -144,30 +158,31 @@ - (void)addBottomEdge:(CGSize)size lightSource:(NSInteger)lightSource toPath:(CG CGFloat fw = size.width; CGFloat fh = size.height; CGFloat pointX = 0; + CGFloat radius = RD(_radius); if (reset) { - CGPathMoveToPoint(path, nil, fw-RD(_radius), fh); + CGPathMoveToPoint(path, nil, fw-radius, fh); } - if (_pointLocation >= 225 && _pointLocation <= 315) { + if (_pointLocation >= 225 && _pointLocation < 315) { CGFloat ph; - if (_pointAngle >= 0 && _pointAngle < 180) { + if (_pointAngle >= 0 && _pointAngle <= 180) { ph = _pointSize.height; } else { ph = -_pointSize.height; } - pointX = fw - (((_pointLocation-225)/90) * fw); - CGPathAddArcToPoint(path, nil, fw-RD(_radius), fh, floor(fw/2), fh, RD(_radius)); + pointX = fw - (((_pointLocation-225)/90) * (fw - 2 *radius - _pointSize.width) + + radius + floor(_pointSize.width/2)); + CGPathAddLineToPoint(path, nil, pointX+floor(_pointSize.width/2), fh); CGPathAddLineToPoint(path, nil, pointX, fh-ph); CGPathAddLineToPoint(path, nil, pointX-floor(_pointSize.width/2), fh); - CGPathAddLineToPoint(path, nil, RD(_radius), fh); } - CGPathAddArcToPoint(path, nil, 0, fh, 0, fh-RD(_radius), RD(_radius)); + CGPathAddArcToPoint(path, nil, 0, fh, 0, fh-radius, radius); } @@ -175,16 +190,44 @@ - (void)addBottomEdge:(CGSize)size lightSource:(NSInteger)lightSource toPath:(CG - (void)addLeftEdge:(CGSize)size lightSource:(NSInteger)lightSource toPath:(CGMutablePathRef)path reset:(BOOL)reset { CGFloat fh = size.height; + CGFloat pointY = 0; + CGFloat radius = RD(_radius); if (reset) { - CGPathMoveToPoint(path, nil, 0, fh-RD(_radius)); + CGPathMoveToPoint(path, nil, 0, fh-radius); + } + + // Use a custom pointLocation with a value between 315 and 405 instead of 0-45 and 315-360 + // to ease calculations + CGFloat myPointLocation = _pointLocation; + if (myPointLocation >= 0 && myPointLocation < 45) { + myPointLocation += 360; + } + + if (myPointLocation >= 315 && myPointLocation < 405) { + + // Compute extension of arrow + CGFloat pw = _pointAngle >= 270 || _pointAngle <= 90 ? -_pointSize.width : _pointSize.width; + + // Compute location of arrow on line + // Do not place the arrow on the arcs at the corner! + pointY = fh - (((myPointLocation - 315) / 90) * (fh - 2 * radius - _pointSize.height) + + radius + _pointSize.height/2); + + // Draw the lines, first to the arrow... + CGPathAddLineToPoint(path, nil, 0, pointY+floor(_pointSize.height/2)); + // Then up to the point... + CGPathAddLineToPoint(path, nil, pw, pointY); + // And back again to the rectangle.. + CGPathAddLineToPoint(path, nil, 0, pointY-floor(_pointSize.height/2)); } + // Then continue the rest of the line if (lightSource >= 0 && lightSource <= 90) { - CGPathAddArcToPoint(path, nil, 0, 0, RD(_radius), 0, RD(_radius)); + CGPathAddArcToPoint(path, nil, 0, 0, radius, 0, radius); } else { - CGPathAddLineToPoint(path, nil, 0, RD(_radius)); + CGPathAddLineToPoint(path, nil, 0, radius); } } diff --git a/src/Three20Style/Sources/TTStyle.m b/src/Three20Style/Sources/TTStyle.m index bdb72213f2..a1d1976697 100644 --- a/src/Three20Style/Sources/TTStyle.m +++ b/src/Three20Style/Sources/TTStyle.m @@ -36,7 +36,8 @@ @implementation TTStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super init]) { + self = [super init]; + if (self) { _next = [next retain]; } @@ -46,7 +47,8 @@ - (id)initWithNext:(TTStyle*)next { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNext:nil]) { + self = [self initWithNext:nil]; + if (self) { } return self; diff --git a/src/Three20Style/Sources/TTStyleContext.m b/src/Three20Style/Sources/TTStyleContext.m index f09b628f17..9c1c8d8946 100644 --- a/src/Three20Style/Sources/TTStyleContext.m +++ b/src/Three20Style/Sources/TTStyleContext.m @@ -38,7 +38,8 @@ @implementation TTStyleContext /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _frame = CGRectZero; _contentFrame = CGRectZero; } diff --git a/src/Three20Style/Sources/TTStyleSheet.m b/src/Three20Style/Sources/TTStyleSheet.m index b799f5abee..5f1749f424 100644 --- a/src/Three20Style/Sources/TTStyleSheet.m +++ b/src/Three20Style/Sources/TTStyleSheet.m @@ -33,7 +33,8 @@ @implementation TTStyleSheet /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(didReceiveMemoryWarning:) diff --git a/src/Three20Style/Sources/TTStyledBoxFrame.m b/src/Three20Style/Sources/TTStyledBoxFrame.m index 2e06c26dae..43b58ef0f5 100644 --- a/src/Three20Style/Sources/TTStyledBoxFrame.m +++ b/src/Three20Style/Sources/TTStyledBoxFrame.m @@ -71,18 +71,22 @@ - (void)drawLayer:(TTStyleContext*)context withStyle:(TTStyle*)style { TTTextStyle* textStyle = (TTTextStyle*)style; UIFont* font = context.font; context.font = textStyle.font; + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGContextSaveGState(ctx); + if (textStyle.color) { - CGContextRef ctx = UIGraphicsGetCurrentContext(); - CGContextSaveGState(ctx); [textStyle.color setFill]; + } - [self drawSubframes]; + if (textStyle.shadowColor) { + CGSize offset = CGSizeMake(textStyle.shadowOffset.width, -textStyle.shadowOffset.height); + CGContextSetShadowWithColor(ctx, offset, 0, textStyle.shadowColor.CGColor); + } - CGContextRestoreGState(ctx); + [self drawSubframes]; - } else { - [self drawSubframes]; - } + CGContextRestoreGState(ctx); context.font = font; diff --git a/src/Three20Style/Sources/TTStyledButtonNode.m b/src/Three20Style/Sources/TTStyledButtonNode.m index 6454f74569..b5fc65c6fe 100644 --- a/src/Three20Style/Sources/TTStyledButtonNode.m +++ b/src/Three20Style/Sources/TTStyledButtonNode.m @@ -31,7 +31,8 @@ @implementation TTStyledButtonNode /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURL:(NSString*)URL { - if (self = [self initWithText:nil URL:URL next:nil]) { + self = [self initWithText:nil URL:URL next:nil]; + if (self) { } return self; @@ -40,7 +41,8 @@ - (id)initWithURL:(NSString*)URL { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURL:(NSString*)URL next:(TTStyledNode*)nextSibling { - if (self = [self initWithText:nil URL:URL next:nextSibling]) { + self = [self initWithText:nil URL:URL next:nextSibling]; + if (self) { } return self; @@ -49,7 +51,8 @@ - (id)initWithURL:(NSString*)URL next:(TTStyledNode*)nextSibling { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text URL:(NSString*)URL next:(TTStyledNode*)nextSibling { - if (self = [super initWithText:text next:nextSibling]) { + self = [super initWithText:text next:nextSibling]; + if (self) { self.URL = URL; } @@ -59,7 +62,8 @@ - (id)initWithText:(NSString*)text URL:(NSString*)URL next:(TTStyledNode*)nextSi /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithText:nil URL:nil next:nil]) { + self = [self initWithText:nil URL:nil next:nil]; + if (self) { } return self; diff --git a/src/Three20Style/Sources/TTStyledElement.m b/src/Three20Style/Sources/TTStyledElement.m index c389ccae05..01554ac4d2 100644 --- a/src/Three20Style/Sources/TTStyledElement.m +++ b/src/Three20Style/Sources/TTStyledElement.m @@ -38,7 +38,8 @@ @implementation TTStyledElement /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text { - if (self = [self initWithText:text next:nil]) { + self = [self initWithText:text next:nil]; + if (self) { } return self; @@ -47,7 +48,8 @@ - (id)initWithText:(NSString*)text { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text next:(TTStyledNode*)nextSibling { - if (self = [super initWithNextSibling:nextSibling]) { + self = [super initWithNextSibling:nextSibling]; + if (self) { if (nil != text) { [self addChild:[[[TTStyledTextNode alloc] initWithText:text] autorelease]]; } @@ -59,7 +61,8 @@ - (id)initWithText:(NSString*)text next:(TTStyledNode*)nextSibling { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithText:nil next:nil]) { + self = [self initWithText:nil next:nil]; + if (self) { } return self; diff --git a/src/Three20Style/Sources/TTStyledFrame.m b/src/Three20Style/Sources/TTStyledFrame.m index 0af921c913..fde079ca69 100644 --- a/src/Three20Style/Sources/TTStyledFrame.m +++ b/src/Three20Style/Sources/TTStyledFrame.m @@ -32,7 +32,8 @@ @implementation TTStyledFrame /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithElement:(TTStyledElement*)element { - if (self = [super init]) { + self = [super init]; + if (self) { _element = element; _bounds = CGRectZero; } diff --git a/src/Three20Style/Sources/TTStyledImageFrame.m b/src/Three20Style/Sources/TTStyledImageFrame.m index f939240565..6f5693f4aa 100644 --- a/src/Three20Style/Sources/TTStyledImageFrame.m +++ b/src/Three20Style/Sources/TTStyledImageFrame.m @@ -38,7 +38,8 @@ @implementation TTStyledImageFrame /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithElement:(TTStyledElement*)element node:(TTStyledImageNode*)node { - if (self = [super initWithElement:element]) { + self = [super initWithElement:element]; + if (self) { _imageNode = node; } diff --git a/src/Three20Style/Sources/TTStyledImageNode.m b/src/Three20Style/Sources/TTStyledImageNode.m index bc897bcb7c..e65ad8e120 100644 --- a/src/Three20Style/Sources/TTStyledImageNode.m +++ b/src/Three20Style/Sources/TTStyledImageNode.m @@ -37,7 +37,8 @@ @implementation TTStyledImageNode /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURL:(NSString*)URL { - if (self = [super initWithText:nil next:nil]) { + self = [super initWithText:nil next:nil]; + if (self) { self.URL = URL; } @@ -47,7 +48,8 @@ - (id)initWithURL:(NSString*)URL { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithURL:nil]) { + self = [self initWithURL:nil]; + if (self) { } return self; diff --git a/src/Three20Style/Sources/TTStyledLayout.m b/src/Three20Style/Sources/TTStyledLayout.m index 1400e1d997..8dd6032b41 100644 --- a/src/Three20Style/Sources/TTStyledLayout.m +++ b/src/Three20Style/Sources/TTStyledLayout.m @@ -51,12 +51,14 @@ @implementation TTStyledLayout @synthesize height = _height; @synthesize rootFrame = _rootFrame; @synthesize font = _font; +@synthesize textAlignment = _textAlignment; @synthesize invalidImages = _invalidImages; /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithRootNode:(TTStyledNode*)rootNode { - if (self = [super init]) { + self = [super init]; + if (self) { _rootNode = rootNode; } @@ -66,7 +68,8 @@ - (id)initWithRootNode:(TTStyledNode*)rootNode { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithX:(CGFloat)x width:(CGFloat)width height:(CGFloat)height { - if (self = [super init]) { + self = [super init]; + if (self) { _x = x; _minX = x; _width = width; @@ -177,6 +180,20 @@ - (void)offsetFrame:(TTStyledFrame*)frame by:(CGFloat)y { } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)offsetFrame:(TTStyledFrame*)frame byX:(CGFloat)x { + frame.x += x; + + if ([frame isKindOfClass:[TTStyledInlineFrame class]]) { + TTStyledInlineFrame* inlineFrame = (TTStyledInlineFrame*)frame; + TTStyledFrame* child = inlineFrame.firstChildFrame; + while (child) { + [self offsetFrame:child byX:x]; + child = child.nextFrame; + } + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)expandLineWidth:(CGFloat)width { @@ -340,18 +357,44 @@ - (void)breakLine { // Vertically align all frames on the current line if (_lineFirstFrame.nextFrame) { - TTStyledFrame* frame = _lineFirstFrame; + TTStyledFrame* frame; + + // Find the descender that descends the farthest below the baseline. + // font.descender is a negative number if the descender descends below + // the baseline (as most descenders do), but can also be a positive + // number for a descender above the baseline. + CGFloat lowestDescender = MAXFLOAT; + frame = _lineFirstFrame; + while (frame) { + UIFont* font = frame.font ? frame.font : _font; + lowestDescender = MIN(lowestDescender, font.descender); + frame = frame.nextFrame; + } + + frame = _lineFirstFrame; while (frame) { // Align to the text baseline // XXXjoe Support top, bottom, and center alignment also if (frame.height < _lineHeight) { UIFont* font = frame.font ? frame.font : _font; - [self offsetFrame:frame by:(_lineHeight - (frame.height - font.descender))]; + [self offsetFrame:frame by:(_lineHeight - (frame.height - (lowestDescender - font.descender)))]; } frame = frame.nextFrame; } } + // Horizontally align all frames on current line if required + if (_textAlignment != UITextAlignmentLeft) { + CGFloat remainingSpace = _width - _lineWidth; + CGFloat offset = _textAlignment == UITextAlignmentCenter ? remainingSpace/2 : remainingSpace; + + TTStyledFrame* frame = _lineFirstFrame; + while (frame) { + [self offsetFrame:frame byX:offset]; + frame = frame.nextFrame; + } + } + _height += _lineHeight; [self checkFloats]; @@ -698,7 +741,7 @@ - (void)layoutText:(TTStyledTextNode*)textNode container:(TTStyledElement*)eleme stringIndex - frameStart)] sizeWithFont:_font].width; [self addFrameForText:line element:element node:textNode width:frameWidth - height:_lineHeight ? _lineHeight : [_font ttLineHeight]]; + height:[_font ttLineHeight]]; } if (_lineWidth) { @@ -720,7 +763,7 @@ - (void)layoutText:(TTStyledTextNode*)textNode container:(TTStyledElement*)eleme frameWidth = [[text substringWithRange:NSMakeRange(frameStart, stringIndex - frameStart)] sizeWithFont:_font].width; [self addFrameForText:line element:element node:textNode width:frameWidth - height:_lineHeight ? _lineHeight : [_font ttLineHeight]]; + height:[_font ttLineHeight]]; lineStartIndex = lineRange.location + lineRange.length; frameStart = stringIndex; @@ -736,7 +779,7 @@ - (void)layoutText:(TTStyledTextNode*)textNode container:(TTStyledElement*)eleme frameWidth = [[text substringWithRange:NSMakeRange(frameStart, stringIndex - frameStart)] sizeWithFont:_font].width; [self addFrameForText:line element:element node:textNode width:frameWidth - height:_lineHeight ? _lineHeight : [_font ttLineHeight]]; + height:[_font ttLineHeight]]; } if (_lineWidth) { diff --git a/src/Three20Style/Sources/TTStyledLinkNode.m b/src/Three20Style/Sources/TTStyledLinkNode.m index 5e3bb8887d..e0e635fb83 100644 --- a/src/Three20Style/Sources/TTStyledLinkNode.m +++ b/src/Three20Style/Sources/TTStyledLinkNode.m @@ -31,7 +31,8 @@ @implementation TTStyledLinkNode /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURL:(NSString*)URL { - if (self = [self initWithText:nil URL:URL next:nil]) { + self = [self initWithText:nil URL:URL next:nil]; + if (self) { } return self; @@ -40,7 +41,8 @@ - (id)initWithURL:(NSString*)URL { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURL:(NSString*)URL next:(TTStyledNode*)nextSibling { - if (self = [self initWithText:nil URL:URL next:nextSibling]) { + self = [self initWithText:nil URL:URL next:nextSibling]; + if (self) { } return self; @@ -49,7 +51,8 @@ - (id)initWithURL:(NSString*)URL next:(TTStyledNode*)nextSibling { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text URL:(NSString*)URL next:(TTStyledNode*)nextSibling { - if (self = [super initWithText:text next:nextSibling]) { + self = [super initWithText:text next:nextSibling]; + if (self) { self.URL = URL; } @@ -59,7 +62,8 @@ - (id)initWithText:(NSString*)text URL:(NSString*)URL next:(TTStyledNode*)nextSi /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithText:nil URL:nil next:nil]) { + self = [self initWithText:nil URL:nil next:nil]; + if (self) { } return self; diff --git a/src/Three20Style/Sources/TTStyledNode.m b/src/Three20Style/Sources/TTStyledNode.m index da2c9f449e..71f5c95373 100644 --- a/src/Three20Style/Sources/TTStyledNode.m +++ b/src/Three20Style/Sources/TTStyledNode.m @@ -31,7 +31,8 @@ @implementation TTStyledNode /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNextSibling:(TTStyledNode*)nextSibling { - if (self = [super init]) { + self = [super init]; + if (self) { self.nextSibling = nextSibling; } @@ -41,7 +42,8 @@ - (id)initWithNextSibling:(TTStyledNode*)nextSibling { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNextSibling:nil]) { + self = [self initWithNextSibling:nil]; + if (self) { } return self; diff --git a/src/Three20Style/Sources/TTStyledText.m b/src/Three20Style/Sources/TTStyledText.m index 632afde0c2..14e4aa449f 100644 --- a/src/Three20Style/Sources/TTStyledText.m +++ b/src/Three20Style/Sources/TTStyledText.m @@ -57,6 +57,7 @@ @implementation TTStyledText @synthesize rootNode = _rootNode; @synthesize font = _font; +@synthesize textAlignment = _textAlignment; @synthesize width = _width; @synthesize height = _height; @synthesize invalidImages = _invalidImages; @@ -65,7 +66,8 @@ @implementation TTStyledText /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNode:(TTStyledNode*)rootNode { - if (self = [super init]) { + self = [super init]; + if (self) { _rootNode = [rootNode retain]; } @@ -299,6 +301,14 @@ - (void)setFont:(UIFont*)font { } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)setTextAlignment:(UITextAlignment)textAlignment { + if (textAlignment != _textAlignment) { + _textAlignment = textAlignment; + [self setNeedsLayout]; + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)setWidth:(CGFloat)width { @@ -327,6 +337,7 @@ - (void)layoutFrames { TTStyledLayout* layout = [[TTStyledLayout alloc] initWithRootNode:_rootNode]; layout.width = _width; layout.font = _font; + layout.textAlignment = _textAlignment; [layout layout:_rootNode]; [_rootFrame release]; diff --git a/src/Three20Style/Sources/TTStyledTextFrame.m b/src/Three20Style/Sources/TTStyledTextFrame.m index 5e74e8f6a5..b6950936e5 100644 --- a/src/Three20Style/Sources/TTStyledTextFrame.m +++ b/src/Three20Style/Sources/TTStyledTextFrame.m @@ -32,7 +32,8 @@ @implementation TTStyledTextFrame /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text element:(TTStyledElement*)element node:(TTStyledTextNode*)node { - if (self = [super initWithElement:element]) { + self = [super initWithElement:element]; + if (self) { _text = [text copy]; _node = node; } diff --git a/src/Three20Style/Sources/TTStyledTextNode.m b/src/Three20Style/Sources/TTStyledTextNode.m index 4df6d2c34b..0ebd23967c 100644 --- a/src/Three20Style/Sources/TTStyledTextNode.m +++ b/src/Three20Style/Sources/TTStyledTextNode.m @@ -30,7 +30,8 @@ @implementation TTStyledTextNode /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text { - if (self = [self initWithText:text next:nil]) { + self = [self initWithText:text next:nil]; + if (self) { } return self; @@ -39,7 +40,8 @@ - (id)initWithText:(NSString*)text { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text next:(TTStyledNode*)nextSibling { - if (self = [super initWithNextSibling:nextSibling]) { + self = [super initWithNextSibling:nextSibling]; + if (self) { self.text = text; } diff --git a/src/Three20Style/Sources/TTStyledTextParser.m b/src/Three20Style/Sources/TTStyledTextParser.m index b4c6bea2ca..388cf9a300 100644 --- a/src/Three20Style/Sources/TTStyledTextParser.m +++ b/src/Three20Style/Sources/TTStyledTextParser.m @@ -114,8 +114,22 @@ - (void)parseURLs:(NSString*)string { while (stringIndex < string.length) { NSRange searchRange = NSMakeRange(stringIndex, string.length - stringIndex); - NSRange startRange = [string rangeOfString:@"http://" options:NSCaseInsensitiveSearch + NSRange httpRange = [string rangeOfString:@"http://" options:NSCaseInsensitiveSearch + range:searchRange]; + NSRange httpsRange = [string rangeOfString:@"https://" options:NSCaseInsensitiveSearch range:searchRange]; + + NSRange startRange; + if (httpRange.location == NSNotFound) { + startRange = httpsRange; + + } else if (httpsRange.location == NSNotFound) { + startRange = httpRange; + + } else { + startRange = (httpRange.location < httpsRange.location) ? httpRange : httpsRange; + } + if (startRange.location == NSNotFound) { NSString* text = [string substringWithRange:searchRange]; TTStyledTextNode* node = [[[TTStyledTextNode alloc] initWithText:text] autorelease]; @@ -256,6 +270,17 @@ - (NSData *) parser:(NSXMLParser *)parser } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)parseText:(NSString*)string URLs:(BOOL)shouldParseURLs { + if (shouldParseURLs) { + [self parseURLs:string]; + } else { + TTStyledTextNode* node = [[[TTStyledTextNode alloc] initWithText:string] autorelease]; + [self addNode:node]; + } +} + + /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark - @@ -278,7 +303,7 @@ - (void)parseText:(NSString*)string { NSCharacterSet* newLines = [NSCharacterSet newlineCharacterSet]; NSInteger stringIndex = 0; NSInteger length = string.length; - + while (1) { NSRange searchRange = NSMakeRange(stringIndex, length - stringIndex); NSRange range = [string rangeOfCharacterFromSet:newLines options:0 range:searchRange]; @@ -286,28 +311,24 @@ - (void)parseText:(NSString*)string { // Find all text before the line break and parse it NSRange textRange = NSMakeRange(stringIndex, range.location - stringIndex); NSString* substr = [string substringWithRange:textRange]; - [self parseURLs:substr]; - + [self parseText:substr URLs:_parseURLs]; + // Add a line break node after the text TTStyledLineBreakNode* br = [[[TTStyledLineBreakNode alloc] init] autorelease]; [self addNode:br]; - + stringIndex = stringIndex + substr.length + 1; - + } else { // Find all text until the end of hte string and parse it NSString* substr = [string substringFromIndex:stringIndex]; - [self parseURLs:substr]; + [self parseText:substr URLs:_parseURLs]; break; } } - - } else if (_parseURLs) { - [self parseURLs:string]; - + } else { - TTStyledTextNode* node = [[[TTStyledTextNode alloc] initWithText:string] autorelease]; - [self addNode:node]; + [self parseText:string URLs:_parseURLs]; } } diff --git a/src/Three20Style/Sources/TTTextStyle.m b/src/Three20Style/Sources/TTTextStyle.m index 3764bae821..a2e498df3f 100644 --- a/src/Three20Style/Sources/TTTextStyle.m +++ b/src/Three20Style/Sources/TTTextStyle.m @@ -43,7 +43,8 @@ @implementation TTTextStyle /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNext:(TTStyle*)next { - if (self = [super initWithNext:next]) { + self = [super initWithNext:next]; + if (self) { _shadowOffset = CGSizeZero; _numberOfLines = 1; _textAlignment = UITextAlignmentCenter; diff --git a/src/Three20Style/Three20Style.xcodeproj/project.pbxproj b/src/Three20Style/Three20Style.xcodeproj/project.pbxproj index 993219ee6d..97855e1b69 100755 --- a/src/Three20Style/Three20Style.xcodeproj/project.pbxproj +++ b/src/Three20Style/Three20Style.xcodeproj/project.pbxproj @@ -1201,7 +1201,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -1223,7 +1222,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; HEADER_SEARCH_PATHS = .; PREBINDING = NO; SDKROOT = iphoneos; diff --git a/src/Three20UI/Headers/TTLauncherItem.h b/src/Three20UI/Headers/TTLauncherItem.h index f17e508238..7dfd6caa08 100644 --- a/src/Three20UI/Headers/TTLauncherItem.h +++ b/src/Three20UI/Headers/TTLauncherItem.h @@ -30,6 +30,7 @@ NSString* _badgeValue; BOOL _canDelete; + id _userInfo; } @property (nonatomic, assign) TTLauncherView* launcher; @@ -40,6 +41,7 @@ @property (nonatomic) NSInteger badgeNumber; @property (nonatomic, copy) NSString* badgeValue; @property (nonatomic) BOOL canDelete; +@property (nonatomic, retain) id userInfo; - (id)initWithTitle:(NSString*)title image:(NSString*)image URL:(NSString*)URL; - (id)initWithTitle:(NSString*)title image:(NSString*)image URL:(NSString*)URL diff --git a/src/Three20UI/Headers/TTLauncherView.h b/src/Three20UI/Headers/TTLauncherView.h index 6b6774afe2..391fc7fa6d 100644 --- a/src/Three20UI/Headers/TTLauncherView.h +++ b/src/Three20UI/Headers/TTLauncherView.h @@ -49,6 +49,7 @@ BOOL _editing; BOOL _springing; + BOOL _editable; id _delegate; } @@ -68,6 +69,7 @@ @property (nonatomic, copy) NSString* prompt; @property (nonatomic, readonly) BOOL editing; +@property (nonatomic, assign) BOOL editable; - (void)addItem:(TTLauncherItem*)item animated:(BOOL)animated; diff --git a/src/Three20UI/Headers/TTStyledTextLabel.h b/src/Three20UI/Headers/TTStyledTextLabel.h index 78f3c1da62..b79e460948 100644 --- a/src/Three20UI/Headers/TTStyledTextLabel.h +++ b/src/Three20UI/Headers/TTStyledTextLabel.h @@ -68,7 +68,7 @@ @property (nonatomic, retain) UIColor* highlightedTextColor; /** - * The alignment of the text. (NOT YET IMPLEMENTED) + * The alignment of the text. */ @property (nonatomic) UITextAlignment textAlignment; diff --git a/src/Three20UI/Headers/TTTableViewController.h b/src/Three20UI/Headers/TTTableViewController.h index 0a0e819b8c..389a8958bb 100644 --- a/src/Three20UI/Headers/TTTableViewController.h +++ b/src/Three20UI/Headers/TTTableViewController.h @@ -39,6 +39,7 @@ BOOL _variableHeightRows; BOOL _showTableShadows; + BOOL _clearsSelectionOnViewWillAppear; id _dataSource; id _tableDelegate; @@ -88,6 +89,12 @@ */ @property (nonatomic) BOOL showTableShadows; +/** + * A Boolean value indicating if the controller clears the selection when the table appears. + * Default is YES. + */ +@property(nonatomic) BOOL clearsSelectionOnViewWillAppear; + /** * Initializes and returns a controller having the given style. */ diff --git a/src/Three20UI/Headers/TTWebController.h b/src/Three20UI/Headers/TTWebController.h index eab84c9a27..86f83f217f 100644 --- a/src/Three20UI/Headers/TTWebController.h +++ b/src/Three20UI/Headers/TTWebController.h @@ -53,7 +53,7 @@ @property (nonatomic, retain) UIView* headerView; /** - * The web controller delegate, currently does nothing. + * The web controller delegate */ @property (nonatomic, assign) id delegate; @@ -71,10 +71,18 @@ @end - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * The web controller delegate, similar to UIWebViewDelegate, but prefixed with controller + */ @protocol TTWebControllerDelegate -// XXXjoe Need to make this similar to UIWebViewDelegate + +@optional +- (BOOL)webController:(TTWebController *)controller webView:(UIWebView *)webView + shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType; +- (void)webController:(TTWebController *)controller webViewDidStartLoad:(UIWebView *)webView; +- (void)webController:(TTWebController *)controller webViewDidFinishLoad:(UIWebView *)webView; +- (void)webController:(TTWebController *)controller webView:(UIWebView *)webView + didFailLoadWithError:(NSError *)error; + @end diff --git a/src/Three20UI/Headers/Three20UI.h b/src/Three20UI/Headers/Three20UI.h index 161d57c45c..55771c6c3b 100644 --- a/src/Three20UI/Headers/Three20UI.h +++ b/src/Three20UI/Headers/Three20UI.h @@ -89,7 +89,7 @@ #import "Three20UI/TTSectionedDataSource.h" #import "Three20UI/TTTableHeaderView.h" #import "Three20UI/TTTableFooterInfiniteScrollView.h" -#import "THree20UI/TTTableHeaderDragRefreshView.h" +#import "Three20UI/TTTableHeaderDragRefreshView.h" #import "Three20UI/TTTableViewCell.h" // Table Items diff --git a/src/Three20UI/Sources/TTActionSheetController.m b/src/Three20UI/Sources/TTActionSheetController.m index 76d59a4ccf..476d37bca9 100644 --- a/src/Three20UI/Sources/TTActionSheetController.m +++ b/src/Three20UI/Sources/TTActionSheetController.m @@ -36,7 +36,8 @@ @implementation TTActionSheetController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _URLs = [[NSMutableArray alloc] init]; } @@ -46,7 +47,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title delegate:(id)delegate { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { _delegate = delegate; if (nil != title) { @@ -60,7 +62,8 @@ - (id)initWithTitle:(NSString*)title delegate:(id)delegate { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title { - if (self = [self initWithTitle:title delegate:nil]) { + self = [self initWithTitle:title delegate:nil]; + if (self) { } return self; @@ -69,7 +72,8 @@ - (id)initWithTitle:(NSString*)title { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithTitle:nil delegate:nil]) { + self = [self initWithTitle:nil delegate:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTActivityLabel.m b/src/Three20UI/Sources/TTActivityLabel.m index 91ba603d53..a5f1c4924a 100644 --- a/src/Three20UI/Sources/TTActivityLabel.m +++ b/src/Three20UI/Sources/TTActivityLabel.m @@ -51,7 +51,8 @@ @implementation TTActivityLabel /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame style:(TTActivityLabelStyle)style text:(NSString*)text { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _style = style; _progress = 0; _smoothesProgress = NO; @@ -139,7 +140,8 @@ - (id)initWithFrame:(CGRect)frame style:(TTActivityLabelStyle)style text:(NSStri /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame style:(TTActivityLabelStyle)style { - if (self = [self initWithFrame:frame style:style text:nil]) { + self = [self initWithFrame:frame style:style text:nil]; + if (self) { } return self; @@ -148,7 +150,8 @@ - (id)initWithFrame:(CGRect)frame style:(TTActivityLabelStyle)style { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(TTActivityLabelStyle)style { - if (self = [self initWithFrame:CGRectZero style:style text:nil]) { + self = [self initWithFrame:CGRectZero style:style text:nil]; + if (self) { } return self; @@ -157,7 +160,8 @@ - (id)initWithStyle:(TTActivityLabelStyle)style { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [self initWithFrame:frame style:TTActivityLabelStyleWhiteBox text:nil]) { + self = [self initWithFrame:frame style:TTActivityLabelStyleWhiteBox text:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTAlertViewController.m b/src/Three20UI/Sources/TTAlertViewController.m index 9e63a39e31..02e732a095 100644 --- a/src/Three20UI/Sources/TTAlertViewController.m +++ b/src/Three20UI/Sources/TTAlertViewController.m @@ -36,7 +36,8 @@ @implementation TTAlertViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _URLs = [[NSMutableArray alloc] init]; } @@ -46,7 +47,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)delegate { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { _delegate = delegate; if (nil != title) { @@ -64,7 +66,8 @@ - (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)del /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title message:(NSString*)message { - if (self = [self initWithTitle:title message:message delegate:nil]) { + self = [self initWithTitle:title message:message delegate:nil]; + if (self) { } return self; @@ -73,7 +76,8 @@ - (id)initWithTitle:(NSString*)title message:(NSString*)message { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithTitle:nil message:nil delegate:nil]) { + self = [self initWithTitle:nil message:nil delegate:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTButton.m b/src/Three20UI/Sources/TTButton.m index 9d8282e342..5725c8baec 100644 --- a/src/Three20UI/Sources/TTButton.m +++ b/src/Three20UI/Sources/TTButton.m @@ -51,7 +51,8 @@ @implementation TTButton /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.backgroundColor = [UIColor clearColor]; self.contentMode = UIViewContentModeRedraw; } diff --git a/src/Three20UI/Sources/TTButtonBar.m b/src/Three20UI/Sources/TTButtonBar.m index 9ebcd391f1..75f132f6db 100644 --- a/src/Three20UI/Sources/TTButtonBar.m +++ b/src/Three20UI/Sources/TTButtonBar.m @@ -39,7 +39,8 @@ @implementation TTButtonBar /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _buttons = [[NSMutableArray alloc] init]; self.buttonStyle = @"toolbarButton:"; diff --git a/src/Three20UI/Sources/TTButtonContent.m b/src/Three20UI/Sources/TTButtonContent.m index 864c5c9ec9..cd984b340e 100644 --- a/src/Three20UI/Sources/TTButtonContent.m +++ b/src/Three20UI/Sources/TTButtonContent.m @@ -42,7 +42,8 @@ @implementation TTButtonContent /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithButton:(TTButton*)button { - if (self = [super init]) { + self = [super init]; + if (self) { _button = button; } return self; diff --git a/src/Three20UI/Sources/TTErrorView.m b/src/Three20UI/Sources/TTErrorView.m index c5f2a8c7bf..d8af483f8b 100644 --- a/src/Three20UI/Sources/TTErrorView.m +++ b/src/Three20UI/Sources/TTErrorView.m @@ -39,7 +39,8 @@ @implementation TTErrorView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title subtitle:(NSString*)subtitle image:(UIImage*)image { - if (self = [self init]) { + self = [self init]; + if (self) { self.title = title; self.subtitle = subtitle; self.image = image; @@ -51,7 +52,8 @@ - (id)initWithTitle:(NSString*)title subtitle:(NSString*)subtitle image:(UIImage /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _imageView = [[UIImageView alloc] init]; _imageView.contentMode = UIViewContentModeCenter; [self addSubview:_imageView]; diff --git a/src/Three20UI/Sources/TTExtensionInfoController.m b/src/Three20UI/Sources/TTExtensionInfoController.m index e6a5892b25..3f24d4425d 100644 --- a/src/Three20UI/Sources/TTExtensionInfoController.m +++ b/src/Three20UI/Sources/TTExtensionInfoController.m @@ -38,7 +38,8 @@ @implementation TTExtensionInfoController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithExtensionID:(NSString*)identifier { - if (self = [super initWithNibName:nil bundle:nil]) { + self = [super initWithNibName:nil bundle:nil]; + if (self) { self.title = @"Extension Info"; self.tableViewStyle = UITableViewStyleGrouped; diff --git a/src/Three20UI/Sources/TTExtensionsController.m b/src/Three20UI/Sources/TTExtensionsController.m index f6d63d5ec3..2742927578 100644 --- a/src/Three20UI/Sources/TTExtensionsController.m +++ b/src/Three20UI/Sources/TTExtensionsController.m @@ -40,7 +40,8 @@ @implementation TTExtensionsController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.title = @"Extensions"; self.variableHeightRows = YES; diff --git a/src/Three20UI/Sources/TTImageView.m b/src/Three20UI/Sources/TTImageView.m index 1c34db6b81..3457224814 100644 --- a/src/Three20UI/Sources/TTImageView.m +++ b/src/Three20UI/Sources/TTImageView.m @@ -50,7 +50,8 @@ @implementation TTImageView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _autoresizesToImage = NO; } return self; diff --git a/src/Three20UI/Sources/TTLabel.m b/src/Three20UI/Sources/TTLabel.m index 3a8d3e02e7..3d4af53018 100644 --- a/src/Three20UI/Sources/TTLabel.m +++ b/src/Three20UI/Sources/TTLabel.m @@ -37,7 +37,8 @@ @implementation TTLabel /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithText:(NSString*)text { - if (self = [self init]) { + self = [self init]; + if (self) { self.text = text; } return self; @@ -46,7 +47,8 @@ - (id)initWithText:(NSString*)text { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _text = nil; _font = nil; } diff --git a/src/Three20UI/Sources/TTLauncherButton.m b/src/Three20UI/Sources/TTLauncherButton.m index c61c138fb3..7ed90725a5 100644 --- a/src/Three20UI/Sources/TTLauncherButton.m +++ b/src/Three20UI/Sources/TTLauncherButton.m @@ -57,7 +57,8 @@ @implementation TTLauncherButton /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithItem:(TTLauncherItem*)item { - if (self = [self init]) { + self = [self init]; + if (self) { _item = [item retain]; NSString* title = [[NSBundle mainBundle] localizedStringForKey:item.title value:nil table:nil]; @@ -80,7 +81,8 @@ - (id)initWithItem:(TTLauncherItem*)item { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.isVertical = YES; } diff --git a/src/Three20UI/Sources/TTLauncherItem.m b/src/Three20UI/Sources/TTLauncherItem.m index 56d576a9fe..23d3eda49c 100644 --- a/src/Three20UI/Sources/TTLauncherItem.m +++ b/src/Three20UI/Sources/TTLauncherItem.m @@ -32,6 +32,7 @@ @implementation TTLauncherItem @synthesize style = _style; @synthesize badgeValue = _badgeValue; @synthesize canDelete = _canDelete; +@synthesize userInfo = _userInfo; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -42,7 +43,8 @@ @implementation TTLauncherItem /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title image:(NSString*)image URL:(NSString*)URL { - if (self = [self initWithTitle:title image:image URL:URL canDelete:NO]) { + self = [self initWithTitle:title image:image URL:URL canDelete:NO]; + if (self) { } return self; @@ -52,7 +54,8 @@ - (id)initWithTitle:(NSString*)title image:(NSString*)image URL:(NSString*)URL { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title image:(NSString*)image URL:(NSString*)URL canDelete:(BOOL)canDelete { - if (self = [super init]) { + self = [super init]; + if (self) { _canDelete = canDelete; self.title = title; @@ -70,6 +73,7 @@ - (void)dealloc { TT_RELEASE_SAFELY(_image); TT_RELEASE_SAFELY(_URL); TT_RELEASE_SAFELY(_style); + TT_RELEASE_SAFELY(_userInfo); [super dealloc]; } @@ -83,7 +87,8 @@ - (void)dealloc { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super init]) { + self = [super init]; + if (self) { self.title = [decoder decodeObjectForKey:@"title"]; self.image = [decoder decodeObjectForKey:@"image"]; self.URL = [decoder decodeObjectForKey:@"URL"]; diff --git a/src/Three20UI/Sources/TTLauncherView.m b/src/Three20UI/Sources/TTLauncherView.m index f8095aa7bf..5356169632 100644 --- a/src/Three20UI/Sources/TTLauncherView.m +++ b/src/Three20UI/Sources/TTLauncherView.m @@ -68,11 +68,12 @@ @implementation TTLauncherView @synthesize prompt = _prompt; @synthesize editing = _editing; @synthesize delegate = _delegate; - +@synthesize editable = _editable; /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _scrollView = [[TTLauncherScrollView alloc] initWithFrame: CGRectMake(0, 0, self.width, self.height - kPagerHeight)]; _scrollView.delegate = self; @@ -97,6 +98,7 @@ - (id)initWithFrame:(CGRect)frame { self.autoresizesSubviews = YES; self.columnCount = kDefaultColumnCount; + self.editable = YES; } return self; @@ -482,6 +484,9 @@ - (void)buttonTouchedUpOutside:(TTLauncherButton*)button { /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)buttonTouchedDown:(TTLauncherButton*)button withEvent:(UIEvent*)event { + if (!self.editable) + return; + if (_editing) { if (!_dragButton) { [self startDraggingButton:button withEvent:event]; diff --git a/src/Three20UI/Sources/TTLink.m b/src/Three20UI/Sources/TTLink.m index c79a0847e0..cf99fc9c9d 100644 --- a/src/Three20UI/Sources/TTLink.m +++ b/src/Three20UI/Sources/TTLink.m @@ -42,7 +42,8 @@ @implementation TTLink /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.userInteractionEnabled = NO; [self addTarget: self action: @selector(linkTouched) forControlEvents:UIControlEventTouchUpInside]; diff --git a/src/Three20UI/Sources/TTListDataSource.m b/src/Three20UI/Sources/TTListDataSource.m index 2353fa3b96..4702d5e1ca 100644 --- a/src/Three20UI/Sources/TTListDataSource.m +++ b/src/Three20UI/Sources/TTListDataSource.m @@ -33,7 +33,8 @@ @implementation TTListDataSource /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithItems:(NSArray*)items { - if (self = [self init]) { + self = [self init]; + if (self) { _items = [items mutableCopy]; } diff --git a/src/Three20UI/Sources/TTMessageController.m b/src/Three20UI/Sources/TTMessageController.m index b2a117cba3..5075cd1129 100644 --- a/src/Three20UI/Sources/TTMessageController.m +++ b/src/Three20UI/Sources/TTMessageController.m @@ -61,7 +61,8 @@ @implementation TTMessageController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _fields = [[NSArray alloc] initWithObjects: [[[TTMessageRecipientField alloc] initWithTitle: TTLocalizedString(@"To:", @"") required: YES] autorelease], @@ -90,7 +91,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithRecipients:(NSArray*)recipients { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { _initialRecipients = [recipients retain]; } diff --git a/src/Three20UI/Sources/TTMessageField.m b/src/Three20UI/Sources/TTMessageField.m index f00fca895b..58e3414a68 100644 --- a/src/Three20UI/Sources/TTMessageField.m +++ b/src/Three20UI/Sources/TTMessageField.m @@ -31,7 +31,8 @@ @implementation TTMessageField /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title required:(BOOL)required { - if (self = [self init]) { + self = [self init]; + if (self) { _title = [title copy]; _required = required; } diff --git a/src/Three20UI/Sources/TTModelViewController.m b/src/Three20UI/Sources/TTModelViewController.m index 2deaa18bbb..cfbf1c8862 100644 --- a/src/Three20UI/Sources/TTModelViewController.m +++ b/src/Three20UI/Sources/TTModelViewController.m @@ -40,7 +40,8 @@ @implementation TTModelViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _flags.isViewInvalid = YES; } @@ -50,7 +51,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTNavigatorWindow.m b/src/Three20UI/Sources/TTNavigatorWindow.m index 23c0a1577c..670a145b81 100644 --- a/src/Three20UI/Sources/TTNavigatorWindow.m +++ b/src/Three20UI/Sources/TTNavigatorWindow.m @@ -31,7 +31,7 @@ @implementation TTNavigatorWindow /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { - if (event.type == UIEventSubtypeMotionShake + if (UIEventSubtypeMotionShake == motion && [TTNavigator navigator].supportsShakeToReload) { // If you're going to use a custom navigator implementation, you need to ensure that you // implement the reload method. If you're inheriting from TTNavigator, then you're fine. diff --git a/src/Three20UI/Sources/TTPageControl.m b/src/Three20UI/Sources/TTPageControl.m index 16b889f02f..9c33eff5c3 100644 --- a/src/Three20UI/Sources/TTPageControl.m +++ b/src/Three20UI/Sources/TTPageControl.m @@ -42,7 +42,8 @@ @implementation TTPageControl /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.backgroundColor = [UIColor clearColor]; self.dotStyle = @"pageDot:"; self.hidesForSinglePage = NO; diff --git a/src/Three20UI/Sources/TTPhotoView.m b/src/Three20UI/Sources/TTPhotoView.m index c7cbddae7a..432be5f974 100644 --- a/src/Three20UI/Sources/TTPhotoView.m +++ b/src/Three20UI/Sources/TTPhotoView.m @@ -57,7 +57,8 @@ @implementation TTPhotoView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _photoVersion = TTPhotoVersionNone; self.clipsToBounds = NO; } @@ -279,7 +280,16 @@ - (void)setHidesCaption:(BOOL)hidesCaption { /////////////////////////////////////////////////////////////////////////////////////////////////// - (BOOL)loadPreview:(BOOL)fromNetwork { - if (![self loadVersion:TTPhotoVersionLarge fromNetwork:NO]) { + BOOL keepTrying = YES; + // Trying to load the large image first causes scrolling to stall something + // fierce when using local images on older iPhones since the large image + // *always* starts to load in time for this first call to succeed. So we + // skip straight to attempting to load the small version unless we're loading + // off the network. + if (fromNetwork) { + keepTrying = [self loadVersion:TTPhotoVersionLarge fromNetwork:NO]; + } + if (keepTrying) { if (![self loadVersion:TTPhotoVersionSmall fromNetwork:NO]) { if (![self loadVersion:TTPhotoVersionThumbnail fromNetwork:fromNetwork]) { return NO; diff --git a/src/Three20UI/Sources/TTPhotoViewController.m b/src/Three20UI/Sources/TTPhotoViewController.m index 32eb21650b..29c424d47f 100644 --- a/src/Three20UI/Sources/TTPhotoViewController.m +++ b/src/Three20UI/Sources/TTPhotoViewController.m @@ -70,7 +70,8 @@ @implementation TTPhotoViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle: @@ -95,7 +96,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithPhoto:(id)photo { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { self.centerPhoto = photo; } @@ -105,7 +107,8 @@ - (id)initWithPhoto:(id)photo { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithPhotoSource:(id)photoSource { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { self.photoSource = photoSource; } @@ -115,7 +118,8 @@ - (id)initWithPhotoSource:(id)photoSource { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; @@ -235,12 +239,7 @@ - (void)updateChrome { /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)updateToolbarWithOrientation:(UIInterfaceOrientation)interfaceOrientation { - if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) { - _toolbar.height = TT_TOOLBAR_HEIGHT; - - } else { - _toolbar.height = TT_LANDSCAPE_TOOLBAR_HEIGHT+1; - } + _toolbar.height = TTToolbarHeight(); _toolbar.top = self.view.height - _toolbar.height; } diff --git a/src/Three20UI/Sources/TTPickerTextField.m b/src/Three20UI/Sources/TTPickerTextField.m index a0a3f9e108..f3e197427a 100644 --- a/src/Three20UI/Sources/TTPickerTextField.m +++ b/src/Three20UI/Sources/TTPickerTextField.m @@ -52,7 +52,8 @@ @implementation TTPickerTextField /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _cellViews = [[NSMutableArray alloc] init]; _lineCount = 1; _cursorOrigin = CGPointZero; diff --git a/src/Three20UI/Sources/TTPickerViewCell.m b/src/Three20UI/Sources/TTPickerViewCell.m index eab98b4f37..4a237e87c2 100644 --- a/src/Three20UI/Sources/TTPickerViewCell.m +++ b/src/Three20UI/Sources/TTPickerViewCell.m @@ -39,7 +39,8 @@ @implementation TTPickerViewCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _labelView = [[UILabel alloc] init]; _labelView.backgroundColor = [UIColor clearColor]; _labelView.textColor = TTSTYLEVAR(textColor); diff --git a/src/Three20UI/Sources/TTPopupViewController.m b/src/Three20UI/Sources/TTPopupViewController.m index 9d9689a879..285edb8793 100644 --- a/src/Three20UI/Sources/TTPopupViewController.m +++ b/src/Three20UI/Sources/TTPopupViewController.m @@ -28,7 +28,8 @@ @implementation TTPopupViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _statusBarStyle = [UIApplication sharedApplication].statusBarStyle; } @@ -38,7 +39,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTPostController.m b/src/Three20UI/Sources/TTPostController.m index 2eb90f206c..0e597068b2 100644 --- a/src/Three20UI/Sources/TTPostController.m +++ b/src/Three20UI/Sources/TTPostController.m @@ -58,7 +58,8 @@ @implementation TTPostController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemCancel target: self @@ -76,7 +77,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { if (nil != query) { _delegate = [query objectForKey:@"delegate"]; _defaultText = [[query objectForKey:@"text"] copy]; diff --git a/src/Three20UI/Sources/TTRecursiveProgress.m b/src/Three20UI/Sources/TTRecursiveProgress.m index 3cb3beab04..f57c94233d 100644 --- a/src/Three20UI/Sources/TTRecursiveProgress.m +++ b/src/Three20UI/Sources/TTRecursiveProgress.m @@ -48,7 +48,8 @@ + (id)progressWithParent: (TTRecursiveProgress*)parent /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithDelegate:(id)delegate { - if (self = [super init]) { + self = [super init]; + if (self) { _firstPercent = 0; _lastPercent = 1; _parent = nil; @@ -63,7 +64,8 @@ - (id)initWithDelegate:(id)delegate { - (id)initWithParent:(TTRecursiveProgress*)parent firstPercent: (CGFloat)firstPercent lastPercent: (CGFloat)lastPercent { - if (self = [super init]) { + self = [super init]; + if (self) { _firstPercent = firstPercent; _lastPercent = lastPercent; _parent = parent; diff --git a/src/Three20UI/Sources/TTScrollView.m b/src/Three20UI/Sources/TTScrollView.m index 9b7103f9b6..28d523ebc7 100755 --- a/src/Three20UI/Sources/TTScrollView.m +++ b/src/Three20UI/Sources/TTScrollView.m @@ -67,7 +67,8 @@ @implementation TTScrollView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.clipsToBounds = YES; self.multipleTouchEnabled = YES; self.userInteractionEnabled = YES; diff --git a/src/Three20UI/Sources/TTSearchBar.m b/src/Three20UI/Sources/TTSearchBar.m index 05e0bb44f2..38a13e6001 100644 --- a/src/Three20UI/Sources/TTSearchBar.m +++ b/src/Three20UI/Sources/TTSearchBar.m @@ -59,7 +59,8 @@ @implementation TTSearchBar /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _boxView = [[TTView alloc] init]; _boxView.backgroundColor = [UIColor clearColor]; [self addSubview:_boxView]; diff --git a/src/Three20UI/Sources/TTSearchDisplayController.m b/src/Three20UI/Sources/TTSearchDisplayController.m index 5fff396a82..3b1b573c92 100644 --- a/src/Three20UI/Sources/TTSearchDisplayController.m +++ b/src/Three20UI/Sources/TTSearchDisplayController.m @@ -42,7 +42,8 @@ @implementation TTSearchDisplayController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithSearchBar:(UISearchBar*)searchBar contentsController:(UIViewController*)controller { - if (self = [super initWithSearchBar:searchBar contentsController:controller]) { + self = [super initWithSearchBar:searchBar contentsController:controller]; + if (self) { self.delegate = self; } diff --git a/src/Three20UI/Sources/TTSearchTextField.m b/src/Three20UI/Sources/TTSearchTextField.m index fccd5311c3..2a6579fef4 100644 --- a/src/Three20UI/Sources/TTSearchTextField.m +++ b/src/Three20UI/Sources/TTSearchTextField.m @@ -59,7 +59,8 @@ @implementation TTSearchTextField /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _internal = [[TTSearchTextFieldInternal alloc] initWithTextField:self]; self.autocorrectionType = UITextAutocorrectionTypeNo; @@ -268,7 +269,7 @@ - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*) if ([_internal.delegate respondsToSelector:@selector(textField:didSelectObject:)]) { id object = [_dataSource tableView:tableView objectForRowAtIndexPath:indexPath]; UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; - if (cell.selectionStyle != UITableViewCellSeparatorStyleNone) { + if (cell.selectionStyle != UITableViewCellSelectionStyleNone) { [_internal.delegate performSelector:@selector(textField:didSelectObject:) withObject:self withObject:object]; } @@ -480,7 +481,7 @@ - (CGRect)rectForSearchResults:(BOOL)withKeyboard { CGFloat height = self.height; CGFloat keyboardHeight = withKeyboard ? TTKeyboardHeight() : 0; - CGFloat tableHeight = self.window.height - (self.ttScreenY + height + keyboardHeight); + CGFloat tableHeight = self.window.height - (self.screenViewY + height + keyboardHeight); return CGRectMake(0, y + self.height-1, superview.frame.size.width, tableHeight+1); } diff --git a/src/Three20UI/Sources/TTSearchTextFieldInternal.m b/src/Three20UI/Sources/TTSearchTextFieldInternal.m index 71812c9a95..17308555e1 100644 --- a/src/Three20UI/Sources/TTSearchTextFieldInternal.m +++ b/src/Three20UI/Sources/TTSearchTextFieldInternal.m @@ -35,7 +35,8 @@ @implementation TTSearchTextFieldInternal /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTextField:(TTSearchTextField*)textField { - if (self = [super init]) { + self = [super init]; + if (self) { _textField = textField; } diff --git a/src/Three20UI/Sources/TTSearchlightLabel.m b/src/Three20UI/Sources/TTSearchlightLabel.m index d0ee7d696d..07e66a2094 100644 --- a/src/Three20UI/Sources/TTSearchlightLabel.m +++ b/src/Three20UI/Sources/TTSearchlightLabel.m @@ -38,7 +38,8 @@ @implementation TTSearchlightLabel /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.text = @""; self.font = TTSTYLEVAR(font); self.textColor = [UIColor colorWithWhite:0.25 alpha:1]; diff --git a/src/Three20UI/Sources/TTSectionedDataSource.m b/src/Three20UI/Sources/TTSectionedDataSource.m index 055c55da24..feaba86a34 100644 --- a/src/Three20UI/Sources/TTSectionedDataSource.m +++ b/src/Three20UI/Sources/TTSectionedDataSource.m @@ -34,7 +34,8 @@ @implementation TTSectionedDataSource /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithItems:(NSArray*)items sections:(NSArray*)sections { - if (self = [self init]) { + self = [self init]; + if (self) { _items = [items mutableCopy]; _sections = [sections mutableCopy]; } diff --git a/src/Three20UI/Sources/TTSplitViewController.m b/src/Three20UI/Sources/TTSplitViewController.m index 79a1063c2e..2e44bc0e68 100644 --- a/src/Three20UI/Sources/TTSplitViewController.m +++ b/src/Three20UI/Sources/TTSplitViewController.m @@ -37,7 +37,8 @@ @implementation TTSplitViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.delegate = self; self.viewControllers = [NSArray arrayWithObjects: diff --git a/src/Three20UI/Sources/TTStyledTextLabel.m b/src/Three20UI/Sources/TTStyledTextLabel.m index 51eef17a12..e6463b5265 100644 --- a/src/Three20UI/Sources/TTStyledTextLabel.m +++ b/src/Three20UI/Sources/TTStyledTextLabel.m @@ -59,7 +59,8 @@ @implementation TTStyledTextLabel /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _textAlignment = UITextAlignmentLeft; _contentInset = UIEdgeInsetsZero; @@ -454,6 +455,7 @@ - (void)setText:(TTStyledText*)text { _text = [text retain]; _text.delegate = self; _text.font = _font; + _text.textAlignment = _textAlignment; [self setNeedsLayout]; [self setNeedsDisplay]; } @@ -482,6 +484,14 @@ - (void)setFont:(UIFont*)font { } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)setTextAlignment:(UITextAlignment)textAlignment { + if (textAlignment != _textAlignment) { + _textAlignment = textAlignment; + _text.textAlignment = _textAlignment; + [self setNeedsLayout]; + } +} /////////////////////////////////////////////////////////////////////////////////////////////////// - (UIColor*)textColor { diff --git a/src/Three20UI/Sources/TTStyledTextTableCell.m b/src/Three20UI/Sources/TTStyledTextTableCell.m index 1ea05dd33a..a359746856 100644 --- a/src/Three20UI/Sources/TTStyledTextTableCell.m +++ b/src/Three20UI/Sources/TTStyledTextTableCell.m @@ -40,7 +40,8 @@ @implementation TTStyledTextTableCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { _label = [[TTStyledTextLabel alloc] init]; _label.contentMode = UIViewContentModeLeft; [self.contentView addSubview:_label]; diff --git a/src/Three20UI/Sources/TTStyledTextTableItemCell.m b/src/Three20UI/Sources/TTStyledTextTableItemCell.m index 51b1ebc365..467461fbe0 100644 --- a/src/Three20UI/Sources/TTStyledTextTableItemCell.m +++ b/src/Three20UI/Sources/TTStyledTextTableItemCell.m @@ -43,7 +43,8 @@ @implementation TTStyledTextTableItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { _label = [[TTStyledTextLabel alloc] init]; _label.contentMode = UIViewContentModeLeft; [self.contentView addSubview:_label]; @@ -78,10 +79,12 @@ + (CGFloat)tableView:(UITableView*)tableView rowHeightForObject:(id)object { if (item.URL) { padding += kDisclosureIndicatorWidth; } + + CGFloat margin = item.margin.left + item.margin.right; - item.text.width = tableView.width - padding; + item.text.width = tableView.width - padding - margin; - return item.text.height + item.padding.top + item.padding.bottom; + return item.text.height + item.padding.top + item.padding.bottom + item.margin.top + item.margin.bottom; } @@ -96,7 +99,7 @@ - (void)layoutSubviews { [super layoutSubviews]; TTTableStyledTextItem* item = self.object; - _label.frame = CGRectOffset(self.contentView.bounds, item.margin.left, item.margin.top); + _label.frame = UIEdgeInsetsInsetRect(self.contentView.bounds, item.margin); } diff --git a/src/Three20UI/Sources/TTTab.m b/src/Three20UI/Sources/TTTab.m index 39d4cb1355..5cd2f39a7e 100644 --- a/src/Three20UI/Sources/TTTab.m +++ b/src/Three20UI/Sources/TTTab.m @@ -42,7 +42,8 @@ @implementation TTTab /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithItem:(TTTabItem*)tabItem tabBar:(TTTabBar*)tabBar { - if (self = [self init]) { + self = [self init]; + if (self) { self.tabItem = tabItem; } diff --git a/src/Three20UI/Sources/TTTabBar.m b/src/Three20UI/Sources/TTTabBar.m index 0912b2d3e9..a9b01eda0f 100644 --- a/src/Three20UI/Sources/TTTabBar.m +++ b/src/Three20UI/Sources/TTTabBar.m @@ -47,7 +47,8 @@ @implementation TTTabBar /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _selectedTabIndex = NSIntegerMax; _tabViews = [[NSMutableArray alloc] init]; diff --git a/src/Three20UI/Sources/TTTabGrid.m b/src/Three20UI/Sources/TTTabGrid.m index e5369de572..f66ea577a4 100644 --- a/src/Three20UI/Sources/TTTabGrid.m +++ b/src/Three20UI/Sources/TTTabGrid.m @@ -39,7 +39,8 @@ @implementation TTTabGrid /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.style = TTSTYLE(tabGrid); _columnCount = 3; } diff --git a/src/Three20UI/Sources/TTTabItem.m b/src/Three20UI/Sources/TTTabItem.m index 27e8b1ae46..23dee62f42 100644 --- a/src/Three20UI/Sources/TTTabItem.m +++ b/src/Three20UI/Sources/TTTabItem.m @@ -33,7 +33,8 @@ @implementation TTTabItem /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title { - if (self = [self init]) { + self = [self init]; + if (self) { self.title = title; } diff --git a/src/Three20UI/Sources/TTTabStrip.m b/src/Three20UI/Sources/TTTabStrip.m index 638cc4b233..941c17e212 100644 --- a/src/Three20UI/Sources/TTTabStrip.m +++ b/src/Three20UI/Sources/TTTabStrip.m @@ -39,16 +39,19 @@ @implementation TTTabStrip /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _scrollView = [[UIScrollView alloc] init]; _scrollView.scrollEnabled = YES; _scrollView.scrollsToTop = NO; _scrollView.showsVerticalScrollIndicator = NO; _scrollView.showsHorizontalScrollIndicator = NO; + _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; [self addSubview:_scrollView]; self.style = TTSTYLE(tabStrip); self.tabStyle = @"tabRound:"; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; } return self; diff --git a/src/Three20UI/Sources/TTTableActivityItemCell.m b/src/Three20UI/Sources/TTTableActivityItemCell.m index 70932bc078..6147248e15 100644 --- a/src/Three20UI/Sources/TTTableActivityItemCell.m +++ b/src/Three20UI/Sources/TTTableActivityItemCell.m @@ -34,7 +34,8 @@ @implementation TTTableActivityItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { _activityLabel = [[TTActivityLabel alloc] initWithStyle:TTActivityLabelStyleGray]; [self.contentView addSubview:_activityLabel]; diff --git a/src/Three20UI/Sources/TTTableCaptionItem.m b/src/Three20UI/Sources/TTTableCaptionItem.m index 04a99d01a0..31ddaa7283 100644 --- a/src/Three20UI/Sources/TTTableCaptionItem.m +++ b/src/Three20UI/Sources/TTTableCaptionItem.m @@ -81,7 +81,8 @@ + (id)itemWithText:(NSString*)text caption:(NSString*)caption URL:(NSString*)URL /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.caption = [decoder decodeObjectForKey:@"caption"]; } return self; diff --git a/src/Three20UI/Sources/TTTableCaptionItemCell.m b/src/Three20UI/Sources/TTTableCaptionItemCell.m index 8f99d69e2b..caf7288240 100644 --- a/src/Three20UI/Sources/TTTableCaptionItemCell.m +++ b/src/Three20UI/Sources/TTTableCaptionItemCell.m @@ -38,10 +38,12 @@ @implementation TTTableCaptionItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]) { + self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]; + if (self) { self.textLabel.font = TTSTYLEVAR(tableTitleFont); self.textLabel.textColor = TTSTYLEVAR(linkTextColor); self.textLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.textLabel.textAlignment = UITextAlignmentRight; self.textLabel.lineBreakMode = UILineBreakModeTailTruncation; self.textLabel.numberOfLines = 1; @@ -50,6 +52,7 @@ - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)ident self.detailTextLabel.font = TTSTYLEVAR(tableSmallFont); self.detailTextLabel.textColor = TTSTYLEVAR(textColor); self.detailTextLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.detailTextLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.detailTextLabel.adjustsFontSizeToFitWidth = YES; self.detailTextLabel.minimumFontSize = 8; self.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap; diff --git a/src/Three20UI/Sources/TTTableControlCell.m b/src/Three20UI/Sources/TTTableControlCell.m index 41feb9a0eb..806b103756 100644 --- a/src/Three20UI/Sources/TTTableControlCell.m +++ b/src/Three20UI/Sources/TTTableControlCell.m @@ -43,7 +43,8 @@ @implementation TTTableControlCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { self.selectionStyle = UITableViewCellSelectionStyleNone; } @@ -192,7 +193,13 @@ - (id)object { /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)setObject:(id)object { if (object != _control && object != _item) { - [_control removeFromSuperview]; + if (_control.superview == self.contentView) { + //on cell reuse it is possible that another + //cell is already the owner of _control, so + //check if we're its superview first + [_control removeFromSuperview]; + } + TT_RELEASE_SAFELY(_control); TT_RELEASE_SAFELY(_item); diff --git a/src/Three20UI/Sources/TTTableControlItem.m b/src/Three20UI/Sources/TTTableControlItem.m index 780555639e..2c5d4e892b 100644 --- a/src/Three20UI/Sources/TTTableControlItem.m +++ b/src/Three20UI/Sources/TTTableControlItem.m @@ -61,7 +61,8 @@ + (id)itemWithCaption:(NSString*)caption control:(UIControl*)control { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.caption = [decoder decodeObjectForKey:@"caption"]; self.control = [decoder decodeObjectForKey:@"control"]; } diff --git a/src/Three20UI/Sources/TTTableFlushViewCell.m b/src/Three20UI/Sources/TTTableFlushViewCell.m index 7c6b18a156..6e34ab2d98 100644 --- a/src/Three20UI/Sources/TTTableFlushViewCell.m +++ b/src/Three20UI/Sources/TTTableFlushViewCell.m @@ -37,7 +37,8 @@ @implementation TTTableFlushViewCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { self.selectionStyle = UITableViewCellSelectionStyleNone; } diff --git a/src/Three20UI/Sources/TTTableHeaderView.m b/src/Three20UI/Sources/TTTableHeaderView.m index b4c7cb690b..16bda360ff 100644 --- a/src/Three20UI/Sources/TTTableHeaderView.m +++ b/src/Three20UI/Sources/TTTableHeaderView.m @@ -35,7 +35,8 @@ @implementation TTTableHeaderView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTitle:(NSString*)title { - if (self = [super init]) { + self = [super init]; + if (self) { self.backgroundColor = [UIColor clearColor]; self.style = TTSTYLE(tableHeader); diff --git a/src/Three20UI/Sources/TTTableImageItem.m b/src/Three20UI/Sources/TTTableImageItem.m index 3a3b30320f..a49af3698f 100644 --- a/src/Three20UI/Sources/TTTableImageItem.m +++ b/src/Three20UI/Sources/TTTableImageItem.m @@ -98,7 +98,8 @@ + (id)itemWithText:(NSString*)text imageURL:(NSString*)imageURL defaultImage:(UI /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.imageURL = [decoder decodeObjectForKey:@"imageURL"]; } return self; diff --git a/src/Three20UI/Sources/TTTableImageItemCell.m b/src/Three20UI/Sources/TTTableImageItemCell.m index 9a05a05ee2..e01abbe8c6 100644 --- a/src/Three20UI/Sources/TTTableImageItemCell.m +++ b/src/Three20UI/Sources/TTTableImageItemCell.m @@ -47,7 +47,8 @@ @implementation TTTableImageItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { _imageView2 = [[TTImageView alloc] init]; [self.contentView addSubview:_imageView2]; } @@ -147,7 +148,7 @@ - (void)layoutSubviews { ? image.size.height : (item.imageURL ? kDefaultImageSize : 0); - if (_imageView2.urlPath) { + if (_imageView2.urlPath || image) { CGFloat innerWidth = self.contentView.width - (kTableCellHPadding*2 + imageWidth + kKeySpacing); CGFloat innerHeight = self.contentView.height - kTableCellVPadding*2; @@ -165,7 +166,7 @@ - (void)layoutSubviews { } } else { - if (_imageView2.urlPath) { + if (_imageView2.urlPath || image) { CGFloat iconWidth = image ? image.size.width : (item.imageURL ? kDefaultImageSize : 0); diff --git a/src/Three20UI/Sources/TTTableItem.m b/src/Three20UI/Sources/TTTableItem.m index 6d4de51108..4e8d636187 100644 --- a/src/Three20UI/Sources/TTTableItem.m +++ b/src/Three20UI/Sources/TTTableItem.m @@ -44,7 +44,8 @@ - (void)dealloc { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [self init]) { + self = [self init]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTTableLinkedItem.m b/src/Three20UI/Sources/TTTableLinkedItem.m index cd7eeb5cd7..5a76c5d1de 100644 --- a/src/Three20UI/Sources/TTTableLinkedItem.m +++ b/src/Three20UI/Sources/TTTableLinkedItem.m @@ -50,7 +50,8 @@ - (void)dealloc { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.URL = [decoder decodeObjectForKey:@"URL"]; self.accessoryURL = [decoder decodeObjectForKey:@"accessoryURL"]; } diff --git a/src/Three20UI/Sources/TTTableMessageItem.m b/src/Three20UI/Sources/TTTableMessageItem.m index 0849ebff27..68d925d251 100644 --- a/src/Three20UI/Sources/TTTableMessageItem.m +++ b/src/Three20UI/Sources/TTTableMessageItem.m @@ -83,7 +83,8 @@ + (id)itemWithTitle:(NSString*)title caption:(NSString*)caption text:(NSString*) /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.title = [decoder decodeObjectForKey:@"title"]; self.caption = [decoder decodeObjectForKey:@"caption"]; self.timestamp = [decoder decodeObjectForKey:@"timestamp"]; diff --git a/src/Three20UI/Sources/TTTableMessageItemCell.m b/src/Three20UI/Sources/TTTableMessageItemCell.m index 645eddc4f5..08f04ca891 100644 --- a/src/Three20UI/Sources/TTTableMessageItemCell.m +++ b/src/Three20UI/Sources/TTTableMessageItemCell.m @@ -43,10 +43,12 @@ @implementation TTTableMessageItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]) { + self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]; + if (self) { self.textLabel.font = TTSTYLEVAR(font); self.textLabel.textColor = TTSTYLEVAR(textColor); self.textLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.textLabel.textAlignment = UITextAlignmentLeft; self.textLabel.lineBreakMode = UILineBreakModeTailTruncation; self.textLabel.adjustsFontSizeToFitWidth = YES; @@ -55,6 +57,7 @@ - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)ident self.detailTextLabel.font = TTSTYLEVAR(font); self.detailTextLabel.textColor = TTSTYLEVAR(tableSubTextColor); self.detailTextLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.detailTextLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.detailTextLabel.textAlignment = UITextAlignmentLeft; self.detailTextLabel.contentMode = UIViewContentModeTop; self.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation; @@ -101,6 +104,7 @@ - (void)prepareForReuse { [_imageView2 unsetImage]; _titleLabel.text = nil; _timestampLabel.text = nil; + self.captionLabel.text = nil; } diff --git a/src/Three20UI/Sources/TTTableMoreButtonCell.m b/src/Three20UI/Sources/TTTableMoreButtonCell.m index 8912b8894f..d0114bfc73 100644 --- a/src/Three20UI/Sources/TTTableMoreButtonCell.m +++ b/src/Three20UI/Sources/TTTableMoreButtonCell.m @@ -43,8 +43,10 @@ @implementation TTTableMoreButtonCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier]) { + self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier]; + if (self) { self.textLabel.font = TTSTYLEVAR(tableSmallFont); + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); } return self; diff --git a/src/Three20UI/Sources/TTTableRightCaptionItemCell.m b/src/Three20UI/Sources/TTTableRightCaptionItemCell.m index d0dcfe237e..75b9b0cfc4 100644 --- a/src/Three20UI/Sources/TTTableRightCaptionItemCell.m +++ b/src/Three20UI/Sources/TTTableRightCaptionItemCell.m @@ -35,12 +35,15 @@ @implementation TTTableRightCaptionItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]) { + self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]; + if (self) { self.textLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.textLabel.lineBreakMode = UILineBreakModeWordWrap; self.textLabel.numberOfLines = 0; self.detailTextLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.detailTextLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); // XXXjoe TODO } diff --git a/src/Three20UI/Sources/TTTableStyledTextItem.m b/src/Three20UI/Sources/TTTableStyledTextItem.m index c06ee7a497..591c633ba7 100644 --- a/src/Three20UI/Sources/TTTableStyledTextItem.m +++ b/src/Three20UI/Sources/TTTableStyledTextItem.m @@ -32,7 +32,8 @@ @implementation TTTableStyledTextItem /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _margin = UIEdgeInsetsZero; _padding = UIEdgeInsetsMake(6, 6, 6, 6); } @@ -90,7 +91,8 @@ + (id)itemWithText:(TTStyledText*)text URL:(NSString*)URL accessoryURL:(NSString /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.text = [decoder decodeObjectForKey:@"text"]; } return self; diff --git a/src/Three20UI/Sources/TTTableSubtextItemCell.m b/src/Three20UI/Sources/TTTableSubtextItemCell.m index 35d38a5b5d..4506a28b77 100644 --- a/src/Three20UI/Sources/TTTableSubtextItemCell.m +++ b/src/Three20UI/Sources/TTTableSubtextItemCell.m @@ -34,15 +34,18 @@ @implementation TTTableSubtextItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]) { + self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]; + if (self) { self.detailTextLabel.font = TTSTYLEVAR(tableFont); self.detailTextLabel.textColor = TTSTYLEVAR(textColor); self.detailTextLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.detailTextLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.detailTextLabel.adjustsFontSizeToFitWidth = YES; self.textLabel.font = TTSTYLEVAR(font); self.textLabel.textColor = TTSTYLEVAR(tableSubTextColor); self.textLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.textLabel.textAlignment = UITextAlignmentLeft; self.textLabel.contentMode = UIViewContentModeTop; self.textLabel.lineBreakMode = UILineBreakModeWordWrap; diff --git a/src/Three20UI/Sources/TTTableSubtitleItem.m b/src/Three20UI/Sources/TTTableSubtitleItem.m index 64969305c4..295f2b6353 100644 --- a/src/Three20UI/Sources/TTTableSubtitleItem.m +++ b/src/Three20UI/Sources/TTTableSubtitleItem.m @@ -111,7 +111,8 @@ + (id)itemWithText:(NSString*)text subtitle:(NSString*)subtitle imageURL:(NSStri /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.subtitle = [decoder decodeObjectForKey:@"subtitle"]; self.imageURL = [decoder decodeObjectForKey:@"imageURL"]; } diff --git a/src/Three20UI/Sources/TTTableSubtitleItemCell.m b/src/Three20UI/Sources/TTTableSubtitleItemCell.m index dbd4a1f05b..ec291ba23e 100644 --- a/src/Three20UI/Sources/TTTableSubtitleItemCell.m +++ b/src/Three20UI/Sources/TTTableSubtitleItemCell.m @@ -38,10 +38,12 @@ @implementation TTTableSubtitleItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]) { + self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier]; + if (self) { self.textLabel.font = TTSTYLEVAR(tableFont); self.textLabel.textColor = TTSTYLEVAR(textColor); self.textLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.textLabel.textAlignment = UITextAlignmentLeft; self.textLabel.lineBreakMode = UILineBreakModeTailTruncation; self.textLabel.adjustsFontSizeToFitWidth = YES; @@ -49,6 +51,7 @@ - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)ident self.detailTextLabel.font = TTSTYLEVAR(font); self.detailTextLabel.textColor = TTSTYLEVAR(tableSubTextColor); self.detailTextLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); + self.detailTextLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); self.detailTextLabel.textAlignment = UITextAlignmentLeft; self.detailTextLabel.contentMode = UIViewContentModeTop; self.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation; diff --git a/src/Three20UI/Sources/TTTableTextItem.m b/src/Three20UI/Sources/TTTableTextItem.m index 5dcf2cff5c..7c841009e6 100644 --- a/src/Three20UI/Sources/TTTableTextItem.m +++ b/src/Three20UI/Sources/TTTableTextItem.m @@ -87,7 +87,8 @@ + (id)itemWithText:(NSString*)text delegate:(id)delegate selector:(SEL)selector /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.text = [decoder decodeObjectForKey:@"text"]; } return self; diff --git a/src/Three20UI/Sources/TTTableTextItemCell.m b/src/Three20UI/Sources/TTTableTextItemCell.m index 6581946932..28fca6be9d 100644 --- a/src/Three20UI/Sources/TTTableTextItemCell.m +++ b/src/Three20UI/Sources/TTTableTextItemCell.m @@ -43,7 +43,8 @@ @implementation TTTableTextItemCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { self.textLabel.highlightedTextColor = TTSTYLEVAR(highlightedTextColor); self.textLabel.lineBreakMode = kLineBreakMode; self.textLabel.numberOfLines = 0; @@ -124,6 +125,7 @@ - (void)setObject:(id)object { TTTableTextItem* item = object; self.textLabel.text = item.text; + self.textLabel.backgroundColor = TTSTYLEVAR(backgroundTextColor); if ([object isKindOfClass:[TTTableButton class]]) { self.textLabel.font = TTSTYLEVAR(tableButtonFont); diff --git a/src/Three20UI/Sources/TTTableView.m b/src/Three20UI/Sources/TTTableView.m index e2cd5de2eb..2746cf1682 100644 --- a/src/Three20UI/Sources/TTTableView.m +++ b/src/Three20UI/Sources/TTTableView.m @@ -50,7 +50,8 @@ @implementation TTTableView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { - if (self = [super initWithFrame:frame style:style]) { + self = [super initWithFrame:frame style:style]; + if (self) { _highlightStartPoint = CGPointZero; } diff --git a/src/Three20UI/Sources/TTTableViewCell.m b/src/Three20UI/Sources/TTTableViewCell.m index 6c2478fc4f..2fb54a6173 100644 --- a/src/Three20UI/Sources/TTTableViewCell.m +++ b/src/Three20UI/Sources/TTTableViewCell.m @@ -49,6 +49,8 @@ + (CGFloat)tableView:(UITableView*)tableView rowHeightForObject:(id)object { /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)prepareForReuse { self.object = nil; + self.textLabel.text = nil; + self.detailTextLabel.text = nil; [super prepareForReuse]; } diff --git a/src/Three20UI/Sources/TTTableViewController.m b/src/Three20UI/Sources/TTTableViewController.m index 69295e3f91..9dd1aba27b 100644 --- a/src/Three20UI/Sources/TTTableViewController.m +++ b/src/Three20UI/Sources/TTTableViewController.m @@ -61,14 +61,17 @@ @implementation TTTableViewController @synthesize tableViewStyle = _tableViewStyle; @synthesize variableHeightRows = _variableHeightRows; @synthesize showTableShadows = _showTableShadows; +@synthesize clearsSelectionOnViewWillAppear = _clearsSelectionOnViewWillAppear; @synthesize dataSource = _dataSource; /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _lastInterfaceOrientation = self.interfaceOrientation; _tableViewStyle = UITableViewStylePlain; + _clearsSelectionOnViewWillAppear = YES; } return self; @@ -77,7 +80,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewStyle)style { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { _tableViewStyle = style; } @@ -266,7 +270,9 @@ - (void)viewWillAppear:(BOOL)animated { tableView.showShadows = _showTableShadows; } - [_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:animated]; + if (_clearsSelectionOnViewWillAppear) { + [_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:animated]; + } } @@ -574,9 +580,6 @@ - (void)model:(id)model didInsertObject:(id)object atIndexPath:(NSIndex TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"INSERTING ROW AT %@", newIndexPath); [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationTop]; - - [_tableView scrollToRowAtIndexPath:newIndexPath - atScrollPosition:UITableViewScrollPositionNone animated:NO]; } [self invalidateView]; @@ -638,7 +641,18 @@ - (UITableView*)tableView { _tableView = [[TTTableView alloc] initWithFrame:self.view.bounds style:_tableViewStyle]; _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - + + UIColor* separatorColor = _tableViewStyle == UITableViewStyleGrouped + ? TTSTYLEVAR(tableGroupedCellSeparatorColor) + : TTSTYLEVAR(tablePlainCellSeparatorColor); + if (separatorColor) { + _tableView.separatorColor = separatorColor; + } + + _tableView.separatorStyle = _tableViewStyle == UITableViewStyleGrouped + ? TTSTYLEVAR(tableGroupedCellSeparatorStyle) + : TTSTYLEVAR(tablePlainCellSeparatorStyle); + UIColor* backgroundColor = _tableViewStyle == UITableViewStyleGrouped ? TTSTYLEVAR(tableGroupedBackgroundColor) : TTSTYLEVAR(tablePlainBackgroundColor); @@ -931,4 +945,16 @@ - (CGRect)rectForBannerView { } +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)invalidateModel { + [super invalidateModel]; + + // Renew the tableView delegate when the model is refreshed. + // Otherwise the delegate will be retained the model. + + // You need to set it to nil before changing it or it won't have any effect + _tableView.delegate = nil; + [self updateTableDelegate]; +} + @end diff --git a/src/Three20UI/Sources/TTTableViewDelegate.m b/src/Three20UI/Sources/TTTableViewDelegate.m index 43568cf075..9fdd81c651 100644 --- a/src/Three20UI/Sources/TTTableViewDelegate.m +++ b/src/Three20UI/Sources/TTTableViewDelegate.m @@ -60,7 +60,8 @@ @implementation TTTableViewDelegate /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithController:(TTTableViewController*)controller { - if (self = [super init]) { + self = [super init]; + if (self) { _controller = controller; } return self; diff --git a/src/Three20UI/Sources/TTTableViewDragRefreshDelegate.m b/src/Three20UI/Sources/TTTableViewDragRefreshDelegate.m index a93267fb26..494a0e2671 100644 --- a/src/Three20UI/Sources/TTTableViewDragRefreshDelegate.m +++ b/src/Three20UI/Sources/TTTableViewDragRefreshDelegate.m @@ -67,7 +67,8 @@ @implementation TTTableViewDragRefreshDelegate /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithController:(TTTableViewController*)controller { - if (self = [super initWithController:controller]) { + self = [super initWithController:controller]; + if (self) { // Add our refresh header _headerView = [[TTTableHeaderDragRefreshView alloc] initWithFrame:CGRectMake(0, diff --git a/src/Three20UI/Sources/TTTableViewItem.m b/src/Three20UI/Sources/TTTableViewItem.m index 0e1f7e97dd..9df5beed6a 100644 --- a/src/Three20UI/Sources/TTTableViewItem.m +++ b/src/Three20UI/Sources/TTTableViewItem.m @@ -61,7 +61,8 @@ + (id)itemWithCaption:(NSString*)caption view:(UIControl*)view { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithCoder:(NSCoder*)decoder { - if (self = [super initWithCoder:decoder]) { + self = [super initWithCoder:decoder]; + if (self) { self.caption = [decoder decodeObjectForKey:@"caption"]; self.view = [decoder decodeObjectForKey:@"view"]; } diff --git a/src/Three20UI/Sources/TTTableViewNetworkEnabledDelegate.m b/src/Three20UI/Sources/TTTableViewNetworkEnabledDelegate.m index 105fdba721..b0d71281be 100644 --- a/src/Three20UI/Sources/TTTableViewNetworkEnabledDelegate.m +++ b/src/Three20UI/Sources/TTTableViewNetworkEnabledDelegate.m @@ -68,7 +68,8 @@ @implementation TTTableViewNetworkEnabledDelegate - (id)initWithController:(TTTableViewController*)controller withDragRefresh:(BOOL)enableDragRefresh withInfiniteScroll:(BOOL)enableInfiniteScroll { - if (self = [super initWithController:controller]) { + self = [super initWithController:controller]; + if (self) { _dragRefreshEnabled = enableDragRefresh; _infiniteScrollEnabled = enableInfiniteScroll; diff --git a/src/Three20UI/Sources/TTTextBarController.m b/src/Three20UI/Sources/TTTextBarController.m index 2a2f13d0af..df1fb8f60a 100644 --- a/src/Three20UI/Sources/TTTextBarController.m +++ b/src/Three20UI/Sources/TTTextBarController.m @@ -59,7 +59,8 @@ @implementation TTTextBarController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { } return self; @@ -68,7 +69,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { if (nil != query) { _delegate = [query objectForKey:@"delegate"]; _defaultText = [[query objectForKey:@"text"] copy]; @@ -81,7 +83,8 @@ - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNavigatorURL:nil query:nil]) { + self = [self initWithNavigatorURL:nil query:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTTextEditor.m b/src/Three20UI/Sources/TTTextEditor.m index 51c54a3dda..5801542350 100644 --- a/src/Three20UI/Sources/TTTextEditor.m +++ b/src/Three20UI/Sources/TTTextEditor.m @@ -60,7 +60,8 @@ @implementation TTTextEditor /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { _internal = [[TTTextEditorInternal alloc] initWithTextEditor:self]; _autoresizesToText = YES; diff --git a/src/Three20UI/Sources/TTTextEditorInternal.m b/src/Three20UI/Sources/TTTextEditorInternal.m index a082c5114b..2db4ce9d4f 100644 --- a/src/Three20UI/Sources/TTTextEditorInternal.m +++ b/src/Three20UI/Sources/TTTextEditorInternal.m @@ -36,7 +36,8 @@ @implementation TTTextEditorInternal /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTextEditor:(TTTextEditor*)textEditor { - if (self = [super init]) { + self = [super init]; + if (self) { _textEditor = textEditor; } diff --git a/src/Three20UI/Sources/TTThumbView.m b/src/Three20UI/Sources/TTThumbView.m index 004d7240e3..f041eec864 100644 --- a/src/Three20UI/Sources/TTThumbView.m +++ b/src/Three20UI/Sources/TTThumbView.m @@ -29,7 +29,9 @@ @implementation TTThumbView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = TTSTYLEVAR(thumbnailBackgroundColor); self.clipsToBounds = YES; diff --git a/src/Three20UI/Sources/TTThumbsDataSource.m b/src/Three20UI/Sources/TTThumbsDataSource.m index b865834822..0bbcfec466 100644 --- a/src/Three20UI/Sources/TTThumbsDataSource.m +++ b/src/Three20UI/Sources/TTThumbsDataSource.m @@ -48,7 +48,8 @@ @implementation TTThumbsDataSource /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithPhotoSource:(id)photoSource delegate:(id)delegate { - if (self = [super init]) { + self = [super init]; + if (self) { _photoSource = [photoSource retain]; _delegate = delegate; } diff --git a/src/Three20UI/Sources/TTThumbsTableViewCell.m b/src/Three20UI/Sources/TTThumbsTableViewCell.m index 29f3318a77..21976d8da0 100644 --- a/src/Three20UI/Sources/TTThumbsTableViewCell.m +++ b/src/Three20UI/Sources/TTThumbsTableViewCell.m @@ -44,7 +44,8 @@ @implementation TTThumbsTableViewCell /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier { - if (self = [super initWithStyle:style reuseIdentifier:identifier]) { + self = [super initWithStyle:style reuseIdentifier:identifier]; + if (self) { _thumbViews = [[NSMutableArray alloc] init]; _thumbSize = kDefaultThumbSize; _thumbOrigin = CGPointMake(kSpacing, 0); diff --git a/src/Three20UI/Sources/TTThumbsViewController.m b/src/Three20UI/Sources/TTThumbsViewController.m index baa6971fe4..b2f2ef7306 100644 --- a/src/Three20UI/Sources/TTThumbsViewController.m +++ b/src/Three20UI/Sources/TTThumbsViewController.m @@ -55,7 +55,8 @@ @implementation TTThumbsViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.statusBarStyle = UIStatusBarStyleBlackTranslucent; self.navigationBarStyle = UIBarStyleBlackTranslucent; self.navigationBarTintColor = nil; @@ -69,7 +70,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithDelegate:(id)delegate { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { self.delegate = delegate; } @@ -93,7 +95,8 @@ - (id)initWithQuery:(NSDictionary*)query { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTView.m b/src/Three20UI/Sources/TTView.m index 56c424be08..361f67c948 100644 --- a/src/Three20UI/Sources/TTView.m +++ b/src/Three20UI/Sources/TTView.m @@ -36,7 +36,8 @@ @implementation TTView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + self = [super initWithFrame:frame]; + if (self) { self.contentMode = UIViewContentModeRedraw; } diff --git a/src/Three20UI/Sources/TTViewController.m b/src/Three20UI/Sources/TTViewController.m index 4bc03e96d7..afc69c8456 100644 --- a/src/Three20UI/Sources/TTViewController.m +++ b/src/Three20UI/Sources/TTViewController.m @@ -50,7 +50,8 @@ @implementation TTViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.navigationBarTintColor = TTSTYLEVAR(navigationBarTintColor); } @@ -60,7 +61,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; diff --git a/src/Three20UI/Sources/TTWebController.m b/src/Three20UI/Sources/TTWebController.m index 9947b27ebb..6191ea891f 100644 --- a/src/Three20UI/Sources/TTWebController.m +++ b/src/Three20UI/Sources/TTWebController.m @@ -53,7 +53,8 @@ @implementation TTWebController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { self.hidesBottomBarWhenPushed = YES; } @@ -63,7 +64,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { NSURLRequest* request = [query objectForKey:@"request"]; if (nil != request) { [self openRequest:request]; @@ -78,7 +80,8 @@ - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; @@ -230,6 +233,7 @@ - (void)loadView { - (void)viewDidUnload { [super viewDidUnload]; + _delegate = nil; _webView.delegate = nil; TT_RELEASE_SAFELY(_webView); @@ -317,6 +321,11 @@ - (void)restoreView:(NSDictionary*)state { /////////////////////////////////////////////////////////////////////////////////////////////////// - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { + if ([_delegate respondsToSelector:@selector(webController:webView:shouldStartLoadWithRequest:navigationType:)] && + ![_delegate webController:self webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]) { + return NO; + } + if ([[TTNavigator navigator].URLMap isAppURL:request.URL]) { [_loadingURL release]; _loadingURL = [[NSURL URLWithString:@"about:blank"] retain]; @@ -334,6 +343,10 @@ - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)re /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)webViewDidStartLoad:(UIWebView*)webView { + if ([_delegate respondsToSelector:@selector(webController:webViewDidStartLoad:)]) { + [_delegate webController:self webViewDidStartLoad:webView]; + } + self.title = TTLocalizedString(@"Loading...", @""); if (!self.navigationItem.rightBarButtonItem) { [self.navigationItem setRightBarButtonItem:_activityItem animated:YES]; @@ -346,6 +359,10 @@ - (void)webViewDidStartLoad:(UIWebView*)webView { /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)webViewDidFinishLoad:(UIWebView*)webView { + if ([_delegate respondsToSelector:@selector(webController:webViewDidFinishLoad:)]) { + [_delegate webController:self webViewDidFinishLoad:webView]; + } + TT_RELEASE_SAFELY(_loadingURL); self.title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; if (self.navigationItem.rightBarButtonItem == _activityItem) { @@ -360,6 +377,10 @@ - (void)webViewDidFinishLoad:(UIWebView*)webView { /////////////////////////////////////////////////////////////////////////////////////////////////// - (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error { + if ([_delegate respondsToSelector:@selector(webController:webView:didFailLoadWithError:)]) { + [_delegate webController:self webView:webView didFailLoadWithError:error]; + } + TT_RELEASE_SAFELY(_loadingURL); [self webViewDidFinishLoad:webView]; } diff --git a/src/Three20UI/Sources/TTYouTubeView.m b/src/Three20UI/Sources/TTYouTubeView.m index c3881e4be6..df3c1f5293 100644 --- a/src/Three20UI/Sources/TTYouTubeView.m +++ b/src/Three20UI/Sources/TTYouTubeView.m @@ -51,7 +51,8 @@ @implementation TTYouTubeView /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURLPath:(NSString*)urlPath { - if (self = [self initWithFrame:CGRectMake(0, 0, kDefaultWidth, kDefaultHeight)]) { + self = [self initWithFrame:CGRectMake(0, 0, kDefaultWidth, kDefaultHeight)]; + if (self) { self.urlPath = urlPath; } return self; diff --git a/src/Three20UI/Sources/UIViewAdditions.m b/src/Three20UI/Sources/UIViewAdditions.m index fa1c21942a..851f5abd8c 100644 --- a/src/Three20UI/Sources/UIViewAdditions.m +++ b/src/Three20UI/Sources/UIViewAdditions.m @@ -104,7 +104,8 @@ @implementation UITouch (TTCategory) /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initInView:(UIView *)view location:(CGPoint)location { - if (self = [super init]) { + self = [super init]; + if (self) { _tapCount = 1; _locationInWindow = location; _previousLocationInWindow = location; @@ -139,7 +140,8 @@ @implementation UIEvent (TTCategory) /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTouch:(UITouch *)touch { - if (self == [super init]) { + self = [super init]; + if (self) { UIEventFake *selfFake = (UIEventFake*)self; selfFake->_touches = [[NSMutableSet setWithObject:touch] retain]; selfFake->_timestamp = [NSDate timeIntervalSinceReferenceDate]; diff --git a/src/Three20UI/Three20UI.xcodeproj/project.pbxproj b/src/Three20UI/Three20UI.xcodeproj/project.pbxproj index 053305dcac..9452ee2b3e 100755 --- a/src/Three20UI/Three20UI.xcodeproj/project.pbxproj +++ b/src/Three20UI/Three20UI.xcodeproj/project.pbxproj @@ -2400,7 +2400,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -2417,7 +2416,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; PREBINDING = NO; SDKROOT = iphoneos; WRAPPER_EXTENSION = octest; diff --git a/src/Three20UICommon/Headers/TTGlobalUICommon.h b/src/Three20UICommon/Headers/TTGlobalUICommon.h index d139ee062b..614119c90a 100644 --- a/src/Three20UICommon/Headers/TTGlobalUICommon.h +++ b/src/Three20UICommon/Headers/TTGlobalUICommon.h @@ -47,6 +47,16 @@ BOOL TTIsPad(); */ UIDeviceOrientation TTDeviceOrientation(); +/** + * @return TRUE if the current device orientation is portrait or portrait upside down. + */ +BOOL TTDeviceOrientationIsPortrait(); + +/** + * @return TRUE if the current device orientation is landscape left, or landscape right. + */ +BOOL TTDeviceOrientationIsLandscape(); + /** * On iPhone/iPod touch * Checks if the orientation is portrait, landscape left, or landscape right. diff --git a/src/Three20UICommon/Sources/TTBaseViewController.m b/src/Three20UICommon/Sources/TTBaseViewController.m index 56bf5bc480..a19e69eedb 100644 --- a/src/Three20UICommon/Sources/TTBaseViewController.m +++ b/src/Three20UICommon/Sources/TTBaseViewController.m @@ -44,7 +44,8 @@ @implementation TTBaseViewController /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { _navigationBarStyle = UIBarStyleDefault; _statusBarStyle = [[UIApplication sharedApplication] statusBarStyle]; } @@ -55,7 +56,8 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; diff --git a/src/Three20UICommon/Sources/TTGlobalUICommon.m b/src/Three20UICommon/Sources/TTGlobalUICommon.m index 30518f12b8..dd46a8370b 100644 --- a/src/Three20UICommon/Sources/TTGlobalUICommon.m +++ b/src/Three20UICommon/Sources/TTGlobalUICommon.m @@ -110,7 +110,7 @@ BOOL TTIsPad() { /////////////////////////////////////////////////////////////////////////////////////////////////// UIDeviceOrientation TTDeviceOrientation() { - UIDeviceOrientation orient = [UIDevice currentDevice].orientation; + UIDeviceOrientation orient = [UIApplication sharedApplication].statusBarOrientation; if (UIDeviceOrientationUnknown == orient) { return UIDeviceOrientationPortrait; @@ -120,6 +120,34 @@ UIDeviceOrientation TTDeviceOrientation() { } +/////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL TTDeviceOrientationIsPortrait() { + UIDeviceOrientation orient = TTDeviceOrientation(); + + switch (orient) { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + return YES; + default: + return NO; + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL TTDeviceOrientationIsLandscape() { + UIDeviceOrientation orient = TTDeviceOrientation(); + + switch (orient) { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + return YES; + default: + return NO; + } +} + + /////////////////////////////////////////////////////////////////////////////////////////////////// BOOL TTIsSupportedOrientation(UIInterfaceOrientation orientation) { if (TTIsPad()) { diff --git a/src/Three20UICommon/Three20UICommon.xcodeproj/project.pbxproj b/src/Three20UICommon/Three20UICommon.xcodeproj/project.pbxproj index da7cb05dd8..5e18515ab0 100755 --- a/src/Three20UICommon/Three20UICommon.xcodeproj/project.pbxproj +++ b/src/Three20UICommon/Three20UICommon.xcodeproj/project.pbxproj @@ -550,7 +550,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -567,7 +566,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; PREBINDING = NO; SDKROOT = iphoneos; WRAPPER_EXTENSION = octest; diff --git a/src/Three20UINavigator/Sources/TTBaseNavigator.m b/src/Three20UINavigator/Sources/TTBaseNavigator.m index 6526cafc79..59ba5288cb 100644 --- a/src/Three20UINavigator/Sources/TTBaseNavigator.m +++ b/src/Three20UINavigator/Sources/TTBaseNavigator.m @@ -73,7 +73,8 @@ @implementation TTBaseNavigator /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _URLMap = [[TTURLMap alloc] init]; _persistenceMode = TTNavigatorPersistenceModeNone; @@ -316,6 +317,8 @@ - (void)presentModalController: (UIViewController*)controller } else { UINavigationController* navController = [[[[self navigationControllerClass] alloc] init] autorelease]; + navController.modalTransitionStyle = transition; + navController.modalPresentationStyle = controller.modalPresentationStyle; [navController pushViewController: controller animated: NO]; [parentController presentModalViewController: navController diff --git a/src/Three20UINavigator/Sources/TTGlobalNavigatorMetrics.m b/src/Three20UINavigator/Sources/TTGlobalNavigatorMetrics.m index ebb25f219d..87eb393440 100644 --- a/src/Three20UINavigator/Sources/TTGlobalNavigatorMetrics.m +++ b/src/Three20UINavigator/Sources/TTGlobalNavigatorMetrics.m @@ -92,7 +92,7 @@ CGFloat TTBarsHeight() { return frame.size.height + TT_ROW_HEIGHT; } else { - return frame.size.width + TT_LANDSCAPE_TOOLBAR_HEIGHT; + return frame.size.width + (TTIsPad() ? TT_ROW_HEIGHT : TT_LANDSCAPE_TOOLBAR_HEIGHT); } } diff --git a/src/Three20UINavigator/Sources/TTURLAction.m b/src/Three20UINavigator/Sources/TTURLAction.m index 4da3a55a10..e7e1bb5540 100644 --- a/src/Three20UINavigator/Sources/TTURLAction.m +++ b/src/Three20UINavigator/Sources/TTURLAction.m @@ -52,7 +52,8 @@ + (id)actionWithURLPath:(NSString*)urlPath { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithURLPath:(NSString*)urlPath { - if (self = [super init]) { + self = [super init]; + if (self) { self.urlPath = urlPath; self.animated = NO; self.withDelay = NO; @@ -65,7 +66,8 @@ - (id)initWithURLPath:(NSString*)urlPath { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithURLPath:nil]) { + self = [self initWithURLPath:nil]; + if (self) { } return self; diff --git a/src/Three20UINavigator/Sources/TTURLGeneratorPattern.m b/src/Three20UINavigator/Sources/TTURLGeneratorPattern.m index 022bd77c06..abdd2a0d89 100644 --- a/src/Three20UINavigator/Sources/TTURLGeneratorPattern.m +++ b/src/Three20UINavigator/Sources/TTURLGeneratorPattern.m @@ -36,7 +36,8 @@ @implementation TTURLGeneratorPattern /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTargetClass:(id)targetClass { - if (self = [super init]) { + self = [super init]; + if (self) { _targetClass = targetClass; } return self; @@ -45,7 +46,8 @@ - (id)initWithTargetClass:(id)targetClass { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithTargetClass:nil]) { + self = [self initWithTargetClass:nil]; + if (self) { } return self; diff --git a/src/Three20UINavigator/Sources/TTURLNavigatorPattern.m b/src/Three20UINavigator/Sources/TTURLNavigatorPattern.m index 4307a1f798..71b4f79c48 100644 --- a/src/Three20UINavigator/Sources/TTURLNavigatorPattern.m +++ b/src/Three20UINavigator/Sources/TTURLNavigatorPattern.m @@ -47,7 +47,8 @@ @implementation TTURLNavigatorPattern /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTarget: (id)target mode: (TTNavigationMode)navigationMode { - if (self = [super init]) { + self = [super init]; + if (self) { _navigationMode = navigationMode; if ([target class] == target && navigationMode) { @@ -64,7 +65,8 @@ - (id)initWithTarget: (id)target /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithTarget:(id)target { - if (self = [self initWithTarget:target mode:TTNavigationModeNone]) { + self = [self initWithTarget:target mode:TTNavigationModeNone]; + if (self) { } return self; @@ -73,7 +75,8 @@ - (id)initWithTarget:(id)target { /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [self initWithTarget:nil]) { + self = [self initWithTarget:nil]; + if (self) { } return self; diff --git a/src/Three20UINavigator/Sources/TTURLPattern.m b/src/Three20UINavigator/Sources/TTURLPattern.m index 92612509db..8747b38c88 100644 --- a/src/Three20UINavigator/Sources/TTURLPattern.m +++ b/src/Three20UINavigator/Sources/TTURLPattern.m @@ -39,7 +39,8 @@ @implementation TTURLPattern /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _path = [[NSMutableArray alloc] init]; } return self; diff --git a/src/Three20UINavigator/Sources/TTURLSelector.m b/src/Three20UINavigator/Sources/TTURLSelector.m index 6981d2b5a0..f12ce25c8f 100644 --- a/src/Three20UINavigator/Sources/TTURLSelector.m +++ b/src/Three20UINavigator/Sources/TTURLSelector.m @@ -34,7 +34,8 @@ @implementation TTURLSelector /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithName:(NSString*)name { - if (self = [super init]) { + self = [super init]; + if (self) { _name = [name copy]; _selector = NSSelectorFromString(_name); } diff --git a/src/Three20UINavigator/Sources/TTURLWildcard.m b/src/Three20UINavigator/Sources/TTURLWildcard.m index 96a4341eae..389a1afe7e 100644 --- a/src/Three20UINavigator/Sources/TTURLWildcard.m +++ b/src/Three20UINavigator/Sources/TTURLWildcard.m @@ -37,7 +37,8 @@ @implementation TTURLWildcard /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _argIndex = NSNotFound; _argType = TTURLArgumentTypeNone; } diff --git a/src/Three20UINavigator/Sources/UIViewController+TTNavigator.m b/src/Three20UINavigator/Sources/UIViewController+TTNavigator.m index 534bd34f79..a45924814e 100644 --- a/src/Three20UINavigator/Sources/UIViewController+TTNavigator.m +++ b/src/Three20UINavigator/Sources/UIViewController+TTNavigator.m @@ -53,7 +53,8 @@ @implementation UIViewController (TTNavigator) /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query { - if (self = [self initWithNibName:nil bundle:nil]) { + self = [self initWithNibName:nil bundle:nil]; + if (self) { } return self; diff --git a/src/Three20UINavigator/Three20UINavigator.xcodeproj/project.pbxproj b/src/Three20UINavigator/Three20UINavigator.xcodeproj/project.pbxproj index fcef772150..27f7645611 100755 --- a/src/Three20UINavigator/Three20UINavigator.xcodeproj/project.pbxproj +++ b/src/Three20UINavigator/Three20UINavigator.xcodeproj/project.pbxproj @@ -797,7 +797,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -814,7 +813,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; PREBINDING = NO; SDKROOT = iphoneos; WRAPPER_EXTENSION = octest; diff --git a/src/extThree20CSSStyle/Headers/TTCSSGlobalStyle.h b/src/extThree20CSSStyle/Headers/TTCSSGlobalStyle.h new file mode 100644 index 0000000000..d15733b396 --- /dev/null +++ b/src/extThree20CSSStyle/Headers/TTCSSGlobalStyle.h @@ -0,0 +1,45 @@ +// +// Copyright 2009-2011 Facebook +// +// 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. +// + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// CSS Style helpers + +#define TTCSSSTYLESHEET ([[TTDefaultCSSStyleSheet globalCSSStyleSheet] styleSheet]) + +#define TTCSS_color(_SELECTOR, _STATE) \ +([TTCSSSTYLESHEET colorWithCssSelector:_SELECTOR forState:_STATE]) + +#define TTCSS_backgroundColor(_SELECTOR, _STATE) \ +([TTCSSSTYLESHEET backgroundColorWithCssSelector:_SELECTOR forState:_STATE]) + +#define TTCSS_font(_SELECTOR, _STATE) \ +([TTCSSSTYLESHEET fontWithCssSelector:_SELECTOR forState:_STATE]) + +#define TTCSS_shadowColor(_SELECTOR, _STATE) \ +([TTCSSSTYLESHEET textShadowColorWithCssSelector:_SELECTOR forState:_STATE]) + +#define TTCSS_shadowOffset(_SELECTOR, _STATE) \ +([TTCSSSTYLESHEET textShadowOffsetWithCssSelector:_SELECTOR forState:_STATE]) + +#define TTCSS_shadowRadius(_SELECTOR, _STATE) \ +([TTCSSSTYLESHEET textShadowRadiusWithCssSelector:_SELECTOR forState:_STATE]) + +// _VARNAME must be one of: color, backgroundColor, font, shadowColor, shadowOffset, shadowRadius +#define TTCSSSTATE(_SELECTOR, _VARNAME, _STATE) \ +TTCSS_##_VARNAME(_SELECTOR, _STATE) + +#define TTCSS(_SELECTOR, _VARNAME) \ +TTCSSSTATE(_SELECTOR, _VARNAME, UIControlStateNormal) diff --git a/src/extThree20CSSStyle/Headers/TTCSSStyleSheet.h b/src/extThree20CSSStyle/Headers/TTCSSStyleSheet.h index 8cadd98437..32c3e1a8a9 100644 --- a/src/extThree20CSSStyle/Headers/TTCSSStyleSheet.h +++ b/src/extThree20CSSStyle/Headers/TTCSSStyleSheet.h @@ -75,6 +75,11 @@ */ - (CGSize)textShadowOffsetWithCssSelector:(NSString*)selector forState:(UIControlState)state; +/** + * Get text shadow radius from a specific rule set. + */ +- (CGFloat)textShadowRadiusWithCssSelector:(NSString*)selector forState:(UIControlState)state; + /** * Release all cached data. diff --git a/src/extThree20CSSStyle/Headers/TTShadowStyleAdditions.h b/src/extThree20CSSStyle/Headers/TTShadowStyleAdditions.h new file mode 100644 index 0000000000..336ab5ba28 --- /dev/null +++ b/src/extThree20CSSStyle/Headers/TTShadowStyleAdditions.h @@ -0,0 +1,28 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "Three20Style/TTShadowStyle.h" + +@interface TTShadowStyle (TTCSSCategory) + ++ (TTShadowStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + next:(TTStyle*)next; + ++ (TTShadowStyle*)styleWithCssSelector:(NSString*)selector + next:(TTStyle*)next; + +@end diff --git a/src/extThree20CSSStyle/Headers/TTTextStyleAdditions.h b/src/extThree20CSSStyle/Headers/TTTextStyleAdditions.h new file mode 100644 index 0000000000..8742f90ad1 --- /dev/null +++ b/src/extThree20CSSStyle/Headers/TTTextStyleAdditions.h @@ -0,0 +1,55 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "Three20Style/TTTextStyle.h" + +@interface TTTextStyle (TTCSSCategory) + ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + next:(TTStyle*)next; + ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + minimumFontSize:(CGFloat)minimumFontSize + next:(TTStyle*)next; + ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + minimumFontSize:(CGFloat)minimumFontSize + textAlignment:(UITextAlignment)textAlignment + verticalAlignment:(UIControlContentVerticalAlignment)verticalAlignment + lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSInteger)numberOfLines + next:(TTStyle*)next; + + ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + next:(TTStyle*)next; + ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + minimumFontSize:(CGFloat)minimumFontSize + next:(TTStyle*)next; + ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + minimumFontSize:(CGFloat)minimumFontSize + textAlignment:(UITextAlignment)textAlignment + verticalAlignment:(UIControlContentVerticalAlignment)verticalAlignment + lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSInteger)numberOfLines + next:(TTStyle*)next; + +@end diff --git a/src/extThree20CSSStyle/Headers/UILabelAdditions.h b/src/extThree20CSSStyle/Headers/UILabelAdditions.h new file mode 100644 index 0000000000..987f77d074 --- /dev/null +++ b/src/extThree20CSSStyle/Headers/UILabelAdditions.h @@ -0,0 +1,27 @@ +// +// Copyright 2009-2011 Facebook +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +@interface UILabel (TTCSSCategory) + +/** + * Set a css stylesheet selector, will set font, colors and shadow. + */ +- (void)applyCssSelector:(NSString *)selector; + +@end diff --git a/src/extThree20CSSStyle/Headers/extThree20CSSStyle+Additions.h b/src/extThree20CSSStyle/Headers/extThree20CSSStyle+Additions.h new file mode 100644 index 0000000000..46c75bfb53 --- /dev/null +++ b/src/extThree20CSSStyle/Headers/extThree20CSSStyle+Additions.h @@ -0,0 +1,23 @@ +// +// Copyright 2009-2011 Facebook +// +// 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. +// + +// CSS Style +#import "extThree20CSSStyle/extThree20CSSStyle.h" + +// Additions +#import "extThree20CSSStyle/UILabelAdditions.h" +#import "extThree20CSSStyle/TTTextStyleAdditions.h" +#import "extThree20CSSStyle/TTShadowStyleAdditions.h" diff --git a/src/extThree20CSSStyle/Headers/extThree20CSSStyle.h b/src/extThree20CSSStyle/Headers/extThree20CSSStyle.h index 2f96fa3373..3cbdafb0f4 100644 --- a/src/extThree20CSSStyle/Headers/extThree20CSSStyle.h +++ b/src/extThree20CSSStyle/Headers/extThree20CSSStyle.h @@ -18,5 +18,6 @@ #import "extThree20CSSStyle/TTCSSParser.h" // CSS Stylesheet +#import "extThree20CSSStyle/TTCSSGlobalStyle.h" #import "extThree20CSSStyle/TTCSSStyleSheet.h" #import "extThree20CSSStyle/TTDefaultCSSStyleSheet.h" diff --git a/src/extThree20CSSStyle/README.mdown b/src/extThree20CSSStyle/README.mdown index e379c72ea0..02c856161a 100644 --- a/src/extThree20CSSStyle/README.mdown +++ b/src/extThree20CSSStyle/README.mdown @@ -92,11 +92,6 @@ Known Limitations Font-size is always interpreted in points, regardless of what you specify. This is due to the tricky nature of varying DPI on the various iPhone OS devices. -### Text Shadows - -The "blur" property is always interpreted as "0". This is due to the technical limitations -of specifying blur for text shadows for UILabels. - Supported CSS Properties and Values ----------------------------------- @@ -139,15 +134,13 @@ Fonts are defined with a set of properties that collectively create the final UI ### Text Shadow - text-shadow: px px + text-shadow: px px px textShadowColorWithCssSelector textShadowOffsetWithCssSelector + textShadowRadiusWithCssSelector The `text-shadow` property is defined in one chunk, but read using two distinct methods. -The third parameter is ignored. See "Text Shadows" in the `Known Limitations` section for -the technical reasons. - Examples -------- diff --git a/src/extThree20CSSStyle/Sources/TTCSSParser.m b/src/extThree20CSSStyle/Sources/TTCSSParser.m index 9328ec919b..66f3eb2b94 100644 --- a/src/extThree20CSSStyle/Sources/TTCSSParser.m +++ b/src/extThree20CSSStyle/Sources/TTCSSParser.m @@ -64,7 +64,8 @@ @implementation TTCSSParser /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { _ruleSets = [[NSMutableDictionary alloc] init]; _activeCssSelectors = [[NSMutableArray alloc] init]; } diff --git a/src/extThree20CSSStyle/Sources/TTCSSStyleSheet.m b/src/extThree20CSSStyle/Sources/TTCSSStyleSheet.m index 34ba1bc193..4da4b9b663 100644 --- a/src/extThree20CSSStyle/Sources/TTCSSStyleSheet.m +++ b/src/extThree20CSSStyle/Sources/TTCSSStyleSheet.m @@ -52,7 +52,8 @@ @implementation TTCSSStyleSheet /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(didReceiveMemoryWarning:) @@ -394,8 +395,7 @@ - (UIFont*)fontWithCssSelector:(NSString*)selector forState:(UIControlState)stat NSArray* fontFamilyValues = [ruleSet objectForKey:kCssPropertyFontFamily]; if ([fontFamilyValues count] > 0) { - NSArray* systemFontFamilyNames = [UIFont familyNames]; - NSLog(@"Font families: %@", systemFontFamilyNames); + TTDINFO(@"Font families: %@", [UIFont familyNames]); for (NSString* fontName in fontFamilyValues) { } if ([[fontFamilyValues objectAtIndex:0] isEqualToString:@"bold"]) { @@ -439,6 +439,10 @@ - (NSDictionary*)textShadowWithCssSelector:(NSString*)selector forState:(UIContr // The given selector actually exists in the CSS. if (nil != ruleSet) { NSArray* values = [ruleSet objectForKey:kCssPropertyTextShadow]; + + // Safety check + if (nil == values) return nil; + // Anything more or less is unsupported, and therefore this property is ignored // according to the W3C guidelines. TTDASSERT([values count] >= 4); @@ -450,7 +454,7 @@ - (NSDictionary*)textShadowWithCssSelector:(NSString*)selector forState:(UIContr [values subarrayWithRange: NSMakeRange(3, [values count] - 3)]]; - textShadow = [[NSDictionary alloc] initWithObjectsAndKeys: + textShadow = [NSDictionary dictionaryWithObjectsAndKeys: horizOffset, kKeyTextShadowHOffset, vertOffset, kKeyTextShadowVOffset, blurAmount, kKeyTextShadowBlur, @@ -476,7 +480,7 @@ - (UIColor*)textShadowColorWithCssSelector:(NSString*)selector forState:(UIContr NSDictionary* textShadow = [self textShadowWithCssSelector: selector forState: state]; - return [textShadow objectForKey:kKeyTextShadowColor]; + return nil != textShadow ? [textShadow objectForKey:kKeyTextShadowColor] : nil; } @@ -486,8 +490,20 @@ - (CGSize)textShadowOffsetWithCssSelector:(NSString*)selector forState:(UIContro NSDictionary* textShadow = [self textShadowWithCssSelector: selector forState: state]; - return CGSizeMake([[textShadow objectForKey:kKeyTextShadowHOffset] floatValue], - [[textShadow objectForKey:kKeyTextShadowVOffset] floatValue]); + return nil != textShadow ? + CGSizeMake([[textShadow objectForKey:kKeyTextShadowHOffset] floatValue], + [[textShadow objectForKey:kKeyTextShadowVOffset] floatValue]) : + CGSizeZero; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (CGFloat)textShadowRadiusWithCssSelector:(NSString*)selector forState:(UIControlState)state { + selector = [self selector:selector forState:state]; + + NSDictionary* textShadow = [self textShadowWithCssSelector: selector + forState: state]; + return nil != textShadow ? [[textShadow objectForKey:kKeyTextShadowBlur] floatValue] : 0; } diff --git a/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.h b/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.h index a257e879ce..7af86a52e8 100644 --- a/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.h +++ b/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.h @@ -18,9 +18,6 @@ @class TTCSSStyleSheet; -#define TTCSSBGCOLOR(selector) [[TTDefaultCSSStyleSheet globalCSSStyleSheet] \ - backgroundColorForCSSSelector:selector] - @interface TTDefaultCSSStyleSheet : TTDefaultStyleSheet { @private TTCSSStyleSheet* _styleSheet; @@ -30,8 +27,6 @@ - (BOOL)addStyleSheetFromDisk:(NSString*)filename; -- (UIColor*)backgroundColorForCSSSelector:(NSString*)cssSelector; - + (TTDefaultCSSStyleSheet*)globalCSSStyleSheet; @end diff --git a/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.m b/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.m index ea1d10659d..dc78a4ad45 100644 --- a/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.m +++ b/src/extThree20CSSStyle/Sources/TTDefaultCSSStyleSheet.m @@ -37,7 +37,8 @@ @implementation TTDefaultCSSStyleSheet /////////////////////////////////////////////////////////////////////////////////////////////////// - (id)init { - if (self = [super init]) { + self = [super init]; + if (self) { [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(didReceiveMemoryWarning:) @@ -115,13 +116,6 @@ - (BOOL)addStyleSheetFromDisk:(NSString*)filename { } -/////////////////////////////////////////////////////////////////////////////////////////////////// -- (UIColor*)backgroundColorForCSSSelector:(NSString*)cssSelector { - return [_styleSheet backgroundColorWithCssSelector: cssSelector - forState: UIControlStateNormal]; -} - - /////////////////////////////////////////////////////////////////////////////////////////////////// + (TTDefaultCSSStyleSheet*)globalCSSStyleSheet { TTDASSERT([[TTStyleSheet globalStyleSheet] isKindOfClass:[TTDefaultStyleSheet class]]); diff --git a/src/extThree20CSSStyle/Sources/TTShadowStyleAdditions.m b/src/extThree20CSSStyle/Sources/TTShadowStyleAdditions.m new file mode 100644 index 0000000000..83d27d2121 --- /dev/null +++ b/src/extThree20CSSStyle/Sources/TTShadowStyleAdditions.m @@ -0,0 +1,63 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "extThree20CSSStyle/TTShadowStyleAdditions.h" + +#import "extThree20CSSStyle/TTCSSGlobalStyle.h" +#import "extThree20CSSStyle/TTCSSStyleSheet.h" +#import "extThree20CSSStyle/TTDefaultCSSStyleSheet.h" + +// Core +#import "Three20Core/TTCorePreprocessorMacros.h" + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Additions. + */ +TT_FIX_CATEGORY_BUG(TTCSSShadowStyleAdditions) + +@implementation TTShadowStyle (TTCSSCategory) + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Class public + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTShadowStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + next:(TTStyle*)next { + TTShadowStyle* style = [[[self alloc] initWithNext:next] autorelease]; + style.color = TTCSSSTATE(selector, shadowColor, state); + style.blur = TTCSSSTATE(selector, shadowRadius, state); + style.offset = TTCSSSTATE(selector, shadowOffset, state); + return style; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTShadowStyle*)styleWithCssSelector:(NSString*)selector + next:(TTStyle*)next { + return [self styleWithCssSelector:selector + forState:UIControlStateNormal next:next]; +} + + +@end diff --git a/src/extThree20CSSStyle/Sources/TTTextStyleAdditions.m b/src/extThree20CSSStyle/Sources/TTTextStyleAdditions.m new file mode 100644 index 0000000000..792669a6e1 --- /dev/null +++ b/src/extThree20CSSStyle/Sources/TTTextStyleAdditions.m @@ -0,0 +1,130 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "extThree20CSSStyle/TTTextStyleAdditions.h" + +#import "extThree20CSSStyle/TTCSSGlobalStyle.h" +#import "extThree20CSSStyle/TTCSSStyleSheet.h" +#import "extThree20CSSStyle/TTDefaultCSSStyleSheet.h" + +// Core +#import "Three20Core/TTCorePreprocessorMacros.h" + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Additions. + */ +TT_FIX_CATEGORY_BUG(TTCSSTextStyleAdditions) + +@implementation TTTextStyle (TTCSSCategory) + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Class public + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + next:(TTStyle*)next { + TTTextStyle* style = [[[self alloc] initWithNext:next] autorelease]; + UIFont *font = TTCSSSTATE(selector, font, state); + UIColor *color = TTCSSSTATE(selector, color, state); + UIColor *shadowColor = TTCSSSTATE(selector, shadowColor, state); + CGSize shadowOffset = TTCSSSTATE(selector, shadowOffset, state); + + if (font) style.font = font; + if (color) style.color = color; + if (shadowColor) { + style.shadowColor = shadowColor; + style.shadowOffset = shadowOffset; + } + return style; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + minimumFontSize:(CGFloat)minimumFontSize + next:(TTStyle*)next { + TTTextStyle* style = [self styleWithCssSelector:selector forState:state next:next]; + style.minimumFontSize = minimumFontSize; + return style; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + forState:(UIControlState)state + minimumFontSize:(CGFloat)minimumFontSize + textAlignment:(UITextAlignment)textAlignment + verticalAlignment:(UIControlContentVerticalAlignment)verticalAlignment + lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSInteger)numberOfLines + next:(TTStyle*)next { + TTTextStyle* style = [self styleWithCssSelector:selector forState:state next:next]; + style.minimumFontSize = minimumFontSize; + style.textAlignment = textAlignment; + style.verticalAlignment = verticalAlignment; + style.lineBreakMode = lineBreakMode; + style.numberOfLines = numberOfLines; + return style; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector next:(TTStyle*)next { + return [self styleWithCssSelector:selector + forState:UIControlStateNormal + next:next]; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + minimumFontSize:(CGFloat)minimumFontSize + next:(TTStyle*)next { + return [self styleWithCssSelector:selector + forState:UIControlStateNormal + minimumFontSize:minimumFontSize + next:next]; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// ++ (TTTextStyle*)styleWithCssSelector:(NSString*)selector + minimumFontSize:(CGFloat)minimumFontSize + textAlignment:(UITextAlignment)textAlignment + verticalAlignment:(UIControlContentVerticalAlignment)verticalAlignment + lineBreakMode:(UILineBreakMode)lineBreakMode + numberOfLines:(NSInteger)numberOfLines + next:(TTStyle*)next { + return [self styleWithCssSelector:selector + forState:UIControlStateNormal + minimumFontSize:minimumFontSize + textAlignment:textAlignment + verticalAlignment:verticalAlignment + lineBreakMode:lineBreakMode + numberOfLines:numberOfLines + next:next]; +} + +@end diff --git a/src/extThree20CSSStyle/Sources/UILabelAdditions.m b/src/extThree20CSSStyle/Sources/UILabelAdditions.m new file mode 100644 index 0000000000..2f57da426d --- /dev/null +++ b/src/extThree20CSSStyle/Sources/UILabelAdditions.m @@ -0,0 +1,73 @@ +// +// Copyright 2009-2011 Facebook +// +// 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 "extThree20CSSStyle/UILabelAdditions.h" + +#import "extThree20CSSStyle/TTCSSGlobalStyle.h" +#import "extThree20CSSStyle/TTCSSStyleSheet.h" +#import "extThree20CSSStyle/TTDefaultCSSStyleSheet.h" + +// Core +#import "Three20Core/TTCorePreprocessorMacros.h" + +#ifdef __IPHONE_3_2 +#import +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Additions. + */ +TT_FIX_CATEGORY_BUG(TTCSSLabelAdditions) + +@implementation UILabel (TTCSSCategory) + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)applyCssSelector:(NSString *)selector { + UIFont *font = TTCSS(selector, font); + UIColor *color = TTCSS(selector, color); + UIColor *backgroundColor = TTCSS(selector, backgroundColor); + + if (font) self.font = font; + if (color) self.textColor = color; + if (backgroundColor) self.backgroundColor = backgroundColor; + + UIColor *shadowColor = TTCSS(selector, shadowColor); + CGSize shadowOffset = TTCSS(selector, shadowOffset); + if (shadowColor) { +#ifdef __IPHONE_3_2 + CGFloat shadowRadius = TTCSS(selector, shadowRadius); + if (shadowRadius) { + self.layer.shadowOpacity = 1.0; + self.layer.shadowColor = shadowColor.CGColor; + self.layer.shadowOffset = shadowOffset; + self.layer.shadowRadius = shadowRadius; + self.layer.masksToBounds = NO; + } + else { + self.shadowColor = shadowColor; + self.shadowOffset = shadowOffset; + } +#else + self.shadowColor = shadowColor; + self.shadowOffset = shadowOffset; +#endif + } +} + +@end diff --git a/src/extThree20CSSStyle/extThree20CSSStyle.xcodeproj/project.pbxproj b/src/extThree20CSSStyle/extThree20CSSStyle.xcodeproj/project.pbxproj index 0bf3019949..1439d4208c 100755 --- a/src/extThree20CSSStyle/extThree20CSSStyle.xcodeproj/project.pbxproj +++ b/src/extThree20CSSStyle/extThree20CSSStyle.xcodeproj/project.pbxproj @@ -42,6 +42,14 @@ 6E85154011B2E8660071A4FD /* TTCSSParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E85145111B1B5200071A4FD /* TTCSSParser.m */; }; 6EAE4E5611B2F0A7001073B4 /* testcase.css in Resources */ = {isa = PBXBuildFile; fileRef = 6EAE4E5511B2F0A7001073B4 /* testcase.css */; }; 6EB460DA1183D8CB00685649 /* libextThree20CSSStyle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF31F3A0F352DF5000DE5D2 /* libextThree20CSSStyle.a */; }; + 90C3A115132B9BE100AC06A2 /* TTCSSGlobalStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 90C3A114132B9BE100AC06A2 /* TTCSSGlobalStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90C3A1B5132BE3EB00AC06A2 /* UILabelAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 90C3A1B3132BE3EB00AC06A2 /* UILabelAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90C3A1B6132BE3EB00AC06A2 /* UILabelAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 90C3A1B4132BE3EB00AC06A2 /* UILabelAdditions.m */; }; + 90C3A1B8132BE40100AC06A2 /* extThree20CSSStyle+Additions.h in Headers */ = {isa = PBXBuildFile; fileRef = 90C3A1B7132BE40100AC06A2 /* extThree20CSSStyle+Additions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90C3A1BD132BF2D100AC06A2 /* TTTextStyleAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 90C3A1BB132BF2D100AC06A2 /* TTTextStyleAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90C3A1BE132BF2D100AC06A2 /* TTTextStyleAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 90C3A1BC132BF2D100AC06A2 /* TTTextStyleAdditions.m */; }; + 90C3A1C4132BFDFF00AC06A2 /* TTShadowStyleAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 90C3A1C2132BFDFE00AC06A2 /* TTShadowStyleAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90C3A1C5132BFDFF00AC06A2 /* TTShadowStyleAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 90C3A1C3132BFDFF00AC06A2 /* TTShadowStyleAdditions.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -156,6 +164,15 @@ 6EAE4E5511B2F0A7001073B4 /* testcase.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = testcase.css; path = UnitTests/Resources/CSS/testcase.css; sourceTree = ""; }; 6EB460921183D16000685649 /* UnitTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "UnitTests-Info.plist"; path = "UnitTests/Resources/PropertyLists/UnitTests-Info.plist"; sourceTree = ""; }; 6EB460A61183D2AC00685649 /* UnitTests.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = UnitTests.xcconfig; path = Configurations/UnitTests.xcconfig; sourceTree = ""; }; + 90C3A114132B9BE100AC06A2 /* TTCSSGlobalStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTCSSGlobalStyle.h; path = Headers/TTCSSGlobalStyle.h; sourceTree = ""; }; + 90C3A1B3132BE3EB00AC06A2 /* UILabelAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UILabelAdditions.h; path = Headers/UILabelAdditions.h; sourceTree = ""; }; + 90C3A1B4132BE3EB00AC06A2 /* UILabelAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UILabelAdditions.m; path = Sources/UILabelAdditions.m; sourceTree = ""; }; + 90C3A1B7132BE40100AC06A2 /* extThree20CSSStyle+Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "extThree20CSSStyle+Additions.h"; path = "Headers/extThree20CSSStyle+Additions.h"; sourceTree = ""; }; + 90C3A1B9132BEB7100AC06A2 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 90C3A1BB132BF2D100AC06A2 /* TTTextStyleAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTTextStyleAdditions.h; path = Headers/TTTextStyleAdditions.h; sourceTree = ""; }; + 90C3A1BC132BF2D100AC06A2 /* TTTextStyleAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTTextStyleAdditions.m; path = Sources/TTTextStyleAdditions.m; sourceTree = ""; }; + 90C3A1C2132BFDFE00AC06A2 /* TTShadowStyleAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTShadowStyleAdditions.h; path = Headers/TTShadowStyleAdditions.h; sourceTree = ""; }; + 90C3A1C3132BFDFF00AC06A2 /* TTShadowStyleAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTShadowStyleAdditions.m; path = Sources/TTShadowStyleAdditions.m; sourceTree = ""; }; BEF31F3A0F352DF5000DE5D2 /* libextThree20CSSStyle.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libextThree20CSSStyle.a; sourceTree = BUILT_PRODUCTS_DIR; }; EB9E6C6210B6A8F800DE563C /* extCSSStyleUnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = extCSSStyleUnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -197,6 +214,7 @@ children = ( 6E036B3611B38A420025E8EE /* README.mdown */, 6E646518118805EB00F08CB1 /* extThree20CSSStyle.h */, + 90C3A1B7132BE40100AC06A2 /* extThree20CSSStyle+Additions.h */, 66313D671267BFCF00C09C9F /* extThree20CSSStyle_Prefix.pch */, 66E722D1129392EB007134B0 /* TTExtensionLoader.h */, 66E722CC129392DD007134B0 /* TTExtensionLoader.m */, @@ -272,6 +290,7 @@ 6E85154F11B2E93E0071A4FD /* Foundation.framework */, 6E85155511B2E94E0071A4FD /* CoreGraphics.framework */, 6E036B6111B38D1B0025E8EE /* UIKit.framework */, + 90C3A1B9132BEB7100AC06A2 /* QuartzCore.framework */, ); name = Frameworks; sourceTree = ""; @@ -318,6 +337,8 @@ 6EB4609C1183D1E000685649 /* Source */ = { isa = PBXGroup; children = ( + 90C3A10F132B9B5000AC06A2 /* Global */, + 90C3A1AF132BE3C800AC06A2 /* Additions */, 6E036A2A11B364AF0025E8EE /* CSS Parser */, 6E036A2B11B364B60025E8EE /* CSS Stylesheet */, ); @@ -354,6 +375,27 @@ name = Configurations; sourceTree = ""; }; + 90C3A10F132B9B5000AC06A2 /* Global */ = { + isa = PBXGroup; + children = ( + 90C3A114132B9BE100AC06A2 /* TTCSSGlobalStyle.h */, + ); + name = Global; + sourceTree = ""; + }; + 90C3A1AF132BE3C800AC06A2 /* Additions */ = { + isa = PBXGroup; + children = ( + 90C3A1B3132BE3EB00AC06A2 /* UILabelAdditions.h */, + 90C3A1B4132BE3EB00AC06A2 /* UILabelAdditions.m */, + 90C3A1BB132BF2D100AC06A2 /* TTTextStyleAdditions.h */, + 90C3A1BC132BF2D100AC06A2 /* TTTextStyleAdditions.m */, + 90C3A1C2132BFDFE00AC06A2 /* TTShadowStyleAdditions.h */, + 90C3A1C3132BFDFF00AC06A2 /* TTShadowStyleAdditions.m */, + ); + name = Additions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -362,11 +404,16 @@ buildActionMask = 2147483647; files = ( 6E3C3470118806590079637E /* extThree20CSSStyle.h in Headers */, + 90C3A1B8132BE40100AC06A2 /* extThree20CSSStyle+Additions.h in Headers */, + 90C3A115132B9BE100AC06A2 /* TTCSSGlobalStyle.h in Headers */, 6E85145411B1B5390071A4FD /* TTCSSParser.h in Headers */, 6E8514ED11B2E3F30071A4FD /* CssTokens.h in Headers */, 6E036A3211B364DF0025E8EE /* TTCSSStyleSheet.h in Headers */, 6E036D1911B487CF0025E8EE /* TTDefaultCSSStyleSheet.h in Headers */, 66E722D3129392EB007134B0 /* TTExtensionLoader.h in Headers */, + 90C3A1B5132BE3EB00AC06A2 /* UILabelAdditions.h in Headers */, + 90C3A1BD132BF2D100AC06A2 /* TTTextStyleAdditions.h in Headers */, + 90C3A1C4132BFDFF00AC06A2 /* TTShadowStyleAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -583,6 +630,9 @@ 6E036A3011B364CC0025E8EE /* TTCSSStyleSheet.m in Sources */, 6E036D1A11B487CF0025E8EE /* TTDefaultCSSStyleSheet.m in Sources */, 66E722D0129392DD007134B0 /* TTExtensionLoader.m in Sources */, + 90C3A1B6132BE3EB00AC06A2 /* UILabelAdditions.m in Sources */, + 90C3A1BE132BF2D100AC06A2 /* TTTextStyleAdditions.m in Sources */, + 90C3A1C5132BFDFF00AC06A2 /* TTShadowStyleAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/extThree20JSON/Source/TTURLJSONResponse.m b/src/extThree20JSON/Source/TTURLJSONResponse.m index e3bc5938b8..19cdd7936c 100644 --- a/src/extThree20JSON/Source/TTURLJSONResponse.m +++ b/src/extThree20JSON/Source/TTURLJSONResponse.m @@ -62,6 +62,9 @@ - (NSError*)request:(TTURLRequest*)request processResponse:(NSHTTPURLResponse*)r if ([data isKindOfClass:[NSData class]]) { #ifdef EXTJSON_SBJSON NSString* json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + // When there are newline characters in the JSON string, + // the error "Unescaped control character '0x9'" will be thrown. This removes those characters. + json = [json stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; _rootObject = [[json JSONValue] retain]; TT_RELEASE_SAFELY(json); if (!_rootObject) { diff --git a/src/extThree20JSON/Vendors/JSON/Changes.markdown b/src/extThree20JSON/Vendors/JSON/Changes.markdown old mode 100755 new mode 100644 index 922afccf35..f448f850a4 --- a/src/extThree20JSON/Vendors/JSON/Changes.markdown +++ b/src/extThree20JSON/Vendors/JSON/Changes.markdown @@ -1,11 +1,70 @@ -# JSON Framework Changes +# JSON Framework Changes + +## Version 3.0beta2 (TBD) + +## Changes +* [Issue 46][#46]: Stream Parser delegate methods are now optional. + +### Bug Fixes +* [Issue 42][#42]: Fix bug in handling of Unicode Surrogate Pairs. +* [Issue 48][#48]: Increase precision when writing floating-point numbers so NSTimeInterval instances since epoch can be represented fully. + +[#42]: http://github.com/stig/json-framework/issues/#issue/42 +[#46]: http://github.com/stig/json-framework/issues/#issue/46 +[#48]: http://github.com/stig/json-framework/issues/#issue/48 + +## Version 3.0beta1 (January 30th, 2011) + +### Bug Fixes +* [Issue 36][#36]: Fix bug in build script that caused it to break if $SRCROOT has spaces. + +[#36]: http://github.com/stig/json-framework/issues/#issue/36 + +### Changes + +* Remove the hacky dataToHere method in favour of just exposing the internal NSMutableData buffer. +* Added a minimal Mac example project showing how to link to an external JSON framework rather than copying the sources into your project. + +## Version 3.0alpha3 (January 2nd, 2011) + +* Added documentation to the TwitterStream sample project. +* Fixed a few warnings, bugs & a memory leak reported by Andy Warwick. + +## Version 3.0alpha2 (December 28th, 2010) + +### Changes + +* Minor changes to formatting when the HumanReadable flag is set. Empty arrays and objects are no longer special-cased to appear on a single line. The separator between key and value in an object has changed to ': ' rather than ' : '. +* [Issue 25][#25]: Simplified error handling. + +### New Features + +* [Issue 16][#16]: Added support for parsing a UTF8 data stream. This means you can start parsing huge documents before it's all downloaded. Supports skipping the outer-most layer of huge arrays/objects or parsing multiple whitespace-separated completed documents. +* [Issue 12][#12]: Added support for writing JSON to a data stream. This means you can write huge JSON documents to disk, or an HTTP destination, without having to hold the entire structure in memory. You can even generate it as you go, and just stream snapshots to an external process. +* [Issue 18][#18] & [27][#27]: Re-orient API to be NSData-based. The NSString-oriented API methods now delegates to this. + +### Enhancements + +* [Issue 9][#9]: Improve performance of the SBJsonWriter. This implementation is nearly twice as fast as 2.3.x on Sam Soffes' [benchmarks][bench]. +* [Issue 30][#30]: Added *TwitterStream* example project showing how to interact with Twitter's multi-document stream. (See `Examples/TwitterStream` in the distribution.) + +[bench]: http://github.com/samsoffes/json-benchmarks +[#9]: http://github.com/stig/json-framework/issues/#issue/9 +[#12]: http://github.com/stig/json-framework/issues/#issue/12 +[#16]: http://github.com/stig/json-framework/issues/#issue/16 +[#18]: http://github.com/stig/json-framework/issues/#issue/18 +[#27]: http://github.com/stig/json-framework/issues/#issue/27 +[#30]: http://github.com/stig/json-framework/issues/#issue/30 +[#25]: http://github.com/stig/json-framework/issues/#issue/25 ## Version 2.3.1 (September 25th, 2010) ### Changes +* Move to host releases on Github rather than Google code. * Renamed .md files to .markdown. * Removed bench target--use [Sam Soffes's benchmarks][json-benchmark] instead. +* Releases are no longer a munged form of the source tree, but identical to the tagged source. [json-benchmark]: http://github.com/samsoffes/json-benchmark @@ -29,7 +88,7 @@ ### Changes * **Parsing performance improvements.** -[Issue 56][issue-56]: Dewvinci & Tobias Hoehmann came up with a patch to improve parsing of short JSON texts with lots of numbers by over 60%. +[Issue 56][issue-56]: Dewvinci & Tobias Höhmann came up with a patch to improve parsing of short JSON texts with lots of numbers by over 60%. * **Refactored tests to be more data-driven.** This should make the source leaner and easier to maintain. * **Removed problematic SDK** diff --git a/src/extThree20JSON/Vendors/JSON/Credits.markdown b/src/extThree20JSON/Vendors/JSON/Credits.markdown old mode 100755 new mode 100644 index cc16c08402..761c028ee0 --- a/src/extThree20JSON/Vendors/JSON/Credits.markdown +++ b/src/extThree20JSON/Vendors/JSON/Credits.markdown @@ -45,3 +45,17 @@ Reported bug in SBJsonWriter's handling of NaN, Infinity and -Infinity. ## jinksys Reported bug with header inclusion of framework. + +## Lloyd Hilaiel +For his work on [YAJL][yajl] (Yet Another JSON Library) + +[yajl]: http://github.com/lloyd/yajl + +## Gabriel Handford + +For his work on [YAJL-ObjC][yajl-objc]. + +[yajl-objc]: http://github.com/gabriel/yajl-objc + +## Andy Warwick +Reported several build-issues that I didn't catch. \ No newline at end of file diff --git a/src/extThree20JSON/Vendors/JSON/JSON.h b/src/extThree20JSON/Vendors/JSON/JSON.h index 23f2ea176e..abce2d857e 100755 --- a/src/extThree20JSON/Vendors/JSON/JSON.h +++ b/src/extThree20JSON/Vendors/JSON/JSON.h @@ -1,20 +1,20 @@ /* Copyright (C) 2009-2010 Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - + to endorse or promote products derived from this software without specific + prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -28,38 +28,42 @@ */ /** - A strict JSON parser and generator for Objective-C - + @mainpage A strict JSON parser and generator for Objective-C + JSON (JavaScript Object Notation) is a lightweight data-interchange format. This framework provides two apis for parsing and generating JSON. One standard object-based and a higher level api consisting of categories added to existing Objective-C classes. - - This framework does its best to be as strict as possible, both in what it accepts and what it generates. For example, it does not support trailing commas in arrays or objects. Nor does it support embedded comments, or anything else not in the JSON specification. This is considered a feature. - + + This framework does its best to be as strict as possible, both in what it accepts and what it generates. For example, it does not support trailing commas in arrays or objects. Nor does it support embedded comments, or anything else not in the JSON specification. This is considered a feature. + @section Links - + @li Project home page. - @li Online version of the API documentation. - -*/ + @li Online version of the API documentation. + + */ -// This setting of 1 is best if you copy the source into your project. +// This setting of 1 is best if you copy the source into your project. // The build transforms the 1 to a 0 when building the framework and static lib. #if 1 #import "extThree20JSON/SBJsonParser.h" #import "extThree20JSON/SBJsonWriter.h" -#import "extThree20JSON/NSObject+SBJSON.h" -#import "extThree20JSON/NSString+SBJSON.h" +#import "extThree20JSON/SBJsonStreamWriter.h" +#import "extThree20JSON/SBJsonStreamParser.h" +#import "extThree20JSON/SBJsonStreamParserAdapter.h" +#import "extThree20JSON/NSObject+JSON.h" #else #import #import -#import -#import +#import +#import +#import +#import #endif diff --git a/src/extThree20JSON/Vendors/JSON/NSObject+JSON.h b/src/extThree20JSON/Vendors/JSON/NSObject+JSON.h new file mode 100644 index 0000000000..4a9e7603bb --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/NSObject+JSON.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#pragma mark JSON Writing + +/// Adds JSON generation to NSArray +@interface NSArray (NSArray_SBJsonWriting) + +/// Returns a string containing the receiver encoded in JSON. +- (NSString *)JSONRepresentation; + +@end + + +/// Adds JSON generation to NSArray +@interface NSDictionary (NSDictionary_SBJsonWriting) + +/// Returns a string containing the receiver encoded in JSON. +- (NSString *)JSONRepresentation; + +@end + +#pragma mark JSON Parsing + +/// Adds JSON parsing methods to NSString +@interface NSString (NSString_SBJsonParsing) + +/// Returns the NSDictionary or NSArray represented by the receiver's JSON representation, or nil on error +- (id)JSONValue; + +@end + + diff --git a/src/extThree20JSON/Vendors/JSON/NSObject+JSON.m b/src/extThree20JSON/Vendors/JSON/NSObject+JSON.m new file mode 100644 index 0000000000..9c7ebaf2ae --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/NSObject+JSON.m @@ -0,0 +1,60 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "NSObject+JSON.h" +#import "SBJsonWriter.h" +#import "SBJsonParser.h" + +@implementation NSObject (NSObject_SBJsonWriting) + +- (NSString *)JSONRepresentation { + SBJsonWriter *jsonWriter = [SBJsonWriter new]; + NSString *json = [jsonWriter stringWithObject:self]; + if (!json) + NSLog(@"-JSONRepresentation failed. Error is: %@", jsonWriter.error); + [jsonWriter release]; + return json; +} + +@end + + + +@implementation NSString (NSString_SBJsonParsing) + +- (id)JSONValue { + SBJsonParser *jsonParser = [SBJsonParser new]; + id repr = [jsonParser objectWithString:self]; + if (!repr) + NSLog(@"-JSONValue failed. Error is: %@", jsonParser.error); + [jsonParser release]; + return repr; +} + +@end diff --git a/src/extThree20JSON/Vendors/JSON/Readme.markdown b/src/extThree20JSON/Vendors/JSON/Readme.markdown old mode 100755 new mode 100644 diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonParser.h b/src/extThree20JSON/Vendors/JSON/SBJsonParser.h old mode 100755 new mode 100644 index ab9831ac1b..8f3d0a3011 --- a/src/extThree20JSON/Vendors/JSON/SBJsonParser.h +++ b/src/extThree20JSON/Vendors/JSON/SBJsonParser.h @@ -1,20 +1,20 @@ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -28,51 +28,79 @@ */ #import -#import "extThree20JSON/private/SBJsonBase.h" /** @brief The JSON parser class. - + JSON is mapped to Objective-C types in the following way: - + @li Null -> NSNull @li String -> NSMutableString @li Array -> NSMutableArray @li Object -> NSMutableDictionary @li Boolean -> NSNumber (initialised with -initWithBool:) @li Number -> (NSNumber | NSDecimalNumber) - + Since Objective-C doesn't have a dedicated class for boolean values, these turns into NSNumber - instances. These are initialised with the -initWithBool: method, and + instances. These are initialised with the -initWithBool: method, and round-trip back to JSON properly. (They won't silently suddenly become 0 or 1; they'll be represented as 'true' and 'false' again.) - + As an optimisation short JSON integers turn into NSNumber instances, while complex ones turn into NSDecimalNumber instances. We can thus avoid any loss of precision as JSON allows ridiculously large numbers. - + */ -@interface SBJsonParser : SBJsonBase { -@private - const char *c; +@interface SBJsonParser : NSObject { + id value; + NSString *error; + NSUInteger depth, maxDepth; + } /** - @brief Return the object represented by the given string + @brief The maximum recursing depth. + + Defaults to 512. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property NSUInteger maxDepth; +/** + @brief Return an error trace, or nil if there was no errors. + + Note that this method returns the trace of the last method that failed. + You need to check the return value of the call you're making to figure out + if the call actually failed, before you know call this method. + */ +@property(copy) NSString *error; + +/** + @brief Return the object represented by the given NSData object. + + The data *must* be UTF8 encoded. + @param data the data to parse. + + */ +- (id)objectWithData:(NSData*)data; + +/** + @brief Return the object represented by the given string + Returns the object represented by the passed-in string or nil on error. The returned object can be a string, number, boolean, null, array or dictionary. - + @param repr the json string to parse */ - (id)objectWithString:(NSString *)repr; /** @brief Return the object represented by the given string - + Returns the object represented by the passed-in string or nil on error. The returned object can be a string, number, boolean, null, array or dictionary. - + @param jsonText the json string to parse @param error pointer to an NSError object to populate on error */ @@ -80,7 +108,6 @@ - (id)objectWithString:(NSString*)jsonText error:(NSError**)error; - @end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonParser.m b/src/extThree20JSON/Vendors/JSON/SBJsonParser.m old mode 100755 new mode 100644 index d498993a8d..24ccb86e5d --- a/src/extThree20JSON/Vendors/JSON/SBJsonParser.m +++ b/src/extThree20JSON/Vendors/JSON/SBJsonParser.m @@ -1,20 +1,20 @@ /* Copyright (C) 2009,2010 Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -27,490 +27,94 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "extThree20JSON/SBJsonParser.h" - -@interface SBJsonParser () - -- (BOOL)scanValue:(NSObject **)o; - -- (BOOL)scanRestOfArray:(NSMutableArray **)o; -- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o; -- (BOOL)scanRestOfNull:(NSNull **)o; -- (BOOL)scanRestOfFalse:(NSNumber **)o; -- (BOOL)scanRestOfTrue:(NSNumber **)o; -- (BOOL)scanRestOfString:(NSMutableString **)o; - -// Cannot manage without looking at the first digit -- (BOOL)scanNumber:(NSNumber **)o; - -- (BOOL)scanHexQuad:(unichar *)x; -- (BOOL)scanUnicodeChar:(unichar *)x; - -- (BOOL)scanIsAtEnd; +#import "SBJsonParser.h" +#import "SBJsonStreamParser.h" +#import "SBJsonStreamParserAdapter.h" +@interface SBJsonParser () @end -#define skipWhitespace(c) while (isspace(*c)) c++ -#define skipDigits(c) while (isdigit(*c)) c++ - @implementation SBJsonParser -static char ctrl[0x22]; +@synthesize maxDepth; +@synthesize error; +#pragma mark SBJsonStreamParserAdapterDelegate -+ (void)initialize { - ctrl[0] = '\"'; - ctrl[1] = '\\'; - for (int i = 1; i < 0x20; i++) - ctrl[i+1] = i; - ctrl[0x21] = 0; +- (void)parser:(SBJsonStreamParser*)parser foundArray:(NSArray *)array { + value = [array retain]; } -- (id)objectWithString:(NSString *)repr { - [self clearErrorTrace]; - - if (!repr) { - [self addErrorWithCode:EINPUT description:@"Input was 'nil'"]; - return nil; - } - - depth = 0; - c = [repr UTF8String]; - - id o; - if (![self scanValue:&o]) { - return nil; - } - - // We found some valid JSON. But did it also contain something else? - if (![self scanIsAtEnd]) { - [self addErrorWithCode:ETRAILGARBAGE description:@"Garbage after JSON"]; - return nil; - } - - NSAssert1(o, @"Should have a valid object from %@", repr); - - // Check that the object we've found is a valid JSON container. - if (![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) { - [self addErrorWithCode:EFRAGMENT description:@"Valid fragment, but not JSON"]; - return nil; - } - - return o; +- (void)parser:(SBJsonStreamParser*)parser foundObject:(NSDictionary *)dict { + value = [dict retain]; } -- (id)objectWithString:(NSString*)repr error:(NSError**)error { - id tmp = [self objectWithString:repr]; - if (tmp) - return tmp; - - if (error) - *error = [self.errorTrace lastObject]; - return nil; +- (id)init { + self = [super init]; + if (self) + self.maxDepth = 512; + return self; } - -/* - In contrast to the public methods, it is an error to omit the error parameter here. - */ -- (BOOL)scanValue:(NSObject **)o -{ - skipWhitespace(c); - - switch (*c++) { - case '{': - return [self scanRestOfDictionary:(NSMutableDictionary **)o]; - break; - case '[': - return [self scanRestOfArray:(NSMutableArray **)o]; - break; - case '"': - return [self scanRestOfString:(NSMutableString **)o]; - break; - case 'f': - return [self scanRestOfFalse:(NSNumber **)o]; - break; - case 't': - return [self scanRestOfTrue:(NSNumber **)o]; - break; - case 'n': - return [self scanRestOfNull:(NSNull **)o]; - break; - case '-': - case '0'...'9': - c--; // cannot verify number correctly without the first character - return [self scanNumber:(NSNumber **)o]; - break; - case '+': - [self addErrorWithCode:EPARSENUM description: @"Leading + disallowed in number"]; - return NO; - break; - case 0x0: - [self addErrorWithCode:EEOF description:@"Unexpected end of string"]; - return NO; - break; - default: - [self addErrorWithCode:EPARSE description: @"Unrecognised leading character"]; - return NO; - break; - } - - NSAssert(0, @"Should never get here"); - return NO; +- (void)dealloc { + [error release]; + [super dealloc]; } -- (BOOL)scanRestOfTrue:(NSNumber **)o -{ - if (!strncmp(c, "rue", 3)) { - c += 3; - *o = [NSNumber numberWithBool:YES]; - return YES; - } - [self addErrorWithCode:EPARSE description:@"Expected 'true'"]; - return NO; -} - -- (BOOL)scanRestOfFalse:(NSNumber **)o -{ - if (!strncmp(c, "alse", 4)) { - c += 4; - *o = [NSNumber numberWithBool:NO]; - return YES; - } - [self addErrorWithCode:EPARSE description: @"Expected 'false'"]; - return NO; -} - -- (BOOL)scanRestOfNull:(NSNull **)o { - if (!strncmp(c, "ull", 3)) { - c += 3; - *o = [NSNull null]; - return YES; - } - [self addErrorWithCode:EPARSE description: @"Expected 'null'"]; - return NO; -} - -- (BOOL)scanRestOfArray:(NSMutableArray **)o { - if (maxDepth && ++depth > maxDepth) { - [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; - return NO; - } - - *o = [NSMutableArray arrayWithCapacity:8]; - - for (; *c ;) { - id v; - - skipWhitespace(c); - if (*c == ']' && c++) { - depth--; - return YES; - } - - if (![self scanValue:&v]) { - [self addErrorWithCode:EPARSE description:@"Expected value while parsing array"]; - return NO; - } - - [*o addObject:v]; - - skipWhitespace(c); - if (*c == ',' && c++) { - skipWhitespace(c); - if (*c == ']') { - [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in array"]; - return NO; - } - } - } - - [self addErrorWithCode:EEOF description: @"End of input while parsing array"]; - return NO; -} - -- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o -{ - if (maxDepth && ++depth > maxDepth) { - [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; - return NO; - } +#pragma mark Methods - *o = [NSMutableDictionary dictionaryWithCapacity:7]; +- (id)objectWithData:(NSData *)data { - for (; *c ;) { - id k, v; - - skipWhitespace(c); - if (*c == '}' && c++) { - depth--; - return YES; - } - - if (!(*c == '\"' && c++ && [self scanRestOfString:&k])) { - [self addErrorWithCode:EPARSE description: @"Object key string expected"]; - return NO; - } - - skipWhitespace(c); - if (*c != ':') { - [self addErrorWithCode:EPARSE description: @"Expected ':' separating key and value"]; - return NO; - } - - c++; - if (![self scanValue:&v]) { - NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k]; - [self addErrorWithCode:EPARSE description: string]; - return NO; - } - - [*o setObject:v forKey:k]; - - skipWhitespace(c); - if (*c == ',' && c++) { - skipWhitespace(c); - if (*c == '}') { - [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in object"]; - return NO; - } - } - } - - [self addErrorWithCode:EEOF description: @"End of input while parsing object"]; - return NO; -} - -- (BOOL)scanRestOfString:(NSMutableString **)o -{ - // if the string has no control characters in it, return it in one go, without any temporary allocations. - size_t len = strcspn(c, ctrl); - if (len && *(c + len) == '\"') - { - *o = [[[NSMutableString alloc] initWithBytes:(char*)c length:len encoding:NSUTF8StringEncoding] autorelease]; - c += len + 1; - return YES; - } - - *o = [NSMutableString stringWithCapacity:16]; - do { - // First see if there's a portion we can grab in one go. - // Doing this caused a massive speedup on the long string. - len = strcspn(c, ctrl); - if (len) { - // check for - id t = [[NSString alloc] initWithBytesNoCopy:(char*)c - length:len - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - if (t) { - [*o appendString:t]; - [t release]; - c += len; - } - } - - if (*c == '"') { - c++; - return YES; - - } else if (*c == '\\') { - unichar uc = *++c; - switch (uc) { - case '\\': - case '/': - case '"': - break; - - case 'b': uc = '\b'; break; - case 'n': uc = '\n'; break; - case 'r': uc = '\r'; break; - case 't': uc = '\t'; break; - case 'f': uc = '\f'; break; - - case 'u': - c++; - if (![self scanUnicodeChar:&uc]) { - [self addErrorWithCode:EUNICODE description: @"Broken unicode character"]; - return NO; - } - c--; // hack. - break; - default: - [self addErrorWithCode:EESCAPE description: [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]]; - return NO; - break; - } - CFStringAppendCharacters((CFMutableStringRef)*o, &uc, 1); - c++; - - } else if (*c < 0x20) { - [self addErrorWithCode:ECTRL description: [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]]; - return NO; - - } else { - NSLog(@"should not be able to get here"); - } - } while (*c); - - [self addErrorWithCode:EEOF description:@"Unexpected EOF while parsing string"]; - return NO; -} - -- (BOOL)scanUnicodeChar:(unichar *)x -{ - unichar hi, lo; - - if (![self scanHexQuad:&hi]) { - [self addErrorWithCode:EUNICODE description: @"Missing hex quad"]; - return NO; - } - - if (hi >= 0xd800) { // high surrogate char? - if (hi < 0xdc00) { // yes - expect a low char - - if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo])) { - [self addErrorWithCode:EUNICODE description: @"Missing low character in surrogate pair"]; - return NO; - } - - if (lo < 0xdc00 || lo >= 0xdfff) { - [self addErrorWithCode:EUNICODE description:@"Invalid low surrogate char"]; - return NO; - } - - hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000; - - } else if (hi < 0xe000) { - [self addErrorWithCode:EUNICODE description:@"Invalid high character in surrogate pair"]; - return NO; - } + if (!data) { + self.error = @"Input was 'nil'"; + return nil; } - *x = hi; - return YES; + SBJsonStreamParserAdapter *adapter = [SBJsonStreamParserAdapter new]; + adapter.delegate = self; + + SBJsonStreamParser *parser = [SBJsonStreamParser new]; + parser.maxDepth = self.maxDepth; + parser.delegate = adapter; + + id retval = nil; + switch ([parser parse:data]) { + case SBJsonStreamParserComplete: + retval = [value autorelease]; + break; + + case SBJsonStreamParserWaitingForData: + self.error = @"Didn't find full object before EOF"; + break; + + case SBJsonStreamParserError: + self.error = parser.error; + break; + } + + + [adapter release]; + [parser release]; + + return retval; } -- (BOOL)scanHexQuad:(unichar *)x -{ - *x = 0; - for (int i = 0; i < 4; i++) { - unichar uc = *c; - c++; - int d = (uc >= '0' && uc <= '9') - ? uc - '0' : (uc >= 'a' && uc <= 'f') - ? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F') - ? (uc - 'A' + 10) : -1; - if (d == -1) { - [self addErrorWithCode:EUNICODE description:@"Missing hex digit in quad"]; - return NO; - } - *x *= 16; - *x += d; - } - return YES; -} - -- (BOOL)scanNumber:(NSNumber **)o -{ - BOOL simple = YES; - - const char *ns = c; - - // The logic to test for validity of the number formatting is relicensed - // from JSON::XS with permission from its author Marc Lehmann. - // (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .) - - if ('-' == *c) - c++; - - if ('0' == *c && c++) { - if (isdigit(*c)) { - [self addErrorWithCode:EPARSENUM description: @"Leading 0 disallowed in number"]; - return NO; - } - - } else if (!isdigit(*c) && c != ns) { - [self addErrorWithCode:EPARSENUM description: @"No digits after initial minus"]; - return NO; - - } else { - skipDigits(c); - } - - // Fractional part - if ('.' == *c && c++) { - simple = NO; - if (!isdigit(*c)) { - [self addErrorWithCode:EPARSENUM description: @"No digits after decimal point"]; - return NO; - } - skipDigits(c); - } - - // Exponential part - if ('e' == *c || 'E' == *c) { - simple = NO; - c++; - - if ('-' == *c || '+' == *c) - c++; - - if (!isdigit(*c)) { - [self addErrorWithCode:EPARSENUM description: @"No digits after exponent"]; - return NO; - } - skipDigits(c); - } - - // If we are only reading integers, don't go through the expense of creating an NSDecimal. - // This ends up being a very large perf win. - if (simple) { - BOOL negate = NO; - long long val = 0; - const char *d = ns; - - if (*d == '-') { - negate = YES; - d++; - } - - while (isdigit(*d)) { - val *= 10; - if (val < 0) - goto longlong_overflow; - val += *d - '0'; - if (val < 0) - goto longlong_overflow; - d++; - } - - *o = [NSNumber numberWithLongLong:negate ? -val : val]; - return YES; - - } else { - // jumped to by simple branch, if an overflow occured - longlong_overflow:; - - id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns - length:c - ns - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - [str autorelease]; - if (str && (*o = [NSDecimalNumber decimalNumberWithString:str])) - return YES; - - [self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"]; - return NO; - } +- (id)objectWithString:(NSString *)repr { + return [self objectWithData:[repr dataUsingEncoding:NSUTF8StringEncoding]]; } -- (BOOL)scanIsAtEnd -{ - skipWhitespace(c); - return !*c; +- (id)objectWithString:(NSString*)repr error:(NSError**)error_ { + id tmp = [self objectWithString:repr]; + if (tmp) + return tmp; + + if (error_) { + NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil]; + *error_ = [NSError errorWithDomain:@"org.brautaset.json.parser.ErrorDomain" code:0 userInfo:ui]; + } + + return nil; } - @end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamParser.h b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParser.h new file mode 100644 index 0000000000..8c9455529f --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParser.h @@ -0,0 +1,136 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class SBJsonTokeniser; +@class SBJsonStreamParser; +@class SBJsonStreamParserState; + +typedef enum { + SBJsonStreamParserComplete, + SBJsonStreamParserWaitingForData, + SBJsonStreamParserError, +} SBJsonStreamParserStatus; + + +/** + @brief Delegate for interacting directly with the stream parser + + You will most likely find it much more convenient to implement the + SBJsonStreamParserAdapterDelegate protocol instead. + */ +@protocol SBJsonStreamParserDelegate + +@optional +/// Called when object start is found +- (void)parserFoundObjectStart:(SBJsonStreamParser*)parser; + +/// Called when object key is found +- (void)parser:(SBJsonStreamParser*)parser foundObjectKey:(NSString*)key; + +/// Called when object end is found +- (void)parserFoundObjectEnd:(SBJsonStreamParser*)parser; + +/// Called when array start is found +- (void)parserFoundArrayStart:(SBJsonStreamParser*)parser; + +/// Called when array end is found +- (void)parserFoundArrayEnd:(SBJsonStreamParser*)parser; + +/// Called when a boolean value is found +- (void)parser:(SBJsonStreamParser*)parser foundBoolean:(BOOL)x; + +/// Called when a null value is found +- (void)parserFoundNull:(SBJsonStreamParser*)parser; + +/// Called when a number is found +- (void)parser:(SBJsonStreamParser*)parser foundNumber:(NSNumber*)num; + +/// Called when a string is found +- (void)parser:(SBJsonStreamParser*)parser foundString:(NSString*)string; + +@end + + +/** + @brief JSON Stream-parser class + + */ +@interface SBJsonStreamParser : NSObject { + BOOL multi; + id delegate; + SBJsonTokeniser *tokeniser; + SBJsonStreamParserState **states; + NSUInteger depth, maxDepth; + NSString *error; +} + +/** + @brief Expect multiple documents separated by whitespace + + If you set this property to true the parser will never return SBJsonStreamParserComplete. + Once an object is completed it will expect another object to follow, separated only by whitespace. + + @see The TwitterStream example project. + */ +@property BOOL multi; + +/// Set this to the object you want to receive messages +@property (assign) id delegate; + +/// The current depth in the json document (each [ and { each count 1) +@property (readonly) NSUInteger depth; + +/// The max depth to allow the parser to reach +@property NSUInteger maxDepth; + +/// @internal +@property (readonly) SBJsonStreamParserState **states; + +/// Holds the error after SBJsonStreamParserError was returned +@property (copy) NSString *error; + +/** + @brief Parse some JSON + + The JSON is assumed to be UTF8 encoded. This can be a full JSON document, or a part of one. + + @return + @li SBJsonStreamParserComplete if a full document was found + @li SBJsonStreamParserWaitingForData if a partial document was found and more data is required to complete it + @li SBJsonStreamParserError if an error occured. (See the error property for details in this case.) + + */ +- (SBJsonStreamParserStatus)parse:(NSData*)data; + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamParser.m b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParser.m new file mode 100644 index 0000000000..cc891d857a --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParser.m @@ -0,0 +1,317 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJsonStreamParser.h" +#import "SBJsonTokeniser.h" +#import "SBJsonStreamParserState.h" + + +@implementation SBJsonStreamParser + +@synthesize multi; +@synthesize error; +@synthesize delegate; +@dynamic maxDepth; +@synthesize states; +@synthesize depth; + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + tokeniser = [SBJsonTokeniser new]; + maxDepth = 512; + states = calloc(maxDepth, sizeof(SBJsonStreamParserState*)); + NSAssert(states, @"States not initialised"); + states[0] = [SBJsonStreamParserStateStart sharedInstance]; + } + return self; +} + +- (void)dealloc { + self.error = nil; + free(states); + [tokeniser release]; + [super dealloc]; +} + +#pragma mark Methods + +- (NSString*)tokenName:(sbjson_token_t)token { + switch (token) { + case sbjson_token_array_start: + return @"start of array"; + break; + + case sbjson_token_array_end: + return @"end of array"; + break; + + case sbjson_token_double: + case sbjson_token_integer: + return @"number"; + break; + + case sbjson_token_string: + case sbjson_token_string_encoded: + return @"string"; + break; + + case sbjson_token_true: + case sbjson_token_false: + return @"boolean"; + break; + + case sbjson_token_null: + return @"null"; + break; + + case sbjson_token_key_value_separator: + return @"key-value separator"; + break; + + case sbjson_token_separator: + return @"value separator"; + break; + + case sbjson_token_object_start: + return @"start of object"; + break; + + case sbjson_token_object_end: + return @"end of object"; + break; + + case sbjson_token_eof: + case sbjson_token_error: + break; + } + NSAssert(NO, @"Should not get here"); + return @""; +} + + +- (void)handleObjectStart { + if (depth >= maxDepth) { + self.error = [NSString stringWithFormat:@"Parser exceeded max depth of %lu", maxDepth]; + states[depth] = kSBJsonStreamParserStateError; + + } else { + if (delegate && [delegate respondsToSelector:@selector(parserFoundObjectStart:)]) { + [delegate parserFoundObjectStart:self]; + } + states[++depth] = kSBJsonStreamParserStateObjectStart; + } + +} +- (void)handleArrayStart { + if (depth >= maxDepth) { + self.error = [NSString stringWithFormat:@"Parser exceeded max depth of %lu", maxDepth]; + states[depth] = kSBJsonStreamParserStateError; + } else { + + if (delegate && [delegate respondsToSelector:@selector(parserFoundArrayStart:)]) { + [delegate parserFoundArrayStart:self]; + } + states[++depth] = kSBJsonStreamParserStateArrayStart; + } + +} + +- (void)handleNumber:(sbjson_token_t)tok { + const char *buf; + NSUInteger len; + + if ([tokeniser getToken:&buf length:&len]) { + NSNumber *number; + if (tok == sbjson_token_integer && len < 12) { + char *e = NULL; + long l = strtol(buf, &e, 0); + NSAssert((e-buf) == len, @"unexpected length"); + number = [NSNumber numberWithLong:l]; + + } else if (tok == sbjson_token_double && len < 7) { + char *e = NULL; + double d = strtod(buf, &e); + NSAssert((e-buf) == len, @"unexpected length"); + number = [NSNumber numberWithDouble:d]; + + } else { + NSData *data = [NSData dataWithBytes:buf length:len]; + NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + number = [[[NSDecimalNumber alloc] initWithString:string] autorelease]; + } + NSParameterAssert(number); + if (delegate && [delegate respondsToSelector:@selector(parser:foundNumber:)]) { + [delegate parser:self foundNumber:number]; + } + + } +} + +- (void)handleString:(sbjson_token_t)tok { + const char *buf; + NSUInteger len; + + NSString *string; + if (tok == sbjson_token_string) { + [tokeniser getToken:&buf length:&len]; + string = [[[NSString alloc] initWithBytes:buf+1 length:len-2 encoding:NSUTF8StringEncoding] autorelease]; + } else { + string = [tokeniser getDecodedStringToken]; + } + NSParameterAssert(string); + if ([states[depth] needKey]) { + if (delegate && [delegate respondsToSelector:@selector(parser:foundObjectKey:)]) { + [delegate parser:self foundObjectKey:string]; + } + } + else { + if (delegate && [delegate respondsToSelector:@selector(parser:foundString:)]) { + [delegate parser:self foundString:string]; + } + } +} + +- (SBJsonStreamParserStatus)parse:(NSData *)data_ { + [tokeniser appendData:data_]; + + + for (;;) { + if ([states[depth] parserShouldStop:self]) + return [states[depth] parserShouldReturn:self]; + + sbjson_token_t tok = [tokeniser next]; + + switch (tok) { + case sbjson_token_eof: + return SBJsonStreamParserWaitingForData; + break; + + case sbjson_token_error: + states[depth] = kSBJsonStreamParserStateError; + self.error = tokeniser.error; + return SBJsonStreamParserError; + break; + + default: + + if (![states[depth] parser:self shouldAcceptToken:tok]) { + NSString *tokenName = [self tokenName:tok]; + NSString *stateName = [states[depth] name]; + NSLog(@"STATE: %@", states[depth]); + self.error = [NSString stringWithFormat:@"Token '%@' not expected %@", tokenName, stateName]; + states[depth] = kSBJsonStreamParserStateError; + return SBJsonStreamParserError; + } + + switch (tok) { + case sbjson_token_object_start: + [self handleObjectStart]; + break; + + case sbjson_token_object_end: + [states[--depth] parser:self shouldTransitionTo:tok]; + if (delegate && [delegate respondsToSelector:@selector(parserFoundObjectEnd:)]) { + [delegate parserFoundObjectEnd:self]; + } + break; + + case sbjson_token_array_start: + [self handleArrayStart]; + break; + + case sbjson_token_array_end: + [states[--depth] parser:self shouldTransitionTo:tok]; + if (delegate && [delegate respondsToSelector:@selector(parserFoundArrayEnd:)]) { + [delegate parserFoundArrayEnd:self]; + } + break; + + case sbjson_token_separator: + case sbjson_token_key_value_separator: + [states[depth] parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_true: + if (delegate && [delegate respondsToSelector:@selector(parser:foundBoolean:)]) { + [delegate parser:self foundBoolean:YES]; + } + [states[depth] parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_false: + if (delegate && [delegate respondsToSelector:@selector(parser:foundBoolean:)]) { + [delegate parser:self foundBoolean:NO]; + } + [states[depth] parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_null: + if (delegate && [delegate respondsToSelector:@selector(parserFoundNull:)]) { + [delegate parserFoundNull:self]; + } + [states[depth] parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_integer: + case sbjson_token_double: + [self handleNumber:tok]; + [states[depth] parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_string: + case sbjson_token_string_encoded: + [self handleString:tok]; + [states[depth] parser:self shouldTransitionTo:tok]; + break; + + default: + break; + } + break; + } + } + return SBJsonStreamParserComplete; +} + +#pragma mark Private methods + +- (void)setMaxDepth:(NSUInteger)x { + NSAssert(x, @"maxDepth must be greater than 0"); + maxDepth = x; + states = realloc(states, x); + NSAssert(states, @"Failed to reallocate more memory for states"); +} + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserAdapter.h b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserAdapter.h new file mode 100644 index 0000000000..cfb07d2600 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserAdapter.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import "SBJsonStreamParser.h" + +typedef enum { + SBJsonStreamParserAdapterNone, + SBJsonStreamParserAdapterArray, + SBJsonStreamParserAdapterObject, +} SBJsonStreamParserAdapterType; + +/** + @brief Delegate for getting objects & arrays from the stream parser adapter + + You will most likely find it much more convenient to implement this + than the raw SBJsonStreamParserDelegate protocol. + + @see The TwitterStream example project. + */ +@protocol SBJsonStreamParserAdapterDelegate +@optional +/// Called when a JSON array is found +- (void)parser:(SBJsonStreamParser*)parser foundArray:(NSArray*)array; + +/// Called when a JSON object is found +- (void)parser:(SBJsonStreamParser*)parser foundObject:(NSDictionary*)dict; + +@end + + +@interface SBJsonStreamParserAdapter : NSObject { + id delegate; + NSUInteger skip, depth; + __weak NSMutableArray *array; + __weak NSMutableDictionary *dict; + NSMutableArray *keyStack; + NSMutableArray *stack; + + SBJsonStreamParserAdapterType currentType; +} + +/** + @brief How many levels to skip + + This is useful for parsing HUGE JSON documents, particularly if it consists of an + outer array and multiple objects. + + If you set this to N it will skip the outer N levels and call the -parser:foundArray: + or -parser:foundObject: methods for each of the inner objects, as appropriate. + + @see The StreamParserIntegrationTest.m file for examples +*/ +@property NSUInteger skip; + +/// Set this to the object you want to receive messages +@property (assign) id delegate; + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserAdapter.m b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserAdapter.m new file mode 100644 index 0000000000..b8811d2bc0 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserAdapter.m @@ -0,0 +1,175 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJsonStreamParserAdapter.h" + +@interface SBJsonStreamParserAdapter () + +- (void)pop; +- (void)parser:(SBJsonStreamParser*)parser found:(id)obj; + +@end + + + +@implementation SBJsonStreamParserAdapter + +@synthesize delegate; +@synthesize skip; + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + keyStack = [[NSMutableArray alloc] initWithCapacity:32]; + stack = [[NSMutableArray alloc] initWithCapacity:32]; + + currentType = SBJsonStreamParserAdapterNone; + } + return self; +} + +- (void)dealloc { + [keyStack release]; + [stack release]; + [super dealloc]; +} + +#pragma mark Private methods + +- (void)pop { + [stack removeLastObject]; + array = nil; + dict = nil; + currentType = SBJsonStreamParserAdapterNone; + + id value = [stack lastObject]; + + if ([value isKindOfClass:[NSArray class]]) { + array = value; + currentType = SBJsonStreamParserAdapterArray; + } else if ([value isKindOfClass:[NSDictionary class]]) { + dict = value; + currentType = SBJsonStreamParserAdapterObject; + } +} + +- (void)parser:(SBJsonStreamParser*)parser found:(id)obj { + NSParameterAssert(obj); + + switch (currentType) { + case SBJsonStreamParserAdapterArray: + [array addObject:obj]; + break; + + case SBJsonStreamParserAdapterObject: + NSParameterAssert(keyStack.count); + [dict setObject:obj forKey:[keyStack lastObject]]; + [keyStack removeLastObject]; + break; + + case SBJsonStreamParserAdapterNone: + if ([obj isKindOfClass:[NSArray class]]) { + if (delegate && [delegate respondsToSelector:@selector(parser:foundArray:)]) { + [delegate parser:parser foundArray:obj]; + } + } else { + if (delegate && [delegate respondsToSelector:@selector(parser:foundObject:)]) { + [delegate parser:parser foundObject:obj]; + } + } + break; + + default: + break; + } +} + + +#pragma mark Delegate methods + +- (void)parserFoundObjectStart:(SBJsonStreamParser*)parser { + if (++depth > skip) { + dict = [[NSMutableDictionary new] autorelease]; + [stack addObject:dict]; + currentType = SBJsonStreamParserAdapterObject; + } +} + +- (void)parser:(SBJsonStreamParser*)parser foundObjectKey:(NSString*)key_ { + [keyStack addObject:key_]; +} + +- (void)parserFoundObjectEnd:(SBJsonStreamParser*)parser { + if (depth-- > skip) { + id value = [dict retain]; + [self pop]; + [self parser:parser found:value]; + [value release]; + } +} + +- (void)parserFoundArrayStart:(SBJsonStreamParser*)parser { + if (++depth > skip) { + array = [[NSMutableArray new] autorelease]; + [stack addObject:array]; + currentType = SBJsonStreamParserAdapterArray; + } +} + +- (void)parserFoundArrayEnd:(SBJsonStreamParser*)parser { + if (depth-- > skip) { + id value = [array retain]; + [self pop]; + [self parser:parser found:value]; + [value release]; + } +} + +- (void)parser:(SBJsonStreamParser*)parser foundBoolean:(BOOL)x { + [self parser:parser found:[NSNumber numberWithBool:x]]; +} + +- (void)parserFoundNull:(SBJsonStreamParser*)parser { + [self parser:parser found:[NSNull null]]; +} + +- (void)parser:(SBJsonStreamParser*)parser foundNumber:(NSNumber*)num { + [self parser:parser found:num]; +} + +- (void)parser:(SBJsonStreamParser*)parser foundString:(NSString*)string { + [self parser:parser found:string]; +} + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserState.h b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserState.h new file mode 100644 index 0000000000..1103ed08c3 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserState.h @@ -0,0 +1,89 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "SBJsonTokeniser.h" +#import "SBJsonStreamParser.h" + +@interface SBJsonStreamParserState : NSObject + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token; +- (BOOL)parserShouldStop:(SBJsonStreamParser*)parser; +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser; +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok; +- (BOOL)needKey; + +- (NSString*)name; + +@end + +@interface SBJsonStreamParserStateStart : SBJsonStreamParserState ++ (id)sharedInstance; +@end + +@interface SBJsonStreamParserStateComplete : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateError : SBJsonStreamParserState +@end + + +@interface SBJsonStreamParserStateObjectStart : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectGotKey : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectSeparator : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectGotValue : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectNeedKey : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateArrayStart : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateArrayGotValue : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateArrayNeedValue : SBJsonStreamParserState +@end + +extern SBJsonStreamParserStateStart *kSBJsonStreamParserStateStart; +extern SBJsonStreamParserStateError *kSBJsonStreamParserStateError; +extern SBJsonStreamParserStateObjectStart *kSBJsonStreamParserStateObjectStart; +extern SBJsonStreamParserStateArrayStart *kSBJsonStreamParserStateArrayStart; + diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserState.m b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserState.m new file mode 100644 index 0000000000..a25c664a93 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamParserState.m @@ -0,0 +1,370 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJsonStreamParserState.h" +#import "SBJsonStreamParser.h" + +SBJsonStreamParserStateStart *kSBJsonStreamParserStateStart; +SBJsonStreamParserStateError *kSBJsonStreamParserStateError; +static SBJsonStreamParserStateComplete *kSBJsonStreamParserStateComplete; + +SBJsonStreamParserStateObjectStart *kSBJsonStreamParserStateObjectStart; +static SBJsonStreamParserStateObjectGotKey *kSBJsonStreamParserStateObjectGotKey; +static SBJsonStreamParserStateObjectSeparator *kSBJsonStreamParserStateObjectSeparator; +static SBJsonStreamParserStateObjectGotValue *kSBJsonStreamParserStateObjectGotValue; +static SBJsonStreamParserStateObjectNeedKey *kSBJsonStreamParserStateObjectNeedKey; + +SBJsonStreamParserStateArrayStart *kSBJsonStreamParserStateArrayStart; +static SBJsonStreamParserState *kSBJsonStreamParserStateArrayGotValue; +static SBJsonStreamParserState *kSBJsonStreamParserStateArrayNeedValue; + +@implementation SBJsonStreamParserState + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return NO; +} + +- (BOOL)parserShouldStop:(SBJsonStreamParser*)parser { + return NO; +} + +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser { + return SBJsonStreamParserWaitingForData; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok {} + +- (BOOL)needKey { + return NO; +} + +- (NSString*)name { + return @""; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateStart + ++ (id)sharedInstance { + if (!kSBJsonStreamParserStateStart) { + kSBJsonStreamParserStateStart = [[SBJsonStreamParserStateStart alloc] init]; + kSBJsonStreamParserStateError = [[SBJsonStreamParserStateError alloc] init]; + kSBJsonStreamParserStateComplete = [[SBJsonStreamParserStateComplete alloc] init]; + kSBJsonStreamParserStateObjectStart = [[SBJsonStreamParserStateObjectStart alloc] init]; + kSBJsonStreamParserStateObjectGotKey = [[SBJsonStreamParserStateObjectGotKey alloc] init]; + kSBJsonStreamParserStateObjectSeparator = [[SBJsonStreamParserStateObjectSeparator alloc] init]; + kSBJsonStreamParserStateObjectGotValue = [[SBJsonStreamParserStateObjectGotValue alloc] init]; + kSBJsonStreamParserStateObjectNeedKey = [[SBJsonStreamParserStateObjectNeedKey alloc] init]; + kSBJsonStreamParserStateArrayStart = [[SBJsonStreamParserStateArrayStart alloc] init]; + kSBJsonStreamParserStateArrayGotValue = [[SBJsonStreamParserStateArrayGotValue alloc] init]; + kSBJsonStreamParserStateArrayNeedValue = [[SBJsonStreamParserStateArrayNeedValue alloc] init]; + } + return kSBJsonStreamParserStateStart; +} + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return token == sbjson_token_array_start || token == sbjson_token_object_start; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + + SBJsonStreamParserState *state = nil; + switch (tok) { + case sbjson_token_array_start: + state = kSBJsonStreamParserStateArrayStart; + break; + + case sbjson_token_object_start: + state = kSBJsonStreamParserStateObjectStart; + break; + + case sbjson_token_array_end: + case sbjson_token_object_end: + if (parser.multi) + state = parser.states[parser.depth]; + else + state = kSBJsonStreamParserStateComplete; + break; + + case sbjson_token_eof: + return; + + default: + state = kSBJsonStreamParserStateError; + break; + } + + + parser.states[parser.depth] = state; +} + +- (NSString*)name { return @"before outer-most array or object"; } + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateComplete + +- (NSString*)name { return @"after outer-most array or object"; } + +- (BOOL)parserShouldStop:(SBJsonStreamParser*)parser { + return YES; +} + +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser { + return SBJsonStreamParserComplete; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateError + +- (NSString*)name { return @"in error"; } + +- (BOOL)parserShouldStop:(SBJsonStreamParser*)parser { + return YES; +} + +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser { + return SBJsonStreamParserError; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectStart + +- (NSString*)name { return @"at beginning of object"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_end: + case sbjson_token_string: + case sbjson_token_string_encoded: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateObjectGotKey; +} + +- (BOOL)needKey { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectGotKey + +- (NSString*)name { return @"after object key"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return token == sbjson_token_key_value_separator; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateObjectSeparator; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectSeparator + +- (NSString*)name { return @"as object value"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_start: + case sbjson_token_array_start: + case sbjson_token_true: + case sbjson_token_false: + case sbjson_token_null: + case sbjson_token_integer: + case sbjson_token_double: + case sbjson_token_string: + case sbjson_token_string_encoded: + return YES; + break; + + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateObjectGotValue; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectGotValue + +- (NSString*)name { return @"after object value"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_end: + case sbjson_token_separator: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateObjectNeedKey; +} + + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectNeedKey + +- (NSString*)name { return @"in place of object key"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_string: + case sbjson_token_string_encoded: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateObjectGotKey; +} + +- (BOOL)needKey { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateArrayStart + +- (NSString*)name { return @"at array start"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_end: + case sbjson_token_key_value_separator: + case sbjson_token_separator: + return NO; + break; + + default: + return YES; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateArrayGotValue; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateArrayGotValue + +- (NSString*)name { return @"after array value"; } + + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return token == sbjson_token_array_end || token == sbjson_token_separator; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + if (tok == sbjson_token_separator) + parser.states[parser.depth] = kSBJsonStreamParserStateArrayNeedValue; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateArrayNeedValue + +- (NSString*)name { return @"as array value"; } + + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_array_end: + case sbjson_token_key_value_separator: + case sbjson_token_object_end: + case sbjson_token_separator: + return NO; + break; + + default: + return YES; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.states[parser.depth] = kSBJsonStreamParserStateArrayGotValue; +} + +@end + diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriter.h b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriter.h new file mode 100644 index 0000000000..b7a0372e50 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriter.h @@ -0,0 +1,163 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +/// Enable JSON writing for non-native objects +@interface NSObject (SBProxyForJson) + +/** + @brief Allows generation of JSON for otherwise unsupported classes. + + If you have a custom class that you want to create a JSON representation for you can implement this method in your class. It should return a representation of your object defined in terms of objects that can be translated into JSON. For example, a Person object might implement it like this: + + @code + - (id)proxyForJson { + return [NSDictionary dictionaryWithObjectsAndKeys: + name, @"name", + phone, @"phone", + email, @"email", + nil]; + } + @endcode + + */ +- (id)proxyForJson; + +@end + +@class SBJsonStreamWriterState; + +/** + @brief The Stream Writer class. + + Accepts a stream of messages and writes JSON of these to an internal data object. At any time you can retrieve the amount of data up to now, for example if you want to start sending data to a file before you're finished generating the JSON. + + A range of high-, mid- and low-level methods. You can mix and match calls to these. For example, you may want to call -writeArrayOpen to start an array and then repeatedly call -writeObject: with an object. + + In JSON the keys of an object must be strings. NSDictionary keys need not be, but attempting to convert an NSDictionary with non-string keys into JSON will result in an error. + + NSNumber instances created with the +initWithBool: method are converted into the JSON boolean "true" and "false" values, and vice versa. Any other NSNumber instances are converted to a JSON number the way you would expect. + + */ + +@interface SBJsonStreamWriter : NSObject { + NSString *error; + SBJsonStreamWriterState **states; + NSMutableData *data; + NSUInteger depth, maxDepth; + BOOL sortKeys, humanReadable; +} + +/** + @brief The data written to the stream so far. + + This is a mutable object. This means that you can write a chunk of its + contents to an NSOutputStream, then chop as many bytes as you wrote off + the beginning of the buffer. + */ +@property(readonly) NSMutableData *data; + +@property(readonly) NSObject **states; +@property(readonly) NSUInteger depth; + +/** + @brief The maximum recursing depth. + + Defaults to 512. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property NSUInteger maxDepth; + +/** + @brief Whether we are generating human-readable (multiline) JSON. + + Set whether or not to generate human-readable JSON. The default is NO, which produces + JSON without any whitespace between tokens. If set to YES, generates human-readable + JSON with linebreaks after each array value and dictionary key/value pair, indented two + spaces per nesting level. + */ +@property BOOL humanReadable; + +/** + @brief Whether or not to sort the dictionary keys in the output. + + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. + (This is useful if you need to compare two structures, for example.) The default is NO. + */ +@property BOOL sortKeys; + +/** + @brief Contains the error description after an error has occured. + */ +@property (copy) NSString *error; + +/** @brief Write an NSDictionary to the JSON stream. + */ +- (BOOL)writeObject:(NSDictionary*)dict; + +/** + @brief Write an NSArray to the JSON stream. + */ +- (BOOL)writeArray:(NSArray *)array; + +/// Start writing an Object to the stream +- (BOOL)writeObjectOpen; + +/// Close the current object being written +- (BOOL)writeObjectClose; + +/// Start writing an Array to the stream +- (BOOL)writeArrayOpen; + +/// Close the current Array being written +- (BOOL)writeArrayClose; + +/// Write a null to the stream +- (BOOL)writeNull; + +/// Write a boolean to the stream +- (BOOL)writeBool:(BOOL)x; + +/// Write a Number to the stream +- (BOOL)writeNumber:(NSNumber*)n; + +/// Write a String to the stream +- (BOOL)writeString:(NSString*)s; + +@end + +@interface SBJsonStreamWriter (Private) +- (BOOL)writeValue:(id)v; +@end + diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriter.m b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriter.m new file mode 100644 index 0000000000..ae89539fc4 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriter.m @@ -0,0 +1,372 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJsonStreamWriter.h" +#import "SBJsonStreamWriterState.h" + +static NSMutableDictionary *stringCache; +static NSDecimalNumber *notANumber; + +@implementation SBJsonStreamWriter + +@synthesize error; +@dynamic depth; +@dynamic maxDepth; +@synthesize states; +@synthesize humanReadable; +@synthesize sortKeys; + ++ (void)initialize { + notANumber = [NSDecimalNumber notANumber]; + stringCache = [NSMutableDictionary new]; +} + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + data = [[NSMutableData alloc] initWithCapacity:1024u]; + maxDepth = 512; + states = calloc(maxDepth, sizeof(SBJsonStreamWriterState*)); + NSAssert(states, @"States not initialised"); + + states[0] = [SBJsonStreamWriterStateStart sharedInstance]; + } + return self; +} + +- (void)dealloc { + self.error = nil; + free(states); + [data release]; + [super dealloc]; +} + +#pragma mark Methods + +- (BOOL)writeObject:(NSDictionary *)dict { + if (![self writeObjectOpen]) + return NO; + + NSArray *keys = [dict allKeys]; + if (sortKeys) + keys = [keys sortedArrayUsingSelector:@selector(compare:)]; + + for (id k in keys) { + if (![k isKindOfClass:[NSString class]]) { + self.error = [NSString stringWithFormat:@"JSON object key must be string: %@", k]; + return NO; + } + + if (![self writeString:k]) + return NO; + if (![self writeValue:[dict objectForKey:k]]) + return NO; + } + + return [self writeObjectClose]; +} + +- (BOOL)writeArray:(NSArray*)array { + if (![self writeArrayOpen]) + return NO; + for (id v in array) + if (![self writeValue:v]) + return NO; + return [self writeArrayClose]; +} + + +- (BOOL)writeObjectOpen { + SBJsonStreamWriterState *s = states[depth]; + if ([s isInvalidState:self]) return NO; + if ([s expectingKey:self]) return NO; + [s appendSeparator:self]; + if (humanReadable && depth) [s appendWhitespace:self]; + + if (maxDepth && ++depth > maxDepth) { + self.error = @"Nested too deep"; + return NO; + } + + states[depth] = kSBJsonStreamWriterStateObjectStart; + [data appendBytes:"{" length:1]; + return YES; +} + +- (BOOL)writeObjectClose { + SBJsonStreamWriterState *state = states[depth--]; + if ([state isInvalidState:self]) return NO; + if (humanReadable) [state appendWhitespace:self]; + [data appendBytes:"}" length:1]; + [states[depth] transitionState:self]; + return YES; +} + +- (BOOL)writeArrayOpen { + SBJsonStreamWriterState *s = states[depth]; + if ([s isInvalidState:self]) return NO; + if ([s expectingKey:self]) return NO; + [s appendSeparator:self]; + if (humanReadable && depth) [s appendWhitespace:self]; + + if (maxDepth && ++depth > maxDepth) { + self.error = @"Nested too deep"; + return NO; + } + + states[depth] = kSBJsonStreamWriterStateArrayStart; + [data appendBytes:"[" length:1]; + return YES; +} + +- (BOOL)writeArrayClose { + SBJsonStreamWriterState *state = states[depth--]; + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + if (humanReadable) [state appendWhitespace:self]; + + [data appendBytes:"]" length:1]; + [states[depth] transitionState:self]; + return YES; +} + +- (BOOL)writeNull { + SBJsonStreamWriterState *s = states[depth]; + if ([s isInvalidState:self]) return NO; + if ([s expectingKey:self]) return NO; + [s appendSeparator:self]; + if (humanReadable) [s appendWhitespace:self]; + + [data appendBytes:"null" length:4]; + [s transitionState:self]; + return YES; +} + +- (BOOL)writeBool:(BOOL)x { + SBJsonStreamWriterState *s = states[depth]; + if ([s isInvalidState:self]) return NO; + if ([s expectingKey:self]) return NO; + [s appendSeparator:self]; + if (humanReadable) [s appendWhitespace:self]; + + if (x) + [data appendBytes:"true" length:4]; + else + [data appendBytes:"false" length:5]; + [s transitionState:self]; + return YES; +} + + +- (BOOL)writeValue:(id)o { + if ([o isKindOfClass:[NSDictionary class]]) { + return [self writeObject:o]; + + } else if ([o isKindOfClass:[NSArray class]]) { + return [self writeArray:o]; + + } else if ([o isKindOfClass:[NSString class]]) { + [self writeString:o]; + return YES; + + } else if ([o isKindOfClass:[NSNumber class]]) { + return [self writeNumber:o]; + + } else if ([o isKindOfClass:[NSNull class]]) { + return [self writeNull]; + + } else if ([o respondsToSelector:@selector(proxyForJson)]) { + return [self writeValue:[o proxyForJson]]; + + } + + self.error = [NSString stringWithFormat:@"JSON serialisation not supported for %@", [o class]]; + return NO; +} + +static const char *strForChar(int c) { + switch (c) { + case 0: return "\\u0000"; break; + case 1: return "\\u0001"; break; + case 2: return "\\u0002"; break; + case 3: return "\\u0003"; break; + case 4: return "\\u0004"; break; + case 5: return "\\u0005"; break; + case 6: return "\\u0006"; break; + case 7: return "\\u0007"; break; + case 8: return "\\b"; break; + case 9: return "\\t"; break; + case 10: return "\\n"; break; + case 11: return "\\u000b"; break; + case 12: return "\\f"; break; + case 13: return "\\r"; break; + case 14: return "\\u000e"; break; + case 15: return "\\u000f"; break; + case 16: return "\\u0010"; break; + case 17: return "\\u0011"; break; + case 18: return "\\u0012"; break; + case 19: return "\\u0013"; break; + case 20: return "\\u0014"; break; + case 21: return "\\u0015"; break; + case 22: return "\\u0016"; break; + case 23: return "\\u0017"; break; + case 24: return "\\u0018"; break; + case 25: return "\\u0019"; break; + case 26: return "\\u001a"; break; + case 27: return "\\u001b"; break; + case 28: return "\\u001c"; break; + case 29: return "\\u001d"; break; + case 30: return "\\u001e"; break; + case 31: return "\\u001f"; break; + case 34: return "\\\""; break; + case 92: return "\\\\"; break; + } + NSLog(@"FUTFUTFUT: -->'%c'<---", c); + return "FUTFUTFUT"; +} + +- (BOOL)writeString:(NSString*)string { + SBJsonStreamWriterState *s = states[depth]; + if ([s isInvalidState:self]) return NO; + [s appendSeparator:self]; + if (humanReadable) [s appendWhitespace:self]; + + NSMutableData *buf = [stringCache objectForKey:string]; + if (buf) { + [data appendBytes:[buf bytes] length:[buf length]]; + [s transitionState:self]; + return YES; + } + + NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + const char *utf8 = [string UTF8String]; + NSUInteger written = 0, i = 0; + + buf = [NSMutableData dataWithCapacity:len * 1.1f]; + [buf appendBytes:"\"" length:1]; + + for (i = 0; i < len; i++) { + int c = utf8[i]; + BOOL isControlChar = c >= 0 && c < 32; + if (isControlChar || c == '"' || c == '\\') { + if (i - written) + [buf appendBytes:utf8 + written length:i - written]; + written = i + 1; + + const char *t = strForChar(c); + [buf appendBytes:t length:strlen(t)]; + } + } + + if (i - written) + [buf appendBytes:utf8 + written length:i - written]; + + [buf appendBytes:"\"" length:1]; + [data appendBytes:[buf bytes] length:[buf length]]; + [stringCache setObject:buf forKey:string]; + [s transitionState:self]; + return YES; +} + +- (BOOL)writeNumber:(NSNumber*)number { + if ((CFBooleanRef)number == kCFBooleanTrue || (CFBooleanRef)number == kCFBooleanFalse) + return [self writeBool:[number boolValue]]; + + SBJsonStreamWriterState *s = states[depth]; + if ([s isInvalidState:self]) return NO; + if ([s expectingKey:self]) return NO; + [s appendSeparator:self]; + if (humanReadable) [s appendWhitespace:self]; + + if ((CFNumberRef)number == kCFNumberPositiveInfinity) { + self.error = @"+Infinity is not a valid number in JSON"; + return NO; + + } else if ((CFNumberRef)number == kCFNumberNegativeInfinity) { + self.error = @"-Infinity is not a valid number in JSON"; + return NO; + + } else if ((CFNumberRef)number == kCFNumberNaN) { + self.error = @"NaN is not a valid number in JSON"; + return NO; + + } else if (number == notANumber) { + self.error = @"NaN is not a valid number in JSON"; + return NO; + } + + const char *objcType = [number objCType]; + char num[128]; + size_t len; + + switch (objcType[0]) { + case 'c': case 'i': case 's': case 'l': case 'q': + len = snprintf(num, sizeof num, "%lld", [number longLongValue]); + break; + case 'C': case 'I': case 'S': case 'L': case 'Q': + len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]); + break; + case 'f': case 'd': default: + if ([number isKindOfClass:[NSDecimalNumber class]]) { + char const *utf8 = [[number stringValue] UTF8String]; + [data appendBytes:utf8 length: strlen(utf8)]; + [s transitionState:self]; + return YES; + } + len = snprintf(num, sizeof num, "%.17g", [number doubleValue]); + break; + } + [data appendBytes:num length: len]; + [s transitionState:self]; + return YES; +} + +#pragma mark Private methods + +- (NSUInteger)depth { + return depth; +} + +- (void)setMaxDepth:(NSUInteger)x { + NSAssert(x, @"maxDepth must be greater than 0"); + maxDepth = x; + states = realloc(states, x); + NSAssert(states, @"Failed to reallocate more memory for states"); +} + +- (NSMutableData*)data { + return data; +} + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriterState.h b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriterState.h new file mode 100644 index 0000000000..23310ab593 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriterState.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class SBJsonStreamWriter; + +@interface SBJsonStreamWriterState : NSObject +- (BOOL)isInvalidState:(SBJsonStreamWriter*)writer; +- (void)appendSeparator:(SBJsonStreamWriter*)writer; +- (BOOL)expectingKey:(SBJsonStreamWriter*)writer; +- (void)transitionState:(SBJsonStreamWriter*)writer; +- (void)appendWhitespace:(SBJsonStreamWriter*)writer; +@end + +@interface SBJsonStreamWriterStateObjectStart : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateObjectKey : SBJsonStreamWriterStateObjectStart +@end + +@interface SBJsonStreamWriterStateObjectValue : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateArrayStart : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateArrayValue : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateStart : SBJsonStreamWriterState ++ (id)sharedInstance; +@end + +@interface SBJsonStreamWriterStateComplete : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateError : SBJsonStreamWriterState +@end + +extern SBJsonStreamWriterStateStart *kSBJsonStreamWriterStateStart; +extern SBJsonStreamWriterStateComplete *kSBJsonStreamWriterStateComplete; +extern SBJsonStreamWriterStateError *kSBJsonStreamWriterStateError; +extern SBJsonStreamWriterStateObjectStart *kSBJsonStreamWriterStateObjectStart; +extern SBJsonStreamWriterStateArrayStart *kSBJsonStreamWriterStateArrayStart; + diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriterState.m b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriterState.m new file mode 100644 index 0000000000..711efa0cf5 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonStreamWriterState.m @@ -0,0 +1,132 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJsonStreamWriterState.h" +#import "SBJsonStreamWriter.h" + +// States +SBJsonStreamWriterStateStart *kSBJsonStreamWriterStateStart; +SBJsonStreamWriterStateComplete *kSBJsonStreamWriterStateComplete; +SBJsonStreamWriterStateError *kSBJsonStreamWriterStateError; + +SBJsonStreamWriterStateObjectStart *kSBJsonStreamWriterStateObjectStart; +static SBJsonStreamWriterStateObjectKey *kSBJsonStreamWriterStateObjectKey; +static SBJsonStreamWriterStateObjectValue *kSBJsonStreamWriterStateObjectValue; + +SBJsonStreamWriterStateArrayStart *kSBJsonStreamWriterStateArrayStart; +static SBJsonStreamWriterStateArrayValue *kSBJsonStreamWriterStateArrayValue; + +@implementation SBJsonStreamWriterState +- (BOOL)isInvalidState:(SBJsonStreamWriter*)writer { return NO; } +- (void)appendSeparator:(SBJsonStreamWriter*)writer {} +- (BOOL)expectingKey:(SBJsonStreamWriter*)writer { return NO; } +- (void)transitionState:(SBJsonStreamWriter *)writer {} +- (void)appendWhitespace:(SBJsonStreamWriter*)writer { + [writer.data appendBytes:"\n" length:1]; + for (int i = 0; i < writer.depth; i++) + [writer.data appendBytes:" " length:2]; +} +@end + +@implementation SBJsonStreamWriterStateObjectStart +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.states[writer.depth] = kSBJsonStreamWriterStateObjectValue; +} +- (BOOL)expectingKey:(SBJsonStreamWriter *)writer { + writer.error = @"JSON object key must be string"; + return YES; +} +@end + +@implementation SBJsonStreamWriterStateObjectKey +- (void)appendSeparator:(SBJsonStreamWriter *)writer { + [writer.data appendBytes:"," length:1]; +} +@end + +@implementation SBJsonStreamWriterStateObjectValue +- (void)appendSeparator:(SBJsonStreamWriter *)writer { + [writer.data appendBytes:":" length:1]; +} +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.states[writer.depth] = kSBJsonStreamWriterStateObjectKey; +} +- (void)appendWhitespace:(SBJsonStreamWriter *)writer { + [writer.data appendBytes:" " length:1]; +} +@end + +@implementation SBJsonStreamWriterStateArrayStart +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.states[writer.depth] = kSBJsonStreamWriterStateArrayValue; +} +@end + +@implementation SBJsonStreamWriterStateArrayValue +- (void)appendSeparator:(SBJsonStreamWriter *)writer { + [writer.data appendBytes:"," length:1]; +} +@end + +@implementation SBJsonStreamWriterStateStart + ++ (id)sharedInstance { + if (!kSBJsonStreamWriterStateStart) { + kSBJsonStreamWriterStateStart = [SBJsonStreamWriterStateStart new]; + kSBJsonStreamWriterStateComplete = [SBJsonStreamWriterStateComplete new]; + kSBJsonStreamWriterStateError = [SBJsonStreamWriterStateError new]; + kSBJsonStreamWriterStateObjectStart = [SBJsonStreamWriterStateObjectStart new]; + kSBJsonStreamWriterStateObjectKey = [SBJsonStreamWriterStateObjectKey new]; + kSBJsonStreamWriterStateObjectValue = [SBJsonStreamWriterStateObjectValue new]; + kSBJsonStreamWriterStateArrayStart = [SBJsonStreamWriterStateArrayStart new]; + kSBJsonStreamWriterStateArrayValue = [SBJsonStreamWriterStateArrayValue new]; + } + return kSBJsonStreamWriterStateStart; +} + +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.states[writer.depth] = kSBJsonStreamWriterStateComplete; +} +- (void)appendSeparator:(SBJsonStreamWriter *)writer { +} +@end + +@implementation SBJsonStreamWriterStateComplete +- (BOOL)isInvalidState:(SBJsonStreamWriter*)writer { + writer.error = @"Stream is closed"; + return YES; +} +@end + +@implementation SBJsonStreamWriterStateError +@end + diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonTokeniser.h b/src/extThree20JSON/Vendors/JSON/SBJsonTokeniser.h new file mode 100644 index 0000000000..f3610932fc --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonTokeniser.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +typedef enum { + sbjson_token_eof, + sbjson_token_error, + sbjson_token_object_start, + sbjson_token_key_value_separator, + sbjson_token_object_end, + sbjson_token_array_start, + sbjson_token_array_end, + sbjson_token_separator, + sbjson_token_string, + sbjson_token_string_encoded, + sbjson_token_integer, + sbjson_token_double, + sbjson_token_true, + sbjson_token_false, + sbjson_token_null, +} sbjson_token_t; + +@interface SBJsonTokeniser : NSObject { + NSUInteger tokenStart, tokenLength; + NSMutableData *buf; + const char *bufbytes; + NSUInteger bufbytesLength; + NSString *error; + NSCharacterSet *illegalCharacterSet; +} + +@property(copy, readonly) NSString *error; + +- (void)appendData:(NSData*)data; + +- (sbjson_token_t)next; +- (BOOL)getToken:(const char **)utf8 length:(NSUInteger*)length; +- (NSString*)getDecodedStringToken; + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonTokeniser.m b/src/extThree20JSON/Vendors/JSON/SBJsonTokeniser.m new file mode 100644 index 0000000000..9abfc44c25 --- /dev/null +++ b/src/extThree20JSON/Vendors/JSON/SBJsonTokeniser.m @@ -0,0 +1,508 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SBJsonTokeniser.h" + + +#define isDigit(x) (*x >= '0' && *x <= '9') +#define skipDigits(x) while (isDigit(x)) x++ + +#define SBStringIsIllegalSurrogateHighCharacter(x) (((x) >= 0xd800) && ((x) <= 0xdfff)) + + +@interface SBJsonTokeniser () + +@property (copy) NSString *error; + +- (void)skipWhitespace; + +- (sbjson_token_t)match:(const char *)utf8 ofLength:(NSUInteger)len andReturn:(sbjson_token_t)tok; +- (sbjson_token_t)matchString; +- (sbjson_token_t)matchNumber; + +- (int)parseUnicodeEscape:(const char *)bytes index:(NSUInteger *)index; +- (NSString*)decodeUnicodeEscape:(const char *)bytes index:(NSUInteger *)index; + +@end + +@implementation SBJsonTokeniser + +@synthesize error; + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + tokenStart = tokenLength = 0; + buf = [[NSMutableData alloc] initWithCapacity:4096]; + illegalCharacterSet = [[NSCharacterSet illegalCharacterSet] copy]; + } + return self; +} + +- (void)dealloc { + self.error = nil; + [buf release]; + [illegalCharacterSet release]; + [super dealloc]; +} + +#pragma mark Methods + +- (void)appendData:(NSData *)data { + + // Remove previous NUL char + if (buf.length) + buf.length = buf.length - 1; + + if (tokenStart) { + // Remove stuff in the front of the offset + [buf replaceBytesInRange:NSMakeRange(0, tokenStart) withBytes:"" length:0]; + tokenStart = 0; + } + + [buf appendData:data]; + + // Append NUL byte to simplify logic + [buf appendBytes:"\0" length:1]; + + bufbytes = [buf bytes]; + bufbytesLength = [buf length]; +} + +- (BOOL)getToken:(const char **)utf8 length:(NSUInteger *)len { + if (!tokenLength) + return NO; + + *len = tokenLength; + *utf8 = bufbytes + tokenStart; + return YES; +} + +- (NSString*)getDecodedStringToken { + NSUInteger len; + const char *bytes; + [self getToken:&bytes length:&len]; + + len -= 1; + + NSMutableData *data = [NSMutableData dataWithCapacity:len * 1.1]; + + char c; + NSUInteger i = 1; +again: while (i < len) { + switch (c = bytes[i++]) { + case '\\': + switch (c = bytes[i++]) { + case '\\': + case '/': + case '"': + break; + + case 'b': + c = '\b'; + break; + + case 'n': + c = '\n'; + break; + + case 'r': + c = '\r'; + break; + + case 't': + c = '\t'; + break; + + case 'f': + c = '\f'; + break; + + case 'u': { + NSString *s = [self decodeUnicodeEscape:bytes index:&i]; + NSAssert(s, @"Illegal unicode escape"); + [data appendData:[s dataUsingEncoding:NSUTF8StringEncoding]]; + goto again; + break; + } + + default: + NSAssert(NO, @"Should never get here"); + break; + } + break; + + case 0 ... 0x1F: + self.error = @"Unescaped control chars"; + return nil; + break; + + default: + break; + } + [data appendBytes:&c length:1]; + } + + return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; +} + + +- (sbjson_token_t)next { + tokenStart += tokenLength; + tokenLength = 0; + + [self skipWhitespace]; + + switch (*(bufbytes + tokenStart)) { + case '\0': + return sbjson_token_eof; + break; + + case '[': + tokenLength = 1; + return sbjson_token_array_start; + break; + + case ']': + tokenLength = 1; + return sbjson_token_array_end; + break; + + case '{': + tokenLength = 1; + return sbjson_token_object_start; + break; + + case ':': + tokenLength = 1; + return sbjson_token_key_value_separator; + break; + + case '}': + tokenLength = 1; + return sbjson_token_object_end; + break; + + case ',': + tokenLength = 1; + return sbjson_token_separator; + break; + + case 'n': + return [self match:"null" ofLength:4 andReturn:sbjson_token_null]; + break; + + case 't': + return [self match:"true" ofLength:4 andReturn:sbjson_token_true]; + break; + + case 'f': + return [self match:"false" ofLength:5 andReturn:sbjson_token_false]; + break; + + case '"': + return [self matchString]; + break; + + case '-': + case '0' ... '9': + return [self matchNumber]; + break; + + case '+': + self.error = [NSString stringWithFormat:@"Leading + is illegal in numbers at offset %u", tokenStart]; + return sbjson_token_error; + break; + } + + self.error = [NSString stringWithFormat:@"Unrecognised leading character at offset %u", tokenStart]; + return sbjson_token_error; +} + +#pragma mark Private methods + +- (void)skipWhitespace { + while (tokenStart < bufbytesLength) { + switch (bufbytes[tokenStart]) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + case '\v': + tokenStart++; + break; + default: + return; + break; + } + } +} + +- (sbjson_token_t)match:(const char *)utf8 ofLength:(NSUInteger)len andReturn:(sbjson_token_t)tok { + if (buf.length - tokenStart - 1 < len) + return sbjson_token_eof; + + if (strncmp(bufbytes + tokenStart, utf8, len)) { + NSString *format = [NSString stringWithFormat:@"Expected '%%s' but found '%%.%us'.", len]; + self.error = [NSString stringWithFormat:format, utf8, bufbytes + tokenStart]; + return sbjson_token_error; + } + + tokenLength = len; + return tok; +} + + +- (int)decodeHexQuad:(const char *)hexQuad { + char c; + int ret = 0; + for (int i = 0; i < 4; i++) { + ret *= 16; + switch (c = hexQuad[i]) { + case '\0': + return -2; + break; + + case '0' ... '9': + ret += c - '0'; + break; + + case 'a' ... 'f': + ret += 10 + c - 'a'; + break; + + case 'A' ... 'F': + ret += 10 + c - 'A'; + break; + + default: + self.error = @"XXX illegal digit in hex char"; + return -1; + break; + } + } + return ret; +} + +- (int)parseUnicodeEscape:(const char *)bytes index:(NSUInteger *)index { + int hi = [self decodeHexQuad:bytes + *index]; + if (hi == -2) return -2; // EOF + if (hi < 0) { + self.error = @"Missing hex quad"; + return -1; + } + *index += 4; + + if (CFStringIsSurrogateHighCharacter(hi)) { + int lo = -1; + if (bytes[(*index)++] == '\\' && bytes[(*index)++] == 'u') + lo = [self decodeHexQuad:bytes + *index]; + + if (lo < 0) { + self.error = @"Missing low character in surrogate pair"; + return -1; + } + *index += 4; + + if (!CFStringIsSurrogateLowCharacter(lo)) { + self.error = @"Invalid low surrogate char"; + return -1; + } + } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) { + self.error = @"Invalid high character in surrogate pair"; + return -1; + } + + + return hi; +} + +- (NSString*)decodeUnicodeEscape:(const char *)bytes index:(NSUInteger *)index { + unichar hi = [self decodeHexQuad:bytes + *index]; + if (hi < 0) { + self.error = @"Missing hex quad"; + return nil; + } + *index += 4; + + if (CFStringIsSurrogateHighCharacter(hi)) { // high surrogate char? + int lo = -1; + if (bytes[(*index)++] == '\\' && bytes[(*index)++] == 'u') + lo = [self decodeHexQuad:bytes + *index]; + + if (lo < 0) { + self.error = @"Missing low character in surrogate pair"; + return nil; + } + *index += 4; + + if (!CFStringIsSurrogateLowCharacter(lo)) { + self.error = @"Invalid low surrogate char"; + return nil; + } + + unichar pair[2] = {hi, lo}; + return [NSString stringWithCharacters:pair length:2]; + + } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) { + self.error = @"Invalid high character in surrogate pair"; + return nil; + } + + return [NSString stringWithCharacters:&hi length:1]; +} + + +- (sbjson_token_t)matchString { + sbjson_token_t ret = sbjson_token_string; + + const char *bytes = bufbytes + tokenStart; + NSUInteger idx = 1; + NSUInteger maxIdx = buf.length - 2 - tokenStart; + + while (idx <= maxIdx) { + switch (bytes[idx++]) { + case 0 ... 0x1F: + self.error = [NSString stringWithFormat:@"Unescaped control char 0x%0.2X", (int)bytes[idx-1]]; + return sbjson_token_error; + break; + + case '\\': + ret = sbjson_token_string_encoded; + + if (idx >= maxIdx) + return sbjson_token_eof; + + switch (bytes[idx++]) { + case 'b': + case 't': + case 'n': + case 'r': + case 'f': + case 'v': + case '"': + case '\\': + case '/': + // Valid escape sequence + break; + + case 'u': { + int ch = [self parseUnicodeEscape:bytes index:&idx]; + if (ch == -2) + return sbjson_token_eof; + if (ch == -1) + return sbjson_token_error; + break; + } + default: + self.error = [NSString stringWithFormat:@"Broken escape character at index %u in token starting at offset %u", idx-1, tokenStart]; + return sbjson_token_error; + break; + } + break; + + case '"': + tokenLength = idx; + return ret; + break; + + default: + // any other character + break; + } + } + + return sbjson_token_eof; +} + +- (sbjson_token_t)matchNumber { + + sbjson_token_t ret = sbjson_token_integer; + const char *c = bufbytes + tokenStart; + + if (*c == '-') { + c++; + if (!isDigit(c)) { + self.error = @"No digits after initial minus"; + return sbjson_token_error; + } + } + + if (*c == '0') { + c++; + if (isDigit(c)) { + self.error = [NSString stringWithFormat:@"Leading zero is illegal in number at offset %u", tokenStart]; + return sbjson_token_error; + } + } + + skipDigits(c); + + + if (*c == '.') { + ret = sbjson_token_double; + c++; + + if (!isDigit(c) && *c) { + self.error = [NSString stringWithFormat:@"No digits after decimal point at offset %u", tokenStart]; + return sbjson_token_error; + } + + skipDigits(c); + } + + if (*c == 'e' || *c == 'E') { + ret = sbjson_token_double; + c++; + + if (*c == '-' || *c == '+') + c++; + + if (!isDigit(c) && *c) { + self.error = [NSString stringWithFormat:@"No digits after exponent mark at offset %u", tokenStart]; + return sbjson_token_error; + } + + skipDigits(c); + } + + if (!*c) + return sbjson_token_eof; + + tokenLength = c - (bufbytes + tokenStart); + return ret; +} + +@end diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonWriter.h b/src/extThree20JSON/Vendors/JSON/SBJsonWriter.h old mode 100755 new mode 100644 index f79fbcb2ea..393b85f6c4 --- a/src/extThree20JSON/Vendors/JSON/SBJsonWriter.h +++ b/src/extThree20JSON/Vendors/JSON/SBJsonWriter.h @@ -1,20 +1,20 @@ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -28,39 +28,60 @@ */ #import -#import "extThree20JSON/private/SBJsonBase.h" /** @brief The JSON writer class. - + Objective-C types are mapped to JSON types in the following way: - + @li NSNull -> Null @li NSString -> String @li NSArray -> Array @li NSDictionary -> Object @li NSNumber (-initWithBool:) -> Boolean @li NSNumber -> Number - + In JSON the keys of an object must be strings. NSDictionary keys need not be, but attempting to convert an NSDictionary with non-string keys into JSON will throw an exception. - + NSNumber instances created with the +initWithBool: method are converted into the JSON boolean "true" and "false" values, and vice versa. Any other NSNumber instances are converted to a JSON number the way you would expect. - + */ -@interface SBJsonWriter : SBJsonBase { - +@interface SBJsonWriter : NSObject { + +@protected + NSString *error; + NSUInteger maxDepth; + @private BOOL sortKeys, humanReadable; } /** - @brief Whether we are generating human-readable (multiline) JSON. + @brief The maximum recursing depth. + + Defaults to 512. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property NSUInteger maxDepth; +/** + @brief Return an error trace, or nil if there was no errors. + + Note that this method returns the trace of the last method that failed. + You need to check the return value of the call you're making to figure out + if the call actually failed, before you know call this method. + */ +@property(copy) NSString *error; + +/** + @brief Whether we are generating human-readable (multiline) JSON. + Set whether or not to generate human-readable JSON. The default is NO, which produces JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable JSON with linebreaks after each array value and dictionary key/value pair, indented two @@ -70,58 +91,42 @@ /** @brief Whether or not to sort the dictionary keys in the output. - + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. (This is useful if you need to compare two structures, for example.) The default is NO. */ @property BOOL sortKeys; /** - @brief Return JSON representation (or fragment) for the given object. - + @brief Return JSON representation for the given object. + Returns a string containing JSON representation of the passed in value, or nil on error. If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. - - @param value any instance that can be represented as a JSON fragment - + + @param value any instance that can be represented as JSON text. */ - (NSString*)stringWithObject:(id)value; /** - @brief Return JSON representation (or fragment) for the given object. + @brief Return JSON representation for the given object. + + Returns an NSData object containing JSON represented as UTF8 text, or nil on error. + + @param value any instance that can be represented as JSON text. + */ +- (NSData*)dataWithObject:(id)value; +/** + @brief Return JSON representation (or fragment) for the given object. + Returns a string containing JSON representation of the passed in value, or nil on error. If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. - + @param value any instance that can be represented as a JSON fragment @param error pointer to object to be populated with NSError on failure - + */- (NSString*)stringWithObject:(id)value error:(NSError**)error; @end - -/** - @brief Allows generation of JSON for otherwise unsupported classes. - - If you have a custom class that you want to create a JSON representation for you can implement - this method in your class. It should return a representation of your object defined - in terms of objects that can be translated into JSON. For example, a Person - object might implement it like this: - - @code - - (id)proxyForJson { - return [NSDictionary dictionaryWithObjectsAndKeys: - name, @"name", - phone, @"phone", - email, @"email", - nil]; - } - @endcode - - */ -@interface NSObject (SBProxyForJson) -- (id)proxyForJson; -@end - diff --git a/src/extThree20JSON/Vendors/JSON/SBJsonWriter.m b/src/extThree20JSON/Vendors/JSON/SBJsonWriter.m old mode 100755 new mode 100644 index 02959a4dd5..8321786b7b --- a/src/extThree20JSON/Vendors/JSON/SBJsonWriter.m +++ b/src/extThree20JSON/Vendors/JSON/SBJsonWriter.m @@ -1,20 +1,20 @@ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - + * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -27,213 +27,76 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "extThree20JSON/SBJsonWriter.h" - -@interface SBJsonWriter () - -- (BOOL)appendValue:(id)fragment into:(NSMutableString*)json; -- (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json; -- (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json; -- (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json; - -- (NSString*)indent; - -@end +#import "SBJsonWriter.h" +#import "SBJsonStreamWriter.h" @implementation SBJsonWriter @synthesize sortKeys; @synthesize humanReadable; -static NSMutableCharacterSet *kEscapeChars; +@synthesize error; +@synthesize maxDepth; -+ (void)initialize { - kEscapeChars = [[NSMutableCharacterSet characterSetWithRange: NSMakeRange(0,32)] retain]; - [kEscapeChars addCharactersInString: @"\"\\"]; +- (id)init { + self = [super init]; + if (self) + self.maxDepth = 512; + return self; } -- (NSString*)stringWithObject:(id)value { - [self clearErrorTrace]; - - if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) { - depth = 0; - NSMutableString *json = [NSMutableString stringWithCapacity:128]; - if ([self appendValue:value into:json]) - return json; - } - - if ([value respondsToSelector:@selector(proxyForJson)]) { - NSString *tmp = [self stringWithObject:[value proxyForJson]]; - if (tmp) - return tmp; - } - - [self addErrorWithCode:EFRAGMENT description:@"Not valid type for JSON"]; - return nil; +- (void)dealloc { + [error release]; + [super dealloc]; } -- (NSString*)stringWithObject:(id)value error:(NSError**)error { +- (NSString*)stringWithObject:(id)value { + NSData *data = [self dataWithObject:value]; + if (data) + return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + return nil; +} + +- (NSString*)stringWithObject:(id)value error:(NSError**)error_ { NSString *tmp = [self stringWithObject:value]; if (tmp) return tmp; - - if (error) - *error = [self.errorTrace lastObject]; + + if (error_) { + NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil]; + *error_ = [NSError errorWithDomain:@"org.brautaset.json.parser.ErrorDomain" code:0 userInfo:ui]; + } + return nil; } -- (NSString*)indent { - return [@"\n" stringByPaddingToLength:1 + 2 * depth withString:@" " startingAtIndex:0]; +- (NSData*)dataWithObject:(id)object { + SBJsonStreamWriter *streamWriter = [[[SBJsonStreamWriter alloc] init] autorelease]; + streamWriter.sortKeys = self.sortKeys; + streamWriter.maxDepth = self.maxDepth; + streamWriter.humanReadable = self.humanReadable; + + BOOL ok = NO; + if ([object isKindOfClass:[NSDictionary class]]) + ok = [streamWriter writeObject:object]; + + else if ([object isKindOfClass:[NSArray class]]) + ok = [streamWriter writeArray:object]; + + else if ([object respondsToSelector:@selector(proxyForJson)]) + return [self dataWithObject:[object proxyForJson]]; + else { + self.error = @"Not valid type for JSON"; + return nil; + } + + if (ok) + return streamWriter.data; + + self.error = streamWriter.error; + return nil; } - -- (BOOL)appendValue:(id)fragment into:(NSMutableString*)json { - if ([fragment isKindOfClass:[NSDictionary class]]) { - if (![self appendDictionary:fragment into:json]) - return NO; - - } else if ([fragment isKindOfClass:[NSArray class]]) { - if (![self appendArray:fragment into:json]) - return NO; - - } else if ([fragment isKindOfClass:[NSString class]]) { - if (![self appendString:fragment into:json]) - return NO; - - } else if ([fragment isKindOfClass:[NSNumber class]]) { - if ('c' == *[fragment objCType]) { - [json appendString:[fragment boolValue] ? @"true" : @"false"]; - } else if ([fragment isEqualToNumber:(NSNumber*)kCFNumberNaN]) { - [self addErrorWithCode:EUNSUPPORTED description:@"NaN is not a valid number in JSON"]; - return NO; - - } else if (isinf([fragment doubleValue])) { - [self addErrorWithCode:EUNSUPPORTED description:@"Infinity is not a valid number in JSON"]; - return NO; - - } else { - [json appendString:[fragment stringValue]]; - } - } else if ([fragment isKindOfClass:[NSNull class]]) { - [json appendString:@"null"]; - } else if ([fragment respondsToSelector:@selector(proxyForJson)]) { - [self appendValue:[fragment proxyForJson] into:json]; - - } else { - [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"JSON serialisation not supported for %@", [fragment class]]]; - return NO; - } - return YES; -} - -- (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json { - if (maxDepth && ++depth > maxDepth) { - [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; - return NO; - } - [json appendString:@"["]; - - BOOL addComma = NO; - for (id value in fragment) { - if (addComma) - [json appendString:@","]; - else - addComma = YES; - - if ([self humanReadable]) - [json appendString:[self indent]]; - - if (![self appendValue:value into:json]) { - return NO; - } - } - - depth--; - if ([self humanReadable] && [fragment count]) - [json appendString:[self indent]]; - [json appendString:@"]"]; - return YES; -} - -- (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json { - if (maxDepth && ++depth > maxDepth) { - [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; - return NO; - } - [json appendString:@"{"]; - - NSString *colon = [self humanReadable] ? @" : " : @":"; - BOOL addComma = NO; - NSArray *keys = [fragment allKeys]; - if (self.sortKeys) - keys = [keys sortedArrayUsingSelector:@selector(compare:)]; - - for (id value in keys) { - if (addComma) - [json appendString:@","]; - else - addComma = YES; - - if ([self humanReadable]) - [json appendString:[self indent]]; - - if (![value isKindOfClass:[NSString class]]) { - [self addErrorWithCode:EUNSUPPORTED description: @"JSON object key must be string"]; - return NO; - } - - if (![self appendString:value into:json]) - return NO; - - [json appendString:colon]; - if (![self appendValue:[fragment objectForKey:value] into:json]) { - [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"Unsupported value for key %@ in object", value]]; - return NO; - } - } - - depth--; - if ([self humanReadable] && [fragment count]) - [json appendString:[self indent]]; - [json appendString:@"}"]; - return YES; -} - -- (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json { - - [json appendString:@"\""]; - - NSRange esc = [fragment rangeOfCharacterFromSet:kEscapeChars]; - if ( !esc.length ) { - // No special chars -- can just add the raw string: - [json appendString:fragment]; - - } else { - NSUInteger length = [fragment length]; - for (NSUInteger i = 0; i < length; i++) { - unichar uc = [fragment characterAtIndex:i]; - switch (uc) { - case '"': [json appendString:@"\\\""]; break; - case '\\': [json appendString:@"\\\\"]; break; - case '\t': [json appendString:@"\\t"]; break; - case '\n': [json appendString:@"\\n"]; break; - case '\r': [json appendString:@"\\r"]; break; - case '\b': [json appendString:@"\\b"]; break; - case '\f': [json appendString:@"\\f"]; break; - default: - if (uc < 0x20) { - [json appendFormat:@"\\u%04x", uc]; - } else { - CFStringAppendCharacters((CFMutableStringRef)json, &uc, 1); - } - break; - - } - } - } - - [json appendString:@"\""]; - return YES; -} - + + @end diff --git a/src/extThree20JSON/Vendors/YAJL/GHKit/GHNSBundle+Utils.h b/src/extThree20JSON/Vendors/YAJL/GHKit/GHNSBundle+Utils.h new file mode 100644 index 0000000000..6178c13d21 --- /dev/null +++ b/src/extThree20JSON/Vendors/YAJL/GHKit/GHNSBundle+Utils.h @@ -0,0 +1,32 @@ +// +// GHNSBundle+Utils.h +// GHKit +// +// Created by Gabriel Handford on 4/27/09. +// Copyright 2009. All rights reserved. +// + +@interface NSBundle (YAJL_GHUtils) + +/*! + Load data from resource. + @param resource Name of resource + @result NSData + */ +- (NSData *)yajl_gh_loadDataFromResource:(NSString *)resource; + +/*! + Load string data from resource. + @param resource Name of resource + @result NSString + */ +- (NSString *)yajl_gh_loadStringDataFromResource:(NSString *)resource; + +/*! + Get URL for resource. + @param resource Name of resource + @result URL to resource + */ +- (NSURL *)yajl_gh_URLForResource:(NSString *)resource; + +@end diff --git a/src/extThree20JSON/Vendors/YAJL/GHKit/GHNSBundle+Utils.m b/src/extThree20JSON/Vendors/YAJL/GHKit/GHNSBundle+Utils.m new file mode 100644 index 0000000000..cb79da4b9d --- /dev/null +++ b/src/extThree20JSON/Vendors/YAJL/GHKit/GHNSBundle+Utils.m @@ -0,0 +1,33 @@ +// +// GHNSBundle+Utils.m +// GHKit +// +// Created by Gabriel Handford on 4/27/09. +// Copyright 2009. All rights reserved. +// + +#import "extThree20JSON/private/GHNSBundle+Utils.h" + +@implementation NSBundle (YAJL_GHUtils) + +- (NSData *)yajl_gh_loadDataFromResource:(NSString *)resource { + NSParameterAssert(resource); + NSString *resourcePath = [self pathForResource:[resource stringByDeletingPathExtension] ofType:[resource pathExtension]]; + if (!resourcePath) [NSException raise:NSInvalidArgumentException format:@"Resource not found: %@", resource]; + NSError *error = nil; + NSData *data = [NSData dataWithContentsOfFile:resourcePath options:0 error:&error]; + if (error) [NSException raise:NSInvalidArgumentException format:@"Error loading resource at path (%@): %@", resourcePath, error]; + return data; +} + +- (NSString *)yajl_gh_loadStringDataFromResource:(NSString *)resource { + return [[[NSString alloc] initWithData:[self yajl_gh_loadDataFromResource:resource] encoding:NSUTF8StringEncoding] autorelease]; +} + +- (NSURL *)yajl_gh_URLForResource:(NSString *)resource { + NSParameterAssert(resource); + NSString *resourcePath = [self pathForResource:[resource stringByDeletingPathExtension] ofType:[resource pathExtension]]; + return resourcePath ? [NSURL fileURLWithPath:resourcePath] : nil; +} + +@end diff --git a/src/extThree20JSON/Vendors/YAJL/NSBundle+YAJL.h b/src/extThree20JSON/Vendors/YAJL/NSBundle+YAJL.h new file mode 100644 index 0000000000..43a42393ce --- /dev/null +++ b/src/extThree20JSON/Vendors/YAJL/NSBundle+YAJL.h @@ -0,0 +1,62 @@ +// +// NSBundle+YAJL.h +// YAJL +// +// Created by Gabriel Handford on 7/23/09. +// Copyright 2009. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "extThree20JSON/YAJLParser.h" + +/*! + Utilities for loading JSON from resource bundles. + + @code + id JSONValue = [[NSBundle mainBundle] yajl_JSONFromResource:@"kegs.json"]; + @endcode + */ +@interface NSBundle(YAJL) + +/*! + Load JSON from bundle. + @param resource Resource name with extension, for example, file.json + @throws YAJLParserException On parse error + */ +- (id)yajl_JSONFromResource:(NSString *)resource; + +/*! + Load JSON from bundle. + @param resource Resource name with extension, for example, file.json + @param options Parser options + - YAJLParserOptionsNone: No options + - YAJLParserOptionsAllowComments: Javascript style comments will be allowed in the input (both /&asterisk; &asterisk;/ and //) + - YAJLParserOptionsCheckUTF8: Invalid UTF8 strings will cause a parse error + - YAJLParserOptionsStrictPrecision: If YES will force strict precision and return integer overflow error + + @param error Out error + @result JSON value (NSArray, NSDictionary) or nil if errored + */ +- (id)yajl_JSONFromResource:(NSString *)resource options:(YAJLParserOptions)options error:(NSError **)error; + +@end diff --git a/src/extThree20JSON/Vendors/YAJL/NSBundle+YAJL.m b/src/extThree20JSON/Vendors/YAJL/NSBundle+YAJL.m new file mode 100644 index 0000000000..b1284d25f3 --- /dev/null +++ b/src/extThree20JSON/Vendors/YAJL/NSBundle+YAJL.m @@ -0,0 +1,52 @@ +// +// NSBundle+YAJL.m +// YAJL +// +// Created by Gabriel Handford on 7/23/09. +// Copyright 2009. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "extThree20JSON/NSBundle+YAJL.h" +#import "extThree20JSON/NSObject+YAJL.h" +#import "extThree20JSON/private/GHNSBundle+Utils.h" + +// Core +#import "Three20Core/TTCorePreprocessorMacros.h" + +TT_FIX_CATEGORY_BUG(NSBundle_YAJL) + +@implementation NSBundle(YAJL) + +- (id)yajl_JSONFromResource:(NSString *)resource { + NSError *error = nil; + id JSONValue = [self yajl_JSONFromResource:resource options:YAJLParserOptionsNone error:&error]; + if (error) [NSException raise:YAJLParserException format:[error localizedDescription], nil]; + return JSONValue; +} + +- (id)yajl_JSONFromResource:(NSString *)resource options:(YAJLParserOptions)options error:(NSError **)error { + return [[self yajl_gh_loadStringDataFromResource:resource] yajl_JSONWithOptions:YAJLParserOptionsAllowComments error:error]; +} + +@end diff --git a/src/extThree20JSON/Vendors/YAJL/NSObject+YAJL.m b/src/extThree20JSON/Vendors/YAJL/NSObject+YAJL.m index 6aa9406b24..8a02faab95 100644 --- a/src/extThree20JSON/Vendors/YAJL/NSObject+YAJL.m +++ b/src/extThree20JSON/Vendors/YAJL/NSObject+YAJL.m @@ -34,7 +34,6 @@ // Core #import "Three20Core/TTCorePreprocessorMacros.h" - TT_FIX_CATEGORY_BUG(NSObject_YAJL) @implementation NSObject(YAJL) diff --git a/src/extThree20JSON/Vendors/YAJL/README.md b/src/extThree20JSON/Vendors/YAJL/README.md index 75490041e6..8b6af7e147 100644 --- a/src/extThree20JSON/Vendors/YAJL/README.md +++ b/src/extThree20JSON/Vendors/YAJL/README.md @@ -1,28 +1,23 @@ -# YAJL (Objective-C Wrapper) +# YAJL Framework -YAJL.framework is an Objective-C wrapper around the [YAJL](http://lloyd.github.com/yajl/) SAX-style JSON parser. +The YAJL framework is an Objective-C framework for the [YAJL](http://lloyd.github.com/yajl/) SAX-style JSON parser. -## Download +## Features -### Mac OS X +- Stream parsing, comments in JSON, better error messages. +- Parse directly from NSString or NSData. +- Generate JSON from default or custom types. +- Properly handles large numeric types. +- Document style parser. +- Error by exception or out error. -[YAJL-0.2.16.zip](http://rel.me.s3.amazonaws.com/yajl/YAJL-0.2.16.zip) *YAJL.framework* (2010/03/01) +## Links -### iPhone - -[libYAJLIPhone-0.2.17.zip](http://rel.me.s3.amazonaws.com/yajl/libYAJLIPhone-0.2.17.zip) *Static Library for iPhone OS 3.0 Simulator & Device* (2010/04/15) +- The online [API documentation](http://gabriel.github.com/yajl-objc/). ## Install (Mac OS X) -There are two options. You can install it globally in /Library/Frameworks or with a little extra effort embed it with your project. - -### Installing in /Library/Frameworks - -- Copy `YAJL.framework` to `/Library/Frameworks/` -- In the target Info window, General tab: - - Add a linked library, under `Mac OS X 10.5 SDK` section, select `YAJL.framework` - -### Installing in your project +### Installing in your project (Recommended) - Copy `YAJL.framework` to your project directory (maybe in MyProject/Frameworks/.) - Add the `YAJL.framekwork` files (from MyProject/Frameworks/) to your target. It should be visible as a `Linked Framework` in the target. @@ -32,138 +27,39 @@ There are two options. You can install it globally in /Library/Frameworks or wit - Drag `YAJL.framework` into the the build phase - Make sure the copy phase appears before any `Run Script` phases -## Install (iPhone) - -- Add files (from static library build) to project. -- Under 'Other Linker Flags' in the Test target, add `-ObjC` and `-all_load` (So NSObject+YAJL category is loaded). - -## Usage - -To parse JSON from an NSData (or NSString): - - #import "NSObject+YAJL.h" - - NSData *JSONData = [NSData dataWithContentsOfFile:@"example.json"]; - NSArray *arrayFromData = [JSONData yajl_JSON]; - - NSString *JSONString = @"[\"Test\"]"; - NSArray *arrayFromString = [JSONString yajl_JSON]; - - // With options and out error - NSError *error = nil; - NSArray *arrayFromString = [JSONString yajl_JSONWithOptions:YAJLParserOptionsAllowComments error:&error]; - -To generate JSON from an object: - - #import "NSObject+YAJL.h" - - NSDictionary *dict = [NSDictionary dictionaryWithObject:@"value" forKey:@"key"]; - NSString *JSONString = [dict yajl_JSONString]; - - // Beautified with custon indent string - NSArray *array = [NSArray arrayWithObjects:@"value1", @"value2", nil]; - NSString *JSONString = [dict yajl_JSONStringWithOptions:YAJLGenOptionsBeautify indentString:@" "]; - -To use the streaming (or SAX style) parser, use `YAJLParser`. For higher level (document) streaming, see below. - - NSData *data = [NSData dataWithContentsOfFile:@"example.json"]; - - YAJLParser *parser = [[YAJLParser alloc] initWithParserOptions:YAJLParserOptionsAllowComments]; - parser.delegate = self; - [parser parse:data]; - if (parser.parserError) { - NSLog(@"Error:\n%@", parser.parserError); - } - - parser.delegate = nil; - [parser release]; - - // Include delegate methods from YAJLParserDelegate - /* - - (void)parserDidStartDictionary:(YAJLParser *)parser; - - (void)parserDidEndDictionary:(YAJLParser *)parser; - - - (void)parserDidStartArray:(YAJLParser *)parser; - - (void)parserDidEndArray:(YAJLParser *)parser; - - - (void)parser:(YAJLParser *)parser didMapKey:(NSString *)key; - - (void)parser:(YAJLParser *)parser didAdd:(id)value; - */ - -### Parser Options - -There are options when parsing that can be specified with `YAJLParser#initWithParserOptions:`. - -- `YAJLParserOptionsAllowComments`: Allows comments in JSON -- `YAJLParserOptionsCheckUTF8`: Will verify UTF-8 -- `YAJLParserOptionsStrictPrecision`: Will force strict precision and return integer overflow error, if number is greater than long long. - -### Streaming Example (Parser) - - YAJLParser *parser = [[[YAJLParser alloc] init] autorelease]; - parser.delegate = self; - - // A chunk of data comes... - YAJLParserStatus status = [parser parse:chunk1]; - // 'status' should be YAJLParserStatusInsufficientData, if its not finished - if (parser.parserError) ...; - - // Another chunk of data comes... - YAJLParserStatus status = [parser parse:chunk2]; - // 'status' should be YAJLParserStatusOK if its finished - if (parser.parserError) ...; - -## Usage (Document-style) - -To use the document style, use `YAJLDocument`. Usage should be very similar to `NSXMLDocument`. - - NSData *data = [NSData dataWithContentsOfFile:@"example.json"]; - NSError *error = nil; - YAJLDocument *document = [[YAJLDocument alloc] initWithData:data parserOptions:0 error:&error]; - // Access root element at document.root - NSLog(@"Root: %@", document.root); - [document release]; - -### Streaming Example (Document) - - YAJLDocument *document = [[YAJLDocument alloc] init]; - document.delegate = self; - - NSError *error = nil; - [document parse:chunk1 error:error]; - [document parse:chunk2 error:error]; - - // You can access root element at document.root - NSLog(@"Root: %@", document.root); - [document release]; - - // Or via the YAJLDocumentDelegate delegate methods - - - (void)document:(YAJLDocument *)document didAddDictionary:(NSDictionary *)dict { } - - (void)document:(YAJLDocument *)document didAddArray:(NSArray *)array { } - - (void)document:(YAJLDocument *)document didAddObject:(id)object toArray:(NSArray *)array { } - - (void)document:(YAJLDocument *)document didSetObject:(id)object forKey:(id)key inDictionary:(NSDictionary *)dict { } - -## Customized Encoding - -To implement JSON encodable value for custom objects or override for existing objects, implement `- (id)JSON;` - -For example: - - @interface CustomObject : NSObject - @end - - @implementation CustomObject - - - (id)JSON { - return [NSArray arrayWithObject:[NSNumber numberWithInteger:1]]; - } - - @end - -Then: - - CustomObject *customObject = [[CustomObject alloc] init]; - NSString *JSONString = [customObject yajl_JSON]; - // JSONString == "[1]"; +### Installing in /Library/Frameworks + +- Copy `YAJL.framework` to `/Library/Frameworks/` +- In the target Info window, General tab: + - Add a linked library, under `Mac OS X 10.5 SDK` section, select `YAJL.framework` + +## Install (iOS) + +- Add `YAJLiOS.framework` to your project. +- Add the frameworks to `Linked Libraries`: + - `YAJLiOS.framework` + - `CoreGraphics.framework` + - `Foundation.framework` + - `UIKit.framework` +- Under `Framework Search Paths` make sure the (parent) directory to `YAJLiOS.framework` is listed. +- Under `Other Linker Flags` in your target, add `-ObjC` and `-all_load` + +## Apps + +YAJL framework is used by: + +- [Yelp for iPhone/iPad](http://itunes.apple.com/us/app/yelp/id284910350?mt=8) +- Add your app here! + +## Docset + +Download and copy the YAJL.docset to `~/Library/Developer/Shared/Documentation/DocSets/YAJL.docset` + +(You may need to restart XCode after copying the file.) + +The documentation will appear within XCode: + +![YAJL-Docset](http://rel.me.s3.amazonaws.com/yajl/images/docset.png) + + diff --git a/src/extThree20JSON/Vendors/YAJL/YAJL.h b/src/extThree20JSON/Vendors/YAJL/YAJL.h index 1545c6856e..a4a8301940 100644 --- a/src/extThree20JSON/Vendors/YAJL/YAJL.h +++ b/src/extThree20JSON/Vendors/YAJL/YAJL.h @@ -51,7 +51,10 @@ To use the framework (for Mac OS X or iOS): @code + // For Mac OS X #import + // For iOS + #import @endcode @section Examples Examples @@ -70,7 +73,7 @@ NSArray *arrayFromString = [JSONString yajl_JSON]; @endcode - @subsection Example2 To parse JSON from NSString with error and comments + @subsection Example2_1 To parse JSON from NSString with error and comments @code // With options and out error @@ -84,6 +87,7 @@ @code NSDictionary *dict = [NSDictionary dictionaryWithObject:@"value" forKey:@"key"]; NSString *JSONString = [dict yajl_JSONString]; + // ==> {"key":"value"} @endcode @subsection Example4 To generate JSON from an object, beautified with custom indent diff --git a/src/extThree20JSON/Vendors/YAJL/YAJLDocument.h b/src/extThree20JSON/Vendors/YAJL/YAJLDocument.h index f8c2b2b96b..8eee07c7c7 100644 --- a/src/extThree20JSON/Vendors/YAJL/YAJLDocument.h +++ b/src/extThree20JSON/Vendors/YAJL/YAJLDocument.h @@ -147,6 +147,19 @@ extern NSInteger YAJLDocumentStackCapacity; */ - (id)initWithData:(NSData *)data parserOptions:(YAJLParserOptions)parserOptions error:(NSError **)error; +/*! + Create document from data. + @param data Data to parse + @param parserOptions Parse options + - YAJLParserOptionsNone: No options + - YAJLParserOptionsAllowComments: Javascript style comments will be allowed in the input (both /&asterisk; &asterisk;/ and //) + - YAJLParserOptionsCheckUTF8: Invalid UTF8 strings will cause a parse error + - YAJLParserOptionsStrictPrecision: If YES will force strict precision and return integer overflow error + @param capacity Initial capacity for NSArray and NSDictionary objects (Defaults to 20) + @param error Error to set on failure + */ +- (id)initWithData:(NSData *)data parserOptions:(YAJLParserOptions)parserOptions capacity:(NSInteger)capacity error:(NSError **)error; + /*! Create empty document with parser options. @param parserOptions Parse options @@ -157,6 +170,17 @@ extern NSInteger YAJLDocumentStackCapacity; */ - (id)initWithParserOptions:(YAJLParserOptions)parserOptions; +/*! + Create empty document with parser options. + @param parserOptions Parse options + - YAJLParserOptionsNone: No options + - YAJLParserOptionsAllowComments: Javascript style comments will be allowed in the input (both /&asterisk; &asterisk;/ and //) + - YAJLParserOptionsCheckUTF8: Invalid UTF8 strings will cause a parse error + - YAJLParserOptionsStrictPrecision: If YES will force strict precision and return integer overflow error + @param capacity Initial capacity for NSArray and NSDictionary objects (Defaults to 20) + */ +- (id)initWithParserOptions:(YAJLParserOptions)parserOptions capacity:(NSInteger)capacity; + /*! Parse data. @param data Data to parse diff --git a/src/extThree20JSON/Vendors/YAJL/YAJLDocument.m b/src/extThree20JSON/Vendors/YAJL/YAJLDocument.m index 71040a2c5f..0b07feb8e7 100644 --- a/src/extThree20JSON/Vendors/YAJL/YAJLDocument.m +++ b/src/extThree20JSON/Vendors/YAJL/YAJLDocument.m @@ -46,6 +46,10 @@ - (id)init { } - (id)initWithParserOptions:(YAJLParserOptions)parserOptions { + return [self initWithParserOptions:parserOptions capacity:YAJLDocumentStackCapacity]; +} + +- (id)initWithParserOptions:(YAJLParserOptions)parserOptions capacity:(NSInteger)capacity { if ((self = [super init])) { stack_ = [[NSMutableArray alloc] initWithCapacity:YAJLDocumentStackCapacity]; keyStack_ = [[NSMutableArray alloc] initWithCapacity:YAJLDocumentStackCapacity]; @@ -57,7 +61,11 @@ - (id)initWithParserOptions:(YAJLParserOptions)parserOptions { } - (id)initWithData:(NSData *)data parserOptions:(YAJLParserOptions)parserOptions error:(NSError **)error { - if ((self = [self initWithParserOptions:parserOptions])) { + return [self initWithData:data parserOptions:parserOptions capacity:YAJLDocumentStackCapacity error:error]; +} + +- (id)initWithData:(NSData *)data parserOptions:(YAJLParserOptions)parserOptions capacity:(NSInteger)capacity error:(NSError **)error { + if ((self = [self initWithParserOptions:parserOptions capacity:capacity])) { [self parse:data error:error]; } return self; diff --git a/src/extThree20JSON/extThree20JSON.xcodeproj/project.pbxproj b/src/extThree20JSON/extThree20JSON.xcodeproj/project.pbxproj index d0082d7d27..d2e0e21919 100755 --- a/src/extThree20JSON/extThree20JSON.xcodeproj/project.pbxproj +++ b/src/extThree20JSON/extThree20JSON.xcodeproj/project.pbxproj @@ -17,6 +17,10 @@ 664B29EC12848B1A0008D569 /* TTErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 664B29E812848B1A0008D569 /* TTErrorCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 664B29F012848B270008D569 /* TTErrorCodes.m in Sources */ = {isa = PBXBuildFile; fileRef = 664B29EF12848B270008D569 /* TTErrorCodes.m */; }; 664B29F312848B270008D569 /* TTErrorCodes.m in Sources */ = {isa = PBXBuildFile; fileRef = 664B29EF12848B270008D569 /* TTErrorCodes.m */; }; + 66A16605134266380052CA46 /* NSBundle+YAJL.h in Headers */ = {isa = PBXBuildFile; fileRef = 66A16603134266380052CA46 /* NSBundle+YAJL.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 66A16606134266380052CA46 /* NSBundle+YAJL.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A16604134266380052CA46 /* NSBundle+YAJL.m */; }; + 66A1663513427CD10052CA46 /* GHNSBundle+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 66A1663313427CD10052CA46 /* GHNSBundle+Utils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 66A1663613427CD10052CA46 /* GHNSBundle+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A1663413427CD10052CA46 /* GHNSBundle+Utils.m */; }; 66E71B9112938CE3007134B0 /* yajl_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 66E71B5A12938CE3007134B0 /* yajl_common.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66E71B9212938CE3007134B0 /* yajl_gen.h in Headers */ = {isa = PBXBuildFile; fileRef = 66E71B5B12938CE3007134B0 /* yajl_gen.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66E71B9312938CE3007134B0 /* yajl_parse.h in Headers */ = {isa = PBXBuildFile; fileRef = 66E71B5C12938CE3007134B0 /* yajl_parse.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -35,6 +39,20 @@ 66E71BA012938CE3007134B0 /* yajl_parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 66E71B6D12938CE3007134B0 /* yajl_parser.h */; settings = {ATTRIBUTES = (Private, ); }; }; 66E71BA112938CE3007134B0 /* yajl_version.c in Sources */ = {isa = PBXBuildFile; fileRef = 66E71B6E12938CE3007134B0 /* yajl_version.c */; }; 66E71BA812938CF8007134B0 /* yajl_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 66E71BA512938CF8007134B0 /* yajl_version.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269ECE13CB5BC80006FE1F /* SBJsonTokeniser.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269EC013CB5BC80006FE1F /* SBJsonTokeniser.m */; }; + 6D269ECF13CB5BC80006FE1F /* SBJsonTokeniser.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269EC113CB5BC80006FE1F /* SBJsonTokeniser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269ED013CB5BC80006FE1F /* SBJsonStreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269EC213CB5BC80006FE1F /* SBJsonStreamWriterState.m */; }; + 6D269ED113CB5BC80006FE1F /* SBJsonStreamWriterState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269EC313CB5BC80006FE1F /* SBJsonStreamWriterState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269ED213CB5BC80006FE1F /* SBJsonStreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269EC413CB5BC80006FE1F /* SBJsonStreamWriter.m */; }; + 6D269ED313CB5BC80006FE1F /* SBJsonStreamWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269EC513CB5BC80006FE1F /* SBJsonStreamWriter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269ED413CB5BC80006FE1F /* SBJsonStreamParserState.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269EC613CB5BC80006FE1F /* SBJsonStreamParserState.m */; }; + 6D269ED513CB5BC80006FE1F /* SBJsonStreamParserState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269EC713CB5BC80006FE1F /* SBJsonStreamParserState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269ED613CB5BC80006FE1F /* SBJsonStreamParserAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269EC813CB5BC80006FE1F /* SBJsonStreamParserAdapter.m */; }; + 6D269ED713CB5BC80006FE1F /* SBJsonStreamParserAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269EC913CB5BC80006FE1F /* SBJsonStreamParserAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269ED813CB5BC80006FE1F /* SBJsonStreamParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269ECA13CB5BC80006FE1F /* SBJsonStreamParser.m */; }; + 6D269ED913CB5BC80006FE1F /* SBJsonStreamParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269ECB13CB5BC80006FE1F /* SBJsonStreamParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D269EDA13CB5BC80006FE1F /* NSObject+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D269ECC13CB5BC80006FE1F /* NSObject+JSON.m */; }; + 6D269EDB13CB5BC80006FE1F /* NSObject+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D269ECD13CB5BC80006FE1F /* NSObject+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E645A1D11876C9800F08CB1 /* TTURLJSONResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E645A1A11876C9800F08CB1 /* TTURLJSONResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6E645A1E11876C9800F08CB1 /* TTURLJSONResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E645A1C11876C9800F08CB1 /* TTURLJSONResponse.m */; }; 6E645A3011876CA800F08CB1 /* JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E645A2211876CA800F08CB1 /* JSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -155,6 +173,10 @@ 663DB06D12661EE900CF8CEA /* Library+YAJL.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "Library+YAJL.xcconfig"; path = "Configurations/Library+YAJL.xcconfig"; sourceTree = ""; }; 664B29E812848B1A0008D569 /* TTErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTErrorCodes.h; path = Headers/TTErrorCodes.h; sourceTree = ""; }; 664B29EF12848B270008D569 /* TTErrorCodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTErrorCodes.m; path = Source/TTErrorCodes.m; sourceTree = ""; }; + 66A16603134266380052CA46 /* NSBundle+YAJL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+YAJL.h"; sourceTree = ""; }; + 66A16604134266380052CA46 /* NSBundle+YAJL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+YAJL.m"; sourceTree = ""; }; + 66A1663313427CD10052CA46 /* GHNSBundle+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GHNSBundle+Utils.h"; sourceTree = ""; }; + 66A1663413427CD10052CA46 /* GHNSBundle+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GHNSBundle+Utils.m"; sourceTree = ""; }; 66E71A0312938B22007134B0 /* Changes.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Changes.markdown; sourceTree = ""; }; 66E71A0412938B22007134B0 /* Credits.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Credits.markdown; sourceTree = ""; }; 66E71A0512938B22007134B0 /* Readme.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Readme.markdown; sourceTree = ""; }; @@ -180,6 +202,20 @@ 66E71B6D12938CE3007134B0 /* yajl_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_parser.h; sourceTree = ""; }; 66E71B6E12938CE3007134B0 /* yajl_version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yajl_version.c; sourceTree = ""; }; 66E71BA512938CF8007134B0 /* yajl_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yajl_version.h; sourceTree = ""; }; + 6D269EC013CB5BC80006FE1F /* SBJsonTokeniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonTokeniser.m; sourceTree = ""; }; + 6D269EC113CB5BC80006FE1F /* SBJsonTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonTokeniser.h; sourceTree = ""; }; + 6D269EC213CB5BC80006FE1F /* SBJsonStreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterState.m; sourceTree = ""; }; + 6D269EC313CB5BC80006FE1F /* SBJsonStreamWriterState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterState.h; sourceTree = ""; }; + 6D269EC413CB5BC80006FE1F /* SBJsonStreamWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriter.m; sourceTree = ""; }; + 6D269EC513CB5BC80006FE1F /* SBJsonStreamWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriter.h; sourceTree = ""; }; + 6D269EC613CB5BC80006FE1F /* SBJsonStreamParserState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserState.m; sourceTree = ""; }; + 6D269EC713CB5BC80006FE1F /* SBJsonStreamParserState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserState.h; sourceTree = ""; }; + 6D269EC813CB5BC80006FE1F /* SBJsonStreamParserAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAdapter.m; sourceTree = ""; }; + 6D269EC913CB5BC80006FE1F /* SBJsonStreamParserAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAdapter.h; sourceTree = ""; }; + 6D269ECA13CB5BC80006FE1F /* SBJsonStreamParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParser.m; sourceTree = ""; }; + 6D269ECB13CB5BC80006FE1F /* SBJsonStreamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParser.h; sourceTree = ""; }; + 6D269ECC13CB5BC80006FE1F /* NSObject+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+JSON.m"; sourceTree = ""; }; + 6D269ECD13CB5BC80006FE1F /* NSObject+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+JSON.h"; sourceTree = ""; }; 6E55A4B31183CA80002768DE /* Library+SBJSON.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "Library+SBJSON.xcconfig"; path = "Configurations/Library+SBJSON.xcconfig"; sourceTree = ""; }; 6E6454021184BDD500F08CB1 /* Project.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Project.xcconfig; path = Configurations/Project.xcconfig; sourceTree = ""; }; 6E645A1A11876C9800F08CB1 /* TTURLJSONResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTURLJSONResponse.h; path = Headers/TTURLJSONResponse.h; sourceTree = ""; }; @@ -274,6 +310,15 @@ name = CustomTemplate; sourceTree = ""; }; + 66A1663213427CD10052CA46 /* GHKit */ = { + isa = PBXGroup; + children = ( + 66A1663313427CD10052CA46 /* GHNSBundle+Utils.h */, + 66A1663413427CD10052CA46 /* GHNSBundle+Utils.m */, + ); + path = GHKit; + sourceTree = ""; + }; 66E71B5812938CE3007134B0 /* src */ = { isa = PBXGroup; children = ( @@ -324,6 +369,20 @@ 6E645A2011876CA800F08CB1 /* SBJSON */ = { isa = PBXGroup; children = ( + 6D269EC013CB5BC80006FE1F /* SBJsonTokeniser.m */, + 6D269EC113CB5BC80006FE1F /* SBJsonTokeniser.h */, + 6D269EC213CB5BC80006FE1F /* SBJsonStreamWriterState.m */, + 6D269EC313CB5BC80006FE1F /* SBJsonStreamWriterState.h */, + 6D269EC413CB5BC80006FE1F /* SBJsonStreamWriter.m */, + 6D269EC513CB5BC80006FE1F /* SBJsonStreamWriter.h */, + 6D269EC613CB5BC80006FE1F /* SBJsonStreamParserState.m */, + 6D269EC713CB5BC80006FE1F /* SBJsonStreamParserState.h */, + 6D269EC813CB5BC80006FE1F /* SBJsonStreamParserAdapter.m */, + 6D269EC913CB5BC80006FE1F /* SBJsonStreamParserAdapter.h */, + 6D269ECA13CB5BC80006FE1F /* SBJsonStreamParser.m */, + 6D269ECB13CB5BC80006FE1F /* SBJsonStreamParser.h */, + 6D269ECC13CB5BC80006FE1F /* NSObject+JSON.m */, + 6D269ECD13CB5BC80006FE1F /* NSObject+JSON.h */, 66E71A0312938B22007134B0 /* Changes.markdown */, 66E71A0412938B22007134B0 /* Credits.markdown */, 66E71A0512938B22007134B0 /* Readme.markdown */, @@ -375,8 +434,11 @@ children = ( 6E645AFF1187744C00F08CB1 /* README.md */, 6E645AFC1187744C00F08CB1 /* LICENSE */, + 66A1663213427CD10052CA46 /* GHKit */, 6E645AF91187744C00F08CB1 /* GTM */, 66E71B5812938CE3007134B0 /* src */, + 66A16603134266380052CA46 /* NSBundle+YAJL.h */, + 66A16604134266380052CA46 /* NSBundle+YAJL.m */, 6E645AFD1187744C00F08CB1 /* NSObject+YAJL.h */, 6E645AFE1187744C00F08CB1 /* NSObject+YAJL.m */, 6E645B7C1187744D00F08CB1 /* YAJL.h */, @@ -466,6 +528,8 @@ 66E71BA812938CF8007134B0 /* yajl_version.h in Headers */, 6609982212939800007B1E07 /* TTExtensionLoader.h in Headers */, 6609987112939B06007B1E07 /* GTMBase64.h in Headers */, + 66A16605134266380052CA46 /* NSBundle+YAJL.h in Headers */, + 66A1663513427CD10052CA46 /* GHNSBundle+Utils.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -483,6 +547,13 @@ 6E6462121187DD2F00F08CB1 /* extThree20JSON.h in Headers */, 664B29E912848B1A0008D569 /* TTErrorCodes.h in Headers */, 6609982412939800007B1E07 /* TTExtensionLoader.h in Headers */, + 6D269ECF13CB5BC80006FE1F /* SBJsonTokeniser.h in Headers */, + 6D269ED113CB5BC80006FE1F /* SBJsonStreamWriterState.h in Headers */, + 6D269ED313CB5BC80006FE1F /* SBJsonStreamWriter.h in Headers */, + 6D269ED513CB5BC80006FE1F /* SBJsonStreamParserState.h in Headers */, + 6D269ED713CB5BC80006FE1F /* SBJsonStreamParserAdapter.h in Headers */, + 6D269ED913CB5BC80006FE1F /* SBJsonStreamParser.h in Headers */, + 6D269EDB13CB5BC80006FE1F /* NSObject+JSON.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -695,6 +766,8 @@ 66E71BA112938CE3007134B0 /* yajl_version.c in Sources */, 6609981B129397F5007B1E07 /* TTExtensionLoader.m in Sources */, 6609987212939B06007B1E07 /* GTMBase64.m in Sources */, + 66A16606134266380052CA46 /* NSBundle+YAJL.m in Sources */, + 66A1663613427CD10052CA46 /* GHNSBundle+Utils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -710,6 +783,13 @@ 6E645A3C11876CA800F08CB1 /* SBJsonWriter.m in Sources */, 664B29F012848B270008D569 /* TTErrorCodes.m in Sources */, 6609981F129397F5007B1E07 /* TTExtensionLoader.m in Sources */, + 6D269ECE13CB5BC80006FE1F /* SBJsonTokeniser.m in Sources */, + 6D269ED013CB5BC80006FE1F /* SBJsonStreamWriterState.m in Sources */, + 6D269ED213CB5BC80006FE1F /* SBJsonStreamWriter.m in Sources */, + 6D269ED413CB5BC80006FE1F /* SBJsonStreamParserState.m in Sources */, + 6D269ED613CB5BC80006FE1F /* SBJsonStreamParserAdapter.m in Sources */, + 6D269ED813CB5BC80006FE1F /* SBJsonStreamParser.m in Sources */, + 6D269EDA13CB5BC80006FE1F /* NSObject+JSON.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -924,7 +1004,6 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; PREBINDING = NO; SDKROOT = iphoneos; @@ -940,7 +1019,6 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SRCROOT)/../common/Xcode324iOS41Fix.pch"; PREBINDING = NO; SDKROOT = iphoneos; ZERO_LINK = NO; diff --git a/src/scripts/Pbxproj.py b/src/scripts/Pbxproj.py index 65c270a464..639f724c8c 100644 --- a/src/scripts/Pbxproj.py +++ b/src/scripts/Pbxproj.py @@ -319,23 +319,31 @@ def add_buildfile(self, name, file_ref_hash, default_guid): def add_filereference(self, name, file_type, default_guid, rel_path, source_tree): project_data = self.get_project_data() + quoted_rel_path = '"'+rel_path.strip('"')+'"' + fileref_hash = None match = re.search('([A-Z0-9]+) \/\* '+re.escape(name)+' \*\/ = \{isa = PBXFileReference; lastKnownFileType = "wrapper.'+file_type+'"; name = '+re.escape(name)+'; path = '+re.escape(rel_path)+';', project_data) + + if not match: + # Check again for quoted versions, just to be sure. + match = re.search('([A-Z0-9]+) \/\* '+re.escape(name)+' \*\/ = \{isa = PBXFileReference; lastKnownFileType = "wrapper.'+file_type+'"; name = '+re.escape(name)+'; path = '+re.escape(quoted_rel_path)+';', project_data) + if match: logging.info("This file has already been added.") (fileref_hash, ) = match.groups() else: match = re.search('\/\* Begin PBXFileReference section \*\/\n', project_data) - + if not match: logging.error("Couldn't find the PBXFileReference section.") return False - + fileref_hash = default_guid - - pbxfileref = "\t\t"+fileref_hash+" /* "+name+" */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper."+file_type+"\"; name = "+name+"; path = "+rel_path+"; sourceTree = "+source_tree+"; };\n" + + pbxfileref = "\t\t"+fileref_hash+" /* "+name+" */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper."+file_type+"\"; name = "+name+"; path = "+quoted_rel_path+"; sourceTree = "+source_tree+"; };\n" + project_data = project_data[:match.end()] + pbxfileref + project_data[match.end():] self.set_project_data(project_data) @@ -460,9 +468,9 @@ def add_header_search_path(self, configuration): def add_build_setting(self, configuration, setting_name, value): project_data = self.get_project_data() - match = re.search('\/\* '+configuration+' \*\/ = {\n[ \t]+isa = XCBuildConfiguration;\n[ \t]+buildSettings = \{\n((?:.|\n)+?)\};', project_data) + match = re.search('\/\* '+configuration+' \*\/ = {\n[ \t]+isa = XCBuildConfiguration;\n(?:.|\n)+?[ \t]+buildSettings = \{\n((?:.|\n)+?)\};', project_data) if not match: - print "Couldn't find this configuration." + print "Couldn't find the "+configuration+" configuration in "+self.path() return False settings_start = match.start(1)