PageRenderTime 34ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/extThree20CSSStyle/Sources/TTCSSStyleSheet.m

https://github.com/GetMoPix/three20
Objective C | 477 lines | 260 code | 103 blank | 114 comment | 35 complexity | 2d1e3f65b32a62c9932a85b1a1638705 MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "extThree20CSSStyle/TTCSSStyleSheet.h"
  17. #import "extThree20CSSStyle/TTCSSRuleSet.h"
  18. #import "extThree20CSSStyle/TTDataPopulator.h"
  19. #import "extThree20CSSStyle/TTDataConverter.h"
  20. #import "extThree20CSSStyle/TTCSSParser.h"
  21. // Style
  22. #import "Three20Style/TTGlobalStyle.h"
  23. #import "Three20Style/TTStyle.h"
  24. // Core
  25. #import "Three20Core/TTCorePreprocessorMacros.h"
  26. #import "Three20Core/TTGlobalCore.h"
  27. #import "Three20Core/TTDebug.h"
  28. NSString* kCssPropertyColor = @"color";
  29. NSString* kCssPropertyBackgroundColor = @"background-color";
  30. NSString* kCssPropertyFont = @"font";
  31. NSString* kCssPropertyFontSize = @"font-size";
  32. NSString* kCssPropertyFontWeight = @"font-weight";
  33. NSString* kCssPropertyFontFamily = @"font-family";
  34. NSString* kCssPropertyTextShadow = @"text-shadow";
  35. // Text shadow keys
  36. NSString* kKeyTextShadowHOffset = @"hoffset";
  37. NSString* kKeyTextShadowVOffset = @"voffset";
  38. NSString* kKeyTextShadowBlur = @"blur";
  39. NSString* kKeyTextShadowColor = @"color";
  40. ///////////////////////////////////////////////////////////////////////////////////////////////////
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. ///////////////////////////////////////////////////////////////////////////////////////////////////
  43. @implementation TTCSSStyleSheet
  44. @synthesize cssStyles = _cssStyles;
  45. @synthesize cssRulesSet = _cssRulesSet;
  46. ///////////////////////////////////////////////////////////////////////////////////////////////////
  47. - (id)init {
  48. self = [super init];
  49. if (self) {
  50. [[NSNotificationCenter defaultCenter]
  51. addObserver: self
  52. selector: @selector(didReceiveMemoryWarning:)
  53. name: UIApplicationDidReceiveMemoryWarningNotification
  54. object: nil];
  55. }
  56. return self;
  57. }
  58. ///////////////////////////////////////////////////////////////////////////////////////////////////
  59. - (void)dealloc {
  60. [[NSNotificationCenter defaultCenter]
  61. removeObserver: self
  62. name: UIApplicationDidReceiveMemoryWarningNotification
  63. object: nil];
  64. TT_RELEASE_SAFELY(_cssRulesSet);
  65. TT_RELEASE_SAFELY(_cssStyles);
  66. TT_RELEASE_SAFELY(_propertiesMap);
  67. [super dealloc];
  68. }
  69. ///////////////////////////////////////////////////////////////////////////////////////////////////
  70. //////////////////////////////////////////////////////////////////////////////////////////////////
  71. #pragma mark -
  72. #pragma mark NSNotifications
  73. ///////////////////////////////////////////////////////////////////////////////////////////////////
  74. - (void)didReceiveMemoryWarning:(void*)object {
  75. [self freeMemory];
  76. }
  77. ///////////////////////////////////////////////////////////////////////////////////////////////////
  78. ///////////////////////////////////////////////////////////////////////////////////////////////////
  79. #pragma mark -
  80. #pragma mark CSS Parsing
  81. ///////////////////////////////////////////////////////////////////////////////////////////////////
  82. - (BOOL)loadFromFilename:(NSString*)filename {
  83. TT_RELEASE_SAFELY(_cssStyles);
  84. TT_RELEASE_SAFELY(_cssRulesSet);
  85. BOOL didLoadSuccessfully = NO;
  86. if ([[NSFileManager defaultManager] fileExistsAtPath:filename]) {
  87. TTCSSParser* parser = [[TTCSSParser alloc] init];
  88. NSDictionary* results = [parser parseFilename:filename];
  89. TT_RELEASE_SAFELY(parser);
  90. _cssStyles = [results retain];
  91. _cssRulesSet = [[NSMutableDictionary alloc] initWithCapacity:[_cssStyles count]];
  92. didLoadSuccessfully = YES;
  93. }
  94. return didLoadSuccessfully;
  95. }
  96. ///////////////////////////////////////////////////////////////////////////////////////////////////
  97. - (void)addStyleSheet:(TTCSSStyleSheet*)styleSheet {
  98. TTDASSERT(nil != styleSheet);
  99. if (nil == styleSheet) {
  100. return;
  101. }
  102. // Clear the cache first.
  103. TT_RELEASE_SAFELY(_cssRulesSet);
  104. _cssRulesSet = [[NSMutableDictionary alloc] initWithCapacity:[_cssStyles count]];
  105. // Should init the styles?
  106. if ( !_cssStyles )
  107. _cssStyles = [NSDictionary new];
  108. NSMutableDictionary* newStyles = [_cssStyles mutableCopy];
  109. for (NSString* selector in styleSheet.cssStyles) {
  110. NSDictionary* addingRuleSet = [styleSheet.cssStyles objectForKey:selector];
  111. NSDictionary* existingRuleSet = [_cssStyles objectForKey:selector];
  112. if (nil == existingRuleSet) {
  113. // Easiest case where the old style sheet doesn't have the rule set, we just add it.
  114. [newStyles setObject:addingRuleSet forKey:selector];
  115. continue;
  116. }
  117. if ([addingRuleSet count] > 0) {
  118. NSMutableDictionary* newRuleSet = [existingRuleSet mutableCopy];
  119. [newRuleSet addEntriesFromDictionary:addingRuleSet];
  120. [newStyles setObject:newRuleSet forKey:selector];
  121. TT_RELEASE_SAFELY(newRuleSet);
  122. }
  123. }
  124. TT_RELEASE_SAFELY(_cssStyles);
  125. _cssStyles = newStyles;
  126. }
  127. ///////////////////////////////////////////////////////////////////////////////////////////////////
  128. ///////////////////////////////////////////////////////////////////////////////////////////////////
  129. #pragma mark -
  130. #pragma mark Populate Methods.
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  132. -(NSDictionary*)propertiesMap {
  133. if ( !_propertiesMap ) {
  134. _propertiesMap = [[NSDictionary dictionaryWithObjectsAndKeys:
  135. @"color", @"color",
  136. @"font_family", @"font",
  137. @"font_family", @"font-family",
  138. @"font_weight", @"font-weight",
  139. @"font_size", @"font-size",
  140. @"background_color", @"background-color",
  141. @"background_image", @"background-image",
  142. @"text_shadow", @"text-shadow",
  143. @"text_align", @"text-align",
  144. @"width", @"width",
  145. @"visibility", @"visibility",
  146. @"height", @"height",
  147. @"top", @"top",
  148. @"left", @"left",
  149. @"right", @"right",
  150. @"bottom", @"bottom",
  151. @"text_shadow_opacity", @"text-shadow-opacity",
  152. @"margin_left", @"margin-left",
  153. @"margin_right", @"margin-right",
  154. @"vertical_align", @"vertical-align",
  155. nil] retain];
  156. }
  157. return _propertiesMap;
  158. }
  159. ///////////////////////////////////////////////////////////////////////////////////////////////////
  160. // When the Data Populator can't automatically convert some specific type. He will call
  161. // this method and let you extend the class converting you specific type.
  162. ///////////////////////////////////////////////////////////////////////////////////////////////////
  163. -(id)tryToConvert:(id)object ofClass:(Class)objectClass toClass:(Class)convertToClass {
  164. ///////// /////// /////// /////// /////// /////// /////// /////// /////// ///////
  165. // Text Shadow Model.
  166. if ( convertToClass == [TTCSSTextShadowModel class] ) {
  167. // Anything more or less is unsupported, and therefore this property is ignored
  168. // according to the W3C guidelines.
  169. NSArray* values = object;
  170. TTDASSERT([values count] >= 4);
  171. if ([values count] >= 4) {
  172. TTCSSTextShadowModel *shadowModel;
  173. // Create an Shadow Model from data and return.
  174. shadowModel = [TTCSSTextShadowModel initWithShadowColor:[values subarrayWithRange:
  175. NSMakeRange(3,[values count] - 3)]
  176. andShadowOffset:CGSizeMake([[values objectAtIndex:0] floatValue],
  177. [[values objectAtIndex:1] floatValue])
  178. andShadowBlur:[values objectAtIndex:3]];
  179. // Return.
  180. return shadowModel;
  181. }
  182. }
  183. ///////// /////// /////// /////// /////// /////// /////// /////// /////// ///////
  184. // Strings.
  185. if ( convertToClass == [NSString class] ) {
  186. if ( [object isKindOfClass:[NSArray class]] ) {
  187. NSMutableString *merged = [NSMutableString string];
  188. for ( NSString* part in object ) {
  189. [merged appendString:part];
  190. }
  191. // Return merged.
  192. return merged;
  193. }
  194. else if ( [object isKindOfClass:[NSString class]] ) {
  195. return object;
  196. }
  197. else {
  198. return nil;
  199. }
  200. }
  201. ///////// /////// /////// /////// /////// /////// /////// /////// /////// ///////
  202. // Some crude data is returned as an NSArray...
  203. if ( [object isKindOfClass:[NSArray class]] ) {
  204. // If have more than one element, return the full array.
  205. if ( [object count] > 1 )
  206. return object;
  207. // If not, we just need the first element.
  208. id element = [object objectAtIndex:0];
  209. // Only one parameter is NSNumber, is the size of the font. Sometimes
  210. // this value come with a 'pt' on it.
  211. // We don't need it right? So we clean the string and convert to NSNumber.
  212. if ( convertToClass == [NSNumber class] ) {
  213. // Clean it.
  214. element = [(NSString*)element stringByReplacingOccurrencesOfString:@"pt"
  215. withString:@""];
  216. // Convert to number.
  217. return [TTDataConverter convertToNSNumberThisObject:element];
  218. }
  219. // Return the element.
  220. return element;
  221. }
  222. // If can't convert. Return nil.
  223. return nil;
  224. }
  225. ///////////////////////////////////////////////////////////////////////////////////////////////////
  226. ///////////////////////////////////////////////////////////////////////////////////////////////////
  227. #pragma mark -
  228. ///////////////////////////////////////////////////////////////////////////////////////////////////
  229. - (NSString*)selector:(NSString*)selector forState:(UIControlState)state {
  230. switch (state) {
  231. default:
  232. case UIControlStateNormal:
  233. break;
  234. case UIControlStateHighlighted: {
  235. selector = [selector stringByAppendingString:@":hover"];
  236. break;
  237. }
  238. }
  239. return selector;
  240. }
  241. ///////////////////////////////////////////////////////////////////////////////////////////////////
  242. ///////////////////////////////////////////////////////////////////////////////////////////////////
  243. #pragma mark -
  244. #pragma mark Object Cache
  245. ///////////////////////////////////////////////////////////////////////////////////////////////////
  246. -(TTCSSRuleSet*)css:(NSString*)selector {
  247. TTCSSRuleSet *ruleSet = [_cssRulesSet objectForKey:selector];
  248. ////////////////////////////////////////////////////
  249. // Can't find? Try to create it.
  250. if ( ruleSet == nil ) {
  251. ////////////////////////////////////////////////////
  252. // Retrieve from "crude" repository?
  253. NSDictionary *crudeData = [_cssStyles objectForKey:selector];
  254. // Don't exist? Warn and return nil.
  255. if ( !crudeData ) {
  256. TTDWARNING( @"The CSS selector '%@' don't exist.", selector );
  257. return nil;
  258. }
  259. // Create it.
  260. ruleSet = [TTCSSRuleSet initWithSelectorName:selector];
  261. // Populate
  262. ruleSet = [TTDataPopulator populateObject:ruleSet
  263. withData:crudeData
  264. usingMap:[self propertiesMap]
  265. withDelegate:self];
  266. // Cache it.
  267. [_cssRulesSet setValue:ruleSet forKey:selector];
  268. }
  269. // Return it.
  270. return ruleSet;
  271. }
  272. ///////////////////////////////////////////////////////////////////////////////////////////////////
  273. -(TTCSSRuleSet*)css:(NSString*)selectorName forState:(UIControlState)state {
  274. return [self css:[self selector:selectorName forState:state]];
  275. }
  276. ///////////////////////////////////////////////////////////////////////////////////////////////////
  277. #pragma mark -
  278. #pragma mark Colors
  279. ///////////////////////////////////////////////////////////////////////////////////////////////////
  280. - (UIColor*)colorWithCssSelector:(NSString*)selector forState:(UIControlState)state {
  281. // Try Retrieve Rule Set from Cache.
  282. TTCSSRuleSet *ruleSet = [self css:selector forState:state];
  283. // If don't have an CSS Rule Set, return nil.
  284. if ( !ruleSet ) return nil;
  285. // Return color from rule set.
  286. return ruleSet.color;
  287. }
  288. ///////////////////////////////////////////////////////////////////////////////////////////////////
  289. - (UIColor*)colorWithCssSelector:(NSString*)selector {
  290. return [self colorWithCssSelector:selector forState:UIControlStateNormal];
  291. }
  292. ///////////////////////////////////////////////////////////////////////////////////////////////////
  293. - (UIColor*)backgroundColorWithCssSelector:(NSString*)selector forState:(UIControlState)state {
  294. // Try Retrieve Rule Set from Cache.
  295. TTCSSRuleSet *ruleSet = [self css:selector forState:state];
  296. // If don't have an CSS Rule Set, return nil.
  297. if ( !ruleSet ) return nil;
  298. // Return background color from rule set.
  299. return ruleSet.background_color;
  300. }
  301. ///////////////////////////////////////////////////////////////////////////////////////////////////
  302. - (UIColor*)backgroundColorWithCssSelector:(NSString*)selector {
  303. return [self backgroundColorWithCssSelector:selector forState:UIControlStateNormal];
  304. }
  305. ///////////////////////////////////////////////////////////////////////////////////////////////////
  306. ///////////////////////////////////////////////////////////////////////////////////////////////////
  307. #pragma mark -
  308. #pragma mark Fonts
  309. ///////////////////////////////////////////////////////////////////////////////////////////////////
  310. - (UIFont*)fontWithCssSelector:(NSString*)selector forState:(UIControlState)state {
  311. // Try Retrieve Rule Set from Cache.
  312. TTCSSRuleSet *ruleSet = [self css:selector forState:state];
  313. // If don't have an CSS Rule Set, return nil.
  314. if ( !ruleSet ) return nil;
  315. // Return decoded font from rule set.
  316. return ruleSet.font;
  317. }
  318. ///////////////////////////////////////////////////////////////////////////////////////////////////
  319. - (UIFont*)fontWithCssSelector:(NSString*)selector {
  320. return [self fontWithCssSelector:selector forState:UIControlStateNormal];
  321. }
  322. ///////////////////////////////////////////////////////////////////////////////////////////////////
  323. ///////////////////////////////////////////////////////////////////////////////////////////////////
  324. #pragma mark -
  325. #pragma mark Text Shadows
  326. ///////////////////////////////////////////////////////////////////////////////////////////////////
  327. - (UIColor*)textShadowColorWithCssSelector:(NSString*)selector forState:(UIControlState)state {
  328. // Try Retrieve Rule Set from Cache.
  329. TTCSSRuleSet *ruleSet = [self css:selector forState:state];
  330. // If don't have an CSS Rule Set, return nil.
  331. if ( !ruleSet ) return nil;
  332. // Return Text Shadow Color.
  333. return ruleSet.text_shadow.shadowColor;
  334. }
  335. ///////////////////////////////////////////////////////////////////////////////////////////////////
  336. - (UIColor*)textShadowColorWithCssSelector:(NSString*)selector {
  337. return [self textShadowColorWithCssSelector:selector forState:UIControlStateNormal];
  338. }
  339. ///////////////////////////////////////////////////////////////////////////////////////////////////
  340. - (CGSize)textShadowOffsetWithCssSelector:(NSString*)selector forState:(UIControlState)state {
  341. // Try Retrieve Rule Set from Cache.
  342. TTCSSRuleSet *ruleSet = [self css:selector forState:state];
  343. // If don't have an CSS Rule Set, return zero.
  344. if ( !ruleSet ) return CGSizeZero;
  345. // Return Text Shadow Color.
  346. return ruleSet.text_shadow.shadowOffset;
  347. }
  348. ///////////////////////////////////////////////////////////////////////////////////////////////////
  349. - (CGSize)textShadowOffsetWithCssSelector:(NSString*)selector {
  350. return [self textShadowOffsetWithCssSelector:selector forState:UIControlStateNormal];
  351. }
  352. ///////////////////////////////////////////////////////////////////////////////////////////////////
  353. - (CGFloat)textShadowRadiusWithCssSelector:(NSString*)selector forState:(UIControlState)state {
  354. // Try Retrieve Rule Set from Cache.
  355. TTCSSRuleSet *ruleSet = [self css:selector forState:state];
  356. // If don't have an CSS Rule Set, return zero.
  357. if ( !ruleSet ) return 0.0;
  358. // Return Shadow Blur.
  359. return [[ruleSet.text_shadow shadowBlur] floatValue];
  360. }
  361. ///////////////////////////////////////////////////////////////////////////////////////////////////
  362. - (CGFloat)textShadowRadiusWithCssSelector:(NSString*)selector {
  363. return [self textShadowRadiusWithCssSelector:selector forState:UIControlStateNormal];
  364. }
  365. ///////////////////////////////////////////////////////////////////////////////////////////////////
  366. ///////////////////////////////////////////////////////////////////////////////////////////////////
  367. #pragma mark -
  368. #pragma mark Utilities
  369. ///////////////////////////////////////////////////////////////////////////////////////////////////
  370. - (void)freeMemory {
  371. TT_RELEASE_SAFELY(_cssRulesSet);
  372. _cssRulesSet = [[NSMutableDictionary alloc] initWithCapacity:[_cssStyles count]];
  373. }
  374. @end