Repository: cloudkite/Classy Branch: master Commit: 0ae47bf304c0 Files: 161 Total size: 492.9 KB Directory structure: gitextract_4fisv63u/ ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Classy/ │ ├── Additions/ │ │ ├── CASStyleClassUtilities.h │ │ ├── CASStyleClassUtilities.m │ │ ├── CASStyleableItem.h │ │ ├── CASStyleableItem.m │ │ ├── NSObject+CASSwizzle.h │ │ ├── NSObject+CASSwizzle.m │ │ ├── NSRegularExpression+CASAdditions.h │ │ ├── NSRegularExpression+CASAdditions.m │ │ ├── NSString+CASAdditions.h │ │ ├── NSString+CASAdditions.m │ │ ├── UIBarItem+CASAdditions.h │ │ ├── UIBarItem+CASAdditions.m │ │ ├── UIColor+CASAdditions.h │ │ ├── UIColor+CASAdditions.m │ │ ├── UINavigationBar+CASAdditions.h │ │ ├── UINavigationBar+CASAdditions.m │ │ ├── UINavigationItem+CASAdditions.h │ │ ├── UINavigationItem+CASAdditions.m │ │ ├── UISlider+CASAdditions.h │ │ ├── UISlider+CASAdditions.m │ │ ├── UITabBar+CASAdditions.h │ │ ├── UITabBar+CASAdditions.m │ │ ├── UITextField+CASAdditions.h │ │ ├── UITextField+CASAdditions.m │ │ ├── UIToolbar+CASAdditions.h │ │ ├── UIToolbar+CASAdditions.m │ │ ├── UIView+CASAdditions.h │ │ ├── UIView+CASAdditions.m │ │ ├── UIViewController+CASAdditions.h │ │ └── UIViewController+CASAdditions.m │ ├── Classy.h │ ├── Parser/ │ │ ├── CASDeviceOSVersionItem.h │ │ ├── CASDeviceOSVersionItem.m │ │ ├── CASDeviceScreenSizeItem.h │ │ ├── CASDeviceScreenSizeItem.m │ │ ├── CASDeviceSelector.h │ │ ├── CASDeviceSelector.m │ │ ├── CASDeviceSelectorItem.h │ │ ├── CASDeviceTypeItem.h │ │ ├── CASDeviceTypeItem.m │ │ ├── CASExpressionSolver.h │ │ ├── CASExpressionSolver.m │ │ ├── CASInvocation.h │ │ ├── CASInvocation.m │ │ ├── CASLexer.h │ │ ├── CASLexer.m │ │ ├── CASParser.h │ │ ├── CASParser.m │ │ ├── CASStyleNode.h │ │ ├── CASStyleNode.m │ │ ├── CASStyleProperty.h │ │ ├── CASStyleProperty.m │ │ ├── CASStyleSelector.h │ │ ├── CASStyleSelector.m │ │ ├── CASStyler.h │ │ ├── CASStyler.m │ │ ├── CASTextAttributes.h │ │ ├── CASTextAttributes.m │ │ ├── CASToken.h │ │ ├── CASToken.m │ │ ├── CASUnitToken.h │ │ ├── CASUnitToken.m │ │ ├── CASUtilities.h │ │ └── CASUtilities.m │ ├── Reflection/ │ │ ├── CASArgumentDescriptor.h │ │ ├── CASArgumentDescriptor.m │ │ ├── CASAssociatedObjectsWeakWrapper.h │ │ ├── CASAssociatedObjectsWeakWrapper.m │ │ ├── CASObjectClassDescriptor.h │ │ ├── CASObjectClassDescriptor.m │ │ ├── CASPropertyDescriptor.h │ │ ├── CASPropertyDescriptor.m │ │ ├── CASRuntimeExtensions.h │ │ └── CASRuntimeExtensions.m │ └── Supporting Files/ │ └── Info.plist ├── Classy.podspec ├── Classy.xcodeproj/ │ ├── project.pbxproj │ └── xcshareddata/ │ └── xcschemes/ │ └── Classy.xcscheme ├── Example/ │ ├── ClassyExample/ │ │ ├── CASAppDelegate.h │ │ ├── CASAppDelegate.m │ │ ├── CASCatalogViewController.h │ │ ├── CASCatalogViewController.m │ │ ├── CASRootViewController.h │ │ ├── CASRootViewController.m │ │ ├── CASSimpleFormViewController.h │ │ ├── CASSimpleFormViewController.m │ │ ├── ClassyExample-Info.plist │ │ ├── ClassyExample-Prefix.pch │ │ ├── Images.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage/ │ │ │ └── Contents.json │ │ ├── Stylesheets/ │ │ │ ├── stylesheet.cas │ │ │ └── variables.cas │ │ ├── en.lproj/ │ │ │ └── InfoPlist.strings │ │ └── main.m │ └── ClassyExample.xcodeproj/ │ ├── project.pbxproj │ └── xcshareddata/ │ └── xcschemes/ │ └── ClassyExample.xcscheme ├── LICENSE ├── Podfile ├── README.md ├── Tests/ │ ├── ClassyTests-Info.plist │ ├── ClassyTests-Prefix.pch │ ├── ClassyTests.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── ClassyTests.xccheckout │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── ClassyTests.xcscheme │ ├── ClassyTestsLoader/ │ │ ├── CASTestsAppDelegate.h │ │ ├── CASTestsAppDelegate.m │ │ ├── Classy-Prefix.pch │ │ ├── ClassyTestsLoader-Info.plist │ │ ├── Images.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage/ │ │ │ └── Contents.json │ │ └── main.m │ ├── Specs/ │ │ ├── CASExampleView.h │ │ ├── CASExampleView.m │ │ ├── CASExampleViewController.h │ │ ├── CASExampleViewController.m │ │ ├── CASSwizzler.h │ │ ├── GcovTestObserver.m │ │ ├── Integration Tests/ │ │ │ ├── CASCustomViewSpec.m │ │ │ ├── CASUIAppearanceSpec.m │ │ │ └── CASUIKitSpec.m │ │ ├── UIDevice+CASMockDevice.h │ │ ├── UIDevice+CASMockDevice.m │ │ ├── Unit Tests/ │ │ │ ├── CASArgumentDescriptorSpec.m │ │ │ ├── CASLexerSpec.m │ │ │ ├── CASParserSpec.m │ │ │ ├── CASRuntimeExtensionsSpec.m │ │ │ ├── CASStylePropertySpec.m │ │ │ ├── CASStyleSelectorSpec.m │ │ │ ├── CASStylerSpec.m │ │ │ ├── CASTokenSpec.m │ │ │ └── UIView_CASAdditionsSpec.m │ │ └── XCTest+Spec.h │ ├── Stylesheets/ │ │ ├── CustomView-Basic.cas │ │ ├── Import-1.cas │ │ ├── Import-2.cas │ │ ├── Import-Base.cas │ │ ├── Injected-File.cas │ │ ├── Precedence-1.cas │ │ ├── Precedence-2.cas │ │ ├── Properties-Args.cas │ │ ├── Properties-Basic.cas │ │ ├── Properties-Nested.cas │ │ ├── Selectors-Complex.cas │ │ ├── Selectors-Hierarchy.cas │ │ ├── Selectors-Indentation.cas │ │ ├── Selectors-Media-Queries-styler.cas │ │ ├── Selectors-Media-Queries.cas │ │ ├── Selectors-Messy.cas │ │ ├── Selectors-Nested.cas │ │ ├── UIAppearance-Basic.cas │ │ ├── UIKit-Basic.cas │ │ ├── UIKit-MultipleClasses.cas │ │ ├── Variables-Basic.cas │ │ └── Variables-Injection.cas │ └── UIAppearance-setters.md └── script/ └── coveralls.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode build/* *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata profile *.moved-aside # Desktop Servies .DS_Store .idea/ /script/env.sh /Pods/ /Podfile.lock /Classy.xcworkspace/xcshareddata/Classy.xccheckout Classy.xcodeproj/project.xcworkspace Classy.xcworkspace/contents.xcworkspacedata Artifacts ================================================ FILE: .travis.yml ================================================ language: objective-c before_install: - sudo easy_install cpp-coveralls script: - pod install - xctool -workspace 'Classy.xcworkspace' -scheme 'ClassyTests' -configuration Debug -sdk iphonesimulator7.0 clean build test ARCHS=i386 VALID_ARCHS=i386 ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES after_success: - ./script/coveralls.sh ================================================ FILE: CHANGELOG.md ================================================ v1.0.0 ====== * Promoted v0.2.6 to v1.0.0 as basic functionaltiy is complete and functional. v0.2.6 ====== * Updated list of UIAppearance-setters in tests * Fixed misnamed @param name in code docs * Support for iOS 10 font weights * Reduced warnings caused by missing void in method input declaration v0.2.5 ====== * Styling per window * Caching to binary plist * Load on demand * System font support v0.2.4 ====== * Fixed memory leak * Added ability to inject variables into stylesheets * Added various UIKit properties. Thanks awesome contributors v0.2.3 ====== * Live reload, ensure imported stylesheet files are not watched more than once. * Subtract less precedence points for subclass matching [issue #30](https://github.com/cloudkite/Classy/issues/30) v0.2.2 ====== * Call `cas_updateStyling` on live reload * Added support for `[UIButton setImage:forState:]` ([skeeet](https://github.com/skeeet)) * Only wrap variable values in brackets if variable has more than one value v0.2.1 ====== * Fixed crash when using media queries [issue #24](https://github.com/cloudkite/Classy/issues/24) * Added UILabel textAlignment * Fixed error with style selector nesting * Removed delay in style updates in didMoveToWindow v0.2.0 ====== * Added @media/@device queries ```Scala UIView.widget { /* default background color */ background-color red /* background color on iPads running iOS7 and above */ @media ipad and (version:>=7) { background-color blue } } ``` * Added support for images from documents/caches folder as well as from bundle. ([flippinjoe21](https://github.com/flippinjoe21)) * Fixed Scrolling delaying the update of styling. Fixes [issue #16](https://github.com/cloudkite/Classy/issues/16) * Fixed style selector subclass matching with styleClass. Fixes [issue #18](https://github.com/cloudkite/Classy/issues/18) v0.1.0 ======= * Added ability to seperate stylesheets into multiple files and `@import` them ```scala @import "variables.cas" UITableView UILabel { text-color $mainColor } ``` * Added UIView tintColor ([glancashire](https://github.com/glancashire)) v0.0.3 ======= * Added ability to use rgb, rgba, hsl and hsla colors ([avalanched](https://github.com/avalanched)) ```scala background-color rgb(200, 100, 150) text-color rgba(200, 100, 150, 0.5) layer @{ border-color hsl(200, 60%, 100%) shadow-color hsla(200, 60%, 100%, 0.2) } ``` v0.0.2 ======= * Added ability to specify UIViewController class/subclass in style selector hierarchy. eg ```scala MYHomeViewController > UIButton.main { background-color black } ``` ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to Classy Looking to contribute something to Classy? We would love to have you involved! To make the process as painless as possible, we have just a couple of guidelines that should make life easier for everyone involved. ## Pull Requests If you know exactly how to implement the feature being suggested or fix the bug being reported, please open a pull request instead of an issue. Pull requests are easier than patches or inline code blocks for discussing and merging the changes. If you can't make the change yourself, please open an issue after making sure that one isn't already logged. ## Suggest Enhancements Please [check existing enhancements](https://github.com/cloudkite/classy/issues?labels=enhancement&page=1&state=open) Before submitting your feature request. If it already exists you can vote for it by adding a +1 ## Reporting issues Bugs must be isolated and reproducible problems. Please read the following guidelines before opening any issue. 1. Please [check existing bugs](https://github.com/cloudkite/classy/issues?&page=1&state=open) before submitting a new issue. Moreover, the issue may have already been resolved but has not been released yet. 2. **Create an isolated and reproducible test case.** Try to include the minimum amount of code and stylesheet needed to reproduce the issue. ## Coding standards ### Whitespace * Spaces, not tabs. * Make liberal use of vertical whitespace to divide code into logical chunks. ================================================ FILE: Classy/Additions/CASStyleClassUtilities.h ================================================ // // CASStyleClassUtilities.h // Pods // // Created by Jonas Budelmann on 27/03/14. // // #import #import "CASStyleableItem.h" @interface CASStyleClassUtilities : NSObject + (NSString *)styleClassForItem:(id)item; + (void)setStyleClass:(NSString *)styleClass forItem:(id)item; + (NSMutableArray *)styleClassesForItem:(id)item; + (void)setStyleClasses:(NSMutableArray *)styleClasses forItem:(id)item; + (void)addStyleClass:(NSString *)styleClass forItem:(id)item; + (void)removeStyleClass:(NSString *)styleClass forItem:(id)item; + (BOOL)item:(id)item hasStyleClass:(NSString *)styleClass; @end ================================================ FILE: Classy/Additions/CASStyleClassUtilities.m ================================================ // // CASStyleClassUtilities.m // Pods // // Created by Jonas Budelmann on 27/03/14. // // #import "CASStyleClassUtilities.h" #import #import "CASStyleableItem.h" static void *CASStyleClassesKey = &CASStyleClassesKey; @implementation CASStyleClassUtilities + (NSString *)styleClassForItem:(id)item { NSMutableArray *styleClasses = [self styleClassesForItem:item]; return [styleClasses componentsJoinedByString:CASStyleClassSeparator]; } + (void)setStyleClass:(NSString *)styleClass forItem:(id)item { NSArray *classCandidates = [styleClass componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSMutableArray *styleClasses = [self styleClassesForItem:item]; [styleClasses removeAllObjects]; if (!classCandidates.count) { [self setStyleClasses:nil forItem:item]; return; } if (!styleClasses) { styleClasses = [NSMutableArray array]; [self setStyleClasses:styleClasses forItem:item]; } for (NSString *styleClass in classCandidates) { if ([styleClass isKindOfClass:NSString.class] && styleClass.length) { [styleClasses addObject:styleClass]; } } } + (NSMutableArray *)styleClassesForItem:(id)item { return objc_getAssociatedObject(item, CASStyleClassesKey); } + (void)setStyleClasses:(NSMutableArray *)styleClasses forItem:(id)item { objc_setAssociatedObject(item, CASStyleClassesKey, styleClasses, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + (void)addStyleClass:(NSString *)styleClass forItem:(id)item { if (![styleClass isKindOfClass:NSString.class] || !styleClass.length) return; NSMutableArray *styleClasses = [self styleClassesForItem:item]; if (!styleClasses) { styleClasses = [NSMutableArray array]; [self setStyleClasses:styleClasses forItem:item]; } [styleClasses addObject:styleClass]; } + (void)removeStyleClass:(NSString *)styleClass forItem:(id)item { if (![styleClass isKindOfClass:NSString.class] || !styleClass.length) return; NSMutableArray *styleClasses = [self styleClassesForItem:item]; [styleClasses removeObject:styleClass]; } + (BOOL)item:(id)item hasStyleClass:(NSString *)styleClass { return [[self styleClassesForItem:item] containsObject:styleClass]; } @end ================================================ FILE: Classy/Additions/CASStyleableItem.h ================================================ // // CASStyleableItem.h // // // Created by Jonas Budelmann on 31/10/13. // // #import /** * separator string used to seperate multiple classes in the cas_styleClass property */ extern NSString *const CASStyleClassSeparator; @protocol CASStyleableItem /** * NSString which should relate to a styleClass in your stylesheet * multiple style classes can be set by separating them with the string defined as CASStyleClassSeparator * this property will change cas_styleClasses and is a alternative accessor for the set of style classes (e.g. interface builder etc.) */ @property (nonatomic, copy) NSString *cas_styleClass; /** * Direct parent of receiver * ie in case of UIView will be self.superview */ @property (nonatomic, weak, readonly) id cas_parent; /** * In some cases it is appropriate to specify an alternative parent relationship * ie in case of UIView it's UIViewController is an alternative parent to it's superview */ @property (nonatomic, weak, readonly) id cas_alternativeParent; /** * adds a style class if it was not set previosly */ - (void)cas_addStyleClass:(NSString *)styleClass; /** * removes a style class if it was set previosly */ - (void)cas_removeStyleClass:(NSString *)styleClass; /** * Returns wether or not the receiver has a specific style class assigned */ - (BOOL)cas_hasStyleClass:(NSString *)styleClass; /** * Peforms styling now if receiver needs styling */ - (void)cas_updateStylingIfNeeded; /** * Override this to adjust your styling during a style update pass. * If overriding should call super */ - (void)cas_updateStyling; /** * Returns whether or not receiver has been marked as needing style update */ - (BOOL)cas_needsUpdateStyling; /** * Schedules the receiver to for a style update */ - (void)cas_setNeedsUpdateStyling; @end ================================================ FILE: Classy/Additions/CASStyleableItem.m ================================================ // // CASStyleableItem.h // // // Created by Alexander Ney on 11/03/14. // // #import "CASStyleableItem.h" NSString *const CASStyleClassSeparator = @" "; ================================================ FILE: Classy/Additions/NSObject+CASSwizzle.h ================================================ // // NSObject+CASSwizzle.h // Classy // // Created by Jonas Budelmann on 15/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import @interface NSObject (CASSwizzle) + (void)cas_swizzleInstanceSelector:(SEL)originalSelector withNewSelector:(SEL)newSelector; @end ================================================ FILE: Classy/Additions/NSObject+CASSwizzle.m ================================================ // // NSObject+CASSwizzle.m // Classy // // Created by Jonas Budelmann on 15/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "NSObject+CASSwizzle.h" #import @implementation NSObject (CASSwizzle) + (void)cas_swizzleInstanceSelector:(SEL)originalSelector withNewSelector:(SEL)newSelector { Method originalMethod = class_getInstanceMethod(self, originalSelector); Method newMethod = class_getInstanceMethod(self, newSelector); BOOL methodAdded = class_addMethod([self class], originalSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); if (methodAdded) { class_replaceMethod([self class], newSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, newMethod); } } @end ================================================ FILE: Classy/Additions/NSRegularExpression+CASAdditions.h ================================================ // // NSRegularExpression+CASAdditions.h // Classy // // Created by Jonas Budelmann on 17/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import extern NSRegularExpression *CASRegex(NSString *patternFormat, ...) NS_FORMAT_FUNCTION(1,2); @interface NSRegularExpression (CASAdditions) - (NSUInteger)cas_replaceMatchesInString:(NSMutableString *)string withTemplate:(NSString *)templ; @end ================================================ FILE: Classy/Additions/NSRegularExpression+CASAdditions.m ================================================ // // NSRegularExpression+CASAdditions.m // Classy // // Created by Jonas Budelmann on 17/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "NSRegularExpression+CASAdditions.h" #import "CASUtilities.h" extern NSRegularExpression *CASRegex(NSString *patternFormat, ...) { va_list args; va_start(args, patternFormat); NSString *pattern = [[NSString alloc] initWithFormat:patternFormat arguments:args]; va_end(args); NSError *error = nil; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error]; NSCAssert(error == nil, @"Could not create regex from pattern %@", pattern); if (error) { CASLog(@"error %@", error); } return regex; } @implementation NSRegularExpression (CASAdditions) - (NSUInteger)cas_replaceMatchesInString:(NSMutableString *)string withTemplate:(NSString *)templ { return [self replaceMatchesInString:string options:0 range:NSMakeRange(0, string.length) withTemplate:templ]; } @end ================================================ FILE: Classy/Additions/NSString+CASAdditions.h ================================================ // // NSString+CASAdditions.h // Classy // // Created by Jonas Budelmann on 14/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import @interface NSString (CASAdditions) - (NSString *)cas_stringByCapitalizingFirstLetter; - (NSString *)cas_stringByTrimmingWhitespace; - (NSString *)cas_stringByCamelCasing; - (NSString *)cas_stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet; @end ================================================ FILE: Classy/Additions/NSString+CASAdditions.m ================================================ // // NSString+CASAdditions.m // Classy // // Created by Jonas Budelmann on 14/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "NSString+CASAdditions.h" @implementation NSString (CASAdditions) - (NSString *)cas_stringByCapitalizingFirstLetter { if (!self.length) return self; return [self stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[self substringToIndex:1] capitalizedString]]; } - (NSString *)cas_stringByTrimmingWhitespace { return [self stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; } - (NSString *)cas_stringByCamelCasing { NSArray *components = [self componentsSeparatedByString:@"-"]; if (components.count <= 1) return self; NSMutableString *camelCasedString = [NSMutableString string]; for (NSUInteger i = 0; i < components.count; i++) { if (i == 0) { [camelCasedString appendString:components[i]]; } else { [camelCasedString appendString:[components[i] cas_stringByCapitalizingFirstLetter]]; } } return camelCasedString; } - (NSString *)cas_stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet { NSInteger i = 0; while ((i < self.length) && [characterSet characterIsMember:[self characterAtIndex:i]]) { i++; } return [self substringFromIndex:i]; } @end ================================================ FILE: Classy/Additions/UIBarItem+CASAdditions.h ================================================ // // UIBarItem+CASAdditions.h // // // Created by Jonas Budelmann on 5/11/13. // // #import #import "CASStyleableItem.h" @interface UIBarItem (CASAdditions) + (void)bootstrapClassy; @property (nonatomic, weak, readwrite) id cas_parent; @end ================================================ FILE: Classy/Additions/UIBarItem+CASAdditions.m ================================================ // // UIBarItem+CASAdditions.m // // // Created by Jonas Budelmann on 5/11/13. // // #import "UIBarItem+CASAdditions.h" #import #import "CASStyler.h" #import "NSString+CASAdditions.h" #import "CASStyleClassUtilities.h" #import "NSObject+CASSwizzle.h" #import "CASAssociatedObjectsWeakWrapper.h" @implementation UIBarItem (CASAdditions) CASSynthesize(weak, id, cas_parent, setCas_parent); + (void)bootstrapClassy { [self cas_swizzleInstanceSelector:@selector(init) withNewSelector:@selector(cas_init)]; } - (id)cas_init { [self cas_init]; [self cas_updateStyling]; return self; } #pragma mark - CASStyleableItem - (NSString *)cas_styleClass { return [CASStyleClassUtilities styleClassForItem:self]; } - (void)setCas_styleClass:(NSString *)styleClass { [CASStyleClassUtilities setStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; } - (void)cas_addStyleClass:(NSString *)styleClass { [CASStyleClassUtilities addStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; } - (void)cas_removeStyleClass:(NSString *)styleClass { [CASStyleClassUtilities removeStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; } - (BOOL)cas_hasStyleClass:(NSString *)styleClass { return [CASStyleClassUtilities item:self hasStyleClass:styleClass]; } - (id)cas_alternativeParent { return nil; } - (void)cas_updateStylingIfNeeded { if ([self cas_needsUpdateStyling] && self.cas_parent) { [self cas_updateStyling]; } } - (void)cas_updateStyling { [CASStyler.defaultStyler styleItem:self]; } - (BOOL)cas_needsUpdateStyling { return [self.cas_parent cas_needsUpdateStyling]; } - (void)cas_setNeedsUpdateStyling { [self.cas_parent cas_setNeedsUpdateStyling]; } @end ================================================ FILE: Classy/Additions/UIColor+CASAdditions.h ================================================ // // UIColor+CASAdditions.h // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // // Slightly modified from https://github.com/soffes/sstoolkit/blob/master/SSToolkit/UIColor%2BSSToolkitAdditions.h // Main change namespaced to avoid collisions with other UIColor categories #import @interface UIColor (CASAdditions) /** * Creates and returns an UIColor object containing a given value. * * @param hex The value for the new color. The `#` sign is not required. * * @return An UIColor object containing a value. * You can specify hex values in the following formats: `rgb`, `rrggbb`, or `rrggbbaa`. * The default alpha value is `1.0`. */ + (UIColor *)cas_colorWithHex:(NSString *)hex; /** * Returns the receiver's value as a hex string. * * @return The receiver's value as a hex string. * The value will be `nil` if the color is in a color space other than Grayscale or RGB. * The `#` sign is omitted. Alpha will be omitted. */ - (NSString *)cas_hexValue; /** * Returns the receiver's value as a hex string. * * @param includeAlpha `YES` if alpha should be included. `NO` if it should not. * * @return The receiver's value as a hex string. * The value will be `nil` if the color is in a color space other than Grayscale or RGB. * The `#` sign is omitted. Alpha is included if `includeAlpha` is `YES`. */ - (NSString *)cas_hexValueWithAlpha:(BOOL)includeAlpha; @end ================================================ FILE: Classy/Additions/UIColor+CASAdditions.m ================================================ // // UIColor+CASAdditions.m // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "UIColor+CASAdditions.h" @interface NSString (CASPrivateAdditions) - (NSUInteger)cas_hexValue; @end @implementation NSString (CASPrivateAdditions) - (NSUInteger)cas_hexValue { NSUInteger result = 0; sscanf([self UTF8String], "%lx", &result); return result; } @end @implementation UIColor (CASAdditions) + (UIColor *)cas_colorWithHex:(NSString *)hex { // Remove `#` and `0x` if ([hex hasPrefix:@"#"]) { hex = [hex substringFromIndex:1]; } else if ([hex hasPrefix:@"0x"]) { hex = [hex substringFromIndex:2]; } // Invalid if not 3, 6, or 8 characters NSUInteger length = [hex length]; if (length != 3 && length != 6 && length != 8) { return nil; } // Make the string 8 characters long for easier parsing if (length == 3) { NSString *r = [hex substringWithRange:NSMakeRange(0, 1)]; NSString *g = [hex substringWithRange:NSMakeRange(1, 1)]; NSString *b = [hex substringWithRange:NSMakeRange(2, 1)]; hex = [NSString stringWithFormat:@"%@%@%@%@%@%@ff", r, r, g, g, b, b]; } else if (length == 6) { hex = [hex stringByAppendingString:@"ff"]; } CGFloat red = [[hex substringWithRange:NSMakeRange(0, 2)] cas_hexValue] / 255.0f; CGFloat green = [[hex substringWithRange:NSMakeRange(2, 2)] cas_hexValue] / 255.0f; CGFloat blue = [[hex substringWithRange:NSMakeRange(4, 2)] cas_hexValue] / 255.0f; CGFloat alpha = [[hex substringWithRange:NSMakeRange(6, 2)] cas_hexValue] / 255.0f; return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; } - (NSString *)cas_hexValue { return [self cas_hexValueWithAlpha:NO]; } - (NSString *)cas_hexValueWithAlpha:(BOOL)includeAlpha { CGColorRef color = self.CGColor; size_t count = CGColorGetNumberOfComponents(color); const CGFloat *components = CGColorGetComponents(color); static NSString *stringFormat = @"%02x%02x%02x"; NSString *hex = nil; // Grayscale if (count == 2) { NSUInteger white = (NSUInteger)(components[0] * 255.0f); hex = [NSString stringWithFormat:stringFormat, white, white, white]; } // RGB else if (count == 4) { hex = [NSString stringWithFormat:stringFormat, (NSUInteger)(components[0] * 255.0f), (NSUInteger)(components[1] * 255.0f), (NSUInteger)(components[2] * 255.0f)]; } // Add alpha if (hex && includeAlpha) { hex = [hex stringByAppendingFormat:@"%02lx", (unsigned long)(CGColorGetAlpha(self.CGColor) * 255.0f)]; } // Unsupported color space return hex; } @end ================================================ FILE: Classy/Additions/UINavigationBar+CASAdditions.h ================================================ // // UINavigationBar+CASAdditions.h // // // Created by Jonas Budelmann on 31/10/13. // // #import @interface UINavigationBar (CASAdditions) @end ================================================ FILE: Classy/Additions/UINavigationBar+CASAdditions.m ================================================ // // UINavigationBar+CASAdditions.m // // // Created by Jonas Budelmann on 31/10/13. // // #import "UINavigationBar+CASAdditions.h" #import "CASStyler.h" #import "UIView+CASAdditions.h" #import "UIBarItem+CASAdditions.h" @implementation UINavigationBar (CASAdditions) - (void)cas_updateStyling { [super cas_updateStyling]; for (UINavigationItem *navigationItem in self.items) { for (UIBarButtonItem *barButtonItem in navigationItem.leftBarButtonItems) { barButtonItem.cas_parent = self; [barButtonItem cas_updateStyling]; } for (UIBarButtonItem *barButtonItem in navigationItem.rightBarButtonItems) { barButtonItem.cas_parent = self; [barButtonItem cas_updateStyling]; } } } @end ================================================ FILE: Classy/Additions/UINavigationItem+CASAdditions.h ================================================ // // UINavigationItem+CASAdditions.h // // // Created by Joseph Ridenour on 1/21/15. // // #import @interface UINavigationItem (CASAdditions) + (void)bootstrapClassy; @end ================================================ FILE: Classy/Additions/UINavigationItem+CASAdditions.m ================================================ // // UINavigationItem+CASAdditions.m // // // Created by Joseph Ridenour on 1/21/15. // // #import "UINavigationItem+CASAdditions.h" #import "CASStyler.h" #import "UIBarItem+CASAdditions.h" #import "NSObject+CASSwizzle.h" @implementation UINavigationItem (CASAdditions) + (void)bootstrapClassy { [self cas_swizzleInstanceSelector:@selector(setRightBarButtonItem:animated:) withNewSelector:@selector(cas_setRightBarButtonItem:animated:)]; [self cas_swizzleInstanceSelector:@selector(setLeftBarButtonItem:animated:) withNewSelector:@selector(cas_setLeftBarButtonItem:animated:)]; [self cas_swizzleInstanceSelector:@selector(setLeftBarButtonItems:animated:) withNewSelector:@selector(cas_setRightBarButtonItems:animated:)]; [self cas_swizzleInstanceSelector:@selector(setLeftBarButtonItems:animated:) withNewSelector:@selector(cas_setLeftBarButtonItems:animated:)]; } - (void)cas_setRightBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated { [CASStyler.defaultStyler styleItem:item]; [self cas_setRightBarButtonItem:item animated:animated]; } - (void)cas_setLeftBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated { [CASStyler.defaultStyler styleItem:item]; [self cas_setLeftBarButtonItem:item animated:animated]; } - (void)cas_setRightBarButtonItems:(NSArray *)items animated:(BOOL)animated { [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [CASStyler.defaultStyler styleItem:obj]; }]; [self cas_setRightBarButtonItems:items animated:animated]; } - (void)cas_setLeftBarButtonItems:(NSArray *)items animated:(BOOL)animated { [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [CASStyler.defaultStyler styleItem:obj]; }]; [self cas_setLeftBarButtonItems:items animated:animated]; } @end ================================================ FILE: Classy/Additions/UISlider+CASAdditions.h ================================================ // // UISlider+CASAdditions.h // // // Created by Cail Borrell on 19/02/14. // // #import @interface UISlider (CASAdditions) @end ================================================ FILE: Classy/Additions/UISlider+CASAdditions.m ================================================ // // UISlider+CASAdditions.m // // // Created by Cail Borrell on 19/02/14. // // #import "UISlider+CASAdditions.h" @implementation UISlider (CASAdditions) - (void)didMoveToWindow { [super didMoveToWindow]; } @end ================================================ FILE: Classy/Additions/UITabBar+CASAdditions.h ================================================ // // UITabBar+CASAdditions.h // // // Created by Jonas Budelmann on 1/11/13. // // #import @interface UITabBar (CASAdditions) @end ================================================ FILE: Classy/Additions/UITabBar+CASAdditions.m ================================================ // // UITabBar+CASAdditions.m // // // Created by Jonas Budelmann on 1/11/13. // // #import "UITabBar+CASAdditions.h" #import "CASStyler.h" #import "UIView+CASAdditions.h" #import "UIBarItem+CASAdditions.h" @implementation UITabBar (CASAdditions) - (void)cas_updateStyling { [super cas_updateStyling]; for (UITabBarItem *item in self.items) { item.cas_parent = self; [item cas_updateStyling]; } } @end ================================================ FILE: Classy/Additions/UITextField+CASAdditions.h ================================================ // // UITextField+CASAdditions.h // Classy // // Created by Jonas Budelmann on 14/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import @interface UITextField (CASAdditions) + (void)bootstrapClassy; @property (nonatomic, assign) UIEdgeInsets cas_textEdgeInsets; @end ================================================ FILE: Classy/Additions/UITextField+CASAdditions.m ================================================ // // UITextField+CASAdditions.m // Classy // // Created by Jonas Budelmann on 14/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "UITextField+CASAdditions.h" #import #import "NSObject+CASSwizzle.h" @implementation UITextField (CASAdditions) + (void)bootstrapClassy { [self cas_swizzleInstanceSelector:@selector(textRectForBounds:) withNewSelector:@selector(cas_textRectForBounds:)]; [self cas_swizzleInstanceSelector:@selector(editingRectForBounds:) withNewSelector:@selector(cas_editingRectForBounds:)]; } #pragma mark - text insets - (void)setCas_textEdgeInsets:(UIEdgeInsets)cas_textEdgeInsets { NSValue *value = [NSValue valueWithUIEdgeInsets:cas_textEdgeInsets]; objc_setAssociatedObject(self, @selector(cas_textEdgeInsets), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (UIEdgeInsets)cas_textEdgeInsets { return [objc_getAssociatedObject(self, @selector(cas_textEdgeInsets)) UIEdgeInsetsValue]; } - (CGRect)cas_textRectForBounds:(CGRect)bounds { if (UIEdgeInsetsEqualToEdgeInsets(self.cas_textEdgeInsets, UIEdgeInsetsZero)) { return [self cas_textRectForBounds:bounds]; } return UIEdgeInsetsInsetRect(bounds, self.cas_textEdgeInsets); } - (CGRect)cas_editingRectForBounds:(CGRect)bounds { return [self textRectForBounds:bounds]; } @end ================================================ FILE: Classy/Additions/UIToolbar+CASAdditions.h ================================================ // // UIToolbar+CASAdditions.h // // // Created by Jonas Budelmann on 1/11/13. // // #import @interface UIToolbar (CASAdditions) @end ================================================ FILE: Classy/Additions/UIToolbar+CASAdditions.m ================================================ // // UIToolbar+CASAdditions.m // // // Created by Jonas Budelmann on 1/11/13. // // #import "UIToolbar+CASAdditions.h" #import "CASStyler.h" #import "UIView+CASAdditions.h" #import "UIBarItem+CASAdditions.h" @implementation UIToolbar (CASAdditions) - (void)cas_updateStyling { [super cas_updateStyling]; for (UIBarButtonItem *item in self.items) { item.cas_parent = self; [item cas_updateStyling]; } } @end ================================================ FILE: Classy/Additions/UIView+CASAdditions.h ================================================ // // UIView+CASAdditions.h // Classy // // Created by Jonas Budelmann on 30/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASStyleableItem.h" @interface UIView (CASAdditions) + (void)bootstrapClassy; @property (nonatomic, weak, readwrite) id cas_alternativeParent; @property (nonatomic, copy) IBInspectable NSString *cas_styleClass; - (void)cas_setNeedsUpdateStylingForSubviews; @end ================================================ FILE: Classy/Additions/UIView+CASAdditions.m ================================================ // // UIView+CASAdditions.m // Classy // // Created by Jonas Budelmann on 30/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "UIView+CASAdditions.h" #import #import #import "NSObject+CASSwizzle.h" #import "CASStyler.h" #import "NSString+CASAdditions.h" #import "CASStyleClassUtilities.h" #import "CASAssociatedObjectsWeakWrapper.h" static void *CASStyleHasBeenUpdatedKey = &CASStyleHasBeenUpdatedKey; @implementation UIView (CASAdditions) CASSynthesize(weak, id, cas_alternativeParent, setCas_alternativeParent); + (void)bootstrapClassy { [self cas_swizzleInstanceSelector:@selector(didMoveToWindow) withNewSelector:@selector(cas_didMoveToWindow)]; } - (void)cas_didMoveToWindow { [self cas_updateStyling]; [self cas_didMoveToWindow]; } - (void)cas_setNeedsUpdateStylingForSubviews { [self cas_setNeedsUpdateStyling]; for (UIView *view in self.subviews) { [view cas_setNeedsUpdateStylingForSubviews]; } } #pragma mark - CASStyleableItem - (NSString *)cas_styleClass { return [CASStyleClassUtilities styleClassForItem:self]; } - (void)setCas_styleClass:(NSString *)styleClass { [CASStyleClassUtilities setStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; } - (void)cas_addStyleClass:(NSString *)styleClass { [CASStyleClassUtilities addStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; } - (void)cas_removeStyleClass:(NSString *)styleClass { [CASStyleClassUtilities removeStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; } - (BOOL)cas_hasStyleClass:(NSString *)styleClass { return [CASStyleClassUtilities item:self hasStyleClass:styleClass]; } - (id)cas_parent { return self.superview; } - (void)cas_updateStylingIfNeeded { if ([self cas_needsUpdateStyling]) { [self cas_updateStyling]; } } - (void)cas_updateStyling { if (self.window) { [CASStyler.defaultStyler styleItem:self]; } objc_setAssociatedObject(self, CASStyleHasBeenUpdatedKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC); [CASStyler.defaultStyler unscheduleUpdateForItem:self]; } - (BOOL)cas_needsUpdateStyling { return ![objc_getAssociatedObject(self, CASStyleHasBeenUpdatedKey) boolValue]; } - (void)cas_setNeedsUpdateStyling { objc_setAssociatedObject(self, CASStyleHasBeenUpdatedKey, @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC); [CASStyler.defaultStyler scheduleUpdateForItem:self]; } @end ================================================ FILE: Classy/Additions/UIViewController+CASAdditions.h ================================================ // // UIViewController+CASAdditions.h // // // Created by Jonas Budelmann on 17/11/13. // // #import #import "CASStyleableItem.h" @interface UIViewController (CASAdditions) + (void)bootstrapClassy; @end ================================================ FILE: Classy/Additions/UIViewController+CASAdditions.m ================================================ // // UIViewController+CASAdditions.m // // // Created by Jonas Budelmann on 17/11/13. // // #import "UIViewController+CASAdditions.h" #import "NSObject+CASSwizzle.h" #import "UIView+CASAdditions.h" #import #import "CASStyler.h" #import "NSString+CASAdditions.h" #import "CASStyleClassUtilities.h" static void *CASStyleHasBeenUpdatedKey = &CASStyleHasBeenUpdatedKey; @implementation UIViewController (CASAdditions) + (void)bootstrapClassy { [self cas_swizzleInstanceSelector:@selector(setView:) withNewSelector:@selector(cas_setView:)]; } - (void)cas_setView:(UIView *)view { view.cas_alternativeParent = self; [self cas_setView:view]; [self cas_setNeedsUpdateStyling]; } #pragma mark - CASStyleableItem - (NSString *)cas_styleClass { return [CASStyleClassUtilities styleClassForItem:self]; } - (void)setCas_styleClass:(NSString *)styleClass { [CASStyleClassUtilities setStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; [self.view cas_setNeedsUpdateStylingForSubviews]; } - (void)cas_addStyleClass:(NSString *)styleClass { [CASStyleClassUtilities addStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; [self.view cas_setNeedsUpdateStylingForSubviews]; } - (void)cas_removeStyleClass:(NSString *)styleClass { [CASStyleClassUtilities removeStyleClass:styleClass forItem:self]; [self cas_setNeedsUpdateStyling]; [self.view cas_setNeedsUpdateStylingForSubviews]; } - (BOOL)cas_hasStyleClass:(NSString *)styleClass { return [CASStyleClassUtilities item:self hasStyleClass:styleClass]; } - (id)cas_parent { return self.view.superview; } - (id)cas_alternativeParent { return self.parentViewController; } - (void)cas_updateStylingIfNeeded { if ([self cas_needsUpdateStyling]) { [self cas_updateStyling]; } } - (void)cas_updateStyling { [CASStyler.defaultStyler styleItem:self]; objc_setAssociatedObject(self, CASStyleHasBeenUpdatedKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC); [CASStyler.defaultStyler unscheduleUpdateForItem:self]; } - (BOOL)cas_needsUpdateStyling { return ![objc_getAssociatedObject(self, CASStyleHasBeenUpdatedKey) boolValue]; } - (void)cas_setNeedsUpdateStyling { objc_setAssociatedObject(self, CASStyleHasBeenUpdatedKey, @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC); [CASStyler.defaultStyler scheduleUpdateForItem:self]; } @end ================================================ FILE: Classy/Classy.h ================================================ // // Classy.h // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASStyler.h" #import "CASStyleableItem.h" #import "CASUtilities.h" #import "CASObjectClassDescriptor.h" #import "CASArgumentDescriptor.h" #import "CASPropertyDescriptor.h" #import "UIColor+CASAdditions.h" #import "UITextField+CASAdditions.h" #import "UIView+CASAdditions.h" #import "UIBarItem+CASAdditions.h" #import "UINavigationBar+CASAdditions.h" #import "UITabBar+CASAdditions.h" #import "UIToolbar+CASAdditions.h" #import "UIViewController+CASAdditions.h" #import "UINavigationItem+CASAdditions.h" ================================================ FILE: Classy/Parser/CASDeviceOSVersionItem.h ================================================ // // CASDeviceOSVersionItem.h // Classy // // Created by Jonas Budelmann on 25/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASDeviceSelectorItem.h" @interface CASDeviceOSVersionItem : NSObject @property (nonatomic, copy) NSString *version; @property (nonatomic, assign) CASRelation relation; @end ================================================ FILE: Classy/Parser/CASDeviceOSVersionItem.m ================================================ // // CASDeviceOSVersionItem.m // Classy // // Created by Jonas Budelmann on 25/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASDeviceOSVersionItem.h" #import "CASUtilities.h" #import "CASDeviceSelector.h" @implementation CASDeviceOSVersionItem - (BOOL)isValid { switch (self.relation) { case CASRelationLessThan: return CASDeviceSystemVersionIsLessThan(self.version); case CASRelationLessThanOrEqual: return CASDeviceSystemVersionIsLessThanOrEqualTo(self.version); case CASRelationEqual: return CASDeviceSystemVersionIsEqualTo(self.version); case CASRelationGreaterThanOrEqual: return CASDeviceSystemVersionIsGreaterThanOrEqualTo(self.version); case CASRelationGreaterThan: return CASDeviceSystemVersionIsGreaterThan(self.version); default: NSAssert(NO, @"Invalid ralation"); return NO; } } - (NSString *)stringValue { return [NSString stringWithFormat:@"(version:%@%@)", [CASDeviceSelector stringFromRelation:self.relation], self.version]; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { self.relation = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(relation))]; self.version = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(version))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeInteger:self.relation forKey:NSStringFromSelector(@selector(relation))]; [aCoder encodeObject:self.version forKey:NSStringFromSelector(@selector(version))]; } @end ================================================ FILE: Classy/Parser/CASDeviceScreenSizeItem.h ================================================ // // Created by Ole Gammelgaard Poulsen on 05/04/14. // Copyright (c) 2014 SHAPE A/S. All rights reserved. // #import #import "CASDeviceSelectorItem.h" #import "CASDeviceSelector.h" @interface CASDeviceScreenSizeItem : NSObject @property (nonatomic, assign) float value; @property (nonatomic, assign) CASRelation relation; @property(nonatomic, assign) CASDeviceSelectorScreenDimension dimension; @end ================================================ FILE: Classy/Parser/CASDeviceScreenSizeItem.m ================================================ // // Created by Ole Gammelgaard Poulsen on 05/04/14. // Copyright (c) 2014 SHAPE A/S. All rights reserved. // #import "CASDeviceScreenSizeItem.h" @implementation CASDeviceScreenSizeItem - (BOOL)isValid { CGSize screenSize = [[UIScreen mainScreen] bounds].size; BOOL isPad = UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad; CGFloat minDimension = MIN(screenSize.width, screenSize.height); CGFloat maxDimension = MAX(screenSize.width, screenSize.height); CGFloat width = isPad ? maxDimension : minDimension; CGFloat height = isPad ? minDimension : maxDimension; CGFloat sizeInDimension = self.dimension == CASDeviceSelectorScreenDimensionWidth ? width : height; switch (self.relation) { case CASRelationLessThan: return sizeInDimension < self.value; case CASRelationLessThanOrEqual: return sizeInDimension <= self.value; case CASRelationEqual: return fabs(self.value - sizeInDimension) < 0.001; case CASRelationGreaterThanOrEqual: return sizeInDimension >= self.value; case CASRelationGreaterThan: return sizeInDimension > self.value; default: NSAssert(NO, @"There should not be undefined relations"); return NO; } } - (NSString *)stringValue { NSString *dimensionString = self.dimension == CASDeviceSelectorScreenDimensionWidth ? @"width" : @"height"; return [NSString stringWithFormat:@"(screen-%@:%@%.0f)", dimensionString, [CASDeviceSelector stringFromRelation:self.relation], self.value]; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { self.relation = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(relation))]; self.value = [aDecoder decodeFloatForKey:NSStringFromSelector(@selector(value))]; self.dimension = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(dimension))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeInteger:self.relation forKey:NSStringFromSelector(@selector(relation))]; [aCoder encodeFloat:self.value forKey:NSStringFromSelector(@selector(value))]; [aCoder encodeInteger:self.dimension forKey:NSStringFromSelector(@selector(dimension))]; } @end ================================================ FILE: Classy/Parser/CASDeviceSelector.h ================================================ // // CASStyleMediaSelector.h // Classy // // Created by Jonas Budelmann on 24/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASDeviceTypeItem.h" #import "CASDeviceOSVersionItem.h" typedef NS_ENUM(NSUInteger, CASDeviceSelectorScreenDimension) { CASDeviceSelectorScreenDimensionWidth = 0, CASDeviceSelectorScreenDimensionHeight, }; @interface CASDeviceSelector : NSObject @property (nonatomic, strong, readonly) NSArray *items; + (NSString *)stringFromRelation:(CASRelation)relation; - (void)addItems:(NSArray *)items; - (void)addDeviceType:(CASDeviceType)deviceType; - (BOOL)addOSVersion:(NSString *)versionExpression; - (BOOL)addScreenSize:(NSString *)sizeExpression dimension:(CASDeviceSelectorScreenDimension)dimension; - (BOOL)isValid; - (NSString *)stringValue; @end ================================================ FILE: Classy/Parser/CASDeviceSelector.m ================================================ // // CASStyleMediaSelector.m // Classy // // Created by Jonas Budelmann on 24/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASDeviceSelector.h" #import "NSString+CASAdditions.h" #import "CASDeviceScreenSizeItem.h" @implementation CASDeviceSelector { NSMutableArray *_items; } - (id)init { self = [super init]; if (!self) return nil; _items = NSMutableArray.new; return self; } - (NSArray *)items { return _items; } - (void)addItems:(NSArray *)items { [_items addObjectsFromArray:items]; } - (void)addDeviceType:(CASDeviceType)deviceType { CASDeviceTypeItem *item = CASDeviceTypeItem.new; item.deviceType = deviceType; [_items addObject:item]; } - (BOOL)addOSVersion:(NSString *)versionExpression { NSString *valueString = [self valueFromExpression:versionExpression]; NSString *relationString = [versionExpression substringToIndex:versionExpression.length - valueString.length]; CASRelation relation = [self relationFromExpression:relationString]; if (relation == CASRelationUndefined) return NO; CASDeviceOSVersionItem *item = CASDeviceOSVersionItem.new; item.version = valueString; item.relation = relation; [_items addObject:item]; return YES; } - (BOOL)addScreenSize:(NSString *)sizeExpression dimension:(CASDeviceSelectorScreenDimension)dimension { NSString *valueString = [self valueFromExpression:sizeExpression]; NSString *relationString = [sizeExpression substringToIndex:sizeExpression.length - valueString.length]; CASRelation relation = [self relationFromExpression:relationString]; if (relation == CASRelationUndefined) return NO; CASDeviceScreenSizeItem *item = CASDeviceScreenSizeItem.new; item.value = valueString.floatValue; item.relation = relation; item.dimension = dimension; [_items addObject:item]; return YES; } - (NSString *)valueFromExpression:(NSString *)relation { NSCharacterSet *equalityCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@">=<"]; NSString *valueString = [relation cas_stringByTrimmingLeadingCharactersInSet:equalityCharacterSet]; return valueString; } - (CASRelation)relationFromExpression:(NSString *)expression { CASRelation relation = CASRelationEqual; if ([expression isEqualToString:@"<"]) { relation = CASRelationLessThan; } else if ([expression isEqualToString:@"<="]) { relation = CASRelationLessThanOrEqual; } else if ([expression isEqualToString:@"=="]) { relation = CASRelationEqual; } else if ([expression isEqualToString:@">="]) { relation = CASRelationGreaterThanOrEqual; } else if ([expression isEqualToString:@">"]) { relation = CASRelationGreaterThan; } else if (relation == CASRelationEqual && expression.length) { relation = CASRelationUndefined; } return relation; } - (BOOL)isValid { for (id item in self.items) { if (!item.isValid) { return NO; } } return YES; } - (NSString *)stringValue { NSMutableString *string = NSMutableString.new; [self.items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) { [string appendString:item.stringValue]; if (idx != self.items.count - 1) { [string appendString:@" and "]; } }]; return string; } + (NSString *)stringFromRelation:(CASRelation)relation { switch (relation) { case CASRelationLessThan: return @"<"; case CASRelationLessThanOrEqual: return @"<="; case CASRelationEqual: return @""; case CASRelationGreaterThanOrEqual: return @">="; case CASRelationGreaterThan: return @">"; case CASRelationUndefined: NSAssert(NO, @"Relation should not be undefined"); return nil; default: NSAssert(NO, @"Relation should not be an undefined enum value"); return nil; } } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { _items = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(items))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.items forKey:NSStringFromSelector(@selector(items))]; } @end ================================================ FILE: Classy/Parser/CASDeviceSelectorItem.h ================================================ // // CASDeviceSelectorItem.h // Classy // // Created by Jonas Budelmann on 25/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import typedef NS_ENUM(NSInteger, CASRelation) { CASRelationLessThan = -2, CASRelationLessThanOrEqual = -1, CASRelationEqual = 0, CASRelationGreaterThanOrEqual = 1, CASRelationGreaterThan = 2, CASRelationUndefined = NSNotFound, }; @protocol CASDeviceSelectorItem - (BOOL)isValid; - (NSString *)stringValue; @end ================================================ FILE: Classy/Parser/CASDeviceTypeItem.h ================================================ // // CASDeviceTypeItem.h // Classy // // Created by Jonas Budelmann on 25/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASDeviceSelectorItem.h" typedef NS_ENUM(NSInteger, CASDeviceType) { CASDeviceTypePhone = 0, CASDeviceTypePad = 1, }; @interface CASDeviceTypeItem : NSObject @property (nonatomic, assign) CASDeviceType deviceType; @end ================================================ FILE: Classy/Parser/CASDeviceTypeItem.m ================================================ // // CASDeviceTypeItem.m // Classy // // Created by Jonas Budelmann on 25/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASDeviceTypeItem.h" @implementation CASDeviceTypeItem - (BOOL)isValid { if (self.deviceType == CASDeviceTypePhone) { return UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone; } else { return UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad; } } - (NSString *)stringValue { if (self.deviceType == CASDeviceTypePhone) { return @"phone"; } return @"pad"; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { self.deviceType = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(deviceType))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeInteger:self.deviceType forKey:NSStringFromSelector(@selector(deviceType))]; } @end ================================================ FILE: Classy/Parser/CASExpressionSolver.h ================================================ // // CASExpressionSolver.h // Classy // // Created by Jonas Budelmann on 18/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import @interface CASExpressionSolver : NSObject /** * Takes an array of tokens and reduces any expressions contained within to a numerical value. * Skips tokens which cannot be evaluated in a expression * * @return a NSArray of tokens */ - (NSArray *)tokensByReducingTokens:(NSArray *)tokens; @end ================================================ FILE: Classy/Parser/CASExpressionSolver.m ================================================ // // CASExpressionSolver.m // Classy // // Created by Jonas Budelmann on 18/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASExpressionSolver.h" #import "CASToken.h" @interface CASExpressionSolver () @property (nonatomic, strong) NSMutableArray *tokens; @property (nonatomic, strong) NSMutableDictionary *tupleByIndex; @end @implementation CASExpressionSolver - (NSArray *)tokensByReducingTokens:(NSArray *)tokens { self.tokens = tokens.mutableCopy; self.tupleByIndex = NSMutableDictionary.new; [self extractTuples]; NSMutableArray *tokenStack = NSMutableArray.new; NSMutableArray *expressionStack = NSMutableArray.new; NSMutableDictionary *expressionMap = NSMutableDictionary.new; NSMutableDictionary *tupleMap = NSMutableDictionary.new; for (CASToken *token in self.tokens) { BOOL isPlaceholder = token.value == (id)NSNull.null; BOOL isFunctionKeyword = !isPlaceholder && [self.class.acceptableExpressionKeywords containsObject:token.value]; if (isPlaceholder || isFunctionKeyword || token.isPossiblyExpression ) { if (token.isWhitespace && !expressionStack.count) { [tokenStack addObject:token]; continue; } BOOL split = [token valueIsEqualTo:@","] || [self doesToken:token requireSplitForTokens:self.tokens]; if (split) { [tokenStack addObject:NSNull.null]; expressionMap[@(tokenStack.count-1)] = expressionStack; expressionStack = NSMutableArray.new; } if (isPlaceholder) { tupleMap[@(tokenStack.count)] = self.tupleByIndex[@([self.tokens indexOfObject:token])]; } [expressionStack addObject:token]; } else { [tokenStack addObject:token]; } } if (expressionStack.count) { [tokenStack addObject:NSNull.null]; expressionMap[@(tokenStack.count-1)] = expressionStack; } [expressionMap enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSMutableArray *expressionStack, BOOL *stop) { NSArray *tuple = tupleMap[key]; NSInteger index = [key integerValue]; if (tuple.count) { [tokenStack replaceObjectAtIndex:index withObject:[CASToken tokenOfType:CASTokenTypeLeftRoundBrace value:@"("]]; for (CASToken *tupleToken in tuple) { CASToken *token = [self evaluateTokens:expressionStack withPlaceholderToken:tupleToken]; [tokenStack insertObject:token atIndex:++index]; if ([tuple indexOfObject:tupleToken] != tuple.count - 1) { [tokenStack insertObject:[CASToken tokenOfType:CASTokenTypeOperator value:@","] atIndex:++index]; } } [tokenStack insertObject:[CASToken tokenOfType:CASTokenTypeRightRoundBrace value:@")"] atIndex:++index]; } else { CASToken *token = [self evaluateTokens:expressionStack withPlaceholderToken:nil]; [tokenStack replaceObjectAtIndex:index withObject:token]; } }]; return tokenStack; } #pragma mark - private - (void)extractTuples { NSArray *tokens = [self.tokens copy]; NSInteger braceCount = 0, previousBraceCount = 0; NSRange tupleRange = NSMakeRange(0, 0); NSMutableArray *tuple; // TODO this is bit convuluted and fragile. // look into extending something like DDMathParser to support tuples for (CASToken *token in tokens) { if (token.type == CASTokenTypeLeftRoundBrace) { braceCount++; } else if (token.type == CASTokenTypeRightRoundBrace) { braceCount--; } if (previousBraceCount == 0 && braceCount == 1) { // opening tuple brace tuple = [NSMutableArray arrayWithObject:NSMutableArray.new]; tupleRange.location = [self.tokens indexOfObject:token]; } else if (previousBraceCount > 0 && braceCount == 0) { // closing tuple brace if (tuple.count > 1) { for (NSMutableArray *expressionStack in tuple.copy) { CASToken *token = [self evaluateTokens:expressionStack withPlaceholderToken:nil]; [tuple replaceObjectAtIndex:[tuple indexOfObject:expressionStack] withObject:token]; } NSInteger endIndex = [self.tokens indexOfObject:token] + 1; tupleRange.length = endIndex - tupleRange.location; [self.tokens removeObjectsInRange:tupleRange]; CASToken *placeholderToken = [CASToken tokenOfType:CASTokenTypeUnit value:NSNull.null]; [self.tokens insertObject:placeholderToken atIndex:tupleRange.location]; self.tupleByIndex[@(tupleRange.location)] = tuple; tuple = nil; } } else if (braceCount >= 1) { // within tuple BOOL split = [token valueIsEqualTo:@","] || [self doesToken:token requireSplitForTokens:tokens]; if (split) { [tuple addObject:NSMutableArray.new]; } if (![token valueIsEqualTo:@","] && !token.isWhitespace) { [tuple.lastObject addObject:token]; } } previousBraceCount = braceCount; } } - (BOOL)doesToken:(CASToken *)token requireSplitForTokens:(NSArray *)tokens { CASToken *prevNonWhitespaceToken; for (NSInteger i = [tokens indexOfObject:token] - 1; i >= 0; i--) { if (![tokens[i] isWhitespace]) { prevNonWhitespaceToken = tokens[i]; break; } } return prevNonWhitespaceToken != nil && ( (prevNonWhitespaceToken.type == CASTokenTypeUnit && token.type == CASTokenTypeLeftRoundBrace) || (prevNonWhitespaceToken.type == CASTokenTypeRightRoundBrace && token.type == CASTokenTypeUnit) || (prevNonWhitespaceToken.type == CASTokenTypeUnit && token.type == CASTokenTypeUnit)); } + (NSSet *)acceptableExpressionKeywords { //not a complete list but added ones that seemed useful static NSSet * _acceptableExpressionKeywords = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _acceptableExpressionKeywords = [[NSSet alloc] initWithArray:@[ @"floor", @"abs", @"random", @"ceiling", @"abs", @"uppercase", @"lowercase", @"log" ]]; }); return _acceptableExpressionKeywords; } - (CASToken *)evaluateTokens:(NSArray *)tokens withPlaceholderToken:(CASToken *)placeholderToken { if (tokens.count == 1) { return tokens.lastObject; } NSMutableArray *values = NSMutableArray.new; for (CASToken *token in tokens) { if (token.value == (id)NSNull.null) { [values addObject:placeholderToken.stringValue]; } else { [values addObject:token.stringValue]; } } NSExpression *expression = [NSExpression expressionWithFormat:[values componentsJoinedByString:@""]]; id value = [expression expressionValueWithObject:nil context:nil]; return [CASToken tokenOfType:CASTokenTypeUnit value:value]; } @end ================================================ FILE: Classy/Parser/CASInvocation.h ================================================ // // CASInvocation.h // // // Created by Jonas Budelmann on 5/11/13. // // #import @interface CASInvocation : NSObject /** * Wraps an NSInvocation with an optional keypath. * If keypath is non-nil will alter the target when invokeWithTarget: is called * * @param invocation NSInvocation to wrap * @param keyPath to store for later use * * @return CASInvocation */ - (id)initWithInvocation:(NSInvocation *)invocation forKeyPath:(NSString *)keyPath; /** * Invoke the wrapped NSInvocation. * If keypath is non-nil target will be evaluated keypath * * @param target root object */ - (void)invokeWithTarget:(id)target; @end ================================================ FILE: Classy/Parser/CASInvocation.m ================================================ // // CASInvocation.m // // // Created by Jonas Budelmann on 5/11/13. // // #import "CASInvocation.h" @interface CASInvocation () @property (nonatomic, copy) NSString *keyPath; @property (nonatomic, strong) NSInvocation *invocation; @end @implementation CASInvocation - (id)initWithInvocation:(NSInvocation *)invocation forKeyPath:(NSString *)keyPath { self = [super init]; if (!self) return nil; self.invocation = invocation; self.keyPath = keyPath; return self; } - (void)invokeWithTarget:(id)target { id resolvedTarget = self.keyPath ? [target valueForKeyPath:self.keyPath] : target; [self.invocation invokeWithTarget:resolvedTarget]; } @end ================================================ FILE: Classy/Parser/CASLexer.h ================================================ // // CASLexer.h // Classy // // Created by Jonas Budelmann on 15/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASToken.h" extern NSString * const CASParseErrorDomain; extern NSInteger const CASParseErrorInvalidToken; extern NSInteger const CASParseErrorInvalidIndentation; extern NSString * const CASParseFailingLineNumberErrorKey; extern NSString * const CASParseFailingStringErrorKey; @interface CASLexer : NSObject /** * The error, if any, that occurred during tokenisation */ @property (nonatomic, strong, readonly) NSError *error; /** * The remaining length of string to tokenise */ @property (nonatomic, assign, readonly) NSInteger length; /** * Create new `CASLexer` with a `NSString` to Tokenize */ - (id)initWithString:(NSString *)str; /** * Create a error and attach current string and line number to userInfo */ - (NSError *)errorWithDescription:(NSString *)description reason:(NSString *)reason code:(NSUInteger)code; /** * Token from stash or by advancing. * * @return a `CASToken` which will remain or be added to the stash */ - (CASToken *)peekToken; /** * Token from stash or by advancing. * * @return a `CASToken` which if coming from stash will be removed */ - (CASToken *)nextToken; /** * lookahead advances stash if needed * then returns the token `count` from top of stash * * @param count number of tokens to lookahead, minimum=1 * * @return The token corresponding to the number of lookaheads */ - (CASToken *)lookaheadByCount:(NSUInteger)count; @end ================================================ FILE: Classy/Parser/CASLexer.m ================================================ // // CASLexer.m // Classy // // Created by Jonas Budelmann on 15/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASLexer.h" #import "NSRegularExpression+CASAdditions.h" #import "NSString+CASAdditions.h" #import "UIColor+CASAdditions.h" #import "CASUnitToken.h" NSString * const CASParseErrorDomain = @"CASParseErrorDomain"; NSInteger const CASParseErrorInvalidToken = 1; NSInteger const CASParseErrorInvalidIndentation = 2; NSString * const CASParseFailingLineNumberErrorKey = @"CASParseFailingLineNumberErrorKey"; NSString * const CASParseFailingStringErrorKey = @"CASParseFailingStringErrorKey"; @interface CASLexer () @property (nonatomic, strong, readwrite) NSError *error; @property (nonatomic, strong) NSMutableString *str; @property (nonatomic, strong) NSMutableArray *stash; @property (nonatomic, strong) NSMutableArray *indentStack; @property (nonatomic, strong) CASToken *previousToken; @property (nonatomic, strong) NSDictionary *regexCache; @property (nonatomic, strong) NSRegularExpression *indentRegex; @property (nonatomic, assign) NSInteger lineNumber; @end @implementation CASLexer - (id)initWithString:(NSString *)str { self = [super init]; if (!self) return nil; self.str = [str mutableCopy]; self.stash = NSMutableArray.new; self.indentStack = NSMutableArray.new; self.lineNumber = 1; // replace carriage returns (\r\n | \r) with newlines [CASRegex(@"\\r\\n?") cas_replaceMatchesInString:self.str withTemplate:@"\n"]; // trim whitespace & newlines from end of string [CASRegex(@"\\s+$") cas_replaceMatchesInString:self.str withTemplate:@"\n"]; NSString *units = [@[@"pt", @"px", @"%"] componentsJoinedByString:@"|"]; // cache regex's self.regexCache = @{ // 1 of `;` followed by 0-* of whitespace @(CASTokenTypeSemiColon) : @[ CASRegex(@"^;[ \\t]*") ], // 1 of `^` followed by 0-* of whitespace @(CASTokenTypeCarat) : @[ CASRegex(@"^\\^[ \\t]*") ], // new line followed by tabs or spaces @(CASTokenTypeIndent) : @[ CASRegex(@"^\\n([\\t]*)"), CASRegex(@"^\\n([ ]*)") ], // #rrggbbaa | #rrggbb | #rgb @(CASTokenTypeColor) : @[ CASRegex(@"^#([a-fA-F0-9]{8})[ \\t]*"), CASRegex(@"^#([a-fA-F0-9]{6})[ \\t]*"), CASRegex(@"^#([a-fA-F0-9]{3})[ \\t]*") ], // string enclosed in single or double quotes @(CASTokenTypeString) : @[ CASRegex(@"^(\"[^\"]*\"|'[^']*')[ \\t]*") ], // decimal/integer number with optional (px, pt) suffix @(CASTokenTypeUnit) : @[ CASRegex(@"^(-)?(\\d+\\.\\d+|\\d+|\\.\\d+)(%@)?[ \\t]*", units) ], // true | false | YES | NO @(CASTokenTypeBoolean) : @[ CASRegex(@"^(true|false|YES|NO)\\b([ \\t]*)") ], // optional `@` | `-` then at least one `_a-zA-Z$` following by any alphanumeric char or `-` or `$` @(CASTokenTypeRef) : @[ CASRegex(@"^(@)?(-*[_a-zA-Z$][-\\w\\d$]*)") ], // tests if string looks like math operation @(CASTokenTypeOperator) : @[ CASRegex(@"^([.]{2,3}|&&|\\|\\||[!<>=?:]=|\\*\\*|[-+*\\/%%]=?|[,=?:!~<>&\\[\\]])([ \\t]*)") ], // 1-* of whitespace @(CASTokenTypeSpace) : @[ CASRegex(@"^([ \\t]+)") ], // any character except `\n` | `{` | `,` | whitespace @(CASTokenTypeSelector) : @[ CASRegex(@"^.*?(?=\\/\\/|[ \\t,\\n{])") ] }; return self; } - (NSInteger)length { return self.str.length; } - (CASToken *)peekToken { return [self lookaheadByCount:1]; } - (CASToken *)nextToken { CASToken *token = self.popToken; if (!token) { token = self.advanceToken; [self attachDebugInfoForToken:token]; } self.previousToken = token; return token; } - (CASToken *)lookaheadByCount:(NSUInteger)count { NSAssert(count > 0, @"Invalid lookahead. Count `%lu` must be >= 1", (unsigned long)count); NSInteger fetch = count - self.stash.count; while (fetch > 0) { CASToken *token = self.advanceToken; [self attachDebugInfoForToken:token]; [self.stash addObject:token]; fetch = count - self.stash.count; } return self.stash[count-1]; } - (void)attachDebugInfoForToken:(CASToken *)token { switch (token.type) { case CASTokenTypeNewline: case CASTokenTypeIndent: ++self.lineNumber; break; case CASTokenTypeOutdent: if (CASTokenTypeOutdent != self.previousToken.type) ++self.lineNumber; break; default: break; } token.lineNumber = self.lineNumber; } - (NSError *)errorWithDescription:(NSString *)description reason:(NSString *)reason code:(NSUInteger)code { NSInteger length = MIN(self.str.length, 25); NSString *format = length != self.str.length ? @"\"%@ ...\"" : @"\"%@\""; NSString *string = [NSString stringWithFormat:format, [self.str substringToIndex:length]]; NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: description, NSLocalizedFailureReasonErrorKey: reason, CASParseFailingLineNumberErrorKey: @(self.lineNumber), CASParseFailingStringErrorKey: string }; return [NSError errorWithDomain:CASParseErrorDomain code:code userInfo:userInfo]; } #pragma mark - private - (void)skip:(NSUInteger)n { [self.str deleteCharactersInRange:NSMakeRange(0, n)]; } - (CASToken *)advanceToken { // TODO optimise // this could possibly be faster using simple string scanning (NSScanner), instead of regex // however all these regexs are anchored to start of string so should be fairly quick CASToken *token = self.eos ?: self.seperator ?: self.carat ?: self.comment ?: self.newline ?: self.squareBrace ?: self.curlyBrace ?: self.roundBrace ?: self.color ?: self.string ?: self.unit ?: self.boolean ?: self.ref ?: self.operation ?: self.space ?: self.selector; if (!token) { self.error = [self errorWithDescription:@"Invalid style string" reason:@"Could not determine token" code:CASParseErrorInvalidToken]; return nil; } return token; } - (CASToken *)popToken { // Return the next stashed token and remove it from stash. if (self.stash.count) { CASToken *token = self.stash[0]; [self.stash removeObjectAtIndex:0]; return token; } return nil; } #pragma mark - tokens - (CASToken *)eos { // EOS | trailing outdents. if (self.str.length) return nil; if (self.indentStack.count) { [self.indentStack removeObjectAtIndex:0]; return [CASToken tokenOfType:CASTokenTypeOutdent]; } else { return [CASToken tokenOfType:CASTokenTypeEOS]; } } - (CASToken *)seperator { return [self testForTokenType:CASTokenTypeSemiColon transformValueBlock:nil]; } - (CASToken *)carat { return [self testForTokenType:CASTokenTypeCarat transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return value; }]; } - (CASToken *)comment { // Single line if ([self.str hasPrefix:@"//"]) { NSInteger nextLine = [self.str rangeOfString:@"\n"].location; if (nextLine == NSNotFound) { nextLine = self.str.length; } [self skip:nextLine]; return self.advanceToken; } // Multi-line if ([self.str hasPrefix:@"/*"]) { NSInteger closeComment = [self.str rangeOfString:@"*/"].location; if (closeComment == NSNotFound) { closeComment = self.str.length; } else { closeComment += 2; } NSInteger lines = [[self.str substringWithRange:NSMakeRange(0, closeComment)] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]].count; self.lineNumber += lines - 1; [self skip:closeComment]; return self.advanceToken; } return nil; } - (CASToken *)newline { // we have established the indentation regexp NSTextCheckingResult *match; if (self.indentRegex){ match = [self.indentRegex firstMatchInString:self.str options:0 range:NSMakeRange(0, self.str.length)]; } else { // figure out if we are using tabs or spaces for (NSRegularExpression *regex in self.regexCache[@(CASTokenTypeIndent)]) { match = [regex firstMatchInString:self.str options:0 range:NSMakeRange(0, self.str.length)]; if (match && [match rangeAtIndex:1].length) { self.indentRegex = regex; break; } } } if (!match) return nil; [self skip:match.range.length]; if ([self.str hasPrefix:@" "] || [self.str hasPrefix:@"\t"]) { ++self.lineNumber; self.error = [self errorWithDescription:@"Invalid indentation" reason:@"You can use tabs or spaces to indent, but not both." code:CASParseErrorInvalidIndentation]; return nil; } // Blank line if ([self.str hasPrefix:@"\n"]) { ++self.lineNumber; return self.advanceToken; } NSInteger indents = [match rangeAtIndex:1].length; if (self.indentStack.count && indents < [self.indentStack[0] integerValue]) { while (self.indentStack.count && [self.indentStack[0] integerValue] > indents) { [self.stash addObject:[CASToken tokenOfType:CASTokenTypeOutdent]]; [self.indentStack removeObjectAtIndex:0]; } return [self advanceToken]; } else if (indents && indents != (self.indentStack.count ? [self.indentStack[0] integerValue] : 0)) { [self.indentStack insertObject:@(indents) atIndex:0]; return [CASToken tokenOfType:CASTokenTypeIndent]; } else { return [CASToken tokenOfType:CASTokenTypeNewline]; } return self.advanceToken; } - (CASToken *)squareBrace { if ([self.str hasPrefix:@"["]) { [self skip:1]; return [CASToken tokenOfType:CASTokenTypeLeftSquareBrace value:@"["]; } if ([self.str hasPrefix:@"]"]) { [self skip:1]; return [CASToken tokenOfType:CASTokenTypeRightSquareBrace value:@"]"]; } return nil; } - (CASToken *)curlyBrace { if ([self.str hasPrefix:@"{"]) { [self skip:1]; return [CASToken tokenOfType:CASTokenTypeLeftCurlyBrace value:@"{"]; } if ([self.str hasPrefix:@"}"]) { [self skip:1]; return [CASToken tokenOfType:CASTokenTypeRightCurlyBrace value:@"}"]; } return nil; } - (CASToken *)roundBrace { if ([self.str hasPrefix:@"("]) { [self skip:1]; return [CASToken tokenOfType:CASTokenTypeLeftRoundBrace value:@"("]; } if ([self.str hasPrefix:@")"]) { [self skip:1]; return [CASToken tokenOfType:CASTokenTypeRightRoundBrace value:@")"]; } return nil; } - (CASToken *)color { return [self testForTokenType:CASTokenTypeColor transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return [UIColor cas_colorWithHex:[value cas_stringByTrimmingWhitespace]]; }]; } - (CASToken *)string { return [self testForTokenType:CASTokenTypeString transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { NSString *string = [value cas_stringByTrimmingWhitespace]; return [string substringWithRange:NSMakeRange(1, string.length-2)]; }]; } - (CASToken *)unit { __block NSString *suffix; __block NSString *rawValue; CASUnitToken *unitToken = (id)[self testForTokenType:CASTokenTypeUnit tokenClass:CASUnitToken.class transformValueBlock:^id(NSString *value, NSTextCheckingResult *match){ NSRange suffixRange = [match rangeAtIndex:match.numberOfRanges-1]; if (suffixRange.location != NSNotFound) { suffix = [value substringWithRange:suffixRange]; } NSRange valueRange = [match rangeAtIndex:0]; if (valueRange.location != NSNotFound) { rawValue = [value substringWithRange:valueRange]; } return @([rawValue doubleValue]); }]; if (unitToken) { unitToken.suffix = suffix; unitToken.rawValue = rawValue; } return unitToken; } - (CASToken *)boolean { return [self testForTokenType:CASTokenTypeBoolean transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return @([value hasPrefix:@"true"] || [value hasPrefix:@"YES"]); }]; } - (CASToken *)ref { return [self testForTokenType:CASTokenTypeRef transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return value; }]; } - (CASToken *)operation { return [self testForTokenType:CASTokenTypeOperator transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return [self.str substringWithRange:[match rangeAtIndex:1]]; }]; } - (CASToken *)space { return [self testForTokenType:CASTokenTypeSpace transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return value; }]; } - (CASToken *)selector { return [self testForTokenType:CASTokenTypeSelector transformValueBlock:^id(NSString *value, NSTextCheckingResult *match) { return value; }]; } #pragma mark - helpers - (CASToken *)testForTokenType:(CASTokenType)tokenType transformValueBlock:(id(^)(NSString *value, NSTextCheckingResult *match))transformValueBlock { return [self testForTokenType:tokenType tokenClass:CASToken.class transformValueBlock:transformValueBlock]; } - (CASToken *)testForTokenType:(CASTokenType)tokenType tokenClass:(Class)tokenClass transformValueBlock:(id(^)(NSString *value, NSTextCheckingResult *match))transformValueBlock { NSArray *regexes = self.regexCache[@(tokenType)]; NSAssert(regexes.count, @"Invalid cache. No cached regex for CASTokenType `%@`", [CASToken stringForType:tokenType]); for (NSRegularExpression *regex in regexes) { NSTextCheckingResult *match = [regex firstMatchInString:self.str options:0 range:NSMakeRange(0, self.str.length)]; if (match) { CASToken *token = [tokenClass tokenOfType:tokenType]; if (transformValueBlock) { token.value = transformValueBlock([self.str substringWithRange:match.range], match); } [self skip:match.range.length]; return token; } } return nil; } @end ================================================ FILE: Classy/Parser/CASParser.h ================================================ // // CASParser.h // Classy // // Created by Jonas Budelmann on 15/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASLexer.h" extern NSString * const CASParseFailingFilePathErrorKey; extern NSInteger const CASParseErrorFileContents; @interface CASParser : NSObject @property (nonatomic, strong, readonly) NSArray *styleNodes; @property (nonatomic, strong, readonly) NSSet *importedFileNames; /** * Create a parser with the given file path */ + (CASParser *)parserFromFilePath:(NSString *)filePath variables:(NSDictionary *)variables error:(NSError **)error; @end ================================================ FILE: Classy/Parser/CASParser.m ================================================ // // CASParser.m // Classy // // Created by Jonas Budelmann on 15/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASParser.h" #import "CASLexer.h" #import "CASStyleNode.h" #import "CASToken.h" #import "CASUtilities.h" #import "CASStyleProperty.h" #import "CASStyleSelector.h" #import "CASDeviceSelector.h" #import "NSString+CASAdditions.h" NSString * const CASParseFailingFilePathErrorKey = @"CASParseFailingFilePathErrorKey"; NSInteger const CASParseErrorFileContents = 2; @interface CASParser () @property (nonatomic, strong) CASLexer *lexer; @property (nonatomic, strong, readwrite) NSArray *styleNodes; @property (nonatomic, strong) NSMutableDictionary *styleVars; @property (nonatomic, strong) NSError *error; @property (nonatomic, copy) NSString *filePath; @end @implementation CASParser { NSMutableSet *_importedFileNames; } + (CASParser *)parserFromFilePath:(NSString *)filePath variables:(NSDictionary *)variables error:(NSError **)error { NSError *fileError = nil; NSString *contents = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&fileError]; if (!contents || fileError) { NSMutableDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Could not parse file", NSLocalizedFailureReasonErrorKey: @"File does not exist or is empty", CASParseFailingFilePathErrorKey : filePath ?: @"" }.mutableCopy; if (fileError) { [userInfo setObject:fileError forKey:NSUnderlyingErrorKey]; } if (error) { *error = [NSError errorWithDomain:CASParseErrorDomain code:CASParseErrorFileContents userInfo:userInfo]; } return nil; } CASLog(@"Start parsing file \n%@", filePath); NSError *parseError = nil; CASParser *parser = CASParser.new; parser.filePath = filePath; parser.styleVars = NSMutableDictionary.new; //transform variables into tokens [variables enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { if ([obj isKindOfClass:CASStyleProperty.class]) { parser.styleVars[key] = obj; return; } NSString *stringValue = [obj isKindOfClass:NSString.class] ? obj : [obj stringValue]; CASLexer *lexer = [[CASLexer alloc] initWithString:stringValue]; CASToken *nameToken = [CASToken tokenOfType:CASTokenTypeRef value:key]; NSMutableArray *tokens = NSMutableArray.new; while (lexer.peekToken && lexer.peekToken.type != CASTokenTypeEOS) { [tokens addObject:lexer.nextToken]; } parser.styleVars[key] = [[CASStyleProperty alloc] initWithNameToken:nameToken valueTokens:tokens]; }]; parser.styleNodes = [parser parseString:contents error:&parseError]; if (parseError) { if (error) { *error = parseError; } return nil; } if (parseError) { NSMutableDictionary *userInfo = parseError.userInfo.mutableCopy; [userInfo addEntriesFromDictionary:@{ CASParseFailingFilePathErrorKey : filePath }]; if (error) { *error = [NSError errorWithDomain:parseError.domain code:parseError.code userInfo:userInfo]; } return nil; } return parser; } - (NSError *)error { return _error ?: self.lexer.error; } - (NSSet *)importedFileNames { return _importedFileNames; } - (NSArray *)parseString:(NSString *)string error:(NSError **)error { self.lexer = [[CASLexer alloc] initWithString:string]; _importedFileNames = NSMutableSet.new; NSMutableArray *allStyleNodes = NSMutableArray.new; NSMutableArray *styleNodesStack = NSMutableArray.new; NSMutableArray *stylePropertiesStack = NSMutableArray.new; while (self.peekToken.type != CASTokenTypeEOS) { if (self.error) { if (error) *error = self.error; return nil; } //check for import if (self.peekToken.type == CASTokenTypeRef && [self.peekToken valueIsEqualTo:@"@import"]) { if (styleNodesStack.count) { // can't have vars inside styleNodes if (error) { *error = [self.lexer errorWithDescription:@"@import cannot be used inside style selectors" reason:nil code:CASParseErrorFileContents]; } return nil; } //skip import token [self nextToken]; //skip whitespace [self consumeTokensMatching:^BOOL(CASToken *token) { return token.type == CASTokenTypeSpace; }]; //combine all following tokens until newline | ; NSMutableArray *fileNameComponents = NSMutableArray.new; while (self.peekToken.type != CASTokenTypeNewline && self.peekToken.type != CASTokenTypeSemiColon && self.peekToken.type != CASTokenTypeEOS) { [fileNameComponents addObject:self.nextToken.stringValue]; } NSString *fileName = [[fileNameComponents componentsJoinedByString:@""] cas_stringByTrimmingWhitespace]; fileName = [fileName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@";"]]; if ([fileName hasPrefix:@"$"]) { CASStyleProperty *property = self.styleVars[fileName]; if (property) { fileName = [property.values componentsJoinedByString:@""]; } } if (!fileName.length) { if (error) { *error = [self.lexer errorWithDescription:@"@import does not specify file to import" reason:nil code:CASParseErrorFileContents]; } return nil; } [_importedFileNames addObject:fileName]; NSString *filePath = [[self.filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:fileName]; NSError *importError = nil; CASParser *parser = [CASParser parserFromFilePath:filePath variables:self.styleVars error:&importError]; if (importError) { if (error) { *error = importError; } return nil; } [allStyleNodes addObjectsFromArray:parser.styleNodes]; [self.styleVars addEntriesFromDictionary:parser.styleVars]; [_importedFileNames addObjectsFromArray:parser.importedFileNames.allObjects]; [self consumeTokensMatching:^BOOL(CASToken *token) { return self.peekToken.type == CASTokenTypeNewline || self.peekToken.type == CASTokenTypeSemiColon; }]; continue; } NSArray *styleNodes = [self nextStyleNodes]; if (self.error) { if (error) *error = self.error; return nil; } if (styleNodes.count) { NSMutableArray *flattenStyleNodes = styleNodesStack.count ? NSMutableArray.new : nil; for (CASStyleNode *parentNode in styleNodesStack.lastObject) { for (CASStyleNode *styleNode in styleNodes) { CASStyleNode *flattenStyleNode = CASStyleNode.new; flattenStyleNode.deviceSelector = styleNode.deviceSelector; if (!flattenStyleNode.deviceSelector && parentNode.deviceSelector) { flattenStyleNode.deviceSelector = CASDeviceSelector.new; } [flattenStyleNode.deviceSelector addItems:parentNode.deviceSelector.items]; CASStyleSelector *parentSelector = [parentNode.styleSelector copy]; parentSelector.parent = YES; if (!styleNode.styleSelector) { flattenStyleNode.styleSelector = parentSelector; parentSelector.parent = NO; } else if (styleNode.styleSelector.lastSelector.shouldConcatToParent) { CASStyleSelector *styleSelector = styleNode.styleSelector; if (styleSelector.styleClass) { parentSelector.styleClass = styleSelector.styleClass; } if (styleSelector.arguments) { parentSelector.arguments = styleSelector.arguments; } flattenStyleNode.styleSelector = parentSelector; } else { flattenStyleNode.styleSelector = [styleNode.styleSelector copy]; flattenStyleNode.styleSelector.lastSelector.parentSelector = parentSelector; } [flattenStyleNodes addObject:flattenStyleNode]; } } if (flattenStyleNodes.count) { styleNodes = flattenStyleNodes; } [allStyleNodes addObjectsFromArray:styleNodes]; [styleNodesStack addObject:styleNodes]; [self consumeTokenOfType:CASTokenTypeLeftCurlyBrace]; [self consumeTokenOfType:CASTokenTypeIndent]; continue; } CASStyleProperty *styleVar = [self nextStyleVar]; if (self.error) { if (error) *error = self.error; return nil; } if (styleVar) { if (styleNodesStack.count) { // can't have vars inside styleNodes if (error) { *error = [self.lexer errorWithDescription:@"Variables cannot be declared inside style selectors" reason:[NSString stringWithFormat:@"Variable: %@", styleVar] code:CASParseErrorFileContents]; } return nil; } [styleVar resolveExpressions]; self.styleVars[styleVar.nameToken.value] = styleVar; [self consumeTokensMatching:^BOOL(CASToken *token) { return token.type == CASTokenTypeSpace || token.type == CASTokenTypeSemiColon; }]; continue; } // not a style group therefore must be a property BOOL isStylePropertyParent = NO; CASStyleProperty *styleProperty = [self nextStylePropertyIsParent:&isStylePropertyParent]; if (self.error) { if (error) *error = self.error; return nil; } if (styleProperty) { if (!styleNodesStack.count) { if (error) { *error = [self.lexer errorWithDescription:@"Invalid style property" reason:@"Needs to be within a style node" code:CASParseErrorFileContents]; } return nil; } [styleProperty resolveExpressions]; if (stylePropertiesStack.count) { CASStyleProperty *parent = stylePropertiesStack.lastObject; [parent addChildStyleProperty:styleProperty]; } else { for (CASStyleNode *node in styleNodesStack.lastObject) { [node addStyleProperty:styleProperty]; } } if (isStylePropertyParent) { [stylePropertiesStack addObject:styleProperty]; } continue; } __block NSInteger previousLength = NSNotFound; __block CASToken *previousToken = nil; BOOL acceptableToken = [self consumeTokensMatching:^BOOL(CASToken *token) { BOOL popStack = token.type == CASTokenTypeOutdent || token.type == CASTokenTypeRightCurlyBrace; // make sure we don't double pop BOOL alreadyPopped = previousLength == self.lexer.length && previousToken.type == CASTokenTypeOutdent && token.type == CASTokenTypeRightCurlyBrace; if (!alreadyPopped && popStack) { NSMutableArray *stack = stylePropertiesStack.count ? stylePropertiesStack : styleNodesStack; if (stack.count) { [stack removeObjectAtIndex:stack.count-1]; } } previousLength = self.lexer.length; previousToken = token; return popStack || token.isWhitespace || token.type == CASTokenTypeSemiColon; }]; if (!acceptableToken) { NSString *description = [NSString stringWithFormat:@"Unexpected token `%@`", self.nextToken]; if (error) { *error = [self.lexer errorWithDescription:description reason:@"Token does not belong in current context" code:CASParseErrorFileContents]; } return nil; } } return allStyleNodes; } #pragma mark - token helpers - (CASToken *)peekToken { return self.lexer.peekToken; } - (CASToken *)nextToken { CASToken *token = self.lexer.nextToken; return token; } - (CASToken *)lookaheadByCount:(NSUInteger)count { return [self.lexer lookaheadByCount:count]; } - (CASToken *)consumeTokenOfType:(CASTokenType)type { if (type == self.peekToken.type) { // return token and remove from stack return self.nextToken; } return nil; } - (BOOL)consumeTokensMatching:(BOOL(^)(CASToken *token))matchBlock { BOOL anyMatches = NO; while (matchBlock(self.peekToken)) { anyMatches = YES; [self nextToken]; } return anyMatches; } - (BOOL)isNestedPropertyAtIndex:(NSInteger)index { NSInteger tail = index; while (--tail > 0) { CASToken *tailToken = [self lookaheadByCount:tail]; if ([tailToken valueIsEqualTo:@"@"]) { return YES; } if (!tailToken.isWhitespace && tailToken.type != CASTokenTypeLeftCurlyBrace) { return NO; } } return NO; } #pragma mark - nodes - (CASStyleProperty *)nextStyleVar { // variable if following seq: CASTokenTypeRef, `=`, any token until newline NSInteger i = 1; CASToken *token = [self lookaheadByCount:i]; BOOL hasEqualsSign = NO; CASToken *refToken; while (token && token.isPossiblyVar && !(hasEqualsSign && refToken)) { if (token.type == CASTokenTypeRef) { refToken = token; } if (refToken && [token valueIsEqualTo:@"="]) { hasEqualsSign = YES; } token = [self lookaheadByCount:++i]; } if ([refToken.value hasPrefix:@"@"]) { self.error = [self.lexer errorWithDescription:@"Variables cannot begin with `@` character" reason:@"`@` is reserved for @media, @version and property lookup" code:CASParseErrorFileContents]; } if (hasEqualsSign && refToken) { // consume LHS of var while (--i >= 0) { [self nextToken]; } // collect value tokens, enclose in () NSMutableArray *valueTokens = NSMutableArray.new; NSInteger nonWhitespaceTokenCount = 0; while (token.type != CASTokenTypeNewline && token.type != CASTokenTypeSemiColon && token.type != CASTokenTypeEOS) { [valueTokens addObject:token]; if (!token.isWhitespace) { nonWhitespaceTokenCount++; } token = [self nextToken]; } // only wrap in braces if more than one non-whitespace token present if (nonWhitespaceTokenCount > 1) { [valueTokens insertObject:[CASToken tokenOfType:CASTokenTypeLeftRoundBrace value:@"("] atIndex:0]; [valueTokens addObject:[CASToken tokenOfType:CASTokenTypeRightRoundBrace value:@")"]]; } return [[CASStyleProperty alloc] initWithNameToken:refToken valueTokens:valueTokens]; } return nil; } - (NSArray *)nextDeviceQueries { NSInteger i = 1; CASToken *token = [self lookaheadByCount:i]; BOOL isDeviceQuery = NO; while (token && token.type != CASTokenTypeEOS && token.type != CASTokenTypeNewline && token.type != CASTokenTypeIndent && token.type != CASTokenTypeOutdent && token.type != CASTokenTypeLeftCurlyBrace) { if (!isDeviceQuery && token.type != CASTokenTypeSpace) { if ([token valueIsEqualTo:@"@media"] || [token valueIsEqualTo:@"@device"]) { isDeviceQuery = YES; } else { break; } } token = [self lookaheadByCount:++i]; } if (!isDeviceQuery) return nil; CASStyleNode *currentNode = CASStyleNode.new; currentNode.deviceSelector = CASDeviceSelector.new; NSMutableArray *nodes = NSMutableArray.new; [nodes addObject:currentNode]; BOOL waitingForRightRoundBrace = NO; NSString *itemName; NSMutableString *itemValue = NSMutableString.new; NSMutableString *itemRelation = NSMutableString.new; while (--i > 0) { token = [self nextToken]; if (token.type == CASTokenTypeSpace) { continue; } if (waitingForRightRoundBrace) { if (!itemName) { if (token.type == CASTokenTypeRef) { itemName = token.value; } else { //TODO unexpected token error } continue; } if (token.type == CASTokenTypeRightRoundBrace) { waitingForRightRoundBrace = NO; if ([itemName isEqualToString:@"version"]) { NSString *versionString = [(itemRelation ?: @"") stringByAppendingString:(itemValue ?: @"")]; [currentNode.deviceSelector addOSVersion:versionString]; } else if ([itemName isEqualToString:@"screen-width"]) { NSString *widthConstraintString = [(itemRelation ?: @"") stringByAppendingString:(itemValue ?: @"")]; [currentNode.deviceSelector addScreenSize:widthConstraintString dimension:CASDeviceSelectorScreenDimensionWidth]; } else if ([itemName isEqualToString:@"screen-height"]) { NSString *heightConstraintString = [(itemRelation ?: @"") stringByAppendingString:(itemValue ?: @"")]; [currentNode.deviceSelector addScreenSize:heightConstraintString dimension:CASDeviceSelectorScreenDimensionHeight]; } itemRelation = NSMutableString.new; itemValue = NSMutableString.new; } else if (token.type == CASTokenTypeLeftRoundBrace) { //TODO unexpected token error } else if (token.type == CASTokenTypeRef) { itemName = token.value; } else if (token.type == CASTokenTypeOperator) { if ([token valueIsEqualTo:@":"]) { continue; } else { [itemRelation appendString:token.value]; } } else if (token.type == CASTokenTypeUnit) { [itemRelation appendString:token.stringValue]; } continue; } if (token.type == CASTokenTypeLeftRoundBrace) { waitingForRightRoundBrace = YES; } else if (token.type == CASTokenTypeRightRoundBrace) { //TODO unexpected token error } else if (token.type == CASTokenTypeOperator) { if ([token valueIsEqualTo:@","]) { currentNode = CASStyleNode.new; currentNode.deviceSelector = CASDeviceSelector.new; [nodes addObject:currentNode]; } } else if (token.type == CASTokenTypeRef) { NSString *lowercaseValue = [token.value lowercaseString]; if ([lowercaseValue isEqualToString:@"ipad"] || [lowercaseValue isEqualToString:@"pad"]) { [currentNode.deviceSelector addDeviceType:CASDeviceTypePad]; } else if ([lowercaseValue isEqualToString:@"iphone"] || [lowercaseValue isEqualToString:@"phone"]) { [currentNode.deviceSelector addDeviceType:CASDeviceTypePhone]; } else if ([lowercaseValue isEqualToString:@"and"]) { continue; } else { //TODO unexpected token error } } else { //TODO unexpected token error } } return nodes; } - (NSArray *)nextStyleNodes { NSArray *deviceQueryNodes = [self nextDeviceQueries]; if (deviceQueryNodes) return deviceQueryNodes; NSInteger i = 1; CASToken *token = [self lookaheadByCount:i]; CASToken *previousNonWhitespaceToken; while (token && token.isPossiblySelector) { CASToken *nextToken = [self lookaheadByCount:++i]; //newline must be preceded by comma unless next token is selector delimiter if (token.type == CASTokenTypeNewline && previousNonWhitespaceToken && ![previousNonWhitespaceToken valueIsEqualTo:@","] && !nextToken.isPossiblySelectorDelimiter) { return nil; } if (![token isWhitespace]) { previousNonWhitespaceToken = token; } token = nextToken; } if ([self isNestedPropertyAtIndex:i]) { return nil; } if (!token.isPossiblySelectorDelimiter) { return nil; } NSMutableArray *styleNodes = NSMutableArray.new; NSMutableDictionary *arguments; CASStyleSelector *styleSelector; CASToken *previousToken, *argNameToken, *argValueToken; token = nil; BOOL shouldSelectSubclasses = NO; BOOL shouldSelectIndirectSuperview = YES; BOOL argumentListMode = NO; BOOL shouldConcatToParent = NO; while (--i > 0) { previousToken = token; token = [self nextToken]; if (argumentListMode) { if (token.type == CASTokenTypeRightSquareBrace) { styleSelector.arguments = [arguments copy]; argumentListMode = NO; arguments = nil; } else if (token.type == CASTokenTypeSelector || token.type == CASTokenTypeRef) { if (!argNameToken) { argNameToken = token; } else if (!argValueToken) { argValueToken = token; } if (argNameToken && argValueToken) { if (!arguments) { arguments = NSMutableDictionary.new; } NSString *argValue = [argValueToken.value cas_stringByTrimmingWhitespace]; NSString *argName = [argNameToken.value cas_stringByTrimmingWhitespace]; [arguments setObject:argValue forKey:argName]; argValueToken = nil; argNameToken = nil; } } continue; } if (token.type == CASTokenTypeCarat) { shouldSelectSubclasses = YES; } else if (token.type == CASTokenTypeRef || token.type == CASTokenTypeSelector) { NSString *tokenValue = [token.value cas_stringByTrimmingWhitespace]; BOOL shouldSpawn = ![tokenValue hasPrefix:@"."] || styleSelector == nil || previousToken.isWhitespace || [previousToken valueIsEqualTo:@">"]; if (shouldSpawn) { if (styleSelector) { CASStyleSelector *childSelector = CASStyleSelector.new; childSelector.shouldSelectIndirectSuperview = shouldSelectIndirectSuperview; childSelector.shouldConcatToParent = shouldConcatToParent; childSelector.parentSelector = styleSelector; styleSelector.parent = YES; styleSelector = childSelector; } else { styleSelector = CASStyleSelector.new; styleSelector.shouldSelectIndirectSuperview = shouldSelectIndirectSuperview; styleSelector.shouldConcatToParent = shouldConcatToParent; } styleSelector.shouldSelectSubclasses = shouldSelectSubclasses; } if ([tokenValue hasPrefix:@"."]) { styleSelector.styleClass = [tokenValue substringFromIndex:1]; } else { styleSelector.objectClass = NSClassFromString(tokenValue); if (!styleSelector.objectClass) { // Maybe it's a custom Swift class styleSelector.objectClass = [self swiftClassFromString:tokenValue]; } } if (!styleSelector.objectClass && !shouldConcatToParent) { self.error = [self.lexer errorWithDescription:[NSString stringWithFormat:@"Invalid class name `%@`", tokenValue] reason:@"Every selector must have a objectClass" code:CASParseErrorFileContents]; return nil; } // reset state shouldSelectSubclasses = NO; shouldSelectIndirectSuperview = YES; shouldConcatToParent = NO; } else if (token.type == CASTokenTypeLeftSquareBrace) { argumentListMode = YES; } else if ([token valueIsEqualTo:@"&"]) { shouldConcatToParent = YES; } else if([token valueIsEqualTo:@">"]) { shouldSelectIndirectSuperview = NO; } else if ([token valueIsEqualTo:@","]) { if (styleSelector) { CASStyleNode *node = CASStyleNode.new; node.styleSelector = styleSelector; [styleNodes addObject:node]; } styleSelector = nil; } } if (styleSelector) { CASStyleNode *node = CASStyleNode.new; node.styleSelector = styleSelector; [styleNodes addObject:node]; } return styleNodes; } - (CASStyleProperty *)nextStylePropertyIsParent:(BOOL *)isParent { NSInteger i = 1; BOOL hasName = NO, hasValues = NO; CASToken *token = [self lookaheadByCount:i]; while (token && token.type != CASTokenTypeNewline && token.type != CASTokenTypeRightCurlyBrace && token.type != CASTokenTypeIndent && token.type != CASTokenTypeOutdent && token.type != CASTokenTypeSemiColon && token.type != CASTokenTypeEOS) { if (token.type == CASTokenTypeSpace || token.type == CASTokenTypeIndent || [token valueIsEqualTo:@":"]) { token = [self lookaheadByCount:++i]; continue; } if (!hasName && token.value) { hasName = YES; } else { hasValues = YES; } token = [self lookaheadByCount:++i]; } if (hasName && hasValues) { CASToken *nameToken; NSMutableArray *valueTokens = NSMutableArray.new; NSMutableDictionary *arguments; CASToken *argNameToken, *argValueToken; BOOL argumentListMode = NO; BOOL hasChildren = [self isNestedPropertyAtIndex:i]; // consume tokens while (--i > 0) { token = [self nextToken]; if (!nameToken && token.value) { nameToken = token; continue; } if (argumentListMode) { if (token.type == CASTokenTypeRightSquareBrace) { argumentListMode = NO; } else if (token.type == CASTokenTypeSelector || token.type == CASTokenTypeRef) { if (!argNameToken) { argNameToken = token; } else if (!argValueToken) { argValueToken = token; } if (argNameToken && argValueToken) { if (!arguments) { arguments = NSMutableDictionary.new; } NSString *argValue = [argValueToken.value cas_stringByTrimmingWhitespace]; NSString *argName = [argNameToken.value cas_stringByTrimmingWhitespace]; [arguments setObject:argValue forKey:argName]; argValueToken = nil; argNameToken = nil; } } continue; } if (token.isWhitespace || [token valueIsEqualTo:@":"]) { continue; } if (token.type == CASTokenTypeLeftSquareBrace) { argumentListMode = YES; } else if (token.type == CASTokenTypeRef) { CASStyleProperty *styleVar = self.styleVars[token.value]; if (styleVar) { [valueTokens addObjectsFromArray:styleVar.valueTokens]; } else { [valueTokens addObject:token]; } } else { [valueTokens addObject:token]; } } CASStyleProperty *styleProperty = [[CASStyleProperty alloc] initWithNameToken:nameToken valueTokens:valueTokens]; styleProperty.arguments = [arguments copy]; *isParent = hasChildren; return styleProperty; } return nil; } - (Class)swiftClassFromString:(NSString *)className { NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; NSString *sanitizedAppName = [appName stringByReplacingOccurrencesOfString:@" " withString:@"_"]; NSString *classStringName = [NSString stringWithFormat:@"_TtC%lu%@%lu%@", (unsigned long)sanitizedAppName.length, sanitizedAppName, (unsigned long)className.length, className]; return NSClassFromString(classStringName); } @end ================================================ FILE: Classy/Parser/CASStyleNode.h ================================================ // // CASStyleGroup.h // Classy // // Created by Jonas Budelmann on 25/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASStyleProperty.h" #import "CASStyleSelector.h" #import "CASDeviceSelector.h" @interface CASStyleNode : NSObject /** * NSInvocations to apply to appropriate view */ @property (nonatomic, strong) NSArray *invocations; /** * returns all style properties for the receiver */ @property (nonatomic, strong, readonly) NSArray *styleProperties; /** * style selector related to this node */ @property (nonatomic, strong) CASStyleSelector *styleSelector; /** * device selector related to this node */ @property (nonatomic, strong) CASDeviceSelector *deviceSelector; /** * Add a style property to the receiver */ - (void)addStyleProperty:(CASStyleProperty *)styleProperty; @end ================================================ FILE: Classy/Parser/CASStyleNode.m ================================================ // // CASStyleGroup.m // Classy // // Created by Jonas Budelmann on 25/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASStyleNode.h" @implementation CASStyleNode { NSMutableArray *_styleProperties; } @synthesize styleProperties = _styleProperties; - (id)init { self = [super init]; if (!self) return nil; _styleProperties = NSMutableArray.new; return self; } - (void)addStyleProperty:(CASStyleProperty *)styleProperty { [_styleProperties addObject:styleProperty]; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { self.invocations = nil; _styleProperties = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(styleProperties))]; self.styleSelector = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(styleSelector))]; self.deviceSelector = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(deviceSelector))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.styleProperties forKey:NSStringFromSelector(@selector(styleProperties))]; [aCoder encodeObject:self.styleSelector forKey:NSStringFromSelector(@selector(styleSelector))]; [aCoder encodeObject:self.deviceSelector forKey:NSStringFromSelector(@selector(deviceSelector))]; } @end ================================================ FILE: Classy/Parser/CASStyleProperty.h ================================================ // // MODStyleProperty.h // Mod // // Created by Jonas Budelmann on 25/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import #import "CASToken.h" @interface CASStyleProperty : NSObject /** * Name of the receiver */ @property (nonatomic, strong, readonly) NSString *name; /** * Raw values of the receiver */ @property (nonatomic, strong, readonly) NSArray *values; /** * Name token of the receiver */ @property (nonatomic, strong, readonly) CASToken *nameToken; /** * value tokens of the receiver */ @property (nonatomic, strong, readonly) NSArray *valueTokens; /** * child style properties */ @property (nonatomic, strong, readonly) NSArray *childStyleProperties; /** * Provides support for properties that have extra arguments such as * - setTitle:forState: */ @property (nonatomic, strong) NSDictionary *arguments; /** * Creates property with raw data in the form of CASTokens */ - (id)initWithNameToken:(CASToken *)nameToken valueTokens:(NSArray *)valueTokens; /** * Returns first valueToken of a specific token type * * @param tokenType `CASTokenType` sought * * @return a `CASToken` */ - (id)valueOfTokenType:(CASTokenType)tokenType; /** * Returns all consecutive valueTokens of a specific token type * Will ignore whitespace and commas * * @param tokenType `CASTokenType` sought * * @return a `CASToken` */ - (NSArray *)consecutiveValuesOfTokenType:(CASTokenType)tokenType; /** * Attempts to extract a CGSize from the valueTokens * * @param size CGSize pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToCGSize:(CGSize *)size; /** * Attempts to extract a CGPoint from the valueTokens * * @param point CGPoint pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToCGPoint:(CGPoint *)point; /** * Attempts to extract a CGRect from the valueTokens * * @param rect CGRect pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToCGRect:(CGRect *)rect; /** * Attempts to extract a UIOffset from the valueTokens * * @param offset UIOffset pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToUIOffset:(UIOffset *)offset; /** * Attempts to extract a UIEdgeInsets from the valueTokens * * @param insets UIEdgeInsets pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToUIEdgeInsets:(UIEdgeInsets *)insets; /** * Attempts to extract a UIColor from the valueTokens * * @param color UIColor pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToUIColor:(UIColor **)color; /** * Attempts to extract a UIImage from the valueTokens * * @param image UIImage pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToUIImage:(UIImage **)image; /** * Attempts to extract a NSString from the valueTokens * * @param string NSString pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToNSString:(NSString **)string; /** * Attempts to extract a UIFont from the valueTokens * * @param font UIFont pointer * * @return whether the extraction succeeded */ - (BOOL)transformValuesToUIFont:(UIFont **)font; /** * Replace any detected expressions/equations with a numerical value */ - (void)resolveExpressions; /** * adds nested CASStyleProperty to the receiver */ - (void)addChildStyleProperty:(CASStyleProperty *)styleProperty; @end ================================================ FILE: Classy/Parser/CASStyleProperty.m ================================================ // // CASStyleProperty.m // Classy // // Created by Jonas Budelmann on 25/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASStyleProperty.h" #import "NSString+CASAdditions.h" #import "CASExpressionSolver.h" @interface CASStyleProperty () @property (nonatomic, strong, readwrite) NSString *name; @property (nonatomic, strong, readwrite) NSArray *values; @property (nonatomic, strong, readwrite) CASToken *nameToken; @property (nonatomic, strong, readwrite) NSArray *valueTokens; @end @implementation CASStyleProperty { NSMutableArray *_childStyleProperties; } @synthesize childStyleProperties = _childStyleProperties; - (id)initWithNameToken:(CASToken *)nameToken valueTokens:(NSArray *)valueTokens { self = [super init]; if (!self) return nil; self.nameToken = nameToken; self.valueTokens = valueTokens; return self; } #pragma mark - properties - (NSString *)name { if (!_name) { _name = [self.nameToken.value cas_stringByCamelCasing]; } return _name; } - (NSArray *)values { if (!_values) { NSMutableArray *values = NSMutableArray.new; for (CASToken *valueToken in self.valueTokens) { if (valueToken.value) { [values addObject:valueToken.value]; } } _values = values; } return _values; } #pragma mark - helpers - (id)valueOfTokenType:(CASTokenType)tokenType { for (CASToken *token in self.valueTokens) { if (token.type == tokenType) return token.value; } return nil; } - (NSArray *)consecutiveValuesOfTokenType:(CASTokenType)tokenType { NSMutableArray *tokens = NSMutableArray.new; for (CASToken *token in self.valueTokens) { if (token.type == tokenType) { [tokens addObject:token.value]; } else if (tokens.count && !token.isWhitespace && ![token valueIsEqualTo:@","]) { return tokens; } } return tokens; } #pragma - value transformation - (BOOL)transformValuesToCGSize:(CGSize *)size { NSArray *unitTokens = [self consecutiveValuesOfTokenType:CASTokenTypeUnit]; if (unitTokens.count == 1) { CGFloat value = [unitTokens[0] doubleValue]; *size = CGSizeMake(value, value); return YES; } if (unitTokens.count == 2) { CGFloat value1 = [unitTokens[0] doubleValue]; CGFloat value2 = [unitTokens[1] doubleValue]; *size = CGSizeMake(value1, value2); return YES; } return NO; } - (BOOL)transformValuesToCGPoint:(CGPoint *)point { NSArray *unitTokens = [self consecutiveValuesOfTokenType:CASTokenTypeUnit]; if (unitTokens.count == 1) { CGFloat value = [unitTokens[0] doubleValue]; *point = CGPointMake(value, value); return YES; } if (unitTokens.count == 2) { CGFloat value1 = [unitTokens[0] doubleValue]; CGFloat value2 = [unitTokens[1] doubleValue]; *point = CGPointMake(value1, value2); return YES; } return NO; } - (BOOL)transformValuesToCGRect:(CGRect *)rect { NSArray *unitTokens = [self consecutiveValuesOfTokenType:CASTokenTypeUnit]; if (unitTokens.count == 4) { *rect = CGRectMake([unitTokens[0] doubleValue], [unitTokens[1] doubleValue], [unitTokens[2] doubleValue], [unitTokens[3] doubleValue]); return YES; } return NO; } - (BOOL)transformValuesToUIEdgeInsets:(UIEdgeInsets *)insets { NSArray *unitTokens = [self consecutiveValuesOfTokenType:CASTokenTypeUnit]; if (unitTokens.count == 1) { CGFloat value = [unitTokens[0] doubleValue]; *insets = UIEdgeInsetsMake(value, value, value, value); return YES; } if (unitTokens.count == 2) { CGFloat value1 = [unitTokens[0] doubleValue]; CGFloat value2 = [unitTokens[1] doubleValue]; *insets = UIEdgeInsetsMake(value1, value2, value1, value2); return YES; } if (unitTokens.count == 4) { *insets = UIEdgeInsetsMake([unitTokens[0] doubleValue], [unitTokens[1] doubleValue], [unitTokens[2] doubleValue], [unitTokens[3] doubleValue]); return YES; } return NO; } - (BOOL)transformValuesToUIOffset:(UIOffset *)offset { NSArray *unitTokens = [self consecutiveValuesOfTokenType:CASTokenTypeUnit]; if (unitTokens.count == 1) { CGFloat value = [unitTokens[0] doubleValue]; *offset = UIOffsetMake(value, value); return YES; } if (unitTokens.count == 2) { CGFloat value1 = [unitTokens[0] doubleValue]; CGFloat value2 = [unitTokens[1] doubleValue]; *offset = UIOffsetMake(value1, value2); return YES; } return NO; } - (BOOL)transformValuesToUIColor:(UIColor **)color { UIColor *colorValue = [self valueOfTokenType:CASTokenTypeColor]; if (colorValue) { *color = colorValue; return YES; } NSString *value = [self valueOfTokenType:CASTokenTypeRef] ?: [self valueOfTokenType:CASTokenTypeSelector] ?: [self valueOfTokenType:CASTokenTypeString]; if ([value isEqualToString:@"rgb"] || [value isEqualToString:@"rgba"] || [value isEqualToString:@"hsl"] || [value isEqualToString:@"hsla"]) { NSArray *unitTokens = [self consecutiveValuesOfTokenType:CASTokenTypeUnit]; CGFloat alpha = 1.0; // invalid if you don't have 3 colors if(unitTokens.count < 3) { return NO; } else if(unitTokens.count == 4){ alpha = [unitTokens[3] doubleValue]; } if( [value isEqualToString:@"rgb"] || [value isEqualToString:@"rgba"] ) { *color = [UIColor colorWithRed:[unitTokens[0] doubleValue]/255.0 green:[unitTokens[1] doubleValue]/255.0 blue:[unitTokens[2] doubleValue]/255.0 alpha:alpha]; } else if ( [value isEqualToString:@"hsl"] || [value isEqualToString:@"hsla"] ) { *color = [UIColor colorWithHue:[unitTokens[0] doubleValue]/360.0 saturation:[unitTokens[1] doubleValue]/100.0 brightness:[unitTokens[2] doubleValue]/100.0 alpha:alpha]; } return YES; } value = [value cas_stringByCamelCasing]; SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@Color", value]); if (selector && [UIColor.class respondsToSelector:selector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" *color = [UIColor.class performSelector:selector]; #pragma clang diagnostic pop return YES; } return NO; } - (BOOL)transformValuesToNSString:(NSString **)string { NSString *value = [self valueOfTokenType:CASTokenTypeString] ?: [self valueOfTokenType:CASTokenTypeRef] ?: [self valueOfTokenType:CASTokenTypeSelector]; if (value) { *string = value; return YES; } return NO; } - (BOOL)transformValuesToUIImage:(UIImage **)image { UIEdgeInsets insets; BOOL hasInsets = [self transformValuesToUIEdgeInsets:&insets]; NSString *imageName = [self valueOfTokenType:CASTokenTypeString] ?: [self valueOfTokenType:CASTokenTypeRef]; UIImage *imageValue = nil; NSRange schemeRange = [imageName rangeOfString:@"://"]; if(schemeRange.location != NSNotFound) { // We are a file path instead NSString *scheme = [imageName substringToIndex:schemeRange.location]; NSString *path = [imageName substringFromIndex:NSMaxRange(schemeRange)]; // Checking if we're fetching from one of our built in // document uris NSSearchPathDirectory searchMask = 0; if([scheme isEqualToString:@"caches"]) { searchMask = NSCachesDirectory; } else if([scheme isEqualToString:@"documents"]) { searchMask = NSDocumentDirectory; } else if([scheme isEqualToString:@"appsupport"]) { searchMask = NSApplicationSupportDirectory; } if(searchMask != 0) { // If we found a search mask, then use that NSArray *paths = NSSearchPathForDirectoriesInDomains(searchMask, NSUserDomainMask, YES); NSString *imagePath = [paths firstObject]; imageValue = [UIImage imageWithContentsOfFile:[imagePath stringByAppendingPathComponent:path]]; } else { // Otherwise load from imageNamed as per norm imageValue = [UIImage imageNamed:path]; } } else { // We're just an old boring image name imageValue = [UIImage imageNamed:imageName]; } if (hasInsets) { imageValue = [imageValue resizableImageWithCapInsets:insets]; } if (imageValue) { *image = imageValue; return YES; } return NO; } - (BOOL)transformValuesToUIFont:(UIFont **)font { NSNumber *fontSize = [self valueOfTokenType:CASTokenTypeUnit]; NSString *fontName = [self valueOfTokenType:CASTokenTypeString] ?: [self valueOfTokenType:CASTokenTypeRef] ?: [self valueOfTokenType:CASTokenTypeSelector]; if (!fontSize && !fontName.length) { return NO; } static NSDictionary *textStyleLookupMap = nil; if (!textStyleLookupMap) { // Classy is available also on iOS6, so instead of using UIKit consts for text styles that are available // only on iOS7+ let the strings be hardcoded. This avoids the need for weak-linking UIKit. textStyleLookupMap = @{ @"body" : @"UICTFontTextStyleBody", @"caption1" : @"UICTFontTextStyleCaption1", @"caption2" : @"UICTFontTextStyleCaption2", @"footnote" : @"UICTFontTextStyleFootnote", @"headline" : @"UICTFontTextStyleHeadline", @"subheadline" : @"UICTFontTextStyleSubhead", }; } NSString *textStyle = textStyleLookupMap[fontName]; if (textStyle && !fontSize) { #pragma clang diagnostic push #pragma ide diagnostic ignored "UnavailableInDeploymentTarget" if ([UIFont respondsToSelector:@selector(preferredFontForTextStyle:)]) { *font = [UIFont preferredFontForTextStyle:textStyle]; } else { return NO; } #pragma clang diagnostic pop } else { CGFloat fontSizeValue = [fontSize floatValue] ?: [UIFont systemFontSize]; if (fontName) { if ([fontName hasPrefix:@"System"]) { NSString *weightString = @"Regular"; NSArray *nameComponents = [fontName componentsSeparatedByString:@"-"]; if (nameComponents.count == 2) { weightString = nameComponents[1]; } if ([weightString isEqualToString:@"Regular"]) { *font = [UIFont systemFontOfSize:fontSizeValue]; } else { static NSDictionary *weightNameToFloatMapping = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ weightNameToFloatMapping = @{@"black": @(UIFontWeightBlack), @"heavy": @(UIFontWeightHeavy), @"bold": @(UIFontWeightBold), @"semibold": @(UIFontWeightSemibold), @"medium": @(UIFontWeightMedium), @"regular": @(UIFontWeightRegular), @"thin": @(UIFontWeightThin), @"light": @(UIFontWeightLight), @"ultralight": @(UIFontWeightUltraLight)}; }); UIFont *systemFont = nil; UIFontDescriptor *descriptor = nil; if ([[UIFont class] respondsToSelector:@selector(systemFontOfSize:weight:)]) { CGFloat weight = UIFontWeightRegular; NSNumber *weightNumber = weightNameToFloatMapping[[weightString lowercaseString]]; if (weightNumber != nil) { weight = [weightNumber floatValue]; } systemFont = [UIFont systemFontOfSize:fontSizeValue weight:weight]; descriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: systemFont.fontName}]; } else { systemFont = [UIFont systemFontOfSize:fontSizeValue]; descriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:@{UIFontDescriptorFaceAttribute: weightString, UIFontDescriptorFamilyAttribute: systemFont.familyName}]; } *font = [UIFont fontWithDescriptor:descriptor size:fontSizeValue]; } } else { *font = [UIFont fontWithName:fontName size:fontSizeValue]; } } else { *font = [UIFont systemFontOfSize:fontSizeValue]; } } return YES; } - (void)resolveExpressions { BOOL hasOperator = NO; for (CASToken *token in self.valueTokens) { if (token.type == CASTokenTypeOperator && ![token valueIsEqualTo:@","]) { hasOperator = YES; break; } } if (!hasOperator) return; CASExpressionSolver *solver = CASExpressionSolver.new; self.valueTokens = [solver tokensByReducingTokens:self.valueTokens]; self.values = nil; } - (void)addChildStyleProperty:(CASStyleProperty *)styleProperty { if (!_childStyleProperties) { _childStyleProperties = NSMutableArray.new; } [_childStyleProperties addObject:styleProperty]; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { self.name = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(name))]; self.values = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(values))]; self.nameToken = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(nameToken))]; self.valueTokens = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(valueTokens))]; _childStyleProperties = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(childStyleProperties))]; self.arguments = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(arguments))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:NSStringFromSelector(@selector(name))]; [aCoder encodeObject:self.values forKey:NSStringFromSelector(@selector(values))]; [aCoder encodeObject:self.nameToken forKey:NSStringFromSelector(@selector(nameToken))]; [aCoder encodeObject:self.valueTokens forKey:NSStringFromSelector(@selector(valueTokens))]; [aCoder encodeObject:_childStyleProperties forKey:NSStringFromSelector(@selector(childStyleProperties))]; [aCoder encodeObject:self.arguments forKey:NSStringFromSelector(@selector(arguments))]; } @end ================================================ FILE: Classy/Parser/CASStyleSelector.h ================================================ // // CASStyleSelector.h // Classy // // Created by Jonas Budelmann on 29/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASStyleableItem.h" @interface CASStyleSelector : NSObject /** * Class of View to match */ @property (nonatomic, strong) Class objectClass; /** * If not nil checks the view's cas_styleClass property */ @property (nonatomic, copy) NSString *styleClass; /** * Whether or not to do strict matching against objectClass */ @property (nonatomic, assign) BOOL shouldSelectSubclasses; /** * Whether or not parent selector can be an indirect superview */ @property (nonatomic, assign) BOOL shouldSelectIndirectSuperview; /** * Whether or not this selector is a parent */ @property (nonatomic, assign, getter = isParent) BOOL parent; /** * Whether or not this selector should be concatenated */ @property (nonatomic, assign) BOOL shouldConcatToParent; /** * Provides support for properties that have extra arguments such as * - setTitle:forState: */ @property (nonatomic, strong) NSDictionary *arguments; /** * Parent selector */ @property (nonatomic, strong) CASStyleSelector *parentSelector; /** * Last selector in heirachy */ @property (nonatomic, readonly) CASStyleSelector *lastSelector; /** * Returns a integer representation of how specific this selector is. * Provides a way to order selectors. * * The Rules * * ObjectClass matches * +2 ancestor * +3 superview * +4 view * * if loose match (shouldSelectSubclasses) * -1 * * StyleClass matches * +1000 ancestor * +2000 superview * +3000 view * * @return Precendence score */ - (NSInteger)precedence; /** * Whether is selector matches the given item * * @param item `CASStyleableItem` or a subclass * * @return `YES` if all selectors including parent selectors match the item */ - (BOOL)shouldSelectItem:(id)item; /** * String representation of receiver * * @return a `NSString` value */ - (NSString *)stringValue; @end ================================================ FILE: Classy/Parser/CASStyleSelector.m ================================================ // // CASStyleSelector.m // Classy // // Created by Jonas Budelmann on 29/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASStyleSelector.h" #import "UIView+CASAdditions.h" #import "NSString+CASAdditions.h" @implementation CASStyleSelector - (id)init { self = [super init]; if (!self) return nil; self.shouldSelectSubclasses = NO; self.shouldSelectIndirectSuperview = YES; return self; } - (id)copyWithZone:(NSZone *)zone { CASStyleSelector *newSelector = [[self.class allocWithZone:zone] init]; if (!newSelector) return nil; newSelector.objectClass = self.objectClass; newSelector.styleClass = self.styleClass; newSelector.shouldSelectSubclasses = self.shouldSelectSubclasses; newSelector.shouldSelectIndirectSuperview = self.shouldSelectIndirectSuperview; newSelector.shouldConcatToParent = self.shouldConcatToParent; newSelector.arguments = [self.arguments copy]; newSelector.parentSelector = [self.parentSelector copy]; return newSelector; } #pragma mark - properties - (CASStyleSelector *)lastSelector { return self.parentSelector.lastSelector ?: self; } #pragma mark - public - (NSInteger)precedence { NSInteger precedence = 0; if (self.objectClass) { if (self.isParent) { precedence += self.shouldSelectIndirectSuperview ? 2 : 3; } else { precedence += 4; } if (self.shouldSelectSubclasses) { precedence -= 1; } } if (self.styleClass) { if (self.isParent) { precedence += self.shouldSelectIndirectSuperview ? 1000 : 2000; } else { precedence += 3000; } } precedence += self.parentSelector.precedence; return precedence; } - (BOOL)shouldSelectItem:(id)item { if (![self matchesItem:item]) { return NO; } if (self.parentSelector) { return [self.parentSelector matchesAncestorsOfItem:item traverse:self.shouldSelectIndirectSuperview]; } return YES; } - (NSString *)stringValue { NSMutableString *stringValue = NSMutableString.new; if (self.parentSelector) { [stringValue appendFormat:self.shouldSelectIndirectSuperview ? @"%@ " : @"%@ > ", [self.parentSelector stringValue]]; } if (self.shouldSelectSubclasses) { [stringValue appendString:@"^"]; } if (self.objectClass) { [stringValue appendString:NSStringFromClass(self.objectClass)]; } if (self.arguments) { [stringValue appendString:@"["]; NSArray *keys = [self.arguments.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; [keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) { [stringValue appendFormat:@"%@:%@", key, self.arguments[key]]; if (idx != keys.count - 1) { [stringValue appendString:@", "]; } }]; [stringValue appendString:@"]"]; } if (self.styleClass) { [stringValue appendFormat:@".%@", self.styleClass]; } return stringValue; } #pragma mark - private - (BOOL)matchesAncestorsOfItem:(id)item traverse:(BOOL)traverse { id currentItem = item; while (currentItem.cas_parent != nil || currentItem.cas_alternativeParent != nil) { id ancestor; if ([self matchesItem:currentItem.cas_parent]) { ancestor = currentItem.cas_parent; } else if ([self matchesItem:currentItem.cas_alternativeParent]) { ancestor = currentItem.cas_alternativeParent; } if (ancestor) { if (!self.parentSelector) return YES; BOOL traverse = self.shouldSelectIndirectSuperview; if ([self.parentSelector matchesAncestorsOfItem:ancestor traverse:traverse]) return YES; } if (!traverse) return NO; currentItem = currentItem.cas_parent; } return NO; } - (BOOL)matchesItem:(id)item { if (self.objectClass) { if (self.shouldSelectSubclasses) { if (![item isKindOfClass:self.objectClass]) return NO; } else { if (![item isMemberOfClass:self.objectClass]) return NO; } } if (self.styleClass.length && ![item cas_hasStyleClass:self.styleClass]) { return NO; } return YES; } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (nil != self) { self.objectClass = NSClassFromString([aDecoder decodeObjectForKey:NSStringFromSelector(@selector(objectClass))]); self.styleClass = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(styleClass))]; self.shouldSelectSubclasses = [aDecoder decodeBoolForKey:NSStringFromSelector(@selector(shouldSelectSubclasses))]; self.shouldSelectIndirectSuperview = [aDecoder decodeBoolForKey:NSStringFromSelector(@selector(shouldSelectIndirectSuperview))]; self.parent = [aDecoder decodeBoolForKey:NSStringFromSelector(@selector(isParent))]; self.shouldConcatToParent = [aDecoder decodeBoolForKey:NSStringFromSelector(@selector(shouldConcatToParent))]; self.arguments = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(arguments))]; self.parentSelector = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(parentSelector))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:NSStringFromClass(self.objectClass) forKey:NSStringFromSelector(@selector(objectClass))]; [aCoder encodeObject:self.styleClass forKey:NSStringFromSelector(@selector(styleClass))]; [aCoder encodeBool:self.shouldSelectSubclasses forKey:NSStringFromSelector(@selector(shouldSelectSubclasses))]; [aCoder encodeBool:self.shouldSelectIndirectSuperview forKey:NSStringFromSelector(@selector(shouldSelectIndirectSuperview))]; [aCoder encodeBool:self.parent forKey:NSStringFromSelector(@selector(isParent))]; [aCoder encodeBool:self.shouldConcatToParent forKey:NSStringFromSelector(@selector(shouldConcatToParent))]; [aCoder encodeObject:self.arguments forKey:NSStringFromSelector(@selector(arguments))]; [aCoder encodeObject:self.parentSelector forKey:NSStringFromSelector(@selector(parentSelector))]; } @end ================================================ FILE: Classy/Parser/CASStyler.h ================================================ // // CASStyler.h // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASObjectClassDescriptor.h" #import "CASStyleableItem.h" @class CASStyleNode; /** * Protocol caching the @c CASStyleNode's. Adds the possibility to cache the in-memory representation of Classy's CAS * file, since parsing the file is slower than loading cached version. */ @protocol CASCacheProtocol @optional /** * Loads cached version of stylesheet located at @c path with @c variables */ - (NSArray *)cachedStyleNodesFromCASPath:(NSString *)path withVariables:(NSDictionary *)variables; /** * Stores the stylesheet in-memory representation loaded from @c path with @c variables */ - (void)cacheStyleNodes:(NSArray *)styleNodes fromPath:(NSString *)path variables:(NSDictionary *)variables; @end @interface CASStyler : NSObject + (void)bootstrapClassyWithTargetWindows:(NSArray *)targetWindows; /** * Singleton instance */ + (instancetype)defaultStyler; @property (nonatomic, weak) id cache; @property (nonatomic, copy) NSDictionary *variables; /** * File path which contains style data */ @property (nonatomic, copy) NSString *filePath; /** * File path to watch for changes. * Only use for debugging on simulator */ @property (nonatomic, copy) NSString *watchFilePath; /** * Windows to update views. * Needed for live updates when UIApplication is not available (e.g. in Application Extensions) */ @property (nonatomic, strong) NSArray *targetWindows; /** * Set file path location of styling data and report any errors * * @param filePath The location of the style data * @param error The error that occurred while parsing the filePath */ - (void)setFilePath:(NSString *)filePath error:(NSError **)error; /** * Apply any applicable styles to a CASStyleableItem instance, from low to high precendence * * @param item `CASStyleableItem` to apply styles to */ - (void)styleItem:(id)item; /** * Returns a cached CASObjectClassDescriptor if it exists or creates one */ - (CASObjectClassDescriptor *)objectClassDescriptorForClass:(Class)aClass; /** * Schedule update for styleable item. * This ensures we only update an item once per run loop * * @param item CASStyleableItem to coalesce update calls */ - (void)scheduleUpdateForItem:(id)item; /** * Unschedule update for styleable item * * @param item CASStyleableItem that no longer needs updating */ - (void)unscheduleUpdateForItem:(id)item; @end ================================================ FILE: Classy/Parser/CASStyler.m ================================================ // // CASStyler.m // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASStyler.h" #import "CASParser.h" #import "CASPropertyDescriptor.h" #import "UIView+CASAdditions.h" #import "UITextField+CASAdditions.h" #import "CASUtilities.h" #import "CASStyleNode.h" #import "NSString+CASAdditions.h" #import "CASTextAttributes.h" #import "CASInvocation.h" #import "UIBarItem+CASAdditions.h" #import "UINavigationItem+CASAdditions.h" #import "UITextField+CASAdditions.h" #import "UIView+CASAdditions.h" #import "UIViewController+CASAdditions.h" #import // http://www.cocoawithlove.com/2010/01/getting-subclasses-of-objective-c-class.html NSArray *ClassGetSubclasses(Class parentClass) { int numClasses = objc_getClassList(NULL, 0); Class *classes = NULL; classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses); numClasses = objc_getClassList(classes, numClasses); NSMutableArray *result = [NSMutableArray array]; for (NSInteger i = 0; i < numClasses; i++) { Class superClass = classes[i]; do { superClass = class_getSuperclass(superClass); } while(superClass && superClass != parentClass); if (superClass == nil) { continue; } [result addObject:classes[i]]; } free(classes); return result; } @interface CASStyler () @property (nonatomic, strong) NSMutableArray *styleNodes; @property (nonatomic, strong) NSMapTable *objectClassDescriptorCache; @property (nonatomic, strong) NSHashTable *scheduledItems; @property (nonatomic, strong) NSTimer *updateTimer; @property (nonatomic, strong) NSMutableArray *fileWatchers; @property (nonatomic, strong) NSMutableArray *invocationObjectArguments; @property (nonatomic, strong) NSMutableDictionary *styleClassIndex; @property (nonatomic, strong) NSMutableDictionary *objectClassIndex; @end @implementation CASStyler + (instancetype)defaultStyler { static CASStyler * _defaultStyler = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _defaultStyler = CASStyler.new; }); return _defaultStyler; } + (void)bootstrapClassyWithTargetWindows:(NSArray *)targetWindows { [UIBarItem bootstrapClassy]; [UINavigationItem bootstrapClassy]; [UITextField bootstrapClassy]; [UIView bootstrapClassy]; [UIViewController bootstrapClassy]; [[self defaultStyler] setTargetWindows:targetWindows]; } - (id)init { self = [super init]; if (!self) return nil; self.objectClassDescriptorCache = NSMapTable.strongToStrongObjectsMapTable; self.scheduledItems = [NSHashTable hashTableWithOptions:NSHashTableWeakMemory]; self.fileWatchers = NSMutableArray.new; self.styleClassIndex = [NSMutableDictionary new]; self.objectClassIndex = [NSMutableDictionary new]; [self setupObjectClassDescriptors]; return self; } - (void)styleItem:(id)item { if (!self.filePath) { // load default style file self.filePath = [[NSBundle mainBundle] pathForResource:@"stylesheet.cas" ofType:nil]; } NSMutableArray *possibleStyleNodes = [NSMutableArray new]; Class class = [item class]; [possibleStyleNodes addObjectsFromArray:[self.objectClassIndex valueForKey:NSStringFromClass(class)]]; NSArray *styleClasses = [item.cas_styleClass componentsSeparatedByString:CASStyleClassSeparator]; for (NSString *styleClass in styleClasses) { [possibleStyleNodes addObjectsFromArray:[self.styleClassIndex valueForKey:styleClass]]; } for (CASStyleNode *styleNode in possibleStyleNodes) { if ([styleNode.styleSelector shouldSelectItem:item]) { // apply style nodes for (CASInvocation *invocation in styleNode.invocations) { [invocation invokeWithTarget:item]; } } } } - (void)setVariables:(NSDictionary *)variables { _variables = variables; if (!self.filePath) return; // if stylesheet has already been loaded. reload stylesheet NSString *filePath = _filePath; _filePath = nil; self.filePath = filePath; // reapply styles for (UIWindow *window in self.targetWindows) { [self styleSubviewsOfView:window]; } } - (void)setFilePath:(NSString *)filePath { NSError *error = nil; [self setFilePath:filePath error:&error]; if (error) { CASLog(@"Error: %@", error); } } - (void)setFilePath:(NSString *)filePath error:(NSError **)error { if ([_filePath isEqualToString:filePath]) return; _filePath = filePath; self.styleClassIndex = [NSMutableDictionary new]; self.objectClassIndex = [NSMutableDictionary new]; NSArray *styleNodes = nil; NSSet *importedFileNames = nil; if ([self.cache respondsToSelector:@selector(cachedStyleNodesFromCASPath:withVariables:)]) { styleNodes = [self.cache cachedStyleNodesFromCASPath:filePath withVariables:self.variables]; } if (styleNodes != nil) { importedFileNames = [NSSet set]; self.styleNodes = [NSMutableArray arrayWithArray:styleNodes]; } else { CASParser *parser = [CASParser parserFromFilePath:filePath variables:self.variables error:error]; styleNodes = parser.styleNodes; importedFileNames = parser.importedFileNames; self.styleNodes = [self validNodes:styleNodes]; // order ascending by precedence [self.styleNodes sortWithOptions:NSSortStable usingComparator:^NSComparisonResult(CASStyleNode *n1, CASStyleNode *n2) { NSInteger precedence1 = [n1.styleSelector precedence]; NSInteger precedence2 = [n2.styleSelector precedence]; if (precedence2 > precedence1) { return NSOrderedAscending; } else if (precedence2 < precedence1) { return NSOrderedDescending; } return NSOrderedSame; }]; if ([self.cache respondsToSelector:@selector(cacheStyleNodes:fromPath:variables:)]) { [self.cache cacheStyleNodes:styleNodes fromPath:filePath variables:self.variables]; } } if (self.watchFilePath) { for (dispatch_source_t source in self.fileWatchers) { dispatch_source_cancel(source); } [self.fileWatchers removeAllObjects]; [self reloadOnChangesToFilePath:self.watchFilePath]; NSString *directoryPath = [self.watchFilePath stringByDeletingLastPathComponent]; for (NSString *fileName in importedFileNames) { NSString *resolvedPath = [directoryPath stringByAppendingPathComponent:fileName]; [self reloadOnChangesToFilePath:resolvedPath]; } } if (!styleNodes.count) { return; } [self populateStyleLookupTables:self.styleNodes]; self.invocationObjectArguments = NSMutableArray.new; // precompute values for (CASStyleNode *styleNode in self.styleNodes) { NSMutableArray *invocations = NSMutableArray.new; for (CASStyleProperty *styleProperty in styleNode.styleProperties) { // TODO type checking and throw errors NSArray *propertyInvocations = [self invocationsForClass:styleNode.styleSelector.objectClass styleProperty:styleProperty keyPath:nil]; [invocations addObjectsFromArray:propertyInvocations]; } styleNode.invocations = invocations; } } #pragma mark - private - (NSMutableArray *)validNodes:(NSArray *)styleNodes { // filter redundant nodes NSMutableArray *filteredNodes = NSMutableArray.new; for (CASStyleNode *styleNode in styleNodes) { // invalid if does not have any properties BOOL invalid = !styleNode.styleProperties.count; // invalid if has deviceSelector and deviceSelector is not valid invalid = invalid || (styleNode.deviceSelector && !styleNode.deviceSelector.isValid); if (!invalid) { [filteredNodes addObject:styleNode]; } } return filteredNodes; } - (void)populateStyleLookupTables:(NSArray *)styleNodes { for (CASStyleNode *styleNode in styleNodes) { if (styleNode.styleSelector.styleClass) { NSString *styleClassKey = styleNode.styleSelector.styleClass; if (![self.styleClassIndex valueForKey:styleClassKey]) { [self.styleClassIndex setValue:[@[] mutableCopy] forKey:styleClassKey]; } [[self.styleClassIndex valueForKey:styleClassKey] addObject:styleNode]; } else { Class class = styleNode.styleSelector.objectClass; if (![self.objectClassIndex valueForKey:NSStringFromClass(class)]) { [self.objectClassIndex setValue:[@[] mutableCopy] forKey:NSStringFromClass(class)]; } [[self.objectClassIndex valueForKey:NSStringFromClass(class)] addObject:styleNode]; if (styleNode.styleSelector.shouldSelectSubclasses) { // add an entry in the lookup table for each subclass NSArray *subclasses = ClassGetSubclasses(class); for (Class class in subclasses) { if (![self.objectClassIndex valueForKey:NSStringFromClass(class)]) { [self.objectClassIndex setValue:[@[] mutableCopy] forKey:NSStringFromClass(class)]; } [[self.objectClassIndex valueForKey:NSStringFromClass(class)] addObject:styleNode]; } } } } } - (NSArray *)invocationsForClass:(Class)aClass styleProperty:(CASStyleProperty *)styleProperty keyPath:(NSString *)keypath { CASObjectClassDescriptor *objectClassDescriptor = [self objectClassDescriptorForClass:aClass]; CASPropertyDescriptor *propertyDescriptor = [objectClassDescriptor propertyDescriptorForKey:styleProperty.name]; //Special case textAttributes BOOL isTextAttributesProperty = styleProperty.childStyleProperties.count && [[styleProperty.name lowercaseString] hasSuffix:@"textattributes"]; NSInvocation *invocation; CASInvocation *invocationWrapper;; NSMutableArray *invocations = NSMutableArray.new; if (isTextAttributesProperty || !styleProperty.childStyleProperties.count) { invocation = [objectClassDescriptor invocationForPropertyDescriptor:propertyDescriptor]; invocationWrapper = [[CASInvocation alloc] initWithInvocation:invocation forKeyPath:keypath]; [invocations addObject:invocationWrapper]; } [propertyDescriptor.argumentDescriptors enumerateObjectsUsingBlock:^(CASArgumentDescriptor *argDescriptor, NSUInteger idx, BOOL *stop) { NSInteger argIndex = 2 + idx; if (idx > 0) { //arguments after first only supports enums at moment NSString *valueName = [styleProperty.arguments[argDescriptor.name] cas_stringByCamelCasing]; if (valueName.length) { NSInteger value = [argDescriptor.valuesByName[valueName] integerValue]; [invocation setArgument:&value atIndex:argIndex]; } return; } switch (argDescriptor.primitiveType) { case CASPrimitiveTypeBOOL: { id value = [styleProperty valueOfTokenType:CASTokenTypeBoolean] ?: [styleProperty valueOfTokenType:CASTokenTypeUnit]; BOOL boolValue = [value boolValue]; [invocation setArgument:&boolValue atIndex:argIndex]; break; } case CASPrimitiveTypeInteger: { NSInteger value; if (argDescriptor.valuesByName) { NSString *valueName = [[styleProperty valueOfTokenType:CASTokenTypeRef] cas_stringByCamelCasing]; value = [argDescriptor.valuesByName[valueName] integerValue]; } else { value = [[styleProperty valueOfTokenType:CASTokenTypeUnit] integerValue]; } [invocation setArgument:&value atIndex:argIndex]; break; } case CASPrimitiveTypeDouble: { CGFloat value = [[styleProperty valueOfTokenType:CASTokenTypeUnit] doubleValue]; [invocation setArgument:&value atIndex:argIndex]; break; } case CASPrimitiveTypeFloat: { float value = [[styleProperty valueOfTokenType:CASTokenTypeUnit] floatValue]; [invocation setArgument:&value atIndex:argIndex]; break; } case CASPrimitiveTypeCGSize: { CGSize size; [styleProperty transformValuesToCGSize:&size]; [invocation setArgument:&size atIndex:argIndex]; break; } case CASPrimitiveTypeCGRect: { CGRect rect; [styleProperty transformValuesToCGRect:&rect]; [invocation setArgument:&rect atIndex:argIndex]; break; } case CASPrimitiveTypeCGPoint: { CGPoint point; [styleProperty transformValuesToCGPoint:&point]; [invocation setArgument:&point atIndex:argIndex]; break; } case CASPrimitiveTypeUIEdgeInsets: { UIEdgeInsets insets; [styleProperty transformValuesToUIEdgeInsets:&insets]; [invocation setArgument:&insets atIndex:argIndex]; break; } case CASPrimitiveTypeUIOffset : { UIOffset offset; [styleProperty transformValuesToUIOffset:&offset]; [invocation setArgument:&offset atIndex:argIndex]; break; } case CASPrimitiveTypeCGColorRef : { UIColor *color = nil; [styleProperty transformValuesToUIColor:&color]; if (color) { [self.invocationObjectArguments addObject:color]; } CGColorRef colorRef = color.CGColor; [invocation setArgument:&colorRef atIndex:argIndex]; } default: break; } id objectArg = nil; if (argDescriptor.argumentClass == UIImage.class) { [styleProperty transformValuesToUIImage:&objectArg]; } else if (argDescriptor.argumentClass == UIColor.class) { [styleProperty transformValuesToUIColor:&objectArg]; } else if (argDescriptor.argumentClass == NSString.class) { [styleProperty transformValuesToNSString:&objectArg]; } else if (argDescriptor.argumentClass == UIFont.class) { [styleProperty transformValuesToUIFont:&objectArg]; } if (styleProperty.childStyleProperties.count) { id target = nil; Class targetClass = argDescriptor.argumentClass; NSString *childKeyPath = keypath.length ? [NSString stringWithFormat:@"%@.%@", keypath, styleProperty.name] : styleProperty.name; // handle textAttributes as special case BOOL isTextAttributesArg = targetClass == NSDictionary.class && isTextAttributesProperty; if (isTextAttributesArg) { target = CASTextAttributes.new; targetClass = CASTextAttributes.class; childKeyPath = nil; } for (CASStyleProperty *childStyleProperty in styleProperty.childStyleProperties) { NSArray *childInvocations = [self invocationsForClass:targetClass styleProperty:childStyleProperty keyPath:childKeyPath]; if (target) { [childInvocations makeObjectsPerformSelector:@selector(invokeWithTarget:) withObject:target]; } else { [invocations addObjectsFromArray:childInvocations]; } } // if textAttributes set argument to dictionary value if (isTextAttributesArg) { objectArg = [target dictionary]; [invocation setArgument:&objectArg atIndex:argIndex]; } } if (objectArg != nil) { [invocation setArgument:&objectArg atIndex:argIndex]; [self.invocationObjectArguments addObject:objectArg]; } }]; return invocations; } - (void)setupObjectClassDescriptors { // Common ENUM maps NSDictionary *controlStateMap = @{ @"normal" : @(UIControlStateNormal), @"highlighted" : @(UIControlStateHighlighted), @"disabled" : @(UIControlStateDisabled), @"selected" : @(UIControlStateSelected), @"selectedHighlighted" : @(UIControlStateSelected|UIControlStateHighlighted) }; NSDictionary *textAlignmentMap = @{ @"center" : @(NSTextAlignmentCenter), @"left" : @(NSTextAlignmentLeft), @"right" : @(NSTextAlignmentRight), @"justified" : @(NSTextAlignmentJustified), @"natural" : @(NSTextAlignmentNatural), }; NSDictionary *lineBreakModeMap = @{ @"wordWrapping" : @(NSLineBreakByWordWrapping), @"charWrapping" : @(NSLineBreakByCharWrapping), @"clipping" : @(NSLineBreakByClipping), @"truncatingHead" : @(NSLineBreakByTruncatingHead), @"truncatingTail" : @(NSLineBreakByTruncatingTail), @"truncatingMiddle" : @(NSLineBreakByTruncatingMiddle) }; NSDictionary *barMetricsMap = @{ @"default" : @(UIBarMetricsDefault), @"compact" : @(UIBarMetricsCompact), @"defaultPrompt" : @(UIBarMetricsDefaultPrompt), @"compactPrompt" : @(UIBarMetricsCompactPrompt), }; NSDictionary *searchBarIconMap = @{ @"search" : @(UISearchBarIconSearch), @"clear" : @(UISearchBarIconClear), @"bookmark" : @(UISearchBarIconBookmark), @"resultsList" : @(UISearchBarIconResultsList), }; NSDictionary *barPositionMap = @{ @"any" : @(UIBarPositionAny), @"bottom" : @(UIBarPositionBottom), @"top" : @(UIBarPositionTop), @"topAttached" : @(UIBarPositionTopAttached), }; // Common CASArgumentDescriptors CASArgumentDescriptor *colorArg = [CASArgumentDescriptor argWithClass:UIColor.class]; CASArgumentDescriptor *dictionaryArg = [CASArgumentDescriptor argWithClass:NSDictionary.class]; CASArgumentDescriptor *textAlignmentArg = [CASArgumentDescriptor argWithValuesByName:textAlignmentMap]; CASArgumentDescriptor *lineBreakModeArg = [CASArgumentDescriptor argWithValuesByName:lineBreakModeMap]; CASArgumentDescriptor *stateArg = [CASArgumentDescriptor argWithName:@"state" valuesByName:controlStateMap]; CASArgumentDescriptor *imageArg = [CASArgumentDescriptor argWithClass:UIImage.class]; CASArgumentDescriptor *barMetricsArg = [CASArgumentDescriptor argWithName:@"barMetrics" valuesByName:barMetricsMap]; CASArgumentDescriptor *floatArg = [CASArgumentDescriptor argWithObjCType:@encode(CGFloat)]; CASArgumentDescriptor *boolArg = [CASArgumentDescriptor argWithObjCType:@encode(BOOL)]; CASArgumentDescriptor *offsetArg = [CASArgumentDescriptor argWithObjCType:@encode(UIOffset)]; CASArgumentDescriptor *searchIconArg = [CASArgumentDescriptor argWithName:@"icon" valuesByName:searchBarIconMap]; CASArgumentDescriptor *barPositionArg = [CASArgumentDescriptor argWithName:@"barPosition" valuesByName:barPositionMap]; // UIView CASObjectClassDescriptor *objectClassDescriptor = [self objectClassDescriptorForClass:UIView.class]; NSDictionary *contentModeMap = @{ @"fill" : @(UIViewContentModeScaleToFill), @"aspectFit" : @(UIViewContentModeScaleAspectFit), @"aspectFill" : @(UIViewContentModeScaleAspectFill), @"redraw" : @(UIViewContentModeRedraw), @"center" : @(UIViewContentModeCenter), @"top" : @(UIViewContentModeTop), @"bottom" : @(UIViewContentModeBottom), @"left" : @(UIViewContentModeLeft), @"right" : @(UIViewContentModeRight), @"topLeft" : @(UIViewContentModeTopLeft), @"topRight" : @(UIViewContentModeTopRight), @"bottomLeft" : @(UIViewContentModeBottomLeft), @"bottomRight" : @(UIViewContentModeBottomRight), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:contentModeMap]] forPropertyKey:@cas_propertykey(UIView, contentMode)]; // some properties don't show up via reflection so we need to add them manually [objectClassDescriptor setArgumentDescriptors:@[boolArg] forPropertyKey:@cas_propertykey(UIView, clipsToBounds)]; [objectClassDescriptor setArgumentDescriptors:@[colorArg] forPropertyKey:@cas_propertykey(UIView, backgroundColor)]; [objectClassDescriptor setArgumentDescriptors:@[colorArg] forPropertyKey:@cas_propertykey(UIView, tintColor)]; // UIBarItem objectClassDescriptor = [self objectClassDescriptorForClass:UIBarItem.class]; [objectClassDescriptor setArgumentDescriptors:@[dictionaryArg, stateArg] setter:@selector(setTitleTextAttributes:forState:) forPropertyKey:@"titleTextAttributes"]; // UILabel objectClassDescriptor = [self objectClassDescriptorForClass:UILabel.class]; [objectClassDescriptor setArgumentDescriptors:@[lineBreakModeArg] forPropertyKey:@cas_propertykey(UILabel, lineBreakMode)]; [objectClassDescriptor setArgumentDescriptors:@[textAlignmentArg] forPropertyKey:@cas_propertykey(UILabel, textAlignment)]; // UITextField // TODO border insets objectClassDescriptor = [self objectClassDescriptorForClass:UITextField.class]; objectClassDescriptor.propertyKeyAliases = @{ @"backgroundImage" : @cas_propertykey(UITextField, background), @"textInsets" : @cas_propertykey(UITextField, cas_textEdgeInsets), }; [objectClassDescriptor setArgumentDescriptors:@[textAlignmentArg] forPropertyKey:@cas_propertykey(UITextField, textAlignment)]; NSDictionary *borderStyleMap = @{ @"none" : @(UITextBorderStyleNone), @"line" : @(UITextBorderStyleLine), @"bezel" : @(UITextBorderStyleBezel), @"rounded" : @(UITextBorderStyleRoundedRect), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:borderStyleMap]] forPropertyKey:@cas_propertykey(UITextField, borderStyle)]; NSDictionary *textFieldViewModeMap = @{ @"never" : @(UITextFieldViewModeNever), @"whileEditing" : @(UITextFieldViewModeWhileEditing), @"unlessEditing" : @(UITextFieldViewModeUnlessEditing), @"always" : @(UITextFieldViewModeAlways), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:textFieldViewModeMap]] forPropertyKey:@cas_propertykey(UITextField, leftViewMode)]; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:textFieldViewModeMap]] forPropertyKey:@cas_propertykey(UITextField, rightViewMode)]; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:textFieldViewModeMap]] forPropertyKey:@cas_propertykey(UITextField, clearButtonMode)]; // UIControl objectClassDescriptor = [self objectClassDescriptorForClass:UIControl.class]; NSDictionary *contentVerticalAlignmentMap = @{ @"center" : @(UIControlContentVerticalAlignmentCenter), @"top" : @(UIControlContentVerticalAlignmentTop), @"bottom" : @(UIControlContentVerticalAlignmentBottom), @"fill" : @(UIControlContentVerticalAlignmentFill), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:contentVerticalAlignmentMap]] forPropertyKey:@cas_propertykey(UIControl, contentVerticalAlignment)]; NSDictionary *contentHorizontalAlignmentMap = @{ @"center" : @(UIControlContentHorizontalAlignmentCenter), @"left" : @(UIControlContentHorizontalAlignmentLeft), @"right" : @(UIControlContentHorizontalAlignmentRight), @"fill" : @(UIControlContentHorizontalAlignmentFill), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:contentHorizontalAlignmentMap]] forPropertyKey:@cas_propertykey(UIControl, contentHorizontalAlignment)]; // UIButton objectClassDescriptor = [self objectClassDescriptorForClass:UIButton.class]; [objectClassDescriptor setArgumentDescriptors:@[colorArg, stateArg] setter:@selector(setTitleColor:forState:) forPropertyKey:@"titleColor"]; [objectClassDescriptor setArgumentDescriptors:@[colorArg, stateArg] setter:@selector(setTitleShadowColor:forState:) forPropertyKey:@"titleShadowColor"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setBackgroundImage:forState:) forPropertyKey:@"backgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setImage:forState:) forPropertyKey:@"image"]; // UIBarButtonItem objectClassDescriptor = [self objectClassDescriptorForClass:UIBarButtonItem.class]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg, barMetricsArg] setter:@selector(setBackgroundImage:forState:barMetrics:) forPropertyKey:@"backgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[floatArg, barMetricsArg] setter:@selector(setBackgroundVerticalPositionAdjustment:forBarMetrics:) forPropertyKey:@"backgroundVerticalPositionAdjustment"]; [objectClassDescriptor setArgumentDescriptors:@[offsetArg, barMetricsArg] setter:@selector(setTitlePositionAdjustment:forBarMetrics:) forPropertyKey:@"titlePositionAdjustment"]; //backButton [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg, barMetricsArg] setter:@selector(setBackButtonBackgroundImage:forState:barMetrics:) forPropertyKey:@"backButtonBackgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[floatArg, barMetricsArg] setter:@selector(setBackButtonBackgroundVerticalPositionAdjustment:forBarMetrics:) forPropertyKey:@"backButtonBackgroundVerticalPositionAdjustment"]; [objectClassDescriptor setArgumentDescriptors:@[offsetArg, barMetricsArg] setter:@selector(setBackButtonTitlePositionAdjustment:forBarMetrics:) forPropertyKey:@"backButtonTitlePositionAdjustment"]; // UINavigationBar objectClassDescriptor = [self objectClassDescriptorForClass:UINavigationBar.class]; if (CASKeyDeviceSystemMajorVersion() >= 7) { [objectClassDescriptor setArgumentDescriptors:@[imageArg, barPositionArg, barMetricsArg] setter:@selector(setBackgroundImage:forBarPosition:barMetrics:) forPropertyKey:@"backgroundImage"]; } else { [objectClassDescriptor setArgumentDescriptors:@[imageArg, barMetricsArg] setter:@selector(setBackgroundImage:forBarMetrics:) forPropertyKey:@"backgroundImage"]; } [objectClassDescriptor setArgumentDescriptors:@[boolArg] forPropertyKey:@cas_propertykey(UINavigationBar, translucent)]; [objectClassDescriptor setArgumentDescriptors:@[floatArg, barMetricsArg] setter:@selector(setTitleVerticalPositionAdjustment:forBarMetrics:) forPropertyKey:@"titleVerticalPositionAdjustment"]; // UISearchBar objectClassDescriptor = [self objectClassDescriptorForClass:UISearchBar.class]; if (CASKeyDeviceSystemMajorVersion() >= 7) { [objectClassDescriptor setArgumentDescriptors:@[imageArg, barPositionArg, barMetricsArg] setter:@selector(setBackgroundImage:forBarPosition:barMetrics:) forPropertyKey:@"backgroundImage"]; } [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setSearchFieldBackgroundImage:forState:) forPropertyKey:@"searchFieldBackgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, searchIconArg, stateArg] setter:@selector(setImage:forSearchBarIcon:state:) forPropertyKey:@"iconImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setScopeBarButtonBackgroundImage:forState:) forPropertyKey:@"scopeBarButtonBackgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, [CASArgumentDescriptor argWithName:@"leftSegmentState" valuesByName:controlStateMap], [CASArgumentDescriptor argWithName:@"rightSegmentState" valuesByName:controlStateMap]] setter:@selector(setScopeBarButtonDividerImage:forLeftSegmentState:rightSegmentState:) forPropertyKey:@"scopeBarButtonDividerImage"]; [objectClassDescriptor setArgumentDescriptors:@[offsetArg, searchIconArg] setter:@selector(setPositionAdjustment:forSearchBarIcon:) forPropertyKey:@"iconPositionAdjustment"]; [objectClassDescriptor setArgumentDescriptors:@[dictionaryArg, stateArg] setter:@selector(setScopeBarButtonTitleTextAttributes:forState:) forPropertyKey:@"scopeBarButtonTitleTextAttributes"]; // UISegmentedControl objectClassDescriptor = [self objectClassDescriptorForClass:UISegmentedControl.class]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg, barMetricsArg] setter:@selector(setBackgroundImage:forState:barMetrics:) forPropertyKey:@"backgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, [CASArgumentDescriptor argWithName:@"leftSegmentState" valuesByName:controlStateMap], [CASArgumentDescriptor argWithName:@"rightSegmentState" valuesByName:controlStateMap], barMetricsArg] setter:@selector(setDividerImage:forLeftSegmentState:rightSegmentState:barMetrics:) forPropertyKey:@"dividerImage"]; NSDictionary *segmentedControlSegmentMap = @{ @"any" : @(UISegmentedControlSegmentAny), @"left" : @(UISegmentedControlSegmentLeft), @"center" : @(UISegmentedControlSegmentCenter), @"right" : @(UISegmentedControlSegmentRight), @"alone" : @(UISegmentedControlSegmentAlone), }; [objectClassDescriptor setArgumentDescriptors:@[offsetArg, [CASArgumentDescriptor argWithName:@"segmentType" valuesByName:segmentedControlSegmentMap], barMetricsArg] setter:@selector(setContentPositionAdjustment:forSegmentType:barMetrics:) forPropertyKey:@"contentPositionAdjustment"]; [objectClassDescriptor setArgumentDescriptors:@[dictionaryArg, stateArg] setter:@selector(setTitleTextAttributes:forState:) forPropertyKey:@"titleTextAttributes"]; // UIStepper objectClassDescriptor = [self objectClassDescriptorForClass:UIStepper.class]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setBackgroundImage:forState:) forPropertyKey:@"backgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, [CASArgumentDescriptor argWithName:@"leftSegmentState" valuesByName:controlStateMap], [CASArgumentDescriptor argWithName:@"rightSegmentState" valuesByName:controlStateMap]] setter:@selector(setDividerImage:forLeftSegmentState:rightSegmentState:) forPropertyKey:@"dividerImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setDecrementImage:forState:) forPropertyKey:@"decrementImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setIncrementImage:forState:) forPropertyKey:@"incrementImage"]; // UITabBar objectClassDescriptor = [self objectClassDescriptorForClass:UITabBar.class]; if (CASKeyDeviceSystemMajorVersion() >= 7) { NSDictionary *tabBarItemPositioningMap = @{ @"auto" : @(UITabBarItemPositioningAutomatic), @"automatic" : @(UITabBarItemPositioningAutomatic), @"fill" : @(UITabBarItemPositioningFill), @"centered" : @(UITabBarItemPositioningCentered), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:tabBarItemPositioningMap]] forPropertyKey:@cas_propertykey(UITabBar, itemPositioning)]; NSDictionary *barStyleMap = @{ @"default" : @(UIBarStyleDefault), @"black" : @(UIBarStyleBlack), }; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithValuesByName:barStyleMap]] forPropertyKey:@cas_propertykey(UITabBar, barStyle)]; [objectClassDescriptor setArgumentDescriptors:@[boolArg] forPropertyKey:@cas_propertykey(UITabBar, translucent)]; } // UITabBarItem objectClassDescriptor = [self objectClassDescriptorForClass:UITabBarItem.class]; [objectClassDescriptor setArgumentDescriptors:@[[CASArgumentDescriptor argWithObjCType:@encode(UIOffset)]] forPropertyKey:@cas_propertykey(UITabBarItem, titlePositionAdjustment)]; // UIToolBar objectClassDescriptor = [self objectClassDescriptorForClass:UIToolbar.class]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, [CASArgumentDescriptor argWithName:@"toolbarPosition" valuesByName:barPositionMap], barMetricsArg] setter:@selector(setBackgroundImage:forToolbarPosition:barMetrics:) forPropertyKey:@"backgroundImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, [CASArgumentDescriptor argWithName:@"toolbarPosition" valuesByName:barPositionMap]] setter:@selector(setShadowImage:forToolbarPosition:) forPropertyKey:@"shadowImage"]; // CASTextAttributes objectClassDescriptor = [self objectClassDescriptorForClass:CASTextAttributes.class]; NSDictionary *underlineStyleMap; if (CASKeyDeviceSystemMajorVersion() >= 7) { underlineStyleMap = @{ @"none" : @(NSUnderlineStyleNone), @"single" : @(NSUnderlineStyleSingle), @"thick" : @(NSUnderlineStyleThick), @"double" : @(NSUnderlineStyleDouble), @"solid" : @(NSUnderlinePatternSolid), @"dot" : @(NSUnderlinePatternDot), @"dash" : @(NSUnderlinePatternDash), @"dashDot" : @(NSUnderlinePatternDashDot), @"dotDotDot" : @(NSUnderlinePatternDashDotDot), @"byWord" : @(NSUnderlineByWord), }; } else { underlineStyleMap = @{ @"none" : @(NSUnderlineStyleNone), @"single" : @(NSUnderlineStyleSingle), }; } CASArgumentDescriptor *underlineStyleArg = [CASArgumentDescriptor argWithValuesByName:underlineStyleMap]; [objectClassDescriptor setArgumentDescriptors:@[underlineStyleArg] forPropertyKey:@cas_propertykey(CASTextAttributes, underlineStyle)]; [objectClassDescriptor setArgumentDescriptors:@[underlineStyleArg] forPropertyKey:@cas_propertykey(CASTextAttributes, strikethroughStyle)]; // NSParagraphStyle objectClassDescriptor = [self objectClassDescriptorForClass:NSParagraphStyle.class]; [objectClassDescriptor setArgumentDescriptors:@[textAlignmentArg] forPropertyKey:@cas_propertykey(NSParagraphStyle, alignment)]; [objectClassDescriptor setArgumentDescriptors:@[lineBreakModeArg] forPropertyKey:@cas_propertykey(NSParagraphStyle, lineBreakMode)]; // NSShadow objectClassDescriptor = [self objectClassDescriptorForClass:NSShadow.class]; [objectClassDescriptor setArgumentDescriptors:@[colorArg] forPropertyKey:@cas_propertykey(NSShadow, shadowColor)]; // UISlider objectClassDescriptor = [self objectClassDescriptorForClass:UISlider.class]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setMinimumTrackImage:forState:) forPropertyKey:@"minimumTrackImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setMaximumTrackImage:forState:) forPropertyKey:@"maximumTrackImage"]; [objectClassDescriptor setArgumentDescriptors:@[imageArg, stateArg] setter:@selector(setThumbImage:forState:) forPropertyKey:@"thumbImage"]; } - (CASObjectClassDescriptor *)objectClassDescriptorForClass:(Class)aClass { CASObjectClassDescriptor *objectClassDescriptor = [self.objectClassDescriptorCache objectForKey:aClass]; if (!objectClassDescriptor) { objectClassDescriptor = [[CASObjectClassDescriptor alloc] initWithClass:aClass]; if (aClass.superclass && ![NSObject.class isSubclassOfClass:aClass.superclass] && ![UIResponder.class isSubclassOfClass:aClass.superclass]) { objectClassDescriptor.parent = [self objectClassDescriptorForClass:aClass.superclass]; } [self.objectClassDescriptorCache setObject:objectClassDescriptor forKey:aClass]; } return objectClassDescriptor; } #pragma mark - sceduling - (void)updateScheduledItems { for (id item in self.scheduledItems.allObjects.copy) { if (!item) continue; [item cas_updateStylingIfNeeded]; } if (self.scheduledItems.allObjects.count == 0) { [self.updateTimer invalidate]; self.updateTimer = nil; } } - (void)scheduleUpdateForItem:(id)item { [self.scheduledItems addObject:item]; if (self.scheduledItems.allObjects.count && !self.updateTimer.isValid) { self.updateTimer = [NSTimer timerWithTimeInterval:0.0 target:self selector:@selector(updateScheduledItems) userInfo:nil repeats:YES]; [NSRunLoop.mainRunLoop addTimer:self.updateTimer forMode:NSRunLoopCommonModes]; } } - (void)unscheduleUpdateForItem:(id)item { [self.scheduledItems removeObject:item]; if (self.scheduledItems.allObjects.count == 0) { [self.updateTimer invalidate]; self.updateTimer = nil; } } #pragma mark - file watcher - (void)setWatchFilePath:(NSString *)watchFilePath { _watchFilePath = watchFilePath; self.filePath = watchFilePath; } - (void)reloadOnChangesToFilePath:(NSString *)filePath { dispatch_source_t source = [self.class watchForChangesToFilePath:filePath withCallback:^{ dispatch_async(dispatch_get_main_queue(), ^{ // reload styles _filePath = nil; self.filePath = _watchFilePath; // reapply styles for (UIWindow *window in self.targetWindows) { [self styleSubviewsOfView:window]; } }); }]; [self.fileWatchers addObject:source]; } - (void)styleSubviewsOfView:(UIView *)view { for (UIView *subview in view.subviews) { [subview cas_updateStyling]; [self styleSubviewsOfView:subview]; } } + (dispatch_source_t)watchForChangesToFilePath:(NSString *)filePath withCallback:(dispatch_block_t)callback { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); int fileDescriptor = open([filePath UTF8String], O_EVTONLY); NSAssert(fileDescriptor > 0, @"Error could subscribe to events for file at path: %@", filePath); __block dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileDescriptor, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND, queue); dispatch_source_set_event_handler(source, ^{ unsigned long flags = dispatch_source_get_data(source); if (flags) { dispatch_source_cancel(source); callback(); [self watchForChangesToFilePath:filePath withCallback:callback]; } }); dispatch_source_set_cancel_handler(source, ^(void){ close(fileDescriptor); }); dispatch_resume(source); return source; } @end ================================================ FILE: Classy/Parser/CASTextAttributes.h ================================================ // // CASTextAttributes.h // // // Created by Jonas Budelmann on 4/11/13. // // #import #import /** * Each property maps directly to a attribute name * ie font = NSFontAttributeName */ @interface CASTextAttributes : NSObject @property (nonatomic, strong) UIFont *font; @property (nonatomic, strong) NSMutableParagraphStyle *paragraphStyle; @property (nonatomic, strong) UIColor *foregroundColor; @property (nonatomic, strong) UIColor *backgroundColor; @property (nonatomic, assign) NSInteger ligature; @property (nonatomic, assign) CGFloat kern; @property (nonatomic, assign) NSUnderlineStyle strikethroughStyle; @property (nonatomic, assign) NSUnderlineStyle underlineStyle; @property (nonatomic, strong) UIColor *strokeColor; @property (nonatomic, assign) CGFloat strokeWidth; @property (nonatomic, strong) NSShadow *shadow; @property (nonatomic, assign) CGFloat baselineOffset; /** * Transformer receiver into appropriate NSDictionary * * @return NSDictionary containing text attribute keys and values */ - (NSDictionary *)dictionary; @end ================================================ FILE: Classy/Parser/CASTextAttributes.m ================================================ // // CASTextAttributes.m // // // Created by Jonas Budelmann on 4/11/13. // // #import "CASTextAttributes.h" #import "CASUtilities.h" @implementation CASTextAttributes - (NSMutableParagraphStyle *)paragraphStyle { if (!_paragraphStyle) { _paragraphStyle = [NSParagraphStyle defaultParagraphStyle].mutableCopy; } return _paragraphStyle; } - (NSShadow *)shadow { if (!_shadow) { _shadow = NSShadow.new; } return _shadow; } - (NSDictionary *)dictionary { NSMutableDictionary *dictionary = NSMutableDictionary.new; if (self.font) { dictionary[NSFontAttributeName] = self.font; } if (_paragraphStyle) { dictionary[NSParagraphStyleAttributeName] = _paragraphStyle; } if (self.foregroundColor) { dictionary[NSForegroundColorAttributeName] = self.foregroundColor; } if (self.backgroundColor) { dictionary[NSBackgroundColorAttributeName] = self.backgroundColor; } dictionary[NSLigatureAttributeName] = @(self.ligature); dictionary[NSKernAttributeName] = @(self.kern); dictionary[NSStrikethroughStyleAttributeName] = @(self.strikethroughStyle); dictionary[NSUnderlineStyleAttributeName] = @(self.underlineStyle); if (CASKeyDeviceSystemMajorVersion() >= 7) { dictionary[NSBaselineOffsetAttributeName] = @(self.baselineOffset); } if (self.strokeColor) { dictionary[NSStrokeColorAttributeName] = self.strokeColor; } dictionary[NSStrokeWidthAttributeName] = @(self.strokeWidth); if (_shadow) { dictionary[NSShadowAttributeName] = _shadow; } #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0 if (CASKeyDeviceSystemMajorVersion() < 7) { if (self.font) { dictionary[UITextAttributeFont] = self.font; } if (self.foregroundColor) { dictionary[UITextAttributeTextColor] = self.foregroundColor; } if (_shadow.shadowColor) { dictionary[UITextAttributeTextShadowColor] = _shadow.shadowColor; } dictionary[UITextAttributeTextShadowOffset] = [NSValue valueWithCGSize:_shadow.shadowOffset]; } #endif return dictionary; } @end ================================================ FILE: Classy/Parser/CASToken.h ================================================ // // CASToken.h // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import typedef NS_ENUM(NSInteger, CASTokenType) { CASTokenTypeUnknown, CASTokenTypeIndent, CASTokenTypeOutdent, CASTokenTypeEOS, CASTokenTypeSemiColon, CASTokenTypeCarat, CASTokenTypeNewline, CASTokenTypeLeftSquareBrace, CASTokenTypeRightSquareBrace, CASTokenTypeLeftCurlyBrace, CASTokenTypeRightCurlyBrace, CASTokenTypeLeftRoundBrace, CASTokenTypeRightRoundBrace, CASTokenTypeColor, CASTokenTypeString, CASTokenTypeUnit, CASTokenTypeBoolean, CASTokenTypeRef, CASTokenTypeOperator, CASTokenTypeSpace, CASTokenTypeSelector, }; @interface CASToken : NSObject /** * The type of the token, may not represent the true type which is determined in context of other tokens */ @property (nonatomic, assign, readonly) CASTokenType type; /** * The value of the token, could be a boxed primitive or a `NSString`, `UIColor`, ... */ @property (nonatomic, strong) id value; /** * The line number at which the token appeared in the style file, used for debug and error messages */ @property (nonatomic, assign) NSInteger lineNumber; /** * Factory method for creating tokens with a particular `CASTokenType` */ + (instancetype)tokenOfType:(CASTokenType)type; /** * Factory method for creating tokens with a particular `CASTokenType` and value */ + (instancetype)tokenOfType:(CASTokenType)type value:(id)value; /** * Returns a `NSString` representation of a `CASTokenType` * Mainly used for debug output * * @param type The `CASTokenType` to convert to a `NSString` * * @return a `NSString` representing the passed `CASTokenType` */ + (NSString *)stringForType:(CASTokenType)type; /** * Returns value of receiver as a string */ - (NSString *)stringValue; /** * Returns whether receiver is a whitespace token or not * * @return `YES` if receiver is one of the following types indent, outdent, space, new line. */ - (BOOL)isWhitespace; /** * Returns whether receiver's value is equal to the given value * * @param value the value for comparison * * @return `YES` if receiver is equal to the given value */ - (BOOL)valueIsEqualTo:(id)value; /** * Returns whether the receiver could be a valid selector token. * However context will determine if it is definitely a selector * * @return `YES` if it is possible that the receiver is a selector */ - (BOOL)isPossiblySelector; /** * Returns whether the receiver could be a valid variable token. * However context will determine if it is definitely a variable * * @return `YES` if it is possible that the receiver is a variable */ - (BOOL)isPossiblyVar; /** * Returns whether the receiver could be a valid expression token. * However context will determine if it is definitely part of an expression * * @return `YES` if it is possible that the receiver is a expression token */ - (BOOL)isPossiblyExpression; /** * Returns whether the receiver could be a valid selector delimiting token. * However context will determine if it is definitely a selector delimiter * * @return `YES` if it is possible that the receiver is a selector delimiter */ - (BOOL)isPossiblySelectorDelimiter; @end ================================================ FILE: Classy/Parser/CASToken.m ================================================ // // CASToken.m // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASToken.h" @interface CASToken () @property (nonatomic, assign, readwrite) CASTokenType type; @end @implementation CASToken #pragma mark - debug + (NSString *)stringForType:(CASTokenType)type { switch (type) { case CASTokenTypeUnknown: return @"unknown"; case CASTokenTypeIndent: return @"indent"; case CASTokenTypeOutdent: return @"outdent"; case CASTokenTypeEOS: return @"EOS"; case CASTokenTypeSemiColon: return @"semicolon"; case CASTokenTypeCarat: return @"carat"; case CASTokenTypeNewline: return @"newline"; case CASTokenTypeLeftSquareBrace: return @"left square brace"; case CASTokenTypeRightSquareBrace: return @"right square brace"; case CASTokenTypeLeftCurlyBrace: return @"left curly brace"; case CASTokenTypeRightCurlyBrace: return @"right curly brace"; case CASTokenTypeLeftRoundBrace: return @"left round brace"; case CASTokenTypeRightRoundBrace: return @"right round brace"; case CASTokenTypeColor: return @"color"; case CASTokenTypeString: return @"string"; case CASTokenTypeUnit: return @"unit"; case CASTokenTypeBoolean: return @"boolean"; case CASTokenTypeRef: return @"ref"; case CASTokenTypeOperator: return @"operator"; case CASTokenTypeSpace: return @"space"; case CASTokenTypeSelector: return @"selector"; } } - (NSString *)description { if (self.value) { return [NSString stringWithFormat:@"%@ %@", [self.class stringForType:self.type], self.value]; } return [self.class stringForType:self.type]; } #pragma mark - Factory + (instancetype)tokenOfType:(CASTokenType)type { CASToken *token = self.class.new; token.type = type; return token; } + (instancetype)tokenOfType:(CASTokenType)type value:(id)value { CASToken *token = self.class.new; token.type = type; token.value = value; return token; } #pragma mark - Helpers - (NSString *)stringValue { if ([self.value isKindOfClass:NSString.class]) { return self.value; } return [self.value stringValue]; } - (BOOL)isWhitespace { return self.type == CASTokenTypeIndent || self.type == CASTokenTypeOutdent || self.type == CASTokenTypeNewline || self.type == CASTokenTypeSpace; } - (BOOL)valueIsEqualTo:(id)value { return [self.value isEqual:value]; } - (BOOL)isPossiblySelector { return self.type == CASTokenTypeRef || self.type == CASTokenTypeCarat || self.type == CASTokenTypeLeftSquareBrace || self.type == CASTokenTypeRightSquareBrace || self.type == CASTokenTypeSelector || self.type == CASTokenTypeNewline || self.type == CASTokenTypeSpace || self.type == CASTokenTypeOperator || [self valueIsEqualTo:@":"] || [self valueIsEqualTo:@","]; } - (BOOL)isPossiblyVar { return self.type == CASTokenTypeIndent || self.type == CASTokenTypeSpace || self.type == CASTokenTypeRef || [self valueIsEqualTo:@"="]; } - (BOOL)isPossiblyExpression { return self.type == CASTokenTypeUnit || self.type == CASTokenTypeSpace || self.type == CASTokenTypeLeftRoundBrace || self.type == CASTokenTypeRightRoundBrace || (self.type == CASTokenTypeOperator); } - (BOOL)isPossiblySelectorDelimiter{ return self.type == CASTokenTypeLeftCurlyBrace || self.type == CASTokenTypeIndent; } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[CASToken class]]) { CASToken *other = object; return other.type == self.type && ([other.value isEqual:self.value] || other.value == self.value); } else { return NO; } } #pragma mark - NSCoding - (id)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (self != nil) { self.type = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(type))]; self.value = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(value))]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeInteger:self.type forKey:NSStringFromSelector(@selector(type))]; [aCoder encodeObject:self.value forKey:NSStringFromSelector(@selector(value))]; } @end ================================================ FILE: Classy/Parser/CASUnitToken.h ================================================ // // CASUnitToken.h // Classy // // Created by Jonas Budelmann on 23/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASToken.h" @interface CASUnitToken : CASToken @property (nonatomic, copy) NSString *suffix; @property (nonatomic, copy) NSString *rawValue; @end ================================================ FILE: Classy/Parser/CASUnitToken.m ================================================ // // CASUnitToken.m // Classy // // Created by Jonas Budelmann on 23/11/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASUnitToken.h" @implementation CASUnitToken #pragma mark - Helpers - (NSString *)stringValue { return self.rawValue; } @end ================================================ FILE: Classy/Parser/CASUtilities.h ================================================ // // CASUtilities.h // // // Created by Jonas Budelmann on 22/10/13. // // #import // Logging #ifdef DEBUG # define CASLog(fmt, ...) NSLog((@"[Classy] %s " fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__); #else # define CASLog(...) #endif // Keypath compile check #define cas_propertykey(classname, property) \ (((void)(NO && ((void)(( classname *)(nil)).property, NO)), # property)) // Path resolution #define CASAbsoluteFilePath(relativePath) \ _CASAbsoluteFilePath(__FILE__, relativePath) NSString *_CASAbsoluteFilePath(const char *currentFilePath, NSString *relativeFilePath); // Device versions NSUInteger CASKeyDeviceSystemMajorVersion(void); BOOL CASDeviceSystemVersionIsEqualTo(NSString *systemVersion); BOOL CASDeviceSystemVersionIsGreaterThan(NSString *systemVersion); BOOL CASDeviceSystemVersionIsGreaterThanOrEqualTo(NSString *systemVersion); BOOL CASDeviceSystemVersionIsLessThan(NSString *systemVersion); BOOL CASDeviceSystemVersionIsLessThanOrEqualTo(NSString *systemVersion); ================================================ FILE: Classy/Parser/CASUtilities.m ================================================ // // CASUtilities.m // // // Created by Jonas Budelmann on 22/10/13. // // #import "CASUtilities.h" #import NSString *_CASAbsoluteFilePath(const char *currentFilePath, NSString *relativeFilePath) { NSString *currentDirectory = [[NSString stringWithUTF8String:currentFilePath] stringByDeletingLastPathComponent]; return [currentDirectory stringByAppendingPathComponent:relativeFilePath]; } NSUInteger CASKeyDeviceSystemMajorVersion(void) { static NSUInteger _deviceSystemMajorVersion = -1; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _deviceSystemMajorVersion = [[[UIDevice.currentDevice.systemVersion componentsSeparatedByString:@"."] objectAtIndex:0] intValue]; }); return _deviceSystemMajorVersion; } BOOL CASDeviceSystemVersionIsEqualTo(NSString *systemVersion) { return [UIDevice.currentDevice.systemVersion compare:systemVersion options:NSNumericSearch] == NSOrderedSame; } BOOL CASDeviceSystemVersionIsGreaterThan(NSString *systemVersion) { return [UIDevice.currentDevice.systemVersion compare:systemVersion options:NSNumericSearch] == NSOrderedDescending; } BOOL CASDeviceSystemVersionIsGreaterThanOrEqualTo(NSString *systemVersion) { return [UIDevice.currentDevice.systemVersion compare:systemVersion options:NSNumericSearch] != NSOrderedAscending; } BOOL CASDeviceSystemVersionIsLessThan(NSString *systemVersion) { return [UIDevice.currentDevice.systemVersion compare:systemVersion options:NSNumericSearch] == NSOrderedAscending; } BOOL CASDeviceSystemVersionIsLessThanOrEqualTo(NSString *systemVersion) { return [UIDevice.currentDevice.systemVersion compare:systemVersion options:NSNumericSearch] != NSOrderedDescending; } ================================================ FILE: Classy/Reflection/CASArgumentDescriptor.h ================================================ // // CASArgumentDescriptor.h // Classy // // Created by Jonas Budelmann on 12/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import /** * Supported primitive argument types */ typedef NS_ENUM(NSUInteger, CASPrimitiveType) { CASPrimitiveTypeNone, CASPrimitiveTypeUnsupported, CASPrimitiveTypeBOOL, CASPrimitiveTypeFloat, CASPrimitiveTypeDouble, CASPrimitiveTypeInteger, CASPrimitiveTypeCGPoint, CASPrimitiveTypeCGSize, CASPrimitiveTypeCGRect, CASPrimitiveTypeUIEdgeInsets, CASPrimitiveTypeUIOffset, CASPrimitiveTypeCGColorRef, }; @interface CASArgumentDescriptor : NSObject @property (nonatomic, strong, readonly) NSString *name; @property (nonatomic, strong, readonly) Class argumentClass; @property (nonatomic, assign, readonly) CASPrimitiveType primitiveType; @property (nonatomic, strong, readonly) NSDictionary *valuesByName; + (instancetype)argWithObjCType:(const char *)type; + (instancetype)argWithType:(NSString *)type; + (instancetype)argWithClass:(Class)aClass; + (instancetype)argWithValuesByName:(NSDictionary *)valuesByName; + (instancetype)argWithName:(NSString *)name valuesByName:(NSDictionary *)valuesByName; @end ================================================ FILE: Classy/Reflection/CASArgumentDescriptor.m ================================================ // // CASArgumentDescriptor.m // Classy // // Created by Jonas Budelmann on 12/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASArgumentDescriptor.h" #import @interface CASArgumentDescriptor () @property (nonatomic, strong, readwrite) NSString *name; @property (nonatomic, strong, readwrite) Class argumentClass; @property (nonatomic, strong, readwrite) NSString *type; @property (nonatomic, strong, readwrite) NSDictionary *valuesByName; @end @implementation CASArgumentDescriptor + (instancetype)argWithObjCType:(const char *)type { CASArgumentDescriptor *descriptor = CASArgumentDescriptor.new; descriptor.type = [NSString stringWithUTF8String:type]; return descriptor; } + (instancetype)argWithType:(NSString *)type { CASArgumentDescriptor *descriptor = CASArgumentDescriptor.new; descriptor.type = type; return descriptor; } + (instancetype)argWithClass:(Class)aClass { CASArgumentDescriptor *descriptor = CASArgumentDescriptor.new; descriptor.argumentClass = aClass; return descriptor; } + (instancetype)argWithValuesByName:(NSDictionary *)valuesByName { CASArgumentDescriptor *descriptor = CASArgumentDescriptor.new; descriptor.type = [NSString stringWithUTF8String:@encode(NSInteger)]; descriptor.valuesByName = valuesByName; return descriptor; } + (instancetype)argWithName:(NSString *)name valuesByName:(NSDictionary *)valuesByName { CASArgumentDescriptor *descriptor = [self argWithValuesByName:valuesByName]; descriptor.name = name; return descriptor; } - (CASPrimitiveType)primitiveType { if (!self.type.length) return CASPrimitiveTypeNone; // if char type assume it's a BOOL, since chars aren't very useful for styling BOOL isBOOL = self.type.length == 1 && ([self.type isEqualToString:@"c"] || [self.type isEqualToString:@"B"]); if (isBOOL) return CASPrimitiveTypeBOOL; // check for integer BOOL isInteger = self.type.length == 1 && ( [self.type isEqualToString:@"i"] // An int || [self.type isEqualToString:@"s"] // A short || [self.type isEqualToString:@"l"] // A longl is treated as a 32-bit quantity on 64-bit programs. || [self.type isEqualToString:@"q"] // A long long || [self.type isEqualToString:@"C"] // An unsigned char || [self.type isEqualToString:@"I"] // An unsigned int || [self.type isEqualToString:@"S"] // An unsigned short || [self.type isEqualToString:@"L"] // An unsigned long || [self.type isEqualToString:@"Q"]); // An unsigned long long if (isInteger) return CASPrimitiveTypeInteger; // check for double if ([self.type isEqualToString:@"f"]) { return CASPrimitiveTypeFloat; } else if ([self.type isEqualToString:@"d"]) { return CASPrimitiveTypeDouble; } // check for structs if ([self.type hasPrefix:@"{CGSize"]) { return CASPrimitiveTypeCGSize; } else if ([self.type hasPrefix:@"{CGPoint"]) { return CASPrimitiveTypeCGPoint; } else if ([self.type hasPrefix:@"{CGRect"]) { return CASPrimitiveTypeCGRect; } else if ([self.type hasPrefix:@"{UIEdgeInsets"]) { return CASPrimitiveTypeUIEdgeInsets; } else if ([self.type hasPrefix:@"{UIOffset"]) { return CASPrimitiveTypeUIOffset; } else if ([self.type hasPrefix:@"^{CGColor"]) { return CASPrimitiveTypeCGColorRef; } return CASPrimitiveTypeUnsupported; } @end ================================================ FILE: Classy/Reflection/CASAssociatedObjectsWeakWrapper.h ================================================ // // CASAssociatedObjectsWeakWrapper.h // Pods // // Created by apple on 14/11/20. // // #import @interface CASAssociatedObjectsWeakWrapper : NSObject @property(nonatomic, weak, readonly) id weakObject; - (id)initWithWeakObject:(id)weakObject; @end #define CASSynthesize(ownership, type, getter, setter) \ static const void *_CASSynthesizeKey_##getter = &_CASSynthesizeKey_##getter; \ -(type)getter { \ return _CASSynthesize_get_##ownership(type, getter); \ } \ -(void)setter : (type)getter { \ _CASSynthesize_set_##ownership(type, getter); \ } #define _CASSynthesize_get_weak(type, getter) \ ((CASAssociatedObjectsWeakWrapper *)objc_getAssociatedObject( \ self, _CASSynthesizeKey_##getter)).weakObject #define _CASSynthesize_set_weak(type, getter) \ objc_setAssociatedObject( \ self, _CASSynthesizeKey_##getter, \ [[CASAssociatedObjectsWeakWrapper alloc] initWithWeakObject:getter], \ OBJC_ASSOCIATION_RETAIN_NONATOMIC); ================================================ FILE: Classy/Reflection/CASAssociatedObjectsWeakWrapper.m ================================================ // // CASAssociatedObjectsWeakWrapper.m // Pods // // Created by apple on 14/11/20. // // #import "CASAssociatedObjectsWeakWrapper.h" @implementation CASAssociatedObjectsWeakWrapper - (id)initWithWeakObject:(id)weakObject { self = [super init]; if (!self) { return nil; } _weakObject = weakObject; return self; } @end ================================================ FILE: Classy/Reflection/CASObjectClassDescriptor.h ================================================ // // CASObjectClassDescriptor.h // Classy // // Created by Jonas Budelmann on 30/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASPropertyDescriptor.h" @interface CASObjectClassDescriptor : NSObject /** * object class which receiver relates to */ @property (nonatomic, strong, readonly) Class objectClass; /** * view superclass descriptor */ @property (nonatomic, strong) CASObjectClassDescriptor *parent; /** * alternative names for view property keys */ @property (nonatomic, strong) NSDictionary *propertyKeyAliases; - (id)initWithClass:(Class)aClass; - (NSInvocation *)invocationForPropertyDescriptor:(CASPropertyDescriptor *)propertyDescriptor; - (CASPropertyDescriptor *)propertyDescriptorForKey:(NSString *)key; - (void)setArgumentDescriptors:(NSArray *)argumentDescriptors forPropertyKey:(NSString *)key; - (void)setArgumentDescriptors:(NSArray *)argumentDescriptors setter:(SEL)setter forPropertyKey:(NSString *)key; @end ================================================ FILE: Classy/Reflection/CASObjectClassDescriptor.m ================================================ // // CASObjectClassDescriptor.m // Classy // // Created by Jonas Budelmann on 30/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASObjectClassDescriptor.h" #import "CASRuntimeExtensions.h" #import "NSString+CASAdditions.h" #import "CASUtilities.h" @interface CASObjectClassDescriptor () @property (nonatomic, strong, readwrite) Class objectClass; @property (nonatomic, strong) NSMutableDictionary *propertyDescriptorCache; @end @implementation CASObjectClassDescriptor - (id)initWithClass:(Class)aClass { self = [super init]; if (!self) return nil; self.objectClass = aClass; self.propertyDescriptorCache = NSMutableDictionary.new; return self; } #pragma mark - property descriptor support - (void)setArgumentDescriptors:(NSArray *)argumentDescriptors forPropertyKey:(NSString *)key { CASPropertyDescriptor *propertyDescriptor = [[CASPropertyDescriptor alloc] initWithKey:key argumentDescriptors:argumentDescriptors]; self.propertyDescriptorCache[propertyDescriptor.key] = propertyDescriptor; } - (void)setArgumentDescriptors:(NSArray *)argumentDescriptors setter:(SEL)setter forPropertyKey:(NSString *)key { CASPropertyDescriptor *propertyDescriptor = [[CASPropertyDescriptor alloc] initWithKey:key argumentDescriptors:argumentDescriptors setter:setter]; self.propertyDescriptorCache[propertyDescriptor.key] = propertyDescriptor; } - (NSInvocation *)invocationForPropertyDescriptor:(CASPropertyDescriptor *)propertyDescriptor { if (!propertyDescriptor) return nil; SEL selector = propertyDescriptor.setter; NSMethodSignature *methodSignature = [self.objectClass instanceMethodSignatureForSelector:selector]; if (!methodSignature) { CASLog(@"Selector '%@' not found. Class '%@'", NSStringFromSelector(selector), self.objectClass); return nil; } NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setSelector:selector]; return invocation; } - (CASPropertyDescriptor *)propertyDescriptorForKey:(NSString *)key { // if property descriptor exists on this class descriptor. return it. NSString *propertyKey = self.propertyKeyAliases[key] ?: key; CASPropertyDescriptor *propertyDescriptor = self.propertyDescriptorCache[propertyKey]; if (propertyDescriptor) return propertyDescriptor; // if property descriptor exists on parent class descriptor. return it. propertyDescriptor = [self.parent propertyDescriptorForKey:key]; if (propertyDescriptor) return propertyDescriptor; // create property descriptor on this descriptor if has propertyKeyAlias // or if responds to property and superclass doesn't SEL propertySelector = NSSelectorFromString(propertyKey); if (self.propertyKeyAliases[propertyKey] || ([self.objectClass instancesRespondToSelector:propertySelector] && ![self.objectClass.superclass instancesRespondToSelector:propertySelector])) { objc_property_t property = class_getProperty(self.objectClass, [propertyKey UTF8String]); if (property != NULL) { cas_propertyAttributes *propertyAttributes = cas_copyPropertyAttributes(class_getProperty(self.objectClass, [propertyKey UTF8String])); NSArray *argumentDescriptors; if (propertyAttributes->objectClass) { argumentDescriptors = @[ [CASArgumentDescriptor argWithClass:propertyAttributes->objectClass] ]; } else { NSString *type = [NSString stringWithCString:propertyAttributes->type encoding:NSASCIIStringEncoding]; argumentDescriptors = @[ [CASArgumentDescriptor argWithType:type] ]; } propertyDescriptor = [[CASPropertyDescriptor alloc] initWithKey:propertyKey argumentDescriptors:argumentDescriptors setter:propertyAttributes->setter]; self.propertyDescriptorCache[propertyKey] = propertyDescriptor; free(propertyAttributes); return propertyDescriptor; } else { // TODO error CASLog(@"Property '%@' not found. Class '%@'", propertyKey, self.objectClass); } } return nil; } @end ================================================ FILE: Classy/Reflection/CASPropertyDescriptor.h ================================================ // // CASPropertyDescriptor.h // Classy // // Created by Jonas Budelmann on 12/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import #import "CASArgumentDescriptor.h" @interface CASPropertyDescriptor : NSObject @property (nonatomic, strong, readonly) NSString *key; @property (nonatomic, strong, readonly) NSArray *argumentDescriptors; @property (nonatomic, assign, readonly) SEL setter; - (id)initWithKey:(NSString *)key argumentDescriptors:(NSArray *)argumentDescriptors; - (id)initWithKey:(NSString *)key argumentDescriptors:(NSArray *)argumentDescriptors setter:(SEL)setter; @end ================================================ FILE: Classy/Reflection/CASPropertyDescriptor.m ================================================ // // CASPropertyDescriptor.m // Classy // // Created by Jonas Budelmann on 12/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASPropertyDescriptor.h" #import "NSString+CASAdditions.h" @interface CASPropertyDescriptor () @property (nonatomic, strong, readwrite) NSString *key; @property (nonatomic, strong, readwrite) NSArray *argumentDescriptors; @property (nonatomic, assign, readwrite) SEL setter; @end @implementation CASPropertyDescriptor - (id)initWithKey:(NSString *)key argumentDescriptors:(NSArray *)argumentDescriptors { self = [super init]; if (!self) return nil; self.key = key; self.argumentDescriptors = argumentDescriptors; NSString *setterString = [NSString stringWithFormat:@"set%@:", [key cas_stringByCapitalizingFirstLetter]]; self.setter = NSSelectorFromString(setterString); return self; } - (id)initWithKey:(NSString *)key argumentDescriptors:(NSArray *)argumentDescriptors setter:(SEL)setter { self = [super init]; if (!self) return nil; self.key = key; self.argumentDescriptors = argumentDescriptors; self.setter = setter; return self; } @end ================================================ FILE: Classy/Reflection/CASRuntimeExtensions.h ================================================ // // CASRuntimeExtensions.h // Classy // // Created by Jonas Budelmann on 14/10/13. // Renamespaced version of EXTRuntimeExtensions // // // EXTRuntimeExtensions.h // extobjc // // Created by Justin Spahr-Summers on 2011-03-05. // Copyright (C) 2012 Justin Spahr-Summers. // Released under the MIT license. // #import #import /** * Describes the memory management policy of a property. */ typedef enum { /** * The value is assigned. */ cas_propertyMemoryManagementPolicyAssign = 0, /** * The value is retained. */ cas_propertyMemoryManagementPolicyRetain, /** * The value is copied. */ cas_propertyMemoryManagementPolicyCopy } cas_propertyMemoryManagementPolicy; /** * Describes the attributes and type information of a property. */ typedef struct { /** * Whether this property was declared with the \c readonly attribute. */ BOOL readonly; /** * Whether this property was declared with the \c nonatomic attribute. */ BOOL nonatomic; /** * Whether the property is a weak reference. */ BOOL weak; /** * Whether the property is eligible for garbage collection. */ BOOL canBeCollected; /** * Whether this property is defined with \c \@dynamic. */ BOOL dynamic; /** * The memory management policy for this property. This will always be * #cas_propertyMemoryManagementPolicyAssign if #readonly is \c YES. */ cas_propertyMemoryManagementPolicy memoryManagementPolicy; /** * The selector for the getter of this property. This will reflect any * custom \c getter= attribute provided in the property declaration, or the * inferred getter name otherwise. */ SEL getter; /** * The selector for the setter of this property. This will reflect any * custom \c setter= attribute provided in the property declaration, or the * inferred setter name otherwise. * * @note If #readonly is \c YES, this value will represent what the setter * \e would be, if the property were writable. */ SEL setter; /** * The backing instance variable for this property, or \c NULL if \c * \c @synthesize was not used, and therefore no instance variable exists. This * would also be the case if the property is implemented dynamically. */ const char *ivar; /** * If this property is defined as being an instance of a specific class, * this will be the class object representing it. * * This will be \c nil if the property was defined as type \c id, if the * property is not of an object type, or if the class could not be found at * runtime. */ Class objectClass; /** * The type encoding for the value of this property. This is the type as it * would be returned by the \c \@encode() directive. */ char type[]; } cas_propertyAttributes; /** * Returns a pointer to a structure containing information about \a property. * You must \c free() the returned pointer. Returns \c NULL if there is an error * obtaining information from \a property. */ cas_propertyAttributes *cas_copyPropertyAttributes (objc_property_t property); ================================================ FILE: Classy/Reflection/CASRuntimeExtensions.m ================================================ // // EXTRuntimeExtensions.m // extobjc // // Created by Justin Spahr-Summers on 2011-03-05. // Copyright (C) 2012 Justin Spahr-Summers. // Released under the MIT license. // #import "CASRuntimeExtensions.h" #import cas_propertyAttributes *cas_copyPropertyAttributes (objc_property_t property) { const char * const attrString = property_getAttributes(property); if (!attrString) { fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property)); return NULL; } if (attrString[0] != 'T') { fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property)); return NULL; } const char *typeString = attrString + 1; const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL); if (!next) { fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; } size_t typeLength = next - typeString; if (!typeLength) { fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; } // allocate enough space for the structure and the type string (plus a NUL) cas_propertyAttributes *attributes = calloc(1, sizeof(cas_propertyAttributes) + typeLength + 1); if (!attributes) { fprintf(stderr, "ERROR: Could not allocate cas_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; } // copy the type string strncpy(attributes->type, typeString, typeLength); attributes->type[typeLength] = '\0'; // if this is an object type, and immediately followed by a quoted string... if (typeString[0] == *(@encode(id)) && typeString[1] == '"') { // we should be able to extract a class name const char *className = typeString + 2; next = strchr(className, '"'); if (!next) { fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; } if (className != next) { size_t classNameLength = next - className; char trimmedName[classNameLength + 1]; strncpy(trimmedName, className, classNameLength); trimmedName[classNameLength] = '\0'; // attempt to look up the class in the runtime attributes->objectClass = objc_getClass(trimmedName); } } if (*next != '\0') { // skip past any junk before the first flag next = strchr(next, ','); } while (next && *next == ',') { char flag = next[1]; next += 2; switch (flag) { case '\0': break; case 'R': attributes->readonly = YES; break; case 'C': attributes->memoryManagementPolicy = cas_propertyMemoryManagementPolicyCopy; break; case '&': attributes->memoryManagementPolicy = cas_propertyMemoryManagementPolicyRetain; break; case 'N': attributes->nonatomic = YES; break; case 'G': case 'S': { const char *nextFlag = strchr(next, ','); SEL name = NULL; if (!nextFlag) { // assume that the rest of the string is the selector const char *selectorString = next; next = ""; name = sel_registerName(selectorString); } else { size_t selectorLength = nextFlag - next; if (!selectorLength) { fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); free(attributes); return NULL; } char selectorString[selectorLength + 1]; strncpy(selectorString, next, selectorLength); selectorString[selectorLength] = '\0'; name = sel_registerName(selectorString); next = nextFlag; } if (flag == 'G') attributes->getter = name; else attributes->setter = name; } break; case 'D': attributes->dynamic = YES; attributes->ivar = NULL; break; case 'V': // assume that the rest of the string (if present) is the ivar name if (*next == '\0') { // if there's nothing there, let's assume this is dynamic attributes->ivar = NULL; } else { attributes->ivar = next; next = ""; } break; case 'W': attributes->weak = YES; break; case 'P': attributes->canBeCollected = YES; break; case 't': fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); // skip over this type encoding while (*next != ',' && *next != '\0') ++next; break; default: fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property)); } } if (next && *next != '\0') { fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property)); } if (!attributes->getter) { // use the property name as the getter by default attributes->getter = sel_registerName(property_getName(property)); } if (!attributes->setter) { const char *propertyName = property_getName(property); size_t propertyNameLength = strlen(propertyName); // we want to transform the name to setProperty: style size_t setterLength = propertyNameLength + 4; char setterName[setterLength + 1]; strncpy(setterName, "set", 3); strncpy(setterName + 3, propertyName, propertyNameLength); // capitalize property name for the setter setterName[3] = (char)toupper(setterName[3]); setterName[setterLength - 1] = ':'; setterName[setterLength] = '\0'; attributes->setter = sel_registerName(setterName); } return attributes; } ================================================ FILE: Classy/Supporting Files/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: Classy.podspec ================================================ Pod::Spec.new do |s| s.name = 'Classy' s.version = '1.0.0' s.license = 'MIT' s.summary = 'Expressive, flexible, and powerful stylesheets for UIView and friends.' s.homepage = 'http://classykit.github.io/Classy/' s.author = { 'Jonas Budelmann' => 'jonas.budelmann@gmail.com' } s.social_media_url = "http://twitter.com/cloudkite" s.source = { :git => 'https://github.com/cloudkite/Classy.git', :tag => 'v1.0.0' } s.description = %{ Not CSS. Instead of trying to force UIKit to fit CSS syntax, properties, conventions and constructs. Classy is a stylesheet system built from the ground up to work in harmony with UIKit. It borrows the best ideas from CSS and introduces new syntax, conventions and constructs where appropriate. } s.source_files = 'Classy/**/*.{h,m}' s.ios.frameworks = 'Foundation', 'UIKit', 'QuartzCore' s.ios.deployment_target = '9.0' s.requires_arc = true end ================================================ FILE: Classy.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 1EC719AD1B67AF9C00700A14 /* CASStyleClassUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719611B67AF9C00700A14 /* CASStyleClassUtilities.h */; }; 1EC719AE1B67AF9C00700A14 /* CASStyleClassUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719621B67AF9C00700A14 /* CASStyleClassUtilities.m */; }; 1EC719AF1B67AF9C00700A14 /* CASStyleableItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719631B67AF9C00700A14 /* CASStyleableItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719B01B67AF9C00700A14 /* CASStyleableItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719641B67AF9C00700A14 /* CASStyleableItem.m */; }; 1EC719B11B67AF9C00700A14 /* NSObject+CASSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719651B67AF9C00700A14 /* NSObject+CASSwizzle.h */; }; 1EC719B21B67AF9C00700A14 /* NSObject+CASSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719661B67AF9C00700A14 /* NSObject+CASSwizzle.m */; }; 1EC719B31B67AF9C00700A14 /* NSRegularExpression+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719671B67AF9C00700A14 /* NSRegularExpression+CASAdditions.h */; }; 1EC719B41B67AF9C00700A14 /* NSRegularExpression+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719681B67AF9C00700A14 /* NSRegularExpression+CASAdditions.m */; }; 1EC719B51B67AF9C00700A14 /* NSString+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719691B67AF9C00700A14 /* NSString+CASAdditions.h */; }; 1EC719B61B67AF9C00700A14 /* NSString+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7196A1B67AF9C00700A14 /* NSString+CASAdditions.m */; }; 1EC719B71B67AF9C00700A14 /* UIBarItem+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7196B1B67AF9C00700A14 /* UIBarItem+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719B81B67AF9C00700A14 /* UIBarItem+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7196C1B67AF9C00700A14 /* UIBarItem+CASAdditions.m */; }; 1EC719B91B67AF9C00700A14 /* UIColor+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7196D1B67AF9C00700A14 /* UIColor+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719BA1B67AF9C00700A14 /* UIColor+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7196E1B67AF9C00700A14 /* UIColor+CASAdditions.m */; }; 1EC719BB1B67AF9C00700A14 /* UINavigationBar+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7196F1B67AF9C00700A14 /* UINavigationBar+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719BC1B67AF9C00700A14 /* UINavigationBar+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719701B67AF9C00700A14 /* UINavigationBar+CASAdditions.m */; }; 1EC719BD1B67AF9C00700A14 /* UINavigationItem+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719711B67AF9C00700A14 /* UINavigationItem+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719BE1B67AF9C00700A14 /* UINavigationItem+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719721B67AF9C00700A14 /* UINavigationItem+CASAdditions.m */; }; 1EC719BF1B67AF9C00700A14 /* UISlider+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719731B67AF9C00700A14 /* UISlider+CASAdditions.h */; }; 1EC719C01B67AF9C00700A14 /* UISlider+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719741B67AF9C00700A14 /* UISlider+CASAdditions.m */; }; 1EC719C11B67AF9C00700A14 /* UITabBar+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719751B67AF9C00700A14 /* UITabBar+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719C21B67AF9C00700A14 /* UITabBar+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719761B67AF9C00700A14 /* UITabBar+CASAdditions.m */; }; 1EC719C31B67AF9C00700A14 /* UITextField+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719771B67AF9C00700A14 /* UITextField+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719C41B67AF9C00700A14 /* UITextField+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719781B67AF9C00700A14 /* UITextField+CASAdditions.m */; }; 1EC719C51B67AF9C00700A14 /* UIToolbar+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719791B67AF9C00700A14 /* UIToolbar+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719C61B67AF9C00700A14 /* UIToolbar+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7197A1B67AF9C00700A14 /* UIToolbar+CASAdditions.m */; }; 1EC719C71B67AF9C00700A14 /* UIView+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7197B1B67AF9C00700A14 /* UIView+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719C81B67AF9C00700A14 /* UIView+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7197C1B67AF9C00700A14 /* UIView+CASAdditions.m */; }; 1EC719C91B67AF9C00700A14 /* UIViewController+CASAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7197D1B67AF9C00700A14 /* UIViewController+CASAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719CA1B67AF9C00700A14 /* UIViewController+CASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7197E1B67AF9C00700A14 /* UIViewController+CASAdditions.m */; }; 1EC719CB1B67AF9C00700A14 /* Classy.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7197F1B67AF9C00700A14 /* Classy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719CC1B67AF9C00700A14 /* CASDeviceOSVersionItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719811B67AF9C00700A14 /* CASDeviceOSVersionItem.h */; }; 1EC719CD1B67AF9C00700A14 /* CASDeviceOSVersionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719821B67AF9C00700A14 /* CASDeviceOSVersionItem.m */; }; 1EC719CE1B67AF9C00700A14 /* CASDeviceScreenSizeItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719831B67AF9C00700A14 /* CASDeviceScreenSizeItem.h */; }; 1EC719CF1B67AF9C00700A14 /* CASDeviceScreenSizeItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719841B67AF9C00700A14 /* CASDeviceScreenSizeItem.m */; }; 1EC719D01B67AF9C00700A14 /* CASDeviceSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719851B67AF9C00700A14 /* CASDeviceSelector.h */; }; 1EC719D11B67AF9C00700A14 /* CASDeviceSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719861B67AF9C00700A14 /* CASDeviceSelector.m */; }; 1EC719D21B67AF9C00700A14 /* CASDeviceSelectorItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719871B67AF9C00700A14 /* CASDeviceSelectorItem.h */; }; 1EC719D31B67AF9C00700A14 /* CASDeviceTypeItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719881B67AF9C00700A14 /* CASDeviceTypeItem.h */; }; 1EC719D41B67AF9C00700A14 /* CASDeviceTypeItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719891B67AF9C00700A14 /* CASDeviceTypeItem.m */; }; 1EC719D51B67AF9C00700A14 /* CASExpressionSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7198A1B67AF9C00700A14 /* CASExpressionSolver.h */; }; 1EC719D61B67AF9C00700A14 /* CASExpressionSolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7198B1B67AF9C00700A14 /* CASExpressionSolver.m */; }; 1EC719D71B67AF9C00700A14 /* CASInvocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7198C1B67AF9C00700A14 /* CASInvocation.h */; }; 1EC719D81B67AF9C00700A14 /* CASInvocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7198D1B67AF9C00700A14 /* CASInvocation.m */; }; 1EC719D91B67AF9C00700A14 /* CASLexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7198E1B67AF9C00700A14 /* CASLexer.h */; }; 1EC719DA1B67AF9C00700A14 /* CASLexer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7198F1B67AF9C00700A14 /* CASLexer.m */; }; 1EC719DB1B67AF9C00700A14 /* CASParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719901B67AF9C00700A14 /* CASParser.h */; }; 1EC719DC1B67AF9C00700A14 /* CASParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719911B67AF9C00700A14 /* CASParser.m */; }; 1EC719DD1B67AF9C00700A14 /* CASStyleNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719921B67AF9C00700A14 /* CASStyleNode.h */; }; 1EC719DE1B67AF9C00700A14 /* CASStyleNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719931B67AF9C00700A14 /* CASStyleNode.m */; }; 1EC719DF1B67AF9C00700A14 /* CASStyleProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719941B67AF9C00700A14 /* CASStyleProperty.h */; }; 1EC719E01B67AF9C00700A14 /* CASStyleProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719951B67AF9C00700A14 /* CASStyleProperty.m */; }; 1EC719E11B67AF9C00700A14 /* CASStyleSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719961B67AF9C00700A14 /* CASStyleSelector.h */; }; 1EC719E21B67AF9C00700A14 /* CASStyleSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719971B67AF9C00700A14 /* CASStyleSelector.m */; }; 1EC719E31B67AF9C00700A14 /* CASStyler.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719981B67AF9C00700A14 /* CASStyler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719E41B67AF9C00700A14 /* CASStyler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719991B67AF9C00700A14 /* CASStyler.m */; }; 1EC719E51B67AF9C00700A14 /* CASTextAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7199A1B67AF9C00700A14 /* CASTextAttributes.h */; }; 1EC719E61B67AF9C00700A14 /* CASTextAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7199B1B67AF9C00700A14 /* CASTextAttributes.m */; }; 1EC719E71B67AF9C00700A14 /* CASToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7199C1B67AF9C00700A14 /* CASToken.h */; }; 1EC719E81B67AF9C00700A14 /* CASToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7199D1B67AF9C00700A14 /* CASToken.m */; }; 1EC719E91B67AF9C00700A14 /* CASUnitToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC7199E1B67AF9C00700A14 /* CASUnitToken.h */; }; 1EC719EA1B67AF9C00700A14 /* CASUnitToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC7199F1B67AF9C00700A14 /* CASUnitToken.m */; }; 1EC719EB1B67AF9C00700A14 /* CASUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719A01B67AF9C00700A14 /* CASUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719EC1B67AF9C00700A14 /* CASUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719A11B67AF9C00700A14 /* CASUtilities.m */; }; 1EC719ED1B67AF9C00700A14 /* CASArgumentDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719A31B67AF9C00700A14 /* CASArgumentDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719EE1B67AF9C00700A14 /* CASArgumentDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719A41B67AF9C00700A14 /* CASArgumentDescriptor.m */; }; 1EC719EF1B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719A51B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.h */; }; 1EC719F01B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719A61B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.m */; }; 1EC719F11B67AF9C00700A14 /* CASObjectClassDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719A71B67AF9C00700A14 /* CASObjectClassDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719F21B67AF9C00700A14 /* CASObjectClassDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719A81B67AF9C00700A14 /* CASObjectClassDescriptor.m */; }; 1EC719F31B67AF9C00700A14 /* CASPropertyDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719A91B67AF9C00700A14 /* CASPropertyDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EC719F41B67AF9C00700A14 /* CASPropertyDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719AA1B67AF9C00700A14 /* CASPropertyDescriptor.m */; }; 1EC719F51B67AF9C00700A14 /* CASRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EC719AB1B67AF9C00700A14 /* CASRuntimeExtensions.h */; }; 1EC719F61B67AF9C00700A14 /* CASRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC719AC1B67AF9C00700A14 /* CASRuntimeExtensions.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 1EC719431B67AE9D00700A14 /* Classy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Classy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1EC7195F1B67AF4800700A14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Classy/Supporting Files/Info.plist"; sourceTree = SOURCE_ROOT; }; 1EC719611B67AF9C00700A14 /* CASStyleClassUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASStyleClassUtilities.h; sourceTree = ""; }; 1EC719621B67AF9C00700A14 /* CASStyleClassUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyleClassUtilities.m; sourceTree = ""; }; 1EC719631B67AF9C00700A14 /* CASStyleableItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASStyleableItem.h; sourceTree = ""; }; 1EC719641B67AF9C00700A14 /* CASStyleableItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyleableItem.m; sourceTree = ""; }; 1EC719651B67AF9C00700A14 /* NSObject+CASSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+CASSwizzle.h"; sourceTree = ""; }; 1EC719661B67AF9C00700A14 /* NSObject+CASSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+CASSwizzle.m"; sourceTree = ""; }; 1EC719671B67AF9C00700A14 /* NSRegularExpression+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSRegularExpression+CASAdditions.h"; sourceTree = ""; }; 1EC719681B67AF9C00700A14 /* NSRegularExpression+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSRegularExpression+CASAdditions.m"; sourceTree = ""; }; 1EC719691B67AF9C00700A14 /* NSString+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+CASAdditions.h"; sourceTree = ""; }; 1EC7196A1B67AF9C00700A14 /* NSString+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+CASAdditions.m"; sourceTree = ""; }; 1EC7196B1B67AF9C00700A14 /* UIBarItem+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarItem+CASAdditions.h"; sourceTree = ""; }; 1EC7196C1B67AF9C00700A14 /* UIBarItem+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarItem+CASAdditions.m"; sourceTree = ""; }; 1EC7196D1B67AF9C00700A14 /* UIColor+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+CASAdditions.h"; sourceTree = ""; }; 1EC7196E1B67AF9C00700A14 /* UIColor+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+CASAdditions.m"; sourceTree = ""; }; 1EC7196F1B67AF9C00700A14 /* UINavigationBar+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationBar+CASAdditions.h"; sourceTree = ""; }; 1EC719701B67AF9C00700A14 /* UINavigationBar+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationBar+CASAdditions.m"; sourceTree = ""; }; 1EC719711B67AF9C00700A14 /* UINavigationItem+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationItem+CASAdditions.h"; sourceTree = ""; }; 1EC719721B67AF9C00700A14 /* UINavigationItem+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationItem+CASAdditions.m"; sourceTree = ""; }; 1EC719731B67AF9C00700A14 /* UISlider+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISlider+CASAdditions.h"; sourceTree = ""; }; 1EC719741B67AF9C00700A14 /* UISlider+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+CASAdditions.m"; sourceTree = ""; }; 1EC719751B67AF9C00700A14 /* UITabBar+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITabBar+CASAdditions.h"; sourceTree = ""; }; 1EC719761B67AF9C00700A14 /* UITabBar+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITabBar+CASAdditions.m"; sourceTree = ""; }; 1EC719771B67AF9C00700A14 /* UITextField+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+CASAdditions.h"; sourceTree = ""; }; 1EC719781B67AF9C00700A14 /* UITextField+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+CASAdditions.m"; sourceTree = ""; }; 1EC719791B67AF9C00700A14 /* UIToolbar+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIToolbar+CASAdditions.h"; sourceTree = ""; }; 1EC7197A1B67AF9C00700A14 /* UIToolbar+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIToolbar+CASAdditions.m"; sourceTree = ""; }; 1EC7197B1B67AF9C00700A14 /* UIView+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+CASAdditions.h"; sourceTree = ""; }; 1EC7197C1B67AF9C00700A14 /* UIView+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+CASAdditions.m"; sourceTree = ""; }; 1EC7197D1B67AF9C00700A14 /* UIViewController+CASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+CASAdditions.h"; sourceTree = ""; }; 1EC7197E1B67AF9C00700A14 /* UIViewController+CASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+CASAdditions.m"; sourceTree = ""; }; 1EC7197F1B67AF9C00700A14 /* Classy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Classy.h; path = Classy/Classy.h; sourceTree = SOURCE_ROOT; }; 1EC719811B67AF9C00700A14 /* CASDeviceOSVersionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASDeviceOSVersionItem.h; sourceTree = ""; }; 1EC719821B67AF9C00700A14 /* CASDeviceOSVersionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASDeviceOSVersionItem.m; sourceTree = ""; }; 1EC719831B67AF9C00700A14 /* CASDeviceScreenSizeItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASDeviceScreenSizeItem.h; sourceTree = ""; }; 1EC719841B67AF9C00700A14 /* CASDeviceScreenSizeItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASDeviceScreenSizeItem.m; sourceTree = ""; }; 1EC719851B67AF9C00700A14 /* CASDeviceSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASDeviceSelector.h; sourceTree = ""; }; 1EC719861B67AF9C00700A14 /* CASDeviceSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASDeviceSelector.m; sourceTree = ""; }; 1EC719871B67AF9C00700A14 /* CASDeviceSelectorItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASDeviceSelectorItem.h; sourceTree = ""; }; 1EC719881B67AF9C00700A14 /* CASDeviceTypeItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASDeviceTypeItem.h; sourceTree = ""; }; 1EC719891B67AF9C00700A14 /* CASDeviceTypeItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASDeviceTypeItem.m; sourceTree = ""; }; 1EC7198A1B67AF9C00700A14 /* CASExpressionSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASExpressionSolver.h; sourceTree = ""; }; 1EC7198B1B67AF9C00700A14 /* CASExpressionSolver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASExpressionSolver.m; sourceTree = ""; }; 1EC7198C1B67AF9C00700A14 /* CASInvocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASInvocation.h; sourceTree = ""; }; 1EC7198D1B67AF9C00700A14 /* CASInvocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASInvocation.m; sourceTree = ""; }; 1EC7198E1B67AF9C00700A14 /* CASLexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASLexer.h; sourceTree = ""; }; 1EC7198F1B67AF9C00700A14 /* CASLexer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASLexer.m; sourceTree = ""; }; 1EC719901B67AF9C00700A14 /* CASParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASParser.h; sourceTree = ""; }; 1EC719911B67AF9C00700A14 /* CASParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASParser.m; sourceTree = ""; }; 1EC719921B67AF9C00700A14 /* CASStyleNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASStyleNode.h; sourceTree = ""; }; 1EC719931B67AF9C00700A14 /* CASStyleNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyleNode.m; sourceTree = ""; }; 1EC719941B67AF9C00700A14 /* CASStyleProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASStyleProperty.h; sourceTree = ""; }; 1EC719951B67AF9C00700A14 /* CASStyleProperty.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyleProperty.m; sourceTree = ""; }; 1EC719961B67AF9C00700A14 /* CASStyleSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASStyleSelector.h; sourceTree = ""; }; 1EC719971B67AF9C00700A14 /* CASStyleSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyleSelector.m; sourceTree = ""; }; 1EC719981B67AF9C00700A14 /* CASStyler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASStyler.h; sourceTree = ""; }; 1EC719991B67AF9C00700A14 /* CASStyler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyler.m; sourceTree = ""; }; 1EC7199A1B67AF9C00700A14 /* CASTextAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASTextAttributes.h; sourceTree = ""; }; 1EC7199B1B67AF9C00700A14 /* CASTextAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASTextAttributes.m; sourceTree = ""; }; 1EC7199C1B67AF9C00700A14 /* CASToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASToken.h; sourceTree = ""; }; 1EC7199D1B67AF9C00700A14 /* CASToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASToken.m; sourceTree = ""; }; 1EC7199E1B67AF9C00700A14 /* CASUnitToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASUnitToken.h; sourceTree = ""; }; 1EC7199F1B67AF9C00700A14 /* CASUnitToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASUnitToken.m; sourceTree = ""; }; 1EC719A01B67AF9C00700A14 /* CASUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASUtilities.h; sourceTree = ""; }; 1EC719A11B67AF9C00700A14 /* CASUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASUtilities.m; sourceTree = ""; }; 1EC719A31B67AF9C00700A14 /* CASArgumentDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASArgumentDescriptor.h; sourceTree = ""; }; 1EC719A41B67AF9C00700A14 /* CASArgumentDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASArgumentDescriptor.m; sourceTree = ""; }; 1EC719A51B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASAssociatedObjectsWeakWrapper.h; sourceTree = ""; }; 1EC719A61B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASAssociatedObjectsWeakWrapper.m; sourceTree = ""; }; 1EC719A71B67AF9C00700A14 /* CASObjectClassDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASObjectClassDescriptor.h; sourceTree = ""; }; 1EC719A81B67AF9C00700A14 /* CASObjectClassDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASObjectClassDescriptor.m; sourceTree = ""; }; 1EC719A91B67AF9C00700A14 /* CASPropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASPropertyDescriptor.h; sourceTree = ""; }; 1EC719AA1B67AF9C00700A14 /* CASPropertyDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASPropertyDescriptor.m; sourceTree = ""; }; 1EC719AB1B67AF9C00700A14 /* CASRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASRuntimeExtensions.h; sourceTree = ""; }; 1EC719AC1B67AF9C00700A14 /* CASRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASRuntimeExtensions.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 1EC7193F1B67AE9D00700A14 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1EC719391B67AE9D00700A14 = { isa = PBXGroup; children = ( 1EC719451B67AE9D00700A14 /* Classy */, 1EC719441B67AE9D00700A14 /* Products */, ); sourceTree = ""; }; 1EC719441B67AE9D00700A14 /* Products */ = { isa = PBXGroup; children = ( 1EC719431B67AE9D00700A14 /* Classy.framework */, ); name = Products; sourceTree = ""; }; 1EC719451B67AE9D00700A14 /* Classy */ = { isa = PBXGroup; children = ( 1EC7197F1B67AF9C00700A14 /* Classy.h */, 1EC719601B67AF9C00700A14 /* Additions */, 1EC719801B67AF9C00700A14 /* Parser */, 1EC719A21B67AF9C00700A14 /* Reflection */, 1EC719461B67AE9D00700A14 /* Supporting Files */, ); name = Classy; path = "Classy-Library"; sourceTree = ""; }; 1EC719461B67AE9D00700A14 /* Supporting Files */ = { isa = PBXGroup; children = ( 1EC7195F1B67AF4800700A14 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 1EC719601B67AF9C00700A14 /* Additions */ = { isa = PBXGroup; children = ( 1EC719611B67AF9C00700A14 /* CASStyleClassUtilities.h */, 1EC719621B67AF9C00700A14 /* CASStyleClassUtilities.m */, 1EC719631B67AF9C00700A14 /* CASStyleableItem.h */, 1EC719641B67AF9C00700A14 /* CASStyleableItem.m */, 1EC719651B67AF9C00700A14 /* NSObject+CASSwizzle.h */, 1EC719661B67AF9C00700A14 /* NSObject+CASSwizzle.m */, 1EC719671B67AF9C00700A14 /* NSRegularExpression+CASAdditions.h */, 1EC719681B67AF9C00700A14 /* NSRegularExpression+CASAdditions.m */, 1EC719691B67AF9C00700A14 /* NSString+CASAdditions.h */, 1EC7196A1B67AF9C00700A14 /* NSString+CASAdditions.m */, 1EC7196B1B67AF9C00700A14 /* UIBarItem+CASAdditions.h */, 1EC7196C1B67AF9C00700A14 /* UIBarItem+CASAdditions.m */, 1EC7196D1B67AF9C00700A14 /* UIColor+CASAdditions.h */, 1EC7196E1B67AF9C00700A14 /* UIColor+CASAdditions.m */, 1EC7196F1B67AF9C00700A14 /* UINavigationBar+CASAdditions.h */, 1EC719701B67AF9C00700A14 /* UINavigationBar+CASAdditions.m */, 1EC719711B67AF9C00700A14 /* UINavigationItem+CASAdditions.h */, 1EC719721B67AF9C00700A14 /* UINavigationItem+CASAdditions.m */, 1EC719731B67AF9C00700A14 /* UISlider+CASAdditions.h */, 1EC719741B67AF9C00700A14 /* UISlider+CASAdditions.m */, 1EC719751B67AF9C00700A14 /* UITabBar+CASAdditions.h */, 1EC719761B67AF9C00700A14 /* UITabBar+CASAdditions.m */, 1EC719771B67AF9C00700A14 /* UITextField+CASAdditions.h */, 1EC719781B67AF9C00700A14 /* UITextField+CASAdditions.m */, 1EC719791B67AF9C00700A14 /* UIToolbar+CASAdditions.h */, 1EC7197A1B67AF9C00700A14 /* UIToolbar+CASAdditions.m */, 1EC7197B1B67AF9C00700A14 /* UIView+CASAdditions.h */, 1EC7197C1B67AF9C00700A14 /* UIView+CASAdditions.m */, 1EC7197D1B67AF9C00700A14 /* UIViewController+CASAdditions.h */, 1EC7197E1B67AF9C00700A14 /* UIViewController+CASAdditions.m */, ); name = Additions; path = Classy/Additions; sourceTree = SOURCE_ROOT; }; 1EC719801B67AF9C00700A14 /* Parser */ = { isa = PBXGroup; children = ( 1EC719811B67AF9C00700A14 /* CASDeviceOSVersionItem.h */, 1EC719821B67AF9C00700A14 /* CASDeviceOSVersionItem.m */, 1EC719831B67AF9C00700A14 /* CASDeviceScreenSizeItem.h */, 1EC719841B67AF9C00700A14 /* CASDeviceScreenSizeItem.m */, 1EC719851B67AF9C00700A14 /* CASDeviceSelector.h */, 1EC719861B67AF9C00700A14 /* CASDeviceSelector.m */, 1EC719871B67AF9C00700A14 /* CASDeviceSelectorItem.h */, 1EC719881B67AF9C00700A14 /* CASDeviceTypeItem.h */, 1EC719891B67AF9C00700A14 /* CASDeviceTypeItem.m */, 1EC7198A1B67AF9C00700A14 /* CASExpressionSolver.h */, 1EC7198B1B67AF9C00700A14 /* CASExpressionSolver.m */, 1EC7198C1B67AF9C00700A14 /* CASInvocation.h */, 1EC7198D1B67AF9C00700A14 /* CASInvocation.m */, 1EC7198E1B67AF9C00700A14 /* CASLexer.h */, 1EC7198F1B67AF9C00700A14 /* CASLexer.m */, 1EC719901B67AF9C00700A14 /* CASParser.h */, 1EC719911B67AF9C00700A14 /* CASParser.m */, 1EC719921B67AF9C00700A14 /* CASStyleNode.h */, 1EC719931B67AF9C00700A14 /* CASStyleNode.m */, 1EC719941B67AF9C00700A14 /* CASStyleProperty.h */, 1EC719951B67AF9C00700A14 /* CASStyleProperty.m */, 1EC719961B67AF9C00700A14 /* CASStyleSelector.h */, 1EC719971B67AF9C00700A14 /* CASStyleSelector.m */, 1EC719981B67AF9C00700A14 /* CASStyler.h */, 1EC719991B67AF9C00700A14 /* CASStyler.m */, 1EC7199A1B67AF9C00700A14 /* CASTextAttributes.h */, 1EC7199B1B67AF9C00700A14 /* CASTextAttributes.m */, 1EC7199C1B67AF9C00700A14 /* CASToken.h */, 1EC7199D1B67AF9C00700A14 /* CASToken.m */, 1EC7199E1B67AF9C00700A14 /* CASUnitToken.h */, 1EC7199F1B67AF9C00700A14 /* CASUnitToken.m */, 1EC719A01B67AF9C00700A14 /* CASUtilities.h */, 1EC719A11B67AF9C00700A14 /* CASUtilities.m */, ); name = Parser; path = Classy/Parser; sourceTree = SOURCE_ROOT; }; 1EC719A21B67AF9C00700A14 /* Reflection */ = { isa = PBXGroup; children = ( 1EC719A31B67AF9C00700A14 /* CASArgumentDescriptor.h */, 1EC719A41B67AF9C00700A14 /* CASArgumentDescriptor.m */, 1EC719A51B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.h */, 1EC719A61B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.m */, 1EC719A71B67AF9C00700A14 /* CASObjectClassDescriptor.h */, 1EC719A81B67AF9C00700A14 /* CASObjectClassDescriptor.m */, 1EC719A91B67AF9C00700A14 /* CASPropertyDescriptor.h */, 1EC719AA1B67AF9C00700A14 /* CASPropertyDescriptor.m */, 1EC719AB1B67AF9C00700A14 /* CASRuntimeExtensions.h */, 1EC719AC1B67AF9C00700A14 /* CASRuntimeExtensions.m */, ); name = Reflection; path = Classy/Reflection; sourceTree = SOURCE_ROOT; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 1EC719401B67AE9D00700A14 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 1EC719CB1B67AF9C00700A14 /* Classy.h in Headers */, 1EC719B91B67AF9C00700A14 /* UIColor+CASAdditions.h in Headers */, 1EC719F31B67AF9C00700A14 /* CASPropertyDescriptor.h in Headers */, 1EC719ED1B67AF9C00700A14 /* CASArgumentDescriptor.h in Headers */, 1EC719F11B67AF9C00700A14 /* CASObjectClassDescriptor.h in Headers */, 1EC719EB1B67AF9C00700A14 /* CASUtilities.h in Headers */, 1EC719AF1B67AF9C00700A14 /* CASStyleableItem.h in Headers */, 1EC719E31B67AF9C00700A14 /* CASStyler.h in Headers */, 1EC719BB1B67AF9C00700A14 /* UINavigationBar+CASAdditions.h in Headers */, 1EC719C11B67AF9C00700A14 /* UITabBar+CASAdditions.h in Headers */, 1EC719C91B67AF9C00700A14 /* UIViewController+CASAdditions.h in Headers */, 1EC719B71B67AF9C00700A14 /* UIBarItem+CASAdditions.h in Headers */, 1EC719C31B67AF9C00700A14 /* UITextField+CASAdditions.h in Headers */, 1EC719C71B67AF9C00700A14 /* UIView+CASAdditions.h in Headers */, 1EC719BD1B67AF9C00700A14 /* UINavigationItem+CASAdditions.h in Headers */, 1EC719C51B67AF9C00700A14 /* UIToolbar+CASAdditions.h in Headers */, 1EC719DB1B67AF9C00700A14 /* CASParser.h in Headers */, 1EC719D31B67AF9C00700A14 /* CASDeviceTypeItem.h in Headers */, 1EC719E51B67AF9C00700A14 /* CASTextAttributes.h in Headers */, 1EC719B31B67AF9C00700A14 /* NSRegularExpression+CASAdditions.h in Headers */, 1EC719D51B67AF9C00700A14 /* CASExpressionSolver.h in Headers */, 1EC719D91B67AF9C00700A14 /* CASLexer.h in Headers */, 1EC719F51B67AF9C00700A14 /* CASRuntimeExtensions.h in Headers */, 1EC719BF1B67AF9C00700A14 /* UISlider+CASAdditions.h in Headers */, 1EC719AD1B67AF9C00700A14 /* CASStyleClassUtilities.h in Headers */, 1EC719EF1B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.h in Headers */, 1EC719CE1B67AF9C00700A14 /* CASDeviceScreenSizeItem.h in Headers */, 1EC719B51B67AF9C00700A14 /* NSString+CASAdditions.h in Headers */, 1EC719E71B67AF9C00700A14 /* CASToken.h in Headers */, 1EC719DD1B67AF9C00700A14 /* CASStyleNode.h in Headers */, 1EC719DF1B67AF9C00700A14 /* CASStyleProperty.h in Headers */, 1EC719E11B67AF9C00700A14 /* CASStyleSelector.h in Headers */, 1EC719D01B67AF9C00700A14 /* CASDeviceSelector.h in Headers */, 1EC719D21B67AF9C00700A14 /* CASDeviceSelectorItem.h in Headers */, 1EC719CC1B67AF9C00700A14 /* CASDeviceOSVersionItem.h in Headers */, 1EC719E91B67AF9C00700A14 /* CASUnitToken.h in Headers */, 1EC719B11B67AF9C00700A14 /* NSObject+CASSwizzle.h in Headers */, 1EC719D71B67AF9C00700A14 /* CASInvocation.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 1EC719421B67AE9D00700A14 /* Classy */ = { isa = PBXNativeTarget; buildConfigurationList = 1EC719591B67AE9D00700A14 /* Build configuration list for PBXNativeTarget "Classy" */; buildPhases = ( 1EC7193E1B67AE9D00700A14 /* Sources */, 1EC7193F1B67AE9D00700A14 /* Frameworks */, 1EC719401B67AE9D00700A14 /* Headers */, 1EC719411B67AE9D00700A14 /* Resources */, 1EC71A031B68C8F900700A14 /* Copy Artifacts */, ); buildRules = ( ); dependencies = ( ); name = Classy; productName = "Classy-Library"; productReference = 1EC719431B67AE9D00700A14 /* Classy.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 1EC7193A1B67AE9D00700A14 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Zeta Project Berlin Gmbh"; TargetAttributes = { 1EC719421B67AE9D00700A14 = { CreatedOnToolsVersion = 6.4; }; }; }; buildConfigurationList = 1EC7193D1B67AE9D00700A14 /* Build configuration list for PBXProject "Classy" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 1EC719391B67AE9D00700A14; productRefGroup = 1EC719441B67AE9D00700A14 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 1EC719421B67AE9D00700A14 /* Classy */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 1EC719411B67AE9D00700A14 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 1EC71A031B68C8F900700A14 /* Copy Artifacts */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Copy Artifacts"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "ARTIFACTS=\"Artifacts\"\nBASE_DIR=$SRCROOT/$ARTIFACTS\nif [ -d \"${BASE_DIR}\" ];\nthen\nrm -rf $BASE_DIR\nfi\nmkdir -p $BASE_DIR\ncp -R $DWARF_DSYM_FOLDER_PATH/* $BASE_DIR"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 1EC7193E1B67AE9D00700A14 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1EC719DA1B67AF9C00700A14 /* CASLexer.m in Sources */, 1EC719E01B67AF9C00700A14 /* CASStyleProperty.m in Sources */, 1EC719D11B67AF9C00700A14 /* CASDeviceSelector.m in Sources */, 1EC719B01B67AF9C00700A14 /* CASStyleableItem.m in Sources */, 1EC719CD1B67AF9C00700A14 /* CASDeviceOSVersionItem.m in Sources */, 1EC719B41B67AF9C00700A14 /* NSRegularExpression+CASAdditions.m in Sources */, 1EC719C21B67AF9C00700A14 /* UITabBar+CASAdditions.m in Sources */, 1EC719F01B67AF9C00700A14 /* CASAssociatedObjectsWeakWrapper.m in Sources */, 1EC719CA1B67AF9C00700A14 /* UIViewController+CASAdditions.m in Sources */, 1EC719E81B67AF9C00700A14 /* CASToken.m in Sources */, 1EC719DE1B67AF9C00700A14 /* CASStyleNode.m in Sources */, 1EC719B81B67AF9C00700A14 /* UIBarItem+CASAdditions.m in Sources */, 1EC719F61B67AF9C00700A14 /* CASRuntimeExtensions.m in Sources */, 1EC719EE1B67AF9C00700A14 /* CASArgumentDescriptor.m in Sources */, 1EC719C61B67AF9C00700A14 /* UIToolbar+CASAdditions.m in Sources */, 1EC719DC1B67AF9C00700A14 /* CASParser.m in Sources */, 1EC719D41B67AF9C00700A14 /* CASDeviceTypeItem.m in Sources */, 1EC719F21B67AF9C00700A14 /* CASObjectClassDescriptor.m in Sources */, 1EC719CF1B67AF9C00700A14 /* CASDeviceScreenSizeItem.m in Sources */, 1EC719E21B67AF9C00700A14 /* CASStyleSelector.m in Sources */, 1EC719BE1B67AF9C00700A14 /* UINavigationItem+CASAdditions.m in Sources */, 1EC719D81B67AF9C00700A14 /* CASInvocation.m in Sources */, 1EC719E61B67AF9C00700A14 /* CASTextAttributes.m in Sources */, 1EC719C41B67AF9C00700A14 /* UITextField+CASAdditions.m in Sources */, 1EC719B61B67AF9C00700A14 /* NSString+CASAdditions.m in Sources */, 1EC719F41B67AF9C00700A14 /* CASPropertyDescriptor.m in Sources */, 1EC719EA1B67AF9C00700A14 /* CASUnitToken.m in Sources */, 1EC719AE1B67AF9C00700A14 /* CASStyleClassUtilities.m in Sources */, 1EC719D61B67AF9C00700A14 /* CASExpressionSolver.m in Sources */, 1EC719E41B67AF9C00700A14 /* CASStyler.m in Sources */, 1EC719BC1B67AF9C00700A14 /* UINavigationBar+CASAdditions.m in Sources */, 1EC719C81B67AF9C00700A14 /* UIView+CASAdditions.m in Sources */, 1EC719B21B67AF9C00700A14 /* NSObject+CASSwizzle.m in Sources */, 1EC719C01B67AF9C00700A14 /* UISlider+CASAdditions.m in Sources */, 1EC719BA1B67AF9C00700A14 /* UIColor+CASAdditions.m in Sources */, 1EC719EC1B67AF9C00700A14 /* CASUtilities.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 1EC719571B67AE9D00700A14 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 1EC719581B67AE9D00700A14 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 1EC7195A1B67AE9D00700A14 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Classy/Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "wearezeta.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Classy; SKIP_INSTALL = NO; }; name = Debug; }; 1EC7195B1B67AE9D00700A14 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Classy/Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "wearezeta.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Classy; SKIP_INSTALL = NO; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1EC7193D1B67AE9D00700A14 /* Build configuration list for PBXProject "Classy" */ = { isa = XCConfigurationList; buildConfigurations = ( 1EC719571B67AE9D00700A14 /* Debug */, 1EC719581B67AE9D00700A14 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1EC719591B67AE9D00700A14 /* Build configuration list for PBXNativeTarget "Classy" */ = { isa = XCConfigurationList; buildConfigurations = ( 1EC7195A1B67AE9D00700A14 /* Debug */, 1EC7195B1B67AE9D00700A14 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 1EC7193A1B67AE9D00700A14 /* Project object */; } ================================================ FILE: Classy.xcodeproj/xcshareddata/xcschemes/Classy.xcscheme ================================================ ================================================ FILE: Example/ClassyExample/CASAppDelegate.h ================================================ // // CASAppDelegate.h // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface CASAppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Example/ClassyExample/CASAppDelegate.m ================================================ // // CASAppDelegate.m // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASAppDelegate.h" #import "CASRootViewController.h" #import "Classy.h" @implementation CASAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { #if TARGET_IPHONE_SIMULATOR // get absolute file path of stylesheet, using relative path NSString *absoluteFilePath = CASAbsoluteFilePath(@"Stylesheets/stylesheet.cas"); [CASStyler defaultStyler].watchFilePath = absoluteFilePath; #endif // Move files to caches directory for referencing in stylesheet NSString *caches = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"]; NSString *path = [caches stringByAppendingPathComponent:@"Ball.png"]; [[NSFileManager defaultManager] createDirectoryAtPath:caches withIntermediateDirectories:YES attributes:nil error:nil]; [[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:@"Ball" ofType:@"png"] toPath:path error:nil]; // Move quarter to documents for testing self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.backgroundColor = UIColor.whiteColor; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:CASRootViewController.new]; self.window.rootViewController = navigationController; [self.window makeKeyAndVisible]; return YES; } @end ================================================ FILE: Example/ClassyExample/CASCatalogViewController.h ================================================ // // CASCatalogViewController.h // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface CASCatalogViewController : UIViewController @end ================================================ FILE: Example/ClassyExample/CASCatalogViewController.m ================================================ // // CASCatalogViewController.m // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASCatalogViewController.h" @interface CASCatalogViewController () @end @implementation CASCatalogViewController - (id)init { self = [super init]; if (!self) return nil; self.title = @"Catalog"; return self; } - (void)viewDidLoad { [super viewDidLoad]; } @end ================================================ FILE: Example/ClassyExample/CASRootViewController.h ================================================ // // CASRootViewController.h // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface CASRootViewController : UITableViewController @end ================================================ FILE: Example/ClassyExample/CASRootViewController.m ================================================ // // CASRootViewController.m // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASRootViewController.h" #import "CASSimpleFormViewController.h" #import "CASCatalogViewController.h" static NSString * const CASRootCellReuseIdentifier = @"CASRootCellReuseIdentifier"; @interface CASRootViewController () @property (nonatomic, strong) NSArray *exampleControllers; @end @implementation CASRootViewController - (id)init { self = [super init]; if (!self) return nil; self.title = @"Examples"; self.exampleControllers = @[ CASSimpleFormViewController.new, CASCatalogViewController.new, ]; return self; } - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:CASRootCellReuseIdentifier]; } #pragma mark - UITableViewDataSource - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController *viewController = self.exampleControllers[indexPath.row]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CASRootCellReuseIdentifier forIndexPath:indexPath]; cell.textLabel.text = viewController.title; return cell; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.exampleControllers.count; } #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController *viewController = self.exampleControllers[indexPath.row]; [self.navigationController pushViewController:viewController animated:YES]; } @end ================================================ FILE: Example/ClassyExample/CASSimpleFormViewController.h ================================================ // // CASSimpleFormViewController.h // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface CASSimpleFormViewController : UIViewController @end ================================================ FILE: Example/ClassyExample/CASSimpleFormViewController.m ================================================ // // CASSimpleFormViewController.m // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASSimpleFormViewController.h" #import @interface CASSimpleFormViewController () @end @implementation CASSimpleFormViewController - (id)init { self = [super init]; if (!self) return nil; self.title = @"Simple Form"; return self; } - (void)viewDidLoad { [super viewDidLoad]; UIView *v = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; v.backgroundColor = [UIColor redColor]; v.cas_styleClass = @"shadow-view"; [self.view addSubview:v]; UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 300, self.view.bounds.size.width - 100, 300)]; textLabel.text = @"Quick lazy Swift jumps over the lazy iOS/OS X developer's leg."; textLabel.numberOfLines = 0; textLabel.cas_styleClass = @"text-label"; [self.view addSubview:textLabel]; } @end ================================================ FILE: Example/ClassyExample/ClassyExample-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Example/ClassyExample/ClassyExample-Prefix.pch ================================================ // // Prefix header // // The contents of this file are implicitly included at the beginning of every source file. // #import #ifndef __IPHONE_3_0 #warning "This project uses features only available in iOS SDK 3.0 and later." #endif #ifdef __OBJC__ #import #import #endif ================================================ FILE: Example/ClassyExample/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/ClassyExample/Images.xcassets/LaunchImage.launchimage/Contents.json ================================================ { "images" : [ { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "subtype" : "retina4", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "1x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "1x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/ClassyExample/Stylesheets/stylesheet.cas ================================================ //TODO flesh out example @import "variables.cas" UITableView UILabel { text-color $main-color } ^UIViewController > UIView { background-color: blue; } UIView.shadow-view { layer @ shadow-radius 10 shadow-opacity 1 shadow-color black shadow-offset 0,0 } UILabel.text-label { font: System-Medium 30; backgroundColor: white; } ================================================ FILE: Example/ClassyExample/Stylesheets/variables.cas ================================================ $main-color = red ================================================ FILE: Example/ClassyExample/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: Example/ClassyExample/main.m ================================================ // // main.m // ClassyExample // // Created by Jonas Budelmann on 21/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import #import "CASAppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([CASAppDelegate class])); } } ================================================ FILE: Example/ClassyExample.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 4E506422190EF2F8003A03B0 /* Ball.png in Resources */ = {isa = PBXBuildFile; fileRef = 4E506421190EF2F8003A03B0 /* Ball.png */; }; BA23251D9EB84FA09338B05C /* libPods-ClassyExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E9C8ED2DF59B45B2A38549AB /* libPods-ClassyExample.a */; }; DD355BDB183E9CD30071E543 /* variables.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD355BDA183E9CD30071E543 /* variables.cas */; }; DD61F4441816370F004A8777 /* stylesheet.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD61F4431816370F004A8777 /* stylesheet.cas */; }; DDAB256B1814BBBB00F3DAAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDAB256A1814BBBB00F3DAAE /* Foundation.framework */; }; DDAB256D1814BBBB00F3DAAE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDAB256C1814BBBB00F3DAAE /* CoreGraphics.framework */; }; DDAB256F1814BBBB00F3DAAE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDAB256E1814BBBB00F3DAAE /* UIKit.framework */; }; DDAB25751814BBBB00F3DAAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDAB25731814BBBB00F3DAAE /* InfoPlist.strings */; }; DDAB25771814BBBB00F3DAAE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DDAB25761814BBBB00F3DAAE /* main.m */; }; DDAB257B1814BBBB00F3DAAE /* CASAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DDAB257A1814BBBB00F3DAAE /* CASAppDelegate.m */; }; DDAB257D1814BBBB00F3DAAE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDAB257C1814BBBB00F3DAAE /* Images.xcassets */; }; DDFE54A51814EBBC003BF9C6 /* CASRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDFE54A41814EBBC003BF9C6 /* CASRootViewController.m */; }; DDFE54A81814EF07003BF9C6 /* CASSimpleFormViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDFE54A71814EF07003BF9C6 /* CASSimpleFormViewController.m */; }; DDFE54AB1814EF41003BF9C6 /* CASCatalogViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDFE54AA1814EF41003BF9C6 /* CASCatalogViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 4E506421190EF2F8003A03B0 /* Ball.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Ball.png; sourceTree = ""; }; 7ED1C295E2839DCB632777B7 /* Pods-ClassyExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClassyExample.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-ClassyExample/Pods-ClassyExample.debug.xcconfig"; sourceTree = ""; }; C9672A29499BC4370756B84D /* Pods-ClassyExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClassyExample.release.xcconfig"; path = "../Pods/Target Support Files/Pods-ClassyExample/Pods-ClassyExample.release.xcconfig"; sourceTree = ""; }; DD355BDA183E9CD30071E543 /* variables.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = variables.cas; sourceTree = ""; }; DD61F4431816370F004A8777 /* stylesheet.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = stylesheet.cas; sourceTree = ""; }; DDAB25671814BBBB00F3DAAE /* ClassyExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClassyExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDAB256A1814BBBB00F3DAAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; DDAB256C1814BBBB00F3DAAE /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DDAB256E1814BBBB00F3DAAE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; DDAB25721814BBBB00F3DAAE /* ClassyExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ClassyExample-Info.plist"; sourceTree = ""; }; DDAB25741814BBBB00F3DAAE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DDAB25761814BBBB00F3DAAE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DDAB25781814BBBB00F3DAAE /* ClassyExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ClassyExample-Prefix.pch"; sourceTree = ""; }; DDAB25791814BBBB00F3DAAE /* CASAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CASAppDelegate.h; sourceTree = ""; }; DDAB257A1814BBBB00F3DAAE /* CASAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CASAppDelegate.m; sourceTree = ""; }; DDAB257C1814BBBB00F3DAAE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; DDAB25831814BBBB00F3DAAE /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; DDFE54A31814EBBC003BF9C6 /* CASRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASRootViewController.h; sourceTree = ""; }; DDFE54A41814EBBC003BF9C6 /* CASRootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASRootViewController.m; sourceTree = ""; }; DDFE54A61814EF07003BF9C6 /* CASSimpleFormViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASSimpleFormViewController.h; sourceTree = ""; }; DDFE54A71814EF07003BF9C6 /* CASSimpleFormViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASSimpleFormViewController.m; sourceTree = ""; }; DDFE54A91814EF41003BF9C6 /* CASCatalogViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASCatalogViewController.h; sourceTree = ""; }; DDFE54AA1814EF41003BF9C6 /* CASCatalogViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASCatalogViewController.m; sourceTree = ""; }; E9C8ED2DF59B45B2A38549AB /* libPods-ClassyExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ClassyExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ DDAB25641814BBBB00F3DAAE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DDAB256D1814BBBB00F3DAAE /* CoreGraphics.framework in Frameworks */, DDAB256F1814BBBB00F3DAAE /* UIKit.framework in Frameworks */, DDAB256B1814BBBB00F3DAAE /* Foundation.framework in Frameworks */, BA23251D9EB84FA09338B05C /* libPods-ClassyExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 4E506420190EF2E6003A03B0 /* Images */ = { isa = PBXGroup; children = ( 4E506421190EF2F8003A03B0 /* Ball.png */, ); name = Images; sourceTree = ""; }; C2C33FB1E22E3AB19531DE4B /* Pods */ = { isa = PBXGroup; children = ( 7ED1C295E2839DCB632777B7 /* Pods-ClassyExample.debug.xcconfig */, C9672A29499BC4370756B84D /* Pods-ClassyExample.release.xcconfig */, ); name = Pods; sourceTree = ""; }; DD61F4421816370F004A8777 /* Stylesheets */ = { isa = PBXGroup; children = ( DD61F4431816370F004A8777 /* stylesheet.cas */, DD355BDA183E9CD30071E543 /* variables.cas */, ); path = Stylesheets; sourceTree = ""; }; DDAB255E1814BBBB00F3DAAE = { isa = PBXGroup; children = ( DDAB25701814BBBB00F3DAAE /* ClassyExample */, DDAB25691814BBBB00F3DAAE /* Frameworks */, DDAB25681814BBBB00F3DAAE /* Products */, C2C33FB1E22E3AB19531DE4B /* Pods */, ); sourceTree = ""; }; DDAB25681814BBBB00F3DAAE /* Products */ = { isa = PBXGroup; children = ( DDAB25671814BBBB00F3DAAE /* ClassyExample.app */, ); name = Products; sourceTree = ""; }; DDAB25691814BBBB00F3DAAE /* Frameworks */ = { isa = PBXGroup; children = ( DDAB256A1814BBBB00F3DAAE /* Foundation.framework */, DDAB256C1814BBBB00F3DAAE /* CoreGraphics.framework */, DDAB256E1814BBBB00F3DAAE /* UIKit.framework */, DDAB25831814BBBB00F3DAAE /* XCTest.framework */, E9C8ED2DF59B45B2A38549AB /* libPods-ClassyExample.a */, ); name = Frameworks; sourceTree = ""; }; DDAB25701814BBBB00F3DAAE /* ClassyExample */ = { isa = PBXGroup; children = ( DDAB25791814BBBB00F3DAAE /* CASAppDelegate.h */, DDAB257A1814BBBB00F3DAAE /* CASAppDelegate.m */, DDFE54A31814EBBC003BF9C6 /* CASRootViewController.h */, DDFE54A41814EBBC003BF9C6 /* CASRootViewController.m */, DDFE54A61814EF07003BF9C6 /* CASSimpleFormViewController.h */, DDFE54A71814EF07003BF9C6 /* CASSimpleFormViewController.m */, DDFE54A91814EF41003BF9C6 /* CASCatalogViewController.h */, DDFE54AA1814EF41003BF9C6 /* CASCatalogViewController.m */, DDAB257C1814BBBB00F3DAAE /* Images.xcassets */, DD61F4421816370F004A8777 /* Stylesheets */, 4E506420190EF2E6003A03B0 /* Images */, DDAB25711814BBBB00F3DAAE /* Supporting Files */, ); path = ClassyExample; sourceTree = ""; }; DDAB25711814BBBB00F3DAAE /* Supporting Files */ = { isa = PBXGroup; children = ( DDAB25721814BBBB00F3DAAE /* ClassyExample-Info.plist */, DDAB25731814BBBB00F3DAAE /* InfoPlist.strings */, DDAB25761814BBBB00F3DAAE /* main.m */, DDAB25781814BBBB00F3DAAE /* ClassyExample-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ DDAB25661814BBBB00F3DAAE /* ClassyExample */ = { isa = PBXNativeTarget; buildConfigurationList = DDAB25931814BBBB00F3DAAE /* Build configuration list for PBXNativeTarget "ClassyExample" */; buildPhases = ( 501B6904610449669F2F8026 /* [CP] Check Pods Manifest.lock */, DDAB25631814BBBB00F3DAAE /* Sources */, DDAB25641814BBBB00F3DAAE /* Frameworks */, DDAB25651814BBBB00F3DAAE /* Resources */, 244548EB6398419ABFC738FE /* [CP] Copy Pods Resources */, E70A31B7D70956E8610151C2 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ClassyExample; productName = ClassyExample; productReference = DDAB25671814BBBB00F3DAAE /* ClassyExample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ DDAB255F1814BBBB00F3DAAE /* Project object */ = { isa = PBXProject; attributes = { CLASSPREFIX = CAS; LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Jonas Budelmann"; }; buildConfigurationList = DDAB25621814BBBB00F3DAAE /* Build configuration list for PBXProject "ClassyExample" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = DDAB255E1814BBBB00F3DAAE; productRefGroup = DDAB25681814BBBB00F3DAAE /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( DDAB25661814BBBB00F3DAAE /* ClassyExample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ DDAB25651814BBBB00F3DAAE /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DDAB25751814BBBB00F3DAAE /* InfoPlist.strings in Resources */, DDAB257D1814BBBB00F3DAAE /* Images.xcassets in Resources */, DD61F4441816370F004A8777 /* stylesheet.cas in Resources */, 4E506422190EF2F8003A03B0 /* Ball.png in Resources */, DD355BDB183E9CD30071E543 /* variables.cas in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 244548EB6398419ABFC738FE /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-ClassyExample/Pods-ClassyExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; 501B6904610449669F2F8026 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; E70A31B7D70956E8610151C2 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-ClassyExample/Pods-ClassyExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ DDAB25631814BBBB00F3DAAE /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DDFE54AB1814EF41003BF9C6 /* CASCatalogViewController.m in Sources */, DDFE54A81814EF07003BF9C6 /* CASSimpleFormViewController.m in Sources */, DDAB257B1814BBBB00F3DAAE /* CASAppDelegate.m in Sources */, DDAB25771814BBBB00F3DAAE /* main.m in Sources */, DDFE54A51814EBBC003BF9C6 /* CASRootViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ DDAB25731814BBBB00F3DAAE /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( DDAB25741814BBBB00F3DAAE /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ DDAB25911814BBBB00F3DAAE /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; DDAB25921814BBBB00F3DAAE /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; DDAB25941814BBBB00F3DAAE /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7ED1C295E2839DCB632777B7 /* Pods-ClassyExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ClassyExample/ClassyExample-Prefix.pch"; INFOPLIST_FILE = "ClassyExample/ClassyExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_BUNDLE_IDENTIFIER = "com.cloudling.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; DDAB25951814BBBB00F3DAAE /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = C9672A29499BC4370756B84D /* Pods-ClassyExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ClassyExample/ClassyExample-Prefix.pch"; INFOPLIST_FILE = "ClassyExample/ClassyExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_BUNDLE_IDENTIFIER = "com.cloudling.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ DDAB25621814BBBB00F3DAAE /* Build configuration list for PBXProject "ClassyExample" */ = { isa = XCConfigurationList; buildConfigurations = ( DDAB25911814BBBB00F3DAAE /* Debug */, DDAB25921814BBBB00F3DAAE /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DDAB25931814BBBB00F3DAAE /* Build configuration list for PBXNativeTarget "ClassyExample" */ = { isa = XCConfigurationList; buildConfigurations = ( DDAB25941814BBBB00F3DAAE /* Debug */, DDAB25951814BBBB00F3DAAE /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = DDAB255F1814BBBB00F3DAAE /* Project object */; } ================================================ FILE: Example/ClassyExample.xcodeproj/xcshareddata/xcschemes/ClassyExample.xcscheme ================================================ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Jonas Budelmann 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. ================================================ FILE: Podfile ================================================ workspace 'Classy' platform :ios, '9.0' project 'Example/ClassyExample' target 'ClassyExample' do pod 'Classy', :path => './' end target 'ClassyTestsLoader' do project 'Tests/ClassyTests' pod 'Classy', :path => './' end target 'ClassyTests' do project 'Tests/ClassyTests' pod 'Expecta' pod 'OCMockito' end # add settings needed to generate test coverage data post_install do |installer| COV_TARGET_NAME = "Pods-ClassyTestsLoader" EXPORT_ENV_PHASE_NAME = "Export Environment Vars" EXPORT_ENV_PHASE_SCRIPT = "export | egrep '( BUILT_PRODUCTS_DIR)|(CURRENT_ARCH)|(OBJECT_FILE_DIR_normal)|(SRCROOT)|(OBJROOT)' > " << File.join(installer.config.installation_root, "/script/env.sh") # find target classy_pods_target = installer.pods_project.targets.find{ |target| target.name == COV_TARGET_NAME } unless classy_pods_target raise ::Pod::Informative, "Failed to find '" << COV_TARGET_NAME << "' target" end # add build settings classy_pods_target.build_configurations.each do |config| config.build_settings['GCC_GENERATE_TEST_COVERAGE_FILES'] = 'YES' config.build_settings['GCC_INSTRUMENT_PROGRAM_FLOW_ARCS'] = 'YES' end # add build phase phase = classy_pods_target.shell_script_build_phases.select{ |bp| bp.name == EXPORT_ENV_PHASE_NAME }.first || classy_pods_target.new_shell_script_build_phase(EXPORT_ENV_PHASE_NAME) phase.shell_path = "/bin/sh" phase.shell_script = EXPORT_ENV_PHASE_SCRIPT phase.show_env_vars_in_log = '0' end ================================================ FILE: README.md ================================================ # Classy [![Build Status](https://travis-ci.org/cloudkite/Classy.svg?branch=master)](https://travis-ci.org/cloudkite/Classy) [![Coverage Status](https://img.shields.io/coveralls/cloudkite/Classy.svg)](https://coveralls.io/r/cloudkite/Classy?branch=master) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![iOS Level](https://img.shields.io/badge/iOS-9%2B-brightgreen.svg?style=flat)](https://developer.apple.com) ** Classy is [looking for contributors](https://github.com/cloudkite/Classy/issues/108) ** Not CSS. Instead of trying to force UIKit to fit CSS syntax, properties, conventions and constructs. Classy is a stylesheet system built from the ground up to work in harmony with UIKit. It borrows the best ideas from CSS and introduces new syntax, conventions and constructs where appropriate. Plays nice with Interface Builder and views created in code. For detailed usage see the [docs](http://classykit.github.io/Classy/) or the [wiki](https://github.com/ClassyKit/Classy/wiki). ## Supports UIAppearance Classy supports all [UIAppearance properties and methods](https://github.com/cloudkite/Classy/blob/master/Tests/UIAppearance-setters.md) and much more. But instead of being limited to `+appearanceWhenContainedIn:` and `+appearance`, Classy gives you much greater control over which views are styled and with what values. ## Example Stylesheets Classy features a very flexible, nestable syntax. Classy makes `{` `}` `:` `;` all optional so you can choose a style that suits you. It also saves you from worrying about small syntax mistakes like accidentally forgetting to end a line with a `;` You can use `{` `}` `:` `;` to delimit your stylesheets ```Scala @import "other_stylesheet.cas"; $mainColor = #e1e1e1; // Supports your custom UIView subclasses MYCustomView { background-color: $mainColor; title-insets: 5, 10, 5, 10; > UIProgressView.tinted { progress-tint-color: rgb(200, 155, 110, 0.6); track-tint-color: yellow; } } /* * Supports Single or Multi-line comments */ ^UIButton.warning, UIView.warning ^UIButton { title-color[state:highlighted]: #e3e3e3; } ``` **OR** you can use whitespace to delimit your sytlesheets ```Scala @import "other_stylesheet.cas" $mainColor = #e1e1e1 // Supports your custom UIView subclasses MYCustomView background-color $mainColor title-insets 5, 10, 5, 10 > UIProgressView.tinted progress-tint-color rgb(200, 155, 110, 0.6) track-tint-color yellow /* * Supports Single or Multi-line comments */ ^UIButton.warning, UIView.warning ^UIButton title-color[state:highlighted] #e3e3e3 ``` ## Live Reload Live reload can dramatically speed up your development time, with live reload enabled you can instantaneously see your stylesheet changes. Without having to rebuild and navigate back to the same spot within your app. For more details about these features and more visit the [docs](http://classykit.github.io/Classy/) or the [wiki](https://github.com/ClassyKit/Classy/wiki). ## Inspiration - Syntax inspired by [stylus](http://learnboost.github.io/stylus/) - Property reflection [Mantle](https://github.com/github/mantle) ================================================ FILE: Tests/ClassyTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: Tests/ClassyTests-Prefix.pch ================================================ // // Prefix header // // The contents of this file are implicitly included at the beginning of every source file. // #ifdef __OBJC__ #import #import #import "XCTest+Spec.h" #define EXP_SHORTHAND #import "Expecta.h" #define HC_SHORTHAND #import #define MOCKITO_SHORTHAND #import #endif ================================================ FILE: Tests/ClassyTests.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 4ED0F9A3198B476E00C9FE2C /* UIKit-MultipleClasses.cas in Resources */ = {isa = PBXBuildFile; fileRef = 4ED0F9A1198B465600C9FE2C /* UIKit-MultipleClasses.cas */; }; 9980D2916D344434ACF1CB78 /* libPods-ClassyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C594C855E67D4ABAAB9D8034 /* libPods-ClassyTests.a */; }; BDABE4F7F7AD42D589D779BD /* libPods-ClassyTestsLoader.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13B648C088D7485783343464 /* libPods-ClassyTestsLoader.a */; }; DD10AD361819FA6800A5B71D /* CustomView-Basic.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD2E1819FA6800A5B71D /* CustomView-Basic.cas */; }; DD10AD371819FA6800A5B71D /* Properties-Basic.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD2F1819FA6800A5B71D /* Properties-Basic.cas */; }; DD10AD381819FA6800A5B71D /* Selectors-Messy.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD301819FA6800A5B71D /* Selectors-Messy.cas */; }; DD10AD391819FA6800A5B71D /* Selectors-Complex.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD311819FA6800A5B71D /* Selectors-Complex.cas */; }; DD10AD3A1819FA6800A5B71D /* Selectors-Hierarchy.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD321819FA6800A5B71D /* Selectors-Hierarchy.cas */; }; DD10AD3B1819FA6800A5B71D /* Selectors-Indentation.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD331819FA6800A5B71D /* Selectors-Indentation.cas */; }; DD10AD3C1819FA6800A5B71D /* UIAppearance-Basic.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD341819FA6800A5B71D /* UIAppearance-Basic.cas */; }; DD10AD3D1819FA6800A5B71D /* UIKit-Basic.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD351819FA6800A5B71D /* UIKit-Basic.cas */; }; DD10AD47181A16EA00A5B71D /* CASArgumentDescriptorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD40181A16EA00A5B71D /* CASArgumentDescriptorSpec.m */; }; DD10AD48181A16EA00A5B71D /* CASLexerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD41181A16EA00A5B71D /* CASLexerSpec.m */; }; DD10AD49181A16EA00A5B71D /* CASParserSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD42181A16EA00A5B71D /* CASParserSpec.m */; }; DD10AD4A181A16EA00A5B71D /* CASRuntimeExtensionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD43181A16EA00A5B71D /* CASRuntimeExtensionsSpec.m */; }; DD10AD4B181A16EA00A5B71D /* CASStylePropertySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD44181A16EA00A5B71D /* CASStylePropertySpec.m */; }; DD10AD4C181A16EA00A5B71D /* CASStylerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD45181A16EA00A5B71D /* CASStylerSpec.m */; }; DD10AD4D181A16EA00A5B71D /* CASTokenSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD46181A16EA00A5B71D /* CASTokenSpec.m */; }; DD10AD4F181A172F00A5B71D /* CASUIKitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD4E181A172F00A5B71D /* CASUIKitSpec.m */; }; DD10AD51181A174A00A5B71D /* CASUIAppearanceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD50181A174A00A5B71D /* CASUIAppearanceSpec.m */; }; DD10AD53181A176000A5B71D /* CASCustomViewSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD10AD52181A176000A5B71D /* CASCustomViewSpec.m */; }; DD10AD55181A1C9100A5B71D /* Properties-Args.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD10AD54181A1C9100A5B71D /* Properties-Args.cas */; }; DD148307186482AC00E0F5EF /* Variables-Basic.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD148306186482AC00E0F5EF /* Variables-Basic.cas */; }; DD175F8F1820C81900139EBE /* test_image_1.png in Resources */ = {isa = PBXBuildFile; fileRef = DD175F8B1820C81900139EBE /* test_image_1.png */; }; DD175F901820C81A00139EBE /* test_image_2.png in Resources */ = {isa = PBXBuildFile; fileRef = DD175F8C1820C81900139EBE /* test_image_2.png */; }; DD175F911820C81A00139EBE /* test_image_3.png in Resources */ = {isa = PBXBuildFile; fileRef = DD175F8D1820C81900139EBE /* test_image_3.png */; }; DD175F921820C81A00139EBE /* test_image_4.png in Resources */ = {isa = PBXBuildFile; fileRef = DD175F8E1820C81900139EBE /* test_image_4.png */; }; DD2C8C931863D7E300F06746 /* Selectors-Media-Queries-styler.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD2C8C921863D7E300F06746 /* Selectors-Media-Queries-styler.cas */; }; DD52F105183DCB2600F31A3F /* Import-1.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD52F102183DCB2600F31A3F /* Import-1.cas */; }; DD52F106183DCB2600F31A3F /* Import-2.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD52F103183DCB2600F31A3F /* Import-2.cas */; }; DD52F107183DCB2600F31A3F /* Import-Base.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD52F104183DCB2600F31A3F /* Import-Base.cas */; }; DD592B13181DE5E800C30F7F /* Selectors-Nested.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD592B12181DE5E800C30F7F /* Selectors-Nested.cas */; }; DD5B2DB0189532DC00CB6531 /* Variables-Injection.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD5B2DAF189532DC00CB6531 /* Variables-Injection.cas */; }; DD748A5C184446A700DE426A /* UIDevice+CASMockDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = DD748A5B184446A700DE426A /* UIDevice+CASMockDevice.m */; }; DD7C18441827464C008110D7 /* Properties-Nested.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD7C18431827464C008110D7 /* Properties-Nested.cas */; }; DD824863183964CB00DDD6EF /* CASExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD824862183964CB00DDD6EF /* CASExampleViewController.m */; }; DD8248651839684700DDD6EF /* UIView_CASAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8248641839684700DDD6EF /* UIView_CASAdditionsSpec.m */; }; DD84B3E0183ACB0A0009ACAA /* CASTestsAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD84B3DF183ACB0A0009ACAA /* CASTestsAppDelegate.m */; }; DD84B3E3183ACC370009ACAA /* GcovTestObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = DD84B3E2183ACC370009ACAA /* GcovTestObserver.m */; }; DD85DDBF1841C1DC00126E8C /* Selectors-Media-Queries.cas in Resources */ = {isa = PBXBuildFile; fileRef = DD85DDBE1841C1DC00126E8C /* Selectors-Media-Queries.cas */; }; DD9389DE18206A0100C0FFA7 /* CASStyleSelectorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9389DD18206A0100C0FFA7 /* CASStyleSelectorSpec.m */; }; DDB16C9A1895563900256EC6 /* Injected-File.cas in Resources */ = {isa = PBXBuildFile; fileRef = DDB16C991895563900256EC6 /* Injected-File.cas */; }; DDBF583418113BC10059B8D3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBF582518112ECA0059B8D3 /* XCTest.framework */; }; DDBF583518113BC10059B8D3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDD3D48C1810D6DA00E378A2 /* Foundation.framework */; }; DDBF583618113BC10059B8D3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDD3D4901810D6DA00E378A2 /* UIKit.framework */; }; DDBF586818113FCC0059B8D3 /* CASExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBF586018113FCC0059B8D3 /* CASExampleView.m */; }; DDD3D48D1810D6DA00E378A2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDD3D48C1810D6DA00E378A2 /* Foundation.framework */; }; DDD3D48F1810D6DA00E378A2 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDD3D48E1810D6DA00E378A2 /* CoreGraphics.framework */; }; DDD3D4911810D6DA00E378A2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDD3D4901810D6DA00E378A2 /* UIKit.framework */; }; DDD3D4C01810D8B100E378A2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDD3D4BD1810D8B100E378A2 /* Images.xcassets */; }; DDD3D4C11810D8B100E378A2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD3D4BE1810D8B100E378A2 /* main.m */; }; DDD3D5001810DF7C00E378A2 /* test_image_5.png in Resources */ = {isa = PBXBuildFile; fileRef = DDD3D4FF1810DF7C00E378A2 /* test_image_5.png */; }; DDE7E51F18915B0600BA5718 /* Precedence-1.cas in Resources */ = {isa = PBXBuildFile; fileRef = DDE7E51D18915B0600BA5718 /* Precedence-1.cas */; }; DDE7E52018915B0600BA5718 /* Precedence-2.cas in Resources */ = {isa = PBXBuildFile; fileRef = DDE7E51E18915B0600BA5718 /* Precedence-2.cas */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ DDBF584018113BC10059B8D3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DDD3D4811810D6DA00E378A2 /* Project object */; proxyType = 1; remoteGlobalIDString = DDD3D4881810D6DA00E378A2; remoteInfo = ClassyTestsLoader; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 079339911EDA90D0E7C07ECD /* Pods-ClassyTestsLoader.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClassyTestsLoader.release.xcconfig"; path = "../Pods/Target Support Files/Pods-ClassyTestsLoader/Pods-ClassyTestsLoader.release.xcconfig"; sourceTree = ""; }; 13B648C088D7485783343464 /* libPods-ClassyTestsLoader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ClassyTestsLoader.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 4ED0F9A1198B465600C9FE2C /* UIKit-MultipleClasses.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UIKit-MultipleClasses.cas"; sourceTree = ""; }; 6974873D3DC5638F91AF7A18 /* Pods-ClassyTestsLoader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClassyTestsLoader.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-ClassyTestsLoader/Pods-ClassyTestsLoader.debug.xcconfig"; sourceTree = ""; }; B70022280A544A8418F356ED /* Pods-ClassyTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClassyTests.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-ClassyTests/Pods-ClassyTests.debug.xcconfig"; sourceTree = ""; }; BAA4DBC31A8D4B53D68A49E5 /* Pods-ClassyTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClassyTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-ClassyTests/Pods-ClassyTests.release.xcconfig"; sourceTree = ""; }; C594C855E67D4ABAAB9D8034 /* libPods-ClassyTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ClassyTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; DD10AD2E1819FA6800A5B71D /* CustomView-Basic.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CustomView-Basic.cas"; sourceTree = ""; }; DD10AD2F1819FA6800A5B71D /* Properties-Basic.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Properties-Basic.cas"; sourceTree = ""; }; DD10AD301819FA6800A5B71D /* Selectors-Messy.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Messy.cas"; sourceTree = ""; }; DD10AD311819FA6800A5B71D /* Selectors-Complex.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Complex.cas"; sourceTree = ""; }; DD10AD321819FA6800A5B71D /* Selectors-Hierarchy.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Hierarchy.cas"; sourceTree = ""; }; DD10AD331819FA6800A5B71D /* Selectors-Indentation.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Indentation.cas"; sourceTree = ""; }; DD10AD341819FA6800A5B71D /* UIAppearance-Basic.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UIAppearance-Basic.cas"; sourceTree = ""; }; DD10AD351819FA6800A5B71D /* UIKit-Basic.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UIKit-Basic.cas"; sourceTree = ""; }; DD10AD40181A16EA00A5B71D /* CASArgumentDescriptorSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASArgumentDescriptorSpec.m; sourceTree = ""; }; DD10AD41181A16EA00A5B71D /* CASLexerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASLexerSpec.m; sourceTree = ""; }; DD10AD42181A16EA00A5B71D /* CASParserSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASParserSpec.m; sourceTree = ""; }; DD10AD43181A16EA00A5B71D /* CASRuntimeExtensionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASRuntimeExtensionsSpec.m; sourceTree = ""; }; DD10AD44181A16EA00A5B71D /* CASStylePropertySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStylePropertySpec.m; sourceTree = ""; }; DD10AD45181A16EA00A5B71D /* CASStylerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStylerSpec.m; sourceTree = ""; }; DD10AD46181A16EA00A5B71D /* CASTokenSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASTokenSpec.m; sourceTree = ""; }; DD10AD4E181A172F00A5B71D /* CASUIKitSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASUIKitSpec.m; sourceTree = ""; }; DD10AD50181A174A00A5B71D /* CASUIAppearanceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASUIAppearanceSpec.m; sourceTree = ""; }; DD10AD52181A176000A5B71D /* CASCustomViewSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASCustomViewSpec.m; sourceTree = ""; }; DD10AD54181A1C9100A5B71D /* Properties-Args.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Properties-Args.cas"; sourceTree = ""; }; DD148306186482AC00E0F5EF /* Variables-Basic.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Variables-Basic.cas"; sourceTree = ""; }; DD175F8B1820C81900139EBE /* test_image_1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image_1.png; sourceTree = ""; }; DD175F8C1820C81900139EBE /* test_image_2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image_2.png; sourceTree = ""; }; DD175F8D1820C81900139EBE /* test_image_3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image_3.png; sourceTree = ""; }; DD175F8E1820C81900139EBE /* test_image_4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image_4.png; sourceTree = ""; }; DD2C8C921863D7E300F06746 /* Selectors-Media-Queries-styler.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Media-Queries-styler.cas"; sourceTree = ""; }; DD43DA8A1810F5D40058FBC1 /* Classy-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Classy-Prefix.pch"; sourceTree = ""; }; DD52F102183DCB2600F31A3F /* Import-1.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Import-1.cas"; sourceTree = ""; }; DD52F103183DCB2600F31A3F /* Import-2.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Import-2.cas"; sourceTree = ""; }; DD52F104183DCB2600F31A3F /* Import-Base.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Import-Base.cas"; sourceTree = ""; }; DD592B12181DE5E800C30F7F /* Selectors-Nested.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Nested.cas"; sourceTree = ""; }; DD5B2DAF189532DC00CB6531 /* Variables-Injection.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Variables-Injection.cas"; sourceTree = ""; }; DD748A5A184446A700DE426A /* UIDevice+CASMockDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+CASMockDevice.h"; sourceTree = ""; }; DD748A5B184446A700DE426A /* UIDevice+CASMockDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+CASMockDevice.m"; sourceTree = ""; }; DD748A5D184446F900DE426A /* CASSwizzler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASSwizzler.h; sourceTree = ""; }; DD7C18431827464C008110D7 /* Properties-Nested.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Properties-Nested.cas"; sourceTree = ""; }; DD824861183964CB00DDD6EF /* CASExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASExampleViewController.h; sourceTree = ""; }; DD824862183964CB00DDD6EF /* CASExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASExampleViewController.m; sourceTree = ""; }; DD8248641839684700DDD6EF /* UIView_CASAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIView_CASAdditionsSpec.m; sourceTree = ""; }; DD84B3DE183ACB0A0009ACAA /* CASTestsAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASTestsAppDelegate.h; sourceTree = ""; }; DD84B3DF183ACB0A0009ACAA /* CASTestsAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASTestsAppDelegate.m; sourceTree = ""; }; DD84B3E2183ACC370009ACAA /* GcovTestObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GcovTestObserver.m; sourceTree = ""; }; DD85DDBE1841C1DC00126E8C /* Selectors-Media-Queries.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Selectors-Media-Queries.cas"; sourceTree = ""; }; DD9389DD18206A0100C0FFA7 /* CASStyleSelectorSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASStyleSelectorSpec.m; sourceTree = ""; }; DDB16C991895563900256EC6 /* Injected-File.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Injected-File.cas"; sourceTree = ""; }; DDBF582518112ECA0059B8D3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; DDBF583318113BC10059B8D3 /* ClassyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClassyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DDBF585018113C750059B8D3 /* XCTest+Spec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTest+Spec.h"; sourceTree = ""; }; DDBF585B18113C980059B8D3 /* ClassyTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "ClassyTests-Info.plist"; sourceTree = SOURCE_ROOT; }; DDBF585C18113C980059B8D3 /* ClassyTests-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClassyTests-Prefix.pch"; sourceTree = SOURCE_ROOT; }; DDBF585F18113FCC0059B8D3 /* CASExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASExampleView.h; sourceTree = ""; }; DDBF586018113FCC0059B8D3 /* CASExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASExampleView.m; sourceTree = ""; }; DDD3D4891810D6DA00E378A2 /* ClassyTestsLoader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClassyTestsLoader.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDD3D48C1810D6DA00E378A2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; DDD3D48E1810D6DA00E378A2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DDD3D4901810D6DA00E378A2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; DDD3D4BC1810D8B100E378A2 /* ClassyTestsLoader-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "ClassyTestsLoader-Info.plist"; sourceTree = ""; }; DDD3D4BD1810D8B100E378A2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; DDD3D4BE1810D8B100E378A2 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DDD3D4C71810D9A400E378A2 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; DDD3D4FF1810DF7C00E378A2 /* test_image_5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image_5.png; sourceTree = ""; }; DDD3D5011810E02F00E378A2 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; DDE7E51D18915B0600BA5718 /* Precedence-1.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Precedence-1.cas"; sourceTree = ""; }; DDE7E51E18915B0600BA5718 /* Precedence-2.cas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Precedence-2.cas"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ DDBF583018113BC10059B8D3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DDBF583418113BC10059B8D3 /* XCTest.framework in Frameworks */, DDBF583618113BC10059B8D3 /* UIKit.framework in Frameworks */, DDBF583518113BC10059B8D3 /* Foundation.framework in Frameworks */, 9980D2916D344434ACF1CB78 /* libPods-ClassyTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; DDD3D4861810D6DA00E378A2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DDD3D48F1810D6DA00E378A2 /* CoreGraphics.framework in Frameworks */, DDD3D4911810D6DA00E378A2 /* UIKit.framework in Frameworks */, DDD3D48D1810D6DA00E378A2 /* Foundation.framework in Frameworks */, BDABE4F7F7AD42D589D779BD /* libPods-ClassyTestsLoader.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ CB3DB70017F85796C2A240ED /* Pods */ = { isa = PBXGroup; children = ( B70022280A544A8418F356ED /* Pods-ClassyTests.debug.xcconfig */, BAA4DBC31A8D4B53D68A49E5 /* Pods-ClassyTests.release.xcconfig */, 6974873D3DC5638F91AF7A18 /* Pods-ClassyTestsLoader.debug.xcconfig */, 079339911EDA90D0E7C07ECD /* Pods-ClassyTestsLoader.release.xcconfig */, ); name = Pods; sourceTree = ""; }; DD10AD3E181A16EA00A5B71D /* Integration Tests */ = { isa = PBXGroup; children = ( DD10AD4E181A172F00A5B71D /* CASUIKitSpec.m */, DD10AD50181A174A00A5B71D /* CASUIAppearanceSpec.m */, DD10AD52181A176000A5B71D /* CASCustomViewSpec.m */, ); path = "Integration Tests"; sourceTree = ""; }; DD10AD3F181A16EA00A5B71D /* Unit Tests */ = { isa = PBXGroup; children = ( DD10AD40181A16EA00A5B71D /* CASArgumentDescriptorSpec.m */, DD10AD41181A16EA00A5B71D /* CASLexerSpec.m */, DD10AD42181A16EA00A5B71D /* CASParserSpec.m */, DD10AD43181A16EA00A5B71D /* CASRuntimeExtensionsSpec.m */, DD10AD44181A16EA00A5B71D /* CASStylePropertySpec.m */, DD10AD45181A16EA00A5B71D /* CASStylerSpec.m */, DD10AD46181A16EA00A5B71D /* CASTokenSpec.m */, DD9389DD18206A0100C0FFA7 /* CASStyleSelectorSpec.m */, DD8248641839684700DDD6EF /* UIView_CASAdditionsSpec.m */, ); path = "Unit Tests"; sourceTree = ""; }; DDBF584518113C750059B8D3 /* Specs */ = { isa = PBXGroup; children = ( DD10AD3E181A16EA00A5B71D /* Integration Tests */, DD10AD3F181A16EA00A5B71D /* Unit Tests */, ); path = Specs; sourceTree = ""; }; DDBF585A18113C850059B8D3 /* Supporting Files */ = { isa = PBXGroup; children = ( DDD3D4BB1810D8B100E378A2 /* ClassyTestsLoader */, DD748A5D184446F900DE426A /* CASSwizzler.h */, DD748A5A184446A700DE426A /* UIDevice+CASMockDevice.h */, DD748A5B184446A700DE426A /* UIDevice+CASMockDevice.m */, DD824861183964CB00DDD6EF /* CASExampleViewController.h */, DD824862183964CB00DDD6EF /* CASExampleViewController.m */, DDBF585F18113FCC0059B8D3 /* CASExampleView.h */, DDBF586018113FCC0059B8D3 /* CASExampleView.m */, DDBF585018113C750059B8D3 /* XCTest+Spec.h */, DD84B3E2183ACC370009ACAA /* GcovTestObserver.m */, DDBF585B18113C980059B8D3 /* ClassyTests-Info.plist */, DDBF585C18113C980059B8D3 /* ClassyTests-Prefix.pch */, ); name = "Supporting Files"; path = Specs; sourceTree = ""; }; DDBF586F181145760059B8D3 /* Stylesheets */ = { isa = PBXGroup; children = ( DD10AD2E1819FA6800A5B71D /* CustomView-Basic.cas */, DD10AD54181A1C9100A5B71D /* Properties-Args.cas */, DD10AD2F1819FA6800A5B71D /* Properties-Basic.cas */, DD7C18431827464C008110D7 /* Properties-Nested.cas */, DD10AD301819FA6800A5B71D /* Selectors-Messy.cas */, DD10AD311819FA6800A5B71D /* Selectors-Complex.cas */, DD10AD321819FA6800A5B71D /* Selectors-Hierarchy.cas */, DD10AD331819FA6800A5B71D /* Selectors-Indentation.cas */, DD592B12181DE5E800C30F7F /* Selectors-Nested.cas */, DD85DDBE1841C1DC00126E8C /* Selectors-Media-Queries.cas */, DD2C8C921863D7E300F06746 /* Selectors-Media-Queries-styler.cas */, DD10AD341819FA6800A5B71D /* UIAppearance-Basic.cas */, DD10AD351819FA6800A5B71D /* UIKit-Basic.cas */, DD52F104183DCB2600F31A3F /* Import-Base.cas */, DD52F102183DCB2600F31A3F /* Import-1.cas */, DD52F103183DCB2600F31A3F /* Import-2.cas */, DD148306186482AC00E0F5EF /* Variables-Basic.cas */, DD5B2DAF189532DC00CB6531 /* Variables-Injection.cas */, DDB16C991895563900256EC6 /* Injected-File.cas */, DDE7E51D18915B0600BA5718 /* Precedence-1.cas */, DDE7E51E18915B0600BA5718 /* Precedence-2.cas */, 4ED0F9A1198B465600C9FE2C /* UIKit-MultipleClasses.cas */, ); path = Stylesheets; sourceTree = ""; }; DDD3D4801810D6DA00E378A2 = { isa = PBXGroup; children = ( DDBF586F181145760059B8D3 /* Stylesheets */, DDBF584518113C750059B8D3 /* Specs */, DDBF585A18113C850059B8D3 /* Supporting Files */, DDD3D48B1810D6DA00E378A2 /* Frameworks */, DDD3D48A1810D6DA00E378A2 /* Products */, CB3DB70017F85796C2A240ED /* Pods */, ); sourceTree = ""; }; DDD3D48A1810D6DA00E378A2 /* Products */ = { isa = PBXGroup; children = ( DDD3D4891810D6DA00E378A2 /* ClassyTestsLoader.app */, DDBF583318113BC10059B8D3 /* ClassyTests.xctest */, ); name = Products; sourceTree = ""; }; DDD3D48B1810D6DA00E378A2 /* Frameworks */ = { isa = PBXGroup; children = ( DDBF582518112ECA0059B8D3 /* XCTest.framework */, DDD3D5011810E02F00E378A2 /* QuartzCore.framework */, DDD3D48C1810D6DA00E378A2 /* Foundation.framework */, DDD3D48E1810D6DA00E378A2 /* CoreGraphics.framework */, DDD3D4901810D6DA00E378A2 /* UIKit.framework */, DDD3D4C71810D9A400E378A2 /* SenTestingKit.framework */, C594C855E67D4ABAAB9D8034 /* libPods-ClassyTests.a */, 13B648C088D7485783343464 /* libPods-ClassyTestsLoader.a */, ); name = Frameworks; sourceTree = ""; }; DDD3D4BB1810D8B100E378A2 /* ClassyTestsLoader */ = { isa = PBXGroup; children = ( DD84B3DE183ACB0A0009ACAA /* CASTestsAppDelegate.h */, DD84B3DF183ACB0A0009ACAA /* CASTestsAppDelegate.m */, DD175F8B1820C81900139EBE /* test_image_1.png */, DD175F8C1820C81900139EBE /* test_image_2.png */, DD175F8D1820C81900139EBE /* test_image_3.png */, DD175F8E1820C81900139EBE /* test_image_4.png */, DDD3D4FF1810DF7C00E378A2 /* test_image_5.png */, DDD3D4BC1810D8B100E378A2 /* ClassyTestsLoader-Info.plist */, DDD3D4BD1810D8B100E378A2 /* Images.xcassets */, DDD3D4BE1810D8B100E378A2 /* main.m */, DD43DA8A1810F5D40058FBC1 /* Classy-Prefix.pch */, ); name = ClassyTestsLoader; path = ../ClassyTestsLoader; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ DDBF583218113BC10059B8D3 /* ClassyTests */ = { isa = PBXNativeTarget; buildConfigurationList = DDBF584218113BC10059B8D3 /* Build configuration list for PBXNativeTarget "ClassyTests" */; buildPhases = ( 978F0B8381844F0694B65CD6 /* [CP] Check Pods Manifest.lock */, DDBF582F18113BC10059B8D3 /* Sources */, DDBF583018113BC10059B8D3 /* Frameworks */, DDBF583118113BC10059B8D3 /* Resources */, CC986B10AB494D06AB85AEF5 /* [CP] Copy Pods Resources */, 0D28211D96233A3624E5F34D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( DDBF584118113BC10059B8D3 /* PBXTargetDependency */, ); name = ClassyTests; productName = ClassyTests; productReference = DDBF583318113BC10059B8D3 /* ClassyTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; DDD3D4881810D6DA00E378A2 /* ClassyTestsLoader */ = { isa = PBXNativeTarget; buildConfigurationList = DDD3D4B51810D6DA00E378A2 /* Build configuration list for PBXNativeTarget "ClassyTestsLoader" */; buildPhases = ( 85CB2FDD53654D26A4128DD4 /* [CP] Check Pods Manifest.lock */, DDD3D4851810D6DA00E378A2 /* Sources */, DDD3D4861810D6DA00E378A2 /* Frameworks */, DDD3D4871810D6DA00E378A2 /* Resources */, 4CC291A52E864ADB9C46B39D /* [CP] Copy Pods Resources */, FFF7D3BEA4D7F7C1133042F0 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ClassyTestsLoader; productName = ClassyTests; productReference = DDD3D4891810D6DA00E378A2 /* ClassyTestsLoader.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ DDD3D4811810D6DA00E378A2 /* Project object */ = { isa = PBXProject; attributes = { CLASSPREFIX = CAS; LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Jonas Budelmann"; TargetAttributes = { DDBF583218113BC10059B8D3 = { TestTargetID = DDD3D4881810D6DA00E378A2; }; }; }; buildConfigurationList = DDD3D4841810D6DA00E378A2 /* Build configuration list for PBXProject "ClassyTests" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = DDD3D4801810D6DA00E378A2; productRefGroup = DDD3D48A1810D6DA00E378A2 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( DDD3D4881810D6DA00E378A2 /* ClassyTestsLoader */, DDBF583218113BC10059B8D3 /* ClassyTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ DDBF583118113BC10059B8D3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DDE7E51F18915B0600BA5718 /* Precedence-1.cas in Resources */, DD10AD3A1819FA6800A5B71D /* Selectors-Hierarchy.cas in Resources */, DD592B13181DE5E800C30F7F /* Selectors-Nested.cas in Resources */, DD148307186482AC00E0F5EF /* Variables-Basic.cas in Resources */, DDB16C9A1895563900256EC6 /* Injected-File.cas in Resources */, DD10AD55181A1C9100A5B71D /* Properties-Args.cas in Resources */, DD10AD3D1819FA6800A5B71D /* UIKit-Basic.cas in Resources */, DD10AD361819FA6800A5B71D /* CustomView-Basic.cas in Resources */, DD52F106183DCB2600F31A3F /* Import-2.cas in Resources */, DD10AD371819FA6800A5B71D /* Properties-Basic.cas in Resources */, DD85DDBF1841C1DC00126E8C /* Selectors-Media-Queries.cas in Resources */, DD10AD381819FA6800A5B71D /* Selectors-Messy.cas in Resources */, DD7C18441827464C008110D7 /* Properties-Nested.cas in Resources */, DD10AD3C1819FA6800A5B71D /* UIAppearance-Basic.cas in Resources */, DD10AD3B1819FA6800A5B71D /* Selectors-Indentation.cas in Resources */, DD52F107183DCB2600F31A3F /* Import-Base.cas in Resources */, DD2C8C931863D7E300F06746 /* Selectors-Media-Queries-styler.cas in Resources */, DD52F105183DCB2600F31A3F /* Import-1.cas in Resources */, DD5B2DB0189532DC00CB6531 /* Variables-Injection.cas in Resources */, DDE7E52018915B0600BA5718 /* Precedence-2.cas in Resources */, DD10AD391819FA6800A5B71D /* Selectors-Complex.cas in Resources */, 4ED0F9A3198B476E00C9FE2C /* UIKit-MultipleClasses.cas in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDD3D4871810D6DA00E378A2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DD175F901820C81A00139EBE /* test_image_2.png in Resources */, DDD3D5001810DF7C00E378A2 /* test_image_5.png in Resources */, DDD3D4C01810D8B100E378A2 /* Images.xcassets in Resources */, DD175F911820C81A00139EBE /* test_image_3.png in Resources */, DD175F8F1820C81900139EBE /* test_image_1.png in Resources */, DD175F921820C81A00139EBE /* test_image_4.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 0D28211D96233A3624E5F34D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-ClassyTests/Pods-ClassyTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 4CC291A52E864ADB9C46B39D /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-ClassyTestsLoader/Pods-ClassyTestsLoader-resources.sh\"\n"; showEnvVarsInLog = 0; }; 85CB2FDD53654D26A4128DD4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 978F0B8381844F0694B65CD6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; CC986B10AB494D06AB85AEF5 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-ClassyTests/Pods-ClassyTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; FFF7D3BEA4D7F7C1133042F0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-ClassyTestsLoader/Pods-ClassyTestsLoader-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ DDBF582F18113BC10059B8D3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DD748A5C184446A700DE426A /* UIDevice+CASMockDevice.m in Sources */, DD10AD51181A174A00A5B71D /* CASUIAppearanceSpec.m in Sources */, DD10AD47181A16EA00A5B71D /* CASArgumentDescriptorSpec.m in Sources */, DD10AD49181A16EA00A5B71D /* CASParserSpec.m in Sources */, DD10AD53181A176000A5B71D /* CASCustomViewSpec.m in Sources */, DDBF586818113FCC0059B8D3 /* CASExampleView.m in Sources */, DD8248651839684700DDD6EF /* UIView_CASAdditionsSpec.m in Sources */, DD9389DE18206A0100C0FFA7 /* CASStyleSelectorSpec.m in Sources */, DD10AD4F181A172F00A5B71D /* CASUIKitSpec.m in Sources */, DD10AD4B181A16EA00A5B71D /* CASStylePropertySpec.m in Sources */, DD10AD4D181A16EA00A5B71D /* CASTokenSpec.m in Sources */, DD824863183964CB00DDD6EF /* CASExampleViewController.m in Sources */, DD84B3E3183ACC370009ACAA /* GcovTestObserver.m in Sources */, DD10AD4C181A16EA00A5B71D /* CASStylerSpec.m in Sources */, DD10AD48181A16EA00A5B71D /* CASLexerSpec.m in Sources */, DD10AD4A181A16EA00A5B71D /* CASRuntimeExtensionsSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDD3D4851810D6DA00E378A2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DD84B3E0183ACB0A0009ACAA /* CASTestsAppDelegate.m in Sources */, DDD3D4C11810D8B100E378A2 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ DDBF584118113BC10059B8D3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DDD3D4881810D6DA00E378A2 /* ClassyTestsLoader */; targetProxy = DDBF584018113BC10059B8D3 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ DDBF584318113BC10059B8D3 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = B70022280A544A8418F356ED /* Pods-ClassyTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ClassyTestsLoader.app/ClassyTestsLoader"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ClassyTests-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = "ClassyTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.cloudling.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Debug; }; DDBF584418113BC10059B8D3 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = BAA4DBC31A8D4B53D68A49E5 /* Pods-ClassyTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ClassyTestsLoader.app/ClassyTestsLoader"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ClassyTests-Prefix.pch"; INFOPLIST_FILE = "ClassyTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.cloudling.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Release; }; DDD3D4B31810D6DA00E378A2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; DDD3D4B41810D6DA00E378A2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; DDD3D4B61810D6DA00E378A2 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 6974873D3DC5638F91AF7A18 /* Pods-ClassyTestsLoader.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_GENERATE_TEST_COVERAGE_FILES = YES; GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ClassyTestsLoader/Classy-Prefix.pch"; INFOPLIST_FILE = "ClassyTestsLoader/ClassyTestsLoader-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_BUNDLE_IDENTIFIER = "com.cloudling.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; DDD3D4B71810D6DA00E378A2 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 079339911EDA90D0E7C07ECD /* Pods-ClassyTestsLoader.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_GENERATE_TEST_COVERAGE_FILES = YES; GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ClassyTestsLoader/Classy-Prefix.pch"; INFOPLIST_FILE = "ClassyTestsLoader/ClassyTestsLoader-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_BUNDLE_IDENTIFIER = "com.cloudling.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ DDBF584218113BC10059B8D3 /* Build configuration list for PBXNativeTarget "ClassyTests" */ = { isa = XCConfigurationList; buildConfigurations = ( DDBF584318113BC10059B8D3 /* Debug */, DDBF584418113BC10059B8D3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DDD3D4841810D6DA00E378A2 /* Build configuration list for PBXProject "ClassyTests" */ = { isa = XCConfigurationList; buildConfigurations = ( DDD3D4B31810D6DA00E378A2 /* Debug */, DDD3D4B41810D6DA00E378A2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DDD3D4B51810D6DA00E378A2 /* Build configuration list for PBXNativeTarget "ClassyTestsLoader" */ = { isa = XCConfigurationList; buildConfigurations = ( DDD3D4B61810D6DA00E378A2 /* Debug */, DDD3D4B71810D6DA00E378A2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = DDD3D4811810D6DA00E378A2 /* Project object */; } ================================================ FILE: Tests/ClassyTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Tests/ClassyTests.xcodeproj/project.xcworkspace/xcshareddata/ClassyTests.xccheckout ================================================ IDESourceControlProjectFavoriteDictionaryKey IDESourceControlProjectIdentifier 5AD75AC1-8958-4FBB-A090-A8F1BA88736B IDESourceControlProjectName ClassyTests IDESourceControlProjectOriginsDictionary 8ED57007-8469-49FE-8F73-0498C64D8859 https://github.com/cloudkite/Classy.git IDESourceControlProjectPath Tests/ClassyTests.xcodeproj/project.xcworkspace IDESourceControlProjectRelativeInstallPathDictionary 8ED57007-8469-49FE-8F73-0498C64D8859 ../../.. IDESourceControlProjectURL https://github.com/cloudkite/Classy.git IDESourceControlProjectVersion 110 IDESourceControlProjectWCCIdentifier 8ED57007-8469-49FE-8F73-0498C64D8859 IDESourceControlProjectWCConfigurations IDESourceControlRepositoryExtensionIdentifierKey public.vcs.git IDESourceControlWCCIdentifierKey 8ED57007-8469-49FE-8F73-0498C64D8859 IDESourceControlWCCName Classy ================================================ FILE: Tests/ClassyTests.xcodeproj/xcshareddata/xcschemes/ClassyTests.xcscheme ================================================ ================================================ FILE: Tests/ClassyTestsLoader/CASTestsAppDelegate.h ================================================ // // CASTestsAppDelegate.h // ClassyTests // // Created by Jonas Budelmann on 19/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface CASTestsAppDelegate : UIResponder @end ================================================ FILE: Tests/ClassyTestsLoader/CASTestsAppDelegate.m ================================================ // // CASTestsAppDelegate.m // ClassyTests // // Created by Jonas Budelmann on 19/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASTestsAppDelegate.h" @implementation CASTestsAppDelegate + (void)initialize { [[NSUserDefaults standardUserDefaults] setValue:@"XCTestLog,GcovTestObserver" forKey:@"XCTestObserverClass"]; } - (void)applicationWillTerminate:(UIApplication *)application { extern void __gcov_flush(void); __gcov_flush(); } @end ================================================ FILE: Tests/ClassyTestsLoader/Classy-Prefix.pch ================================================ // // Prefix header // // The contents of this file are implicitly included at the beginning of every source file. // #ifdef __OBJC__ #import #import #import #endif ================================================ FILE: Tests/ClassyTestsLoader/ClassyTestsLoader-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Tests/ClassyTestsLoader/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Tests/ClassyTestsLoader/Images.xcassets/LaunchImage.launchimage/Contents.json ================================================ { "images" : [ { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "subtype" : "retina4", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "1x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "1x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Tests/ClassyTestsLoader/main.m ================================================ // // main.m // ClassyTests // // Created by Jonas Budelmann on 18/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import #import "CASTestsAppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([CASTestsAppDelegate class])); } } ================================================ FILE: Tests/Specs/CASExampleView.h ================================================ // // CASExampleView.h // Classy // // Created by Jonas Budelmann on 11/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import @interface CASExampleView : UIView @property (nonatomic, assign) BOOL testBOOL; @property (nonatomic, assign) NSUInteger testNSUInteger; @property (nonatomic, assign) CGFloat testCGFloat; @property (nonatomic, assign) NSInteger testNSInteger; @property (nonatomic, assign) int testInt; @end ================================================ FILE: Tests/Specs/CASExampleView.m ================================================ // // CASExampleView.m // Classy // // Created by Jonas Budelmann on 11/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASExampleView.h" @implementation CASExampleView @end ================================================ FILE: Tests/Specs/CASExampleViewController.h ================================================ // // CASExampleViewController.h // ClassyTests // // Created by Jonas Budelmann on 18/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface CASExampleViewController : UIViewController @end ================================================ FILE: Tests/Specs/CASExampleViewController.m ================================================ // // CASExampleViewController.m // ClassyTests // // Created by Jonas Budelmann on 18/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASExampleViewController.h" #import "CASExampleView.h" @implementation CASExampleViewController - (void)loadView { self.view = CASExampleView.new; } @end ================================================ FILE: Tests/Specs/CASSwizzler.h ================================================ // // CASSwizzler.h // ClassyTests // // Created by Jonas Budelmann on 26/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import #import void SwizzleClassMethod(Class class, SEL orig, SEL new) { Method origMethod = class_getClassMethod(class, orig); Method newMethod = class_getClassMethod(class, new); class = object_getClass((id)class); if(class_addMethod(class, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) class_replaceMethod(class, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); else method_exchangeImplementations(origMethod, newMethod); } ================================================ FILE: Tests/Specs/GcovTestObserver.m ================================================ // // GcovTestObserver.m // ClassyTests // // Created by Jonas Budelmann on 19/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface GcovTestObserver : XCTestObserver @end @implementation GcovTestObserver - (void)stopObserving { [super stopObserving]; UIApplication* application = [UIApplication sharedApplication]; [application.delegate applicationWillTerminate:application]; } @end ================================================ FILE: Tests/Specs/Integration Tests/CASCustomViewSpec.m ================================================ // // CASCustomViewSpec.m // ClassyTests // // Created by Jonas Budelmann on 25/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "Classy.h" #import "XCTest+Spec.h" #import "CASExampleView.h" SpecBegin(CASCustomView) - (void)testSetCustomProperties { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"CustomView-Basic.cas" ofType:nil]; CASExampleView *exampleView = CASExampleView.new; [styler styleItem:exampleView]; expect(exampleView.testCGFloat).to.equal(4.5); expect(exampleView.testBOOL).to.equal(YES); expect(exampleView.testNSInteger).to.equal(-999); expect(exampleView.testNSUInteger).to.equal(1000); expect(exampleView.testInt).to.equal(345); } SpecEnd ================================================ FILE: Tests/Specs/Integration Tests/CASUIAppearanceSpec.m ================================================ // // CASUIAppearanceSpec.m // ClassyTests // // Created by Jonas Budelmann on 25/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "Classy.h" #import "XCTest+Spec.h" #import "UIColor+CASAdditions.h" SpecBegin(CASUIAppearance) - (void)setUp { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"UIAppearance-Basic.cas" ofType:nil]; [CASStyler.defaultStyler setFilePath:filePath error:&error]; expect(error).to.beNil(); } - (void)testUIActivityIndicatorViewAppearance { UIActivityIndicatorView *view = UIActivityIndicatorView.new; [CASStyler.defaultStyler styleItem:view]; expect(view.color).to.equal([UIColor redColor]); } - (void)testUIButtonAppearance { UIButton *button = UIButton.new; [CASStyler.defaultStyler styleItem:button]; expect(button.contentEdgeInsets).to.equal(UIEdgeInsetsMake(1, 2, 3, 4)); // titleColor expect([button titleColorForState:UIControlStateNormal]).to.equal([UIColor yellowColor]); expect([button titleColorForState:UIControlStateHighlighted]).to.equal([UIColor cas_colorWithHex:@"#e3e3e3"]); expect([button titleColorForState:UIControlStateDisabled]).to.equal([UIColor cas_colorWithHex:@"#e4e4e4"]); expect([button titleColorForState:UIControlStateSelected]).to.equal([UIColor cas_colorWithHex:@"#e5e5e5"]); // titleShadow expect([button titleShadowColorForState:UIControlStateNormal]).to.equal([UIColor cas_colorWithHex:@"#e6e6e6"]); expect([button titleShadowColorForState:UIControlStateHighlighted]).to.equal([UIColor cas_colorWithHex:@"#e7e7e7"]); expect([button titleShadowColorForState:UIControlStateDisabled]).to.equal([UIColor cas_colorWithHex:@"#f4f4f4"]); expect([button titleShadowColorForState:UIControlStateSelected]).to.equal([UIColor cas_colorWithHex:@"#f5f5f5"]); // backgroundImage expect([button backgroundImageForState:UIControlStateNormal]).to.equal([UIImage imageNamed:@"test_image_1"]); UIImage *highlightedImage = [[UIImage imageNamed:@"test_image_2"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 14, 13, 12)]; expect([button backgroundImageForState:UIControlStateHighlighted].CGImage).to.equal(highlightedImage.CGImage); expect([button backgroundImageForState:UIControlStateHighlighted].capInsets).to.equal(highlightedImage.capInsets); expect([button backgroundImageForState:UIControlStateDisabled]).to.equal([UIImage imageNamed:@"test_image_3"]); expect([button backgroundImageForState:UIControlStateSelected]).to.equal([UIImage imageNamed:@"test_image_4"]); } - (void)testUIBarItemAppearance { UITabBarItem *view = UITabBarItem.new; [CASStyler.defaultStyler styleItem:view]; NSDictionary *titleTextAttributes = [view titleTextAttributesForState:UIControlStateHighlighted]; expect(titleTextAttributes[NSFontAttributeName]).to.equal([UIFont fontWithName:@"HelveticaNeue" size:21]); expect(titleTextAttributes[NSForegroundColorAttributeName]).to.equal([UIColor magentaColor]); expect(titleTextAttributes[NSBackgroundColorAttributeName]).to.equal([UIColor redColor]); expect(titleTextAttributes[NSLigatureAttributeName]).to.equal(@6); expect(titleTextAttributes[NSKernAttributeName]).to.equal(@0.6); expect(titleTextAttributes[NSStrikethroughStyleAttributeName]).to.equal(@(NSUnderlineStyleSingle)); expect(titleTextAttributes[NSUnderlineStyleAttributeName]).to.equal(@(NSUnderlineStyleDouble)); expect(titleTextAttributes[NSStrokeColorAttributeName]).to.equal([UIColor blueColor]); NSParagraphStyle *paragraphStyle = titleTextAttributes[NSParagraphStyleAttributeName]; expect(paragraphStyle.lineSpacing).to.equal(20); expect(paragraphStyle.paragraphSpacing).to.equal(15); expect(paragraphStyle.alignment).to.equal(NSTextAlignmentCenter); expect(paragraphStyle.lineBreakMode).to.equal(NSLineBreakByTruncatingTail); NSShadow *shadow = titleTextAttributes[NSShadowAttributeName]; expect(shadow.shadowOffset).to.equal(UIOffsetMake(1, 2)); expect(shadow.shadowBlurRadius).to.equal(10); expect(shadow.shadowColor).to.equal([UIColor purpleColor]); } - (void)testUIBarButtonItemAppearance { UIBarButtonItem *view = UIBarButtonItem.new; [CASStyler.defaultStyler styleItem:view]; expect([view backgroundImageForState:UIControlStateNormal barMetrics:UIBarMetricsDefault]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view backgroundImageForState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]).to.equal([UIImage imageNamed:@"test_image_2"]); expect([view backgroundImageForState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone]).to.equal([UIImage imageNamed:@"test_image_4"]); expect([view backgroundVerticalPositionAdjustmentForBarMetrics:UIBarMetricsDefaultPrompt]).to.equal(10); expect([view backgroundVerticalPositionAdjustmentForBarMetrics:UIBarMetricsLandscapePhone]).to.equal(20); expect([view titlePositionAdjustmentForBarMetrics:UIBarMetricsDefault]).to.equal(UIOffsetMake(11, 12)); expect([view titlePositionAdjustmentForBarMetrics:UIBarMetricsLandscapePhone]).to.equal(UIOffsetMake(21, 22)); //back expect([view backButtonBackgroundImageForState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]).to.equal([UIImage imageNamed:@"test_image_3"]); expect([view backButtonBackgroundImageForState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view backButtonBackgroundVerticalPositionAdjustmentForBarMetrics:UIBarMetricsDefaultPrompt]).to.equal(50); expect([view backButtonBackgroundVerticalPositionAdjustmentForBarMetrics:UIBarMetricsLandscapePhone]).to.equal(60); expect([view backButtonTitlePositionAdjustmentForBarMetrics:UIBarMetricsDefault]).to.equal(UIOffsetMake(51, 52)); expect([view backButtonTitlePositionAdjustmentForBarMetrics:UIBarMetricsLandscapePhone]).to.equal(UIOffsetMake(61, 62)); } - (void)testUINavigationBarAppearance { UINavigationBar *view = UINavigationBar.new; [CASStyler.defaultStyler styleItem:view]; expect([view barTintColor]).to.equal([UIColor blueColor]); expect([view shadowImage].CGImage).to.equal([UIImage imageNamed:@"test_image_1"].CGImage); expect([view backIndicatorImage]).to.equal([UIImage imageNamed:@"test_image_4"]); expect([view backIndicatorTransitionMaskImage]).to.equal([UIImage imageNamed:@"test_image_3"]); expect([view backgroundImageForBarPosition:UIBarPositionTop barMetrics:UIBarMetricsLandscapePhone].CGImage).to.equal([UIImage imageNamed:@"test_image_4"].CGImage); expect([view titleVerticalPositionAdjustmentForBarMetrics:UIBarMetricsDefaultPrompt]); } - (void)testUIPageControlAppearance { UIPageControl *view = UIPageControl.new; [CASStyler.defaultStyler styleItem:view]; expect([view pageIndicatorTintColor]).to.equal([UIColor purpleColor]); expect([view currentPageIndicatorTintColor]).to.equal([UIColor redColor]); } - (void)testUIProgressViewAppearance { UIProgressView *view = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; [CASStyler.defaultStyler styleItem:view]; expect([view progressImage]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view trackImage]).to.equal([UIImage imageNamed:@"test_image_2"]); //view cannot have both progressImage and progressTint. so we need to test seperately view.cas_styleClass = @"tinted"; [CASStyler.defaultStyler styleItem:view]; expect([view progressTintColor]).to.equal([UIColor darkGrayColor]); expect([view trackTintColor]).to.equal([UIColor lightGrayColor]); } - (void)testUISearchBarAppearance { UISearchBar *view = UISearchBar.new; [CASStyler.defaultStyler styleItem:view]; expect([view barTintColor]).to.equal([UIColor brownColor]); expect([view backgroundImage].CGImage).to.equal([UIImage imageNamed:@"test_image_2"].CGImage); expect([view scopeBarBackgroundImage]).to.equal([UIImage imageNamed:@"test_image_3"]); expect([view searchFieldBackgroundPositionAdjustment]).to.equal(UIOffsetMake(20, 30)); expect([view searchTextPositionAdjustment]).to.equal(UIOffsetMake(3, 4)); expect([view scopeBarButtonBackgroundImageForState:UIControlStateSelected]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view scopeBarButtonDividerImageForLeftSegmentState:UIControlStateDisabled rightSegmentState:UIControlStateSelected]).to.equal([UIImage imageNamed:@"test_image_5"]); expect([view scopeBarButtonDividerImageForLeftSegmentState:UIControlStateHighlighted rightSegmentState:UIControlStateSelected]).to.beNil(); expect([view positionAdjustmentForSearchBarIcon:UISearchBarIconClear]).to.equal(UIOffsetMake(5, 6)); expect([view imageForSearchBarIcon:UISearchBarIconBookmark state:UIControlStateSelected]).to.equal([UIImage imageNamed:@"test_image_2"]); } - (void)testUISegmentedControlAppearance { UISegmentedControl *view = UISegmentedControl.new; [CASStyler.defaultStyler styleItem:view]; expect([view backgroundImageForState:UIControlStateDisabled barMetrics:UIBarMetricsLandscapePhone]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view dividerImageForLeftSegmentState:UIControlStateDisabled rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault]).to.equal([UIImage imageNamed:@"test_image_5"]); expect([view contentPositionAdjustmentForSegmentType:UISegmentedControlSegmentLeft barMetrics:UIBarMetricsLandscapePhone]).to.equal(UIOffsetMake(1, 6)); } - (void)testUISliderAppearance { UISlider *view = UISlider.new; [CASStyler.defaultStyler styleItem:view]; expect([view minimumTrackImageForState:UIControlStateNormal]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view minimumTrackImageForState:UIControlStateHighlighted]).to.equal([UIImage imageNamed:@"test_image_2"]); expect([view maximumTrackImageForState:UIControlStateNormal]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view maximumTrackImageForState:UIControlStateHighlighted]).to.equal([UIImage imageNamed:@"test_image_2"]); expect([view thumbImageForState:UIControlStateNormal]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view thumbImageForState:UIControlStateHighlighted]).to.equal([UIImage imageNamed:@"test_image_2"]); // The slider cannot have both tint colors and custom images. So we need to test seperately view.cas_styleClass = @"tinted"; [CASStyler.defaultStyler styleItem:view]; expect([view minimumTrackTintColor]).to.equal([UIColor blackColor]); expect([view maximumTrackTintColor]).to.equal([UIColor purpleColor]); expect([view thumbTintColor]).to.equal([UIColor yellowColor]); } - (void)testUISteppterAppearance { UIStepper *view = UIStepper.new; [CASStyler.defaultStyler styleItem:view]; //UISlider modifies this image. so cant verify //expect([view backgroundImageForState:UIControlStateHighlighted]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view dividerImageForLeftSegmentState:UIControlStateDisabled rightSegmentState:UIControlStateSelected]).to.equal([UIImage imageNamed:@"test_image_5"]); expect([view incrementImageForState:UIControlStateDisabled]).to.equal([UIImage imageNamed:@"test_image_2"]); expect([view decrementImageForState:UIControlStateDisabled]).to.equal([UIImage imageNamed:@"test_image_3"]); } - (void)testUISwitchAppearance { UISwitch *view = UISwitch.new; [CASStyler.defaultStyler styleItem:view]; expect([view onImage]).to.equal([UIImage imageNamed:@"test_image_1"]); expect([view offImage]).to.equal([UIImage imageNamed:@"test_image_2"]); //view cannot have both progressImage and progressTint. so we need to test seperately view.cas_styleClass = @"tinted"; [CASStyler.defaultStyler styleItem:view]; expect([view onTintColor]).to.equal([UIColor cyanColor]); expect([view thumbTintColor]).to.equal([UIColor greenColor]); } - (void)testUITabBarAppearance { UITabBar *view = UITabBar.new; [CASStyler.defaultStyler styleItem:view]; expect([view barTintColor]).to.equal([UIColor redColor]); expect([view backgroundImage].CGImage).to.equal([UIImage imageNamed:@"test_image_1"].CGImage); expect([view selectionIndicatorImage]).to.equal([UIImage imageNamed:@"test_image_2"]); expect([view shadowImage].CGImage).to.equal([UIImage imageNamed:@"test_image_3"].CGImage); expect([view itemPositioning]).to.equal(UITabBarItemPositioningCentered); expect(view.itemWidth).to.equal(10); expect(view.itemSpacing).to.equal(5); expect(view.barStyle).to.equal(UIBarStyleBlack); } - (void)testUITabBarItemAppearance { UITabBarItem *item = UITabBarItem.new; [CASStyler.defaultStyler styleItem:item]; expect(item.titlePositionAdjustment).to.equal(UIOffsetMake(1, 2)); } - (void)testUITableViewAppearance { UITableView *view = UITableView.new; [CASStyler.defaultStyler styleItem:view]; expect(view.separatorInset).to.equal(UIEdgeInsetsMake(1, 2, 3, 4)); expect(view.sectionIndexColor).to.equal([UIColor blueColor]); expect(view.sectionIndexBackgroundColor).to.equal([UIColor grayColor]); expect(view.sectionIndexTrackingBackgroundColor).to.equal([UIColor orangeColor]); } - (void)testUITableViewCellAppearance { UITableViewCell *view = UITableViewCell.new; // In iOS8, layoutMargins affects our separatorInset if ([view respondsToSelector:@selector(layoutMargins)]) { [view setValue:[NSValue valueWithUIEdgeInsets:UIEdgeInsetsZero] forKey:@"layoutMargins"]; } [CASStyler.defaultStyler styleItem:view]; //top and bottom are ignored expect(view.separatorInset).to.equal(UIEdgeInsetsMake(0, 4, 0, 4)); } - (void)testUIToolbarAppearance { UIToolbar *view = UIToolbar.new; [CASStyler.defaultStyler styleItem:view]; expect(view.barTintColor).to.equal([UIColor magentaColor]); expect([view backgroundImageForToolbarPosition:UIBarPositionTop barMetrics:UIBarMetricsLandscapePhone].CGImage).to.equal([UIImage imageNamed:@"test_image_4"].CGImage); expect([view shadowImageForToolbarPosition:UIBarPositionBottom].CGImage).to.equal([UIImage imageNamed:@"test_image_3"].CGImage); } - (void)testUIViewAppearance { UIView *view = UIView.new; [CASStyler.defaultStyler styleItem:view]; expect(view.backgroundColor).to.equal([UIColor cas_colorWithHex:@"#faf"]); } SpecEnd ================================================ FILE: Tests/Specs/Integration Tests/CASUIKitSpec.m ================================================ // // CASUIKitSpec.m // ClassyTests // // Created by Jonas Budelmann on 25/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "Classy.h" #import "XCTest+Spec.h" SpecBegin(CASUIKit) - (void)testSetUIViewProperties { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"UIKit-Basic.cas" ofType:nil]; UIView *view = UIView.new; [styler styleItem:view]; expect([UIColor colorWithCGColor:view.layer.borderColor].cas_hexValue).to.equal(@"a1a1a1"); expect(view.backgroundColor.cas_hexValue).to.equal(@"a2a2a2"); expect(view.layer.borderWidth).to.equal(2); expect(view.layer.cornerRadius).to.equal(7); expect([UIColor colorWithCGColor:view.layer.shadowColor]).to.equal([UIColor redColor]); expect(view.layer.shadowOffset).to.equal(CGSizeMake(1, 3)); expect(view.layer.shadowOpacity).to.equal(0.5); expect(view.layer.shadowRadius).to.equal(4); expect(view.contentMode).to.equal(UIViewContentModeScaleAspectFill); expect(view.clipsToBounds).to.equal(YES); expect(view.tintColor).to.equal([UIColor blueColor]); } - (void)testSetUITextFieldProperties { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"UIKit-Basic.cas" ofType:nil]; UITextField *view = UITextField.new; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsZero); [styler styleItem:view]; CGRect bounds = CGRectMake(0, 0, 10, 10); expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(4, 3, 2, 1)); expect([view textRectForBounds:bounds]).to.equal(UIEdgeInsetsInsetRect(bounds, view.cas_textEdgeInsets)); expect(view.font).to.equal([UIFont fontWithName:@"Avenir-Heavy" size:12]); expect(view.textColor.cas_hexValue).to.equal(@"a0a0a0"); expect(view.textAlignment).to.equal(NSTextAlignmentRight); expect(view.contentVerticalAlignment).to.equal(UIControlContentVerticalAlignmentBottom); expect(view.borderStyle).to.equal(UITextBorderStyleLine); expect(view.background).notTo.beNil(); expect(view.background.capInsets).to.equal(UIEdgeInsetsMake(1, 2, 3, 4)); } - (void)testMultipleStyleClasses { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"UIKit-MultipleClasses.cas" ofType:nil]; UIView *view = UIView.new; view.cas_styleClass = @"class1"; [view cas_addStyleClass:@"class2"]; [styler styleItem:view]; expect(view.backgroundColor).to.equal([UIColor redColor]); expect(view.layer.cornerRadius).to.equal(2); [view cas_addStyleClass:@"class3"]; [styler styleItem:view]; expect(view.backgroundColor).to.equal([UIColor greenColor]); } SpecEnd ================================================ FILE: Tests/Specs/UIDevice+CASMockDevice.h ================================================ // // UIDevice+CASMockDevice.h // ClassyTests // // Created by Jonas Budelmann on 26/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import @interface UIDevice (CASMockDevice) + (void)setMockDevice:(UIDevice *)device; @end ================================================ FILE: Tests/Specs/UIDevice+CASMockDevice.m ================================================ // // UIDevice+CASMockDevice.m // ClassyTests // // Created by Jonas Budelmann on 26/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "UIDevice+CASMockDevice.h" #import "CASSwizzler.h" @implementation UIDevice (CASMockDevice) + (void)load { SwizzleClassMethod(self, @selector(currentDevice), @selector(cas_currentDevice)); } + (UIDevice *)cas_currentDevice { return [self mockDevice] ?: [self cas_currentDevice]; } + (void)setMockDevice:(UIDevice *)mockDevice { objc_setAssociatedObject(self, @selector(mockDevice), mockDevice, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + (UIDevice *)mockDevice { return objc_getAssociatedObject(self, @selector(mockDevice)); } @end ================================================ FILE: Tests/Specs/Unit Tests/CASArgumentDescriptorSpec.m ================================================ // // CASArgumentDescriptorSpec.m // Classy // // Created by Jonas Budelmann on 16/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASArgumentDescriptor.h" #import "XCTest+Spec.h" struct CASStructExample { CGFloat dx; CGFloat dy; }; typedef struct CASStructExample CASStructExample; SpecBegin(CASArgumentDescriptor) - (void)testReturnBOOL { expect([CASArgumentDescriptor argWithObjCType:@encode(BOOL)].primitiveType).to.equal(CASPrimitiveTypeBOOL); } - (void)testReturnInteger { expect([CASArgumentDescriptor argWithObjCType:@encode(NSInteger)].primitiveType).to.equal(CASPrimitiveTypeInteger); expect([CASArgumentDescriptor argWithObjCType:@encode(NSUInteger)].primitiveType).to.equal(CASPrimitiveTypeInteger); expect([CASArgumentDescriptor argWithObjCType:@encode(int)].primitiveType).to.equal(CASPrimitiveTypeInteger); expect([CASArgumentDescriptor argWithObjCType:@encode(long)].primitiveType).to.equal(CASPrimitiveTypeInteger); } - (void)testReturnDouble { expect([CASArgumentDescriptor argWithObjCType:@encode(double)].primitiveType).to.equal(CASPrimitiveTypeDouble); expect([CASArgumentDescriptor argWithObjCType:@encode(float)].primitiveType).to.equal(CASPrimitiveTypeFloat); if (sizeof(void*) == 4) { expect([CASArgumentDescriptor argWithObjCType:@encode(CGFloat)].primitiveType).to.equal(CASPrimitiveTypeFloat); } else if (sizeof(void*) == 8) { expect([CASArgumentDescriptor argWithObjCType:@encode(CGFloat)].primitiveType).to.equal(CASPrimitiveTypeDouble); } } - (void)testReturnCGSize { expect([CASArgumentDescriptor argWithObjCType:@encode(CGSize)].primitiveType).to.equal(CASPrimitiveTypeCGSize); } - (void)testReturnCGRect { expect([CASArgumentDescriptor argWithObjCType:@encode(CGRect)].primitiveType).to.equal(CASPrimitiveTypeCGRect); } - (void)testReturnCGPoint { expect([CASArgumentDescriptor argWithObjCType:@encode(CGPoint)].primitiveType).to.equal(CASPrimitiveTypeCGPoint); } - (void)testReturnUIEdgeInsets { expect([CASArgumentDescriptor argWithObjCType:@encode(UIEdgeInsets)].primitiveType).to.equal(CASPrimitiveTypeUIEdgeInsets); } - (void)testReturnUIOffset { expect([CASArgumentDescriptor argWithObjCType:@encode(UIOffset)].primitiveType).to.equal(CASPrimitiveTypeUIOffset); } - (void)testReturnUnsupported { expect([CASArgumentDescriptor argWithObjCType:@encode(CASStructExample)].primitiveType).to.equal(CASPrimitiveTypeUnsupported); } - (void)testReturnCGColorRef { expect([CASArgumentDescriptor argWithObjCType:@encode(CGColorRef)].primitiveType).to.equal(CASPrimitiveTypeCGColorRef); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASLexerSpec.m ================================================ // // CASLexerSpec.m // Classy // // Created by Jonas Budelmann on 17/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASLexer.h" #import "XCTest+Spec.h" #import "UIColor+CASAdditions.h" #import "CASUnitToken.h" @interface CASLexer () @property (nonatomic, strong) NSMutableString *str; @end SpecBegin(CASLexer) - (void)testCleanUpCarriageReturns { CASLexer *lexer = [[CASLexer alloc] initWithString:@"hello \r\n world \r"]; expect(lexer.str).to.equal(@"hello \n world\n"); } - (void)testCleanUpEndOfString { CASLexer *lexer = [[CASLexer alloc] initWithString:@"hello \r\n \r \n "]; expect(lexer.str).to.equal(@"hello\n"); } - (void)testReturnEOS { CASLexer *lexer = [[CASLexer alloc] initWithString:@""]; expect([lexer lookaheadByCount:100].type).to.equal(CASTokenTypeEOS); } - (void)testReturnError { CASLexer *lexer = [[CASLexer alloc] initWithString:@"†"]; expect(lexer.nextToken).to.beNil(); expect(lexer.error.domain).to.equal(CASParseErrorDomain); expect(lexer.error.localizedDescription).to.equal(@"Invalid style string"); } - (void)testReturnSeperator { CASLexer *lexer = [[CASLexer alloc] initWithString:@"; \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeSemiColon); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@";; world hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeSemiColon); expect(lexer.str).to.equal(@"; world hello"); } - (void)testReturnSpace { CASLexer *lexer = [[CASLexer alloc] initWithString:@" \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeSpace); expect(lexer.str).to.equal(@"hello"); } - (void)testReturnCurlyBrace { CASLexer *lexer = [[CASLexer alloc] initWithString:@"{ \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeLeftCurlyBrace); expect(lexer.peekToken.value).to.equal(@"{"); expect(lexer.str).to.equal(@" \t hello"); lexer = [[CASLexer alloc] initWithString:@"}{ world hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeRightCurlyBrace); expect(lexer.peekToken.value).to.equal(@"}"); expect(lexer.str).to.equal(@"{ world hello"); } - (void)testReturnSquareBrace { CASLexer *lexer = [[CASLexer alloc] initWithString:@"[ \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeLeftSquareBrace); expect(lexer.peekToken.value).to.equal(@"["); expect(lexer.str).to.equal(@" \t hello"); lexer = [[CASLexer alloc] initWithString:@"][ world hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeRightSquareBrace); expect(lexer.peekToken.value).to.equal(@"]"); expect(lexer.str).to.equal(@"[ world hello"); } - (void)testReturnRoundBraces { CASLexer *lexer = [[CASLexer alloc] initWithString:@"size(1,2,3,4)"]; CASToken *token = lexer.nextToken; expect(token.type).to.equal(CASTokenTypeRef); expect(token.value).to.equal(@"size"); expect(lexer.str).to.equal(@"(1,2,3,4)"); token = lexer.nextToken; expect(token.type).to.equal(CASTokenTypeLeftRoundBrace); expect(token.value).to.equal(@"("); expect(lexer.str).to.equal(@"1,2,3,4)"); token = [lexer lookaheadByCount:8]; expect(token.type).to.equal(CASTokenTypeRightRoundBrace); expect(token.value).to.equal(@")"); expect(lexer.str).to.equal(@""); } - (void)testReturnRgbUIColor { CASLexer *lexer = [[CASLexer alloc] initWithString:@"#fff \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeColor); expect([lexer.peekToken.value cas_hexValue]).to.equal(@"ffffff"); expect(lexer.str).to.equal(@"hello"); } - (void)testReturnRrggbbUIColor { CASLexer *lexer = [[CASLexer alloc] initWithString:@"#ffffff \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeColor); expect([lexer.peekToken.value cas_hexValue]).to.equal(@"ffffff"); expect(lexer.str).to.equal(@"hello"); } - (void)testReturnRrggbbaaUIColor { CASLexer *lexer = [[CASLexer alloc] initWithString:@"#ffffffff \t hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeColor); expect([lexer.peekToken.value cas_hexValue]).to.equal(@"ffffff"); expect(lexer.str).to.equal(@"hello"); } - (void)testReturnString { CASLexer *lexer = [[CASLexer alloc] initWithString:@"\" blah\" '\"hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeString); expect(lexer.peekToken.value).to.equal(@" blah"); expect(lexer.str).to.equal(@"'\"hello"); lexer = [[CASLexer alloc] initWithString:@"' \" blah'' hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeString); expect(lexer.peekToken.value).to.equal(@" \" blah"); expect(lexer.str).to.equal(@"' hello"); lexer = [[CASLexer alloc] initWithString:@"\"\" a hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeString); expect(lexer.peekToken.value).to.equal(@""); expect(lexer.str).to.equal(@"a hello"); } - (void)testReturnUnit { CASLexer *lexer = [[CASLexer alloc] initWithString:@"1.5px hello"]; CASUnitToken *unitToken = (id)lexer.peekToken; expect(unitToken.type).to.equal(CASTokenTypeUnit); expect(unitToken.value).to.equal(@1.5); expect(unitToken.suffix).to.equal(@"px"); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@"-10.5pt hello"]; unitToken = (id)lexer.peekToken; expect(unitToken.type).to.equal(CASTokenTypeUnit); expect(unitToken.value).to.equal(@-10.5); expect(unitToken.suffix).to.equal(@"pt"); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@"-20.5 hello"]; unitToken = (id)lexer.peekToken; expect(unitToken.type).to.equal(CASTokenTypeUnit); expect(unitToken.value).to.equal(@-20.5); expect(unitToken.suffix).to.equal(nil); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@"20% hello"]; unitToken = (id)lexer.peekToken; expect(unitToken.type).to.equal(CASTokenTypeUnit); expect(unitToken.value).to.equal(@20); expect(unitToken.suffix).to.equal(@"%"); expect(lexer.str).to.equal(@"hello"); } - (void)testReturnBoolean { CASLexer *lexer = [[CASLexer alloc] initWithString:@"YES hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeBoolean); expect(lexer.peekToken.value).to.equal(@YES); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@"true hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeBoolean); expect(lexer.peekToken.value).to.equal(@YES); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@"NO hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeBoolean); expect(lexer.peekToken.value).to.equal(@NO); expect(lexer.str).to.equal(@"hello"); lexer = [[CASLexer alloc] initWithString:@"false hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeBoolean); expect(lexer.peekToken.value).to.equal(@NO); expect(lexer.str).to.equal(@"hello"); } - (void)testReturnSelector { // any character except `\n` | `{` | `,` | whitespace CASLexer *lexer = [[CASLexer alloc] initWithString:@".hello world {"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeSelector); expect(lexer.peekToken.value).to.equal(@".hello"); expect(lexer.str).to.equal(@" world {"); } - (void)testReturnRef { CASLexer *lexer = [[CASLexer alloc] initWithString:@"@background-color hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeRef); expect(lexer.peekToken.value).to.equal(@"@background-color"); expect(lexer.str).to.equal(@" hello"); lexer = [[CASLexer alloc] initWithString:@"-----true;hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeRef); expect(lexer.peekToken.value).to.equal(@"-----true"); expect(lexer.str).to.equal(@";hello"); lexer = [[CASLexer alloc] initWithString:@"nicer_than-it--3454_(*^&;hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeRef); expect(lexer.peekToken.value).to.equal(@"nicer_than-it--3454_"); expect(lexer.str).to.equal(@"(*^&;hello"); lexer = [[CASLexer alloc] initWithString:@"@_background-_color hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeRef); expect(lexer.peekToken.value).to.equal(@"@_background-_color"); expect(lexer.str).to.equal(@" hello"); } - (void)testSkipComments { CASLexer *lexer = [[CASLexer alloc] initWithString:@"//hello world \n \n stuff"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeIndent); expect(lexer.peekToken.value).to.equal(nil); expect(lexer.peekToken.lineNumber).to.equal(3); expect(lexer.str).to.equal(@"stuff"); lexer = [[CASLexer alloc] initWithString:@"/* hello \n \n world \n */ \n \n stuff"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeSpace); expect(lexer.peekToken.lineNumber).to.equal(4); expect(lexer.str).to.equal(@"\n \n stuff"); //EOS lexer = [[CASLexer alloc] initWithString:@"//hello world again"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeEOS); expect(lexer.peekToken.value).to.equal(nil); expect(lexer.peekToken.lineNumber).to.equal(1); expect(lexer.str).to.equal(@""); lexer = [[CASLexer alloc] initWithString:@"/*hello world again"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeEOS); expect(lexer.peekToken.value).to.equal(nil); expect(lexer.peekToken.lineNumber).to.equal(1); expect(lexer.str).to.equal(@""); } - (void)testReturnIdent { NSString *string = @"UIView\n" " asdf"; CASLexer *lexer = [[CASLexer alloc] initWithString:string]; [lexer nextToken]; CASToken *token = [lexer nextToken]; expect(token.type).to.equal(CASTokenTypeIndent); expect(token.value).to.equal(nil); expect(token.lineNumber).to.equal(2); expect(lexer.str).to.equal(@"asdf"); } - (void)testComplainWhenMixingIndentationTypes { NSString *string = @"UIView\n" " spaces:2;\n" "\ttabs:1;"; CASLexer *lexer = [[CASLexer alloc] initWithString:string]; while(lexer.peekToken && lexer.peekToken.type != CASTokenTypeEOS) { [lexer nextToken]; } expect(lexer.error).notTo.beNil(); expect(lexer.error.domain).to.equal(CASParseErrorDomain); expect(lexer.error.code).to.equal(CASParseErrorInvalidIndentation); expect(lexer.error.userInfo[CASParseFailingLineNumberErrorKey]).to.equal(@3); expect(lexer.error.userInfo[CASParseFailingStringErrorKey]).to.equal(@"\"\ttabs:1;\""); } - (void)testReturnOutdent { NSString *string = @"UIButton\n" " background-color\n" " border-width 1\n" " > UIControl\n" " amazing 23\n" " UISlider\n" " another 34\n" "b"; CASLexer *lexer = [[CASLexer alloc] initWithString:string]; expect([[lexer lookaheadByCount:1] value]).to.equal(@"UIButton"); expect([[lexer lookaheadByCount:2] type]).to.equal(CASTokenTypeIndent); expect([[lexer lookaheadByCount:3] value]).to.equal(@"background-color"); expect([[lexer lookaheadByCount:4] type]).to.equal(CASTokenTypeNewline); expect([[lexer lookaheadByCount:5] value]).to.equal(@"border-width"); expect([[lexer lookaheadByCount:6] type]).to.equal(CASTokenTypeSpace); expect([[lexer lookaheadByCount:7] value]).to.equal(@1); expect([[lexer lookaheadByCount:8] type]).to.equal(CASTokenTypeNewline); expect([[lexer lookaheadByCount:9] value]).to.equal(@">"); expect([[lexer lookaheadByCount:10] value]).to.equal(@"UIControl"); expect([[lexer lookaheadByCount:11] type]).to.equal(CASTokenTypeIndent); expect([[lexer lookaheadByCount:12] value]).to.equal(@"amazing"); expect([[lexer lookaheadByCount:13] type]).to.equal(CASTokenTypeSpace); expect([[lexer lookaheadByCount:14] value]).to.equal(@23); expect([[lexer lookaheadByCount:15] type]).to.equal(CASTokenTypeOutdent); expect([[lexer lookaheadByCount:16] value]).to.equal(@"UISlider"); expect([[lexer lookaheadByCount:17] type]).to.equal(CASTokenTypeIndent); expect([[lexer lookaheadByCount:18] value]).to.equal(@"another"); expect([[lexer lookaheadByCount:19] type]).to.equal(CASTokenTypeSpace); expect([[lexer lookaheadByCount:20] value]).to.equal(@34); expect([[lexer lookaheadByCount:21] type]).to.equal(CASTokenTypeOutdent); expect([[lexer lookaheadByCount:22] type]).to.equal(CASTokenTypeOutdent); expect([[lexer lookaheadByCount:23] value]).to.equal(@"b"); expect([[lexer lookaheadByCount:24] type]).to.equal(CASTokenTypeEOS); } - (void)testReturnOperator { CASLexer *lexer = [[CASLexer alloc] initWithString:@", hello"]; expect(lexer.peekToken.type).to.equal(CASTokenTypeOperator); expect(lexer.peekToken.value).to.equal(@","); expect(lexer.str).to.equal(@"hello"); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASParserSpec.m ================================================ // // CASParserSpec.m // Classy // // Created by Jonas Budelmann on 15/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASParser.h" #import "XCTest+Spec.h" #import "CASStyleNode.h" #import "CASToken.h" #import "UIColor+CASAdditions.h" #import "CASStyleSelector.h" #import "UIDevice+CASMockDevice.h" SpecBegin(CASParser) { UIDevice *mockDevice; } - (void)setUp { mockDevice = mock(UIDevice.class); [UIDevice setMockDevice:mockDevice]; } - (void)tearDown { [UIDevice setMockDevice:nil]; } - (void)testErrorWhenNoFile { NSError *error = nil; NSArray *styles = [CASParser parserFromFilePath:@"dummy.txt" variables:nil error:&error].styleNodes; expect(error.domain).to.equal(CASParseErrorDomain); expect(error.code).to.equal(CASParseErrorFileContents); NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey]; expect(underlyingError.domain).to.equal(NSCocoaErrorDomain); expect(underlyingError.code).to.equal(NSFileReadNoSuchFileError); expect(styles).to.beNil(); } - (void)testLoadFile { NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Messy.cas" ofType:nil]; NSError *error = nil; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(styles).notTo.beNil(); expect(error).to.beNil(); } - (void)testParseBasic { NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Messy.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:nil].styleNodes; expect(styles.count).to.equal(7); expect([styles[0] styleSelector].stringValue).to.equal(@"UIView"); expect([styles[1] styleSelector].stringValue).to.equal(@"UIControl"); expect([styles[2] styleSelector].stringValue).to.equal(@"UIView"); expect([styles[3] styleSelector].stringValue).to.equal(@"UIButton"); expect([styles[4] styleSelector].stringValue).to.equal(@"UITabBar"); expect([styles[5] styleSelector].stringValue).to.equal(@"UIView"); expect([styles[6] styleSelector].stringValue).to.equal(@"UITabBar"); } - (void)testParseComplex { NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Complex.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:nil].styleNodes; expect(styles.count).to.equal(6); expect([styles[0] styleSelector].stringValue).to.equal(@"UIButton[coolness:alot, state:selected].command"); expect([styles[1] styleSelector].stringValue).to.equal(@"UIButton UIImageView.starImage"); expect([styles[2] styleSelector].stringValue).to.equal(@"UIView.bordered"); expect([styles[3] styleSelector].stringValue).to.equal(@"UIView.panel"); expect([styles[4] styleSelector].stringValue).to.equal(@"UISlider"); expect([styles[5] styleSelector].stringValue).to.equal(@"UINavigationBar.videoNavBar UIButton[state:highlighted]"); } - (void)testParseWithoutBraces { NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Indentation.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:nil].styleNodes; expect(styles.count).to.equal(6); expect([styles[0] styleSelector].stringValue).to.equal(@"UIButton UIControl"); expect([styles[0] styleProperties]).to.haveCountOf(2); expect([styles[0] styleSelector].precedence).to.equal(6); expect([styles[1] styleSelector].stringValue).to.equal(@"UIButton UIImageView.starImage"); expect([styles[1] styleProperties]).to.haveCountOf(2); expect([styles[1] styleSelector].precedence).to.equal(3006); expect([styles[2] styleSelector].stringValue).to.equal(@"UIView.bordered"); expect([styles[2] styleProperties]).to.haveCountOf(3); expect([styles[2] styleSelector].precedence).to.equal(3004); expect([styles[3] styleSelector].stringValue).to.equal(@"UIView.panel"); expect([styles[3] styleProperties]).to.haveCountOf(3); expect([styles[3] styleSelector].precedence).to.equal(3004); expect([styles[4] styleSelector].stringValue).to.equal(@"UISlider"); expect([styles[4] styleProperties]).to.haveCountOf(4); expect([styles[4] styleSelector].precedence).to.equal(4); expect([styles[5] styleSelector].stringValue).to.equal(@"UINavigationBar.videoNavBar UIButton"); expect([styles[5] styleProperties]).to.haveCountOf(4); expect([styles[5] styleSelector].precedence).to.equal(1006); } - (void)testParseDirectDescendant { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Hierarchy.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(error).to.beNil(); expect(styles.count).to.equal(4); expect([styles[0] styleSelector].stringValue).to.equal(@"UIButton > UIImageView.starImage"); expect([styles[0] styleSelector].precedence).to.equal(3006); expect([styles[0] styleProperties]).to.haveCountOf(1); expect([styles[1] styleSelector].stringValue).to.equal(@"^UIView > UINavigationBar"); expect([styles[1] styleSelector].precedence).to.equal(5); expect([styles[1] styleProperties]).to.haveCountOf(2); expect([styles[2] styleSelector].stringValue).to.equal(@"UIView.bordered > UIView.panel"); expect([styles[2] styleSelector].parentSelector).notTo.beNil(); expect([styles[2] styleSelector].precedence).to.equal(4006); expect([styles[2] styleProperties]).to.haveCountOf(3); expect([styles[3] styleSelector].stringValue).to.equal(@"^UIView[state:selected] > UIImageView"); expect([styles[3] styleSelector].precedence).to.equal(5); expect([styles[3] styleProperties]).to.haveCountOf(4); } - (void)testParseNestedSelectors { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Nested.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(error).to.beNil(); expect(styles.count).to.equal(14); expect([styles[0] styleSelector].stringValue).to.equal(@"UIButton"); expect([styles[1] styleSelector].stringValue).to.equal(@"UIButton > UIControl"); expect([styles[2] styleSelector].stringValue).to.equal(@"UIButton UISlider"); expect([styles[3] styleSelector].stringValue).to.equal(@"UIView"); expect([styles[4] styleSelector].stringValue).to.equal(@"UIView UINavigationBar"); expect([styles[5] styleSelector].stringValue).to.equal(@"UIView UINavigationBar > UIView"); expect([styles[6] styleSelector].stringValue).to.equal(@"UITabBar.nice"); expect([styles[7] styleSelector].stringValue).to.equal(@"UISegmentedControl.nice"); expect([styles[8] styleSelector].stringValue).to.equal(@"UITabBar.nice UITextField"); expect([styles[9] styleSelector].stringValue).to.equal(@"UISegmentedControl.nice UITextField"); expect([styles[10] styleSelector].stringValue).to.equal(@"UITabBar.nice UITextField.nicer"); expect([styles[11] styleSelector].stringValue).to.equal(@"UISegmentedControl.nice UITextField.nicer"); expect([styles[12] styleSelector].stringValue).to.equal(@"UITableViewCell"); expect([styles[13] styleSelector].stringValue).to.equal(@"UITableViewCell UILabel"); // node 1 CASStyleNode *node = styles[0]; expect(node.styleProperties).to.haveCountOf(2); expect([node.styleProperties[0] name]).to.equal(@"backgroundColor"); expect([node.styleProperties[0] values]).to.equal(@[[UIColor cas_colorWithHex:@"#ffffff"]]); expect([node.styleProperties[1] name]).to.equal(@"borderWidth"); expect([node.styleProperties[1] values]).to.equal(@[@1]); // node 2 node = styles[1]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"amazing"); expect([node.styleProperties[0] values]).to.equal(@[@23]); // node 3 node = styles[2]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"another"); expect([node.styleProperties[0] values]).to.equal((@[@34])); // node 4 node = styles[3]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"borderColor"); expect([node.styleProperties[0] values]).to.equal((@[[UIColor cas_colorWithHex:@"#ddd"]])); // node 5 node = styles[4]; expect(node.styleProperties).to.haveCountOf(0); // node 6 node = styles[5]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"backgroundColor"); expect([node.styleProperties[0] values]).to.equal((@[[UIColor cas_colorWithHex:@"#eee"]])); // node 7 node = styles[6]; expect(node.styleProperties).to.haveCountOf(0); // node 8 node = styles[7]; expect(node.styleProperties).to.haveCountOf(0); // node 9 node = styles[8]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"borderWidth"); expect([node.styleProperties[0] values]).to.equal(@[@2]); // node 10 node = styles[9]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"borderWidth"); expect([node.styleProperties[0] values]).to.equal(@[@2]); // node 11 node = styles[10]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"borderWidth"); expect([node.styleProperties[0] values]).to.equal(@[@3]); // node 12 node = styles[11]; expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"borderWidth"); expect([node.styleProperties[0] values]).to.equal(@[@3]); // node 13 node = styles[12]; expect(node.styleProperties).to.haveCountOf(2); expect([node.styleProperties[0] name]).to.equal(@"normalBgColor"); expect([node.styleProperties[0] values]).to.equal(@[@"$blue-color"]); expect([node.styleProperties[1] name]).to.equal(@"selectedBgColor"); expect([node.styleProperties[1] values]).to.equal(@[@"$light-blue-color"]); // node 14 node = styles[13]; expect(node.styleProperties).to.haveCountOf(2); expect([node.styleProperties[0] name]).to.equal(@"font"); expect([node.styleProperties[0] values]).to.equal((@[@"HelveticaNeue", @14])); expect([node.styleProperties[1] name]).to.equal(@"textAlignment"); expect([node.styleProperties[1] values]).to.equal((@[@"center"])); } - (void)testParseProperties { NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Basic.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:nil].styleNodes; expect(styles.count).to.equal(5); // node 1 CASStyleNode *node = styles[0]; expect(node.styleProperties).to.haveCountOf(2); expect([node.styleProperties[0] name]).to.equal(@"backgroundColor"); expect([node.styleProperties[0] values]).to.equal(@[[UIColor cas_colorWithHex:@"#ffffff"]]); expect([node.styleProperties[1] name]).to.equal(@"borderInset"); expect([node.styleProperties[1] values]).to.equal(@[@1]); // node 2 node = styles[2]; expect(node.styleProperties).to.haveCountOf(2); expect([node.styleProperties[0] name]).to.equal(@"fontColor"); expect([node.styleProperties[0] values]).to.equal(@[[UIColor cas_colorWithHex:@"#ffffff"]]); expect([node.styleProperties[1] name]).to.equal(@"borderWidth"); expect([node.styleProperties[1] values]).to.equal(@[@2]); // node 3 node = styles[3]; expect(node.styleProperties).to.haveCountOf(3); expect([node.styleProperties[0] name]).to.equal(@"fontName"); expect([node.styleProperties[0] values]).to.equal(@[@"helvetica"]); expect([node.styleProperties[1] name]).to.equal(@"size"); expect([node.styleProperties[1] values]).to.equal((@[@40, @50])); expect([node.styleProperties[2] name]).to.equal(@"textColor"); expect([node.styleProperties[2] values]).to.equal(@[[UIColor cas_colorWithHex:@"#444"]]); } - (void)testParsePropertyArguments { NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Args.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:nil].styleNodes; expect(styles.count).to.equal(1); CASStyleNode *node = styles[0]; expect(node.styleProperties).to.haveCountOf(3); expect([node.styleProperties[0] name]).to.equal(@"backgroundColor"); expect([node.styleProperties[0] values]).to.equal(@[[UIColor cas_colorWithHex:@"#ffffff"]]); expect([node.styleProperties[0] arguments]).to.equal(@{ @"state" : @"selected" }); expect([node.styleProperties[1] name]).to.equal(@"fontName"); expect([node.styleProperties[1] values]).to.equal(@[@"helvetica"]); expect([node.styleProperties[1] arguments]).to.equal(@{ @"state" : @"highlighted" }); expect([node.styleProperties[2] name]).to.equal(@"fontSize"); expect([node.styleProperties[2] values]).to.equal(@[@14]); expect([node.styleProperties[2] arguments]).to.equal(@{ @"state" : @"disabled" }); } - (void)testParseNestedProperties { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Nested.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(error).to.beNil(); expect(styles).to.haveCountOf(3); // node 1 CASStyleNode *node = styles[0]; expect(node.styleSelector.stringValue).to.equal(@"UIButton"); expect(node.styleProperties).to.haveCountOf(3); expect([node.styleProperties[0] name]).to.equal(@"backgroundColor"); expect([node.styleProperties[1] name]).to.equal(@"borderWidth"); CASStyleProperty *embed = node.styleProperties[2]; expect(embed.name).to.equal(@"embed"); expect(embed.childStyleProperties).to.haveCountOf(4); expect([embed.childStyleProperties[0] name]).to.equal(@"font"); expect([embed.childStyleProperties[0] values]).to.equal((@[@"arial", @15])); expect([embed.childStyleProperties[2] name]).to.equal(@"kern"); expect([embed.childStyleProperties[2] values]).to.equal(@[@5]); expect([embed.childStyleProperties[3] name]).to.equal(@"stuff"); expect([embed.childStyleProperties[3] values]).to.equal(@[@"1 more thing"]); CASStyleProperty *paragraphStyle = embed.childStyleProperties[1]; expect(paragraphStyle.name).to.equal(@"paragraphStyle"); expect(paragraphStyle.childStyleProperties).to.haveCountOf(2); expect([paragraphStyle.childStyleProperties[0] name]).to.equal(@"maximumLineHeight"); expect([paragraphStyle.childStyleProperties[0] values]).to.equal(@[@2]); expect([paragraphStyle.childStyleProperties[1] name]).to.equal(@"minimumLineHeight"); expect([paragraphStyle.childStyleProperties[1] values]).to.equal(@[@4]); // node 2 node = styles[1]; expect(node.styleSelector.stringValue).to.equal(@"UIButton > UINavigationBar"); expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"amazing"); // node 3 node = styles[2]; expect(node.styleSelector.stringValue).to.equal(@"UIButton UISlider"); expect(node.styleProperties).to.haveCountOf(1); expect([node.styleProperties[0] name]).to.equal(@"another"); } - (void)testMediaQueries { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Media-Queries.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(error).to.beNil(); expect(styles).to.haveCountOf(26); CASStyleProperty *property; CASStyleNode *node; node = styles[0]; expect(node.styleSelector.stringValue).to.equal(@"UIButton"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"backgroundColor1"); expect(property.values).to.equal(@[[UIColor cas_colorWithHex:@"#fff"]]); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[1]; expect(node.styleSelector.stringValue).to.equal(@"UIButton"); expect(node.styleProperties).to.haveCountOf(2); property = node.styleProperties[0]; expect(property.name).to.equal(@"backgroundColor2"); expect(property.values).to.equal(@[@"black"]); property = node.styleProperties[1]; expect(property.name).to.equal(@"textColor"); expect(property.values).to.equal(@[@"orange"]); expect(node.deviceSelector.stringValue).to.equal(@"pad"); [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPad]; expect(node.deviceSelector.isValid).to.beTruthy(); [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPhone]; expect(node.deviceSelector.isValid).to.beFalsy(); node = styles[2]; expect(node.styleSelector.stringValue).to.equal(@"UIButton"); expect(node.styleProperties).to.haveCountOf(2); property = node.styleProperties[0]; expect(property.name).to.equal(@"backgroundColor2"); expect(property.values).to.equal(@[@"black"]); property = node.styleProperties[1]; expect(property.name).to.equal(@"textColor"); expect(property.values).to.equal(@[@"orange"]); expect(node.deviceSelector.stringValue).to.equal(@"(version:>=6)"); [given([mockDevice systemVersion]) willReturn:@"6.1"]; expect(node.deviceSelector.isValid).to.beTruthy(); [given([mockDevice systemVersion]) willReturn:@"6.0.0"]; expect(node.deviceSelector.isValid).to.beTruthy(); [given([mockDevice systemVersion]) willReturn:@"7.0.4"]; expect(node.deviceSelector.isValid).to.beTruthy(); [given([mockDevice systemVersion]) willReturn:@"4.0.0"]; expect(node.deviceSelector.isValid).to.beFalsy(); node = styles[3]; expect(node.styleSelector.stringValue).to.equal(@"UIButton"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"backgroundColor3"); expect(property.values).to.equal(@[@"blue"]); expect(node.deviceSelector.stringValue).to.equal(@"(version:>=7.0.1)"); node = styles[4]; expect(node.styleSelector.stringValue).to.equal(@"UIButton"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.equal(@"phone"); [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPad]; expect(node.deviceSelector.isValid).to.beFalsy(); [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPhone]; expect(node.deviceSelector.isValid).to.beTruthy(); node = styles[5]; expect(node.styleSelector.stringValue).to.equal(@"UIButton > UIControl"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"amazing"); expect(property.values).to.equal(@[@23]); expect(node.deviceSelector.stringValue).to.equal(@"phone"); node = styles[6]; expect(node.styleSelector.stringValue).to.equal(@"UIButton UISlider"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"hello"); expect(property.values).to.equal(@[@61]); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[7]; expect(node.styleSelector.stringValue).to.equal(@"UIButton UISlider"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.equal(@"phone"); node = styles[8]; expect(node.styleSelector.stringValue).to.equal(@"UIButton UISlider"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"textColor"); expect(property.values).to.equal(@[@"brown"]); expect(node.deviceSelector.stringValue).to.equal(@"(version:<=6.1) and phone"); [given([mockDevice systemVersion]) willReturn:@"5.2"]; [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPhone]; expect(node.deviceSelector.isValid).to.beTruthy(); [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPad]; expect(node.deviceSelector.isValid).to.beFalsy(); node = styles[9]; expect(node.styleSelector.stringValue).to.equal(@"UIView"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[10]; expect(node.styleSelector.stringValue).to.equal(@"UIView"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"borderColor"); expect(property.values).to.equal(@[[UIColor cas_colorWithHex:@"#ddd"]]); expect(node.deviceSelector.stringValue).to.equal(@"pad"); node = styles[11]; expect(node.styleSelector.stringValue).to.equal(@"UIView UINavigationBar"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.equal(@"pad"); node = styles[12]; expect(node.styleSelector.stringValue).to.equal(@"UIView UINavigationBar > UIView"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.equal(@"pad"); node = styles[13]; expect(node.styleSelector.stringValue).to.equal(@"UIView UINavigationBar > UIView"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"backgroundColor"); expect(property.values).to.equal(@[[UIColor cas_colorWithHex:@"#eee"]]); expect(node.deviceSelector.stringValue).to.equal(@"(version:5.0.1) and pad"); node = styles[14]; expect(node.styleSelector.stringValue).to.equal(@"UITabBar.nice"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[15]; expect(node.styleSelector.stringValue).to.equal(@"UISegmentedControl.nice"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[16]; expect(node.styleSelector.stringValue).to.equal(@"UITabBar.nice UITextField"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"borderWidth"); expect(property.values).to.equal(@[@2]); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[17]; expect(node.styleSelector.stringValue).to.equal(@"UISegmentedControl.nice UITextField"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"borderWidth"); expect(property.values).to.equal(@[@2]); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[18]; expect(node.styleSelector.stringValue).to.equal(@"UITabBar.nice UITextField"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.equal(@"(version:>5)"); node = styles[19]; expect(node.styleSelector.stringValue).to.equal(@"UISegmentedControl.nice UITextField"); expect(node.styleProperties).to.haveCountOf(0); expect(node.deviceSelector.stringValue).to.equal(@"(version:>5)"); node = styles[20]; expect(node.styleSelector.stringValue).to.equal(@"UITabBar.nice UITextField.nicer"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"borderWidth"); expect(property.values).to.equal(@[@3]); expect(node.deviceSelector.stringValue).to.equal(@"(version:>5)"); node = styles[21]; expect(node.styleSelector.stringValue).to.equal(@"UISegmentedControl.nice UITextField.nicer"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"borderWidth"); expect(property.values).to.equal(@[@3]); expect(node.deviceSelector.stringValue).to.equal(@"(version:>5)"); node = styles[22]; property = node.styleProperties[0]; expect(property.name).to.equal(@"always"); expect(property.values).to.equal(@[@1]); node = styles[23]; property = node.styleProperties[0]; expect(property.name).to.equal(@"heighty"); expect(property.values).to.equal(@[@2]); expect(node.deviceSelector.stringValue).to.equal(@"(screen-height:>200)"); node = styles[24]; property = node.styleProperties[0]; expect(property.name).to.equal(@"combinedtrue"); expect(property.values).to.equal(@[@3]); [given([mockDevice systemVersion]) willReturn:@"7.0"]; expect(node.deviceSelector.stringValue).to.equal(@"(screen-width:>100) and (version:>=6.0)"); expect(node.deviceSelector.isValid).to.beTruthy(); node = styles[25]; property = node.styleProperties[0]; expect(property.name).to.equal(@"combinedfalse"); expect(property.values).to.equal(@[@4]); [given([mockDevice systemVersion]) willReturn:@"6.0"]; expect(node.deviceSelector.stringValue).to.equal(@"(version:<6.0) and (screen-width:>100)"); expect(node.deviceSelector.isValid).to.beFalsy(); } - (void)testVariablesBasic { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Variables-Basic.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(error).to.beNil(); expect(styles).to.haveCountOf(3); CASStyleProperty *property; CASStyleNode *node; node = styles[0]; expect(node.styleSelector.stringValue).to.equal(@"UIView"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"layer"); property = property.childStyleProperties[0]; expect(property.name).to.equal(@"borderWidth"); expect(property.values).to.equal(@[@2]); expect(node.deviceSelector.stringValue).to.beNil(); property = node.styleProperties[0]; property = property.childStyleProperties[1]; expect(property.name).to.equal(@"shadowRadius"); expect(property.values).to.equal(@[@2000]); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[1]; expect(node.styleSelector.stringValue).to.equal(@"UITextField.one"); expect(node.styleProperties).to.haveCountOf(2); property = node.styleProperties[0]; expect(property.name).to.equal(@"font"); expect(property.values).to.equal((@[@"Avenir-Heavy", @12])); expect(node.deviceSelector.stringValue).to.beNil(); property = node.styleProperties[1]; expect(property.name).to.equal(@"textInsets"); expect(property.values).to.equal((@[@"(", @4, @3, @2, @1, @")"])); expect(node.deviceSelector.stringValue).to.beNil(); node = styles[2]; expect(node.styleSelector.stringValue).to.equal(@"UITextField.two"); expect(node.styleProperties).to.haveCountOf(1); property = node.styleProperties[0]; expect(property.name).to.equal(@"textInsets"); expect(property.values).to.equal((@[@10, @2000, @20, @30])); expect(node.deviceSelector.stringValue).to.beNil(); } - (void)testNSCoding { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Nested.cas" ofType:nil]; NSArray *styles = [CASParser parserFromFilePath:filePath variables:nil error:&error].styleNodes; expect(error).to.beNil(); expect(styles).to.haveCountOf(3); NSData *serializedStyles = [NSKeyedArchiver archivedDataWithRootObject:styles]; expect(serializedStyles).notTo.beNil(); NSArray *deserializedStyles = [NSKeyedUnarchiver unarchiveObjectWithData:serializedStyles]; expect(deserializedStyles).notTo.beNil(); expect(styles.count).to.equal(deserializedStyles.count); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASRuntimeExtensionsSpec.m ================================================ // // CASRuntimeExtensionsSpec.m // Classy // // Created by Jonas Budelmann on 15/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASRuntimeExtensions.h" #import "XCTest+Spec.h" #import @protocol EXTRuntimeTestProtocol @optional + (void)optionalClassMethod; - (void)optionalInstanceMethod; @end @interface RuntimeTestClass : NSObject @property (nonatomic, assign, getter = isNormalBool, readonly) BOOL normalBool; @property (nonatomic, strong, getter = whoopsWhatArray, setter = setThatArray:) NSArray *array; @property (copy) NSString *normalString; @property (unsafe_unretained) id untypedObject; @property (nonatomic, weak) NSObject *weakObject; @property (nonatomic, assign) CGColorRef colorRef; @end @implementation RuntimeTestClass @synthesize normalBool = _normalBool; @synthesize array = m_array; @synthesize normalString; - (NSObject *)weakObject { return nil; } - (void)setWeakObject:(NSObject *)weakObject { } @dynamic untypedObject; @end SpecBegin(CASRuntimeExtensions) - (void)testGetPropertyAttributesForBOOL { objc_property_t property = class_getProperty([RuntimeTestClass class], "normalBool"); NSLog(@"property attributes: %s", property_getAttributes(property)); cas_propertyAttributes *attributes = cas_copyPropertyAttributes(property); XCTAssertTrue(attributes != NULL, @"could not get property attributes"); XCTAssertEqual(attributes->readonly, YES, @""); XCTAssertEqual(attributes->nonatomic, YES, @""); XCTAssertEqual(attributes->weak, NO, @""); XCTAssertEqual(attributes->canBeCollected, NO, @""); XCTAssertEqual(attributes->dynamic, NO, @""); XCTAssertEqual(attributes->memoryManagementPolicy, cas_propertyMemoryManagementPolicyAssign, @""); XCTAssertEqual(attributes->getter, @selector(isNormalBool), @""); XCTAssertEqual(attributes->setter, @selector(setNormalBool:), @""); XCTAssertTrue(strcmp(attributes->ivar, "_normalBool") == 0, @"expected property ivar name to be '_normalBool'"); XCTAssertTrue(strlen(attributes->type) > 0, @"property type is missing from attributes"); NSUInteger size = 0; NSGetSizeAndAlignment(attributes->type, &size, NULL); XCTAssertTrue(size > 0, @"invalid property type %s, has no size", attributes->type); XCTAssertNil(attributes->objectClass, @""); free(attributes); } - (void)testGetPropertyAttributeForStructPointer { objc_property_t property = class_getProperty([RuntimeTestClass class], "colorRef"); NSLog(@"property attributes: %s", property_getAttributes(property)); cas_propertyAttributes *attributes = cas_copyPropertyAttributes(property); XCTAssertTrue(attributes != NULL, @"could not get property attributes"); XCTAssertTrue(strlen(attributes->type) > 0, @"property type is missing from attributes"); XCTAssertNil(attributes->objectClass, @""); } - (void)testGetPropertyAttributesForArray { objc_property_t property = class_getProperty([RuntimeTestClass class], "array"); NSLog(@"property attributes: %s", property_getAttributes(property)); cas_propertyAttributes *attributes = cas_copyPropertyAttributes(property); XCTAssertTrue(attributes != NULL, @"could not get property attributes"); XCTAssertEqual(attributes->readonly, NO, @""); XCTAssertEqual(attributes->nonatomic, YES, @""); XCTAssertEqual(attributes->weak, NO, @""); XCTAssertEqual(attributes->canBeCollected, NO, @""); XCTAssertEqual(attributes->dynamic, NO, @""); XCTAssertEqual(attributes->memoryManagementPolicy, cas_propertyMemoryManagementPolicyRetain, @""); XCTAssertEqual(attributes->getter, @selector(whoopsWhatArray), @""); XCTAssertEqual(attributes->setter, @selector(setThatArray:), @""); XCTAssertTrue(strcmp(attributes->ivar, "m_array") == 0, @"expected property ivar name to be 'm_array'"); XCTAssertTrue(strlen(attributes->type) > 0, @"property type is missing from attributes"); NSUInteger size = 0; NSGetSizeAndAlignment(attributes->type, &size, NULL); XCTAssertTrue(size > 0, @"invalid property type %s, has no size", attributes->type); XCTAssertEqualObjects(attributes->objectClass, [NSArray class], @""); free(attributes); } - (void)testGetPropertyAttributesForNormalString { objc_property_t property = class_getProperty([RuntimeTestClass class], "normalString"); NSLog(@"property attributes: %s", property_getAttributes(property)); cas_propertyAttributes *attributes = cas_copyPropertyAttributes(property); XCTAssertTrue(attributes != NULL, @"could not get property attributes"); XCTAssertEqual(attributes->readonly, NO, @""); XCTAssertEqual(attributes->nonatomic, NO, @""); XCTAssertEqual(attributes->weak, NO, @""); XCTAssertEqual(attributes->canBeCollected, NO, @""); XCTAssertEqual(attributes->dynamic, NO, @""); XCTAssertEqual(attributes->memoryManagementPolicy, cas_propertyMemoryManagementPolicyCopy, @""); XCTAssertEqual(attributes->getter, @selector(normalString), @""); XCTAssertEqual(attributes->setter, @selector(setNormalString:), @""); XCTAssertTrue(strcmp(attributes->ivar, "normalString") == 0, @"expected property ivar name to match the name of the property"); XCTAssertTrue(strlen(attributes->type) > 0, @"property type is missing from attributes"); NSUInteger size = 0; NSGetSizeAndAlignment(attributes->type, &size, NULL); XCTAssertTrue(size > 0, @"invalid property type %s, has no size", attributes->type); XCTAssertEqualObjects(attributes->objectClass, [NSString class], @""); free(attributes); } - (void)testGetPropertyAttributesForUntypedObject { objc_property_t property = class_getProperty([RuntimeTestClass class], "untypedObject"); NSLog(@"property attributes: %s", property_getAttributes(property)); cas_propertyAttributes *attributes = cas_copyPropertyAttributes(property); XCTAssertTrue(attributes != NULL, @"could not get property attributes"); XCTAssertEqual(attributes->readonly, NO, @""); XCTAssertEqual(attributes->nonatomic, NO, @""); XCTAssertEqual(attributes->weak, NO, @""); XCTAssertEqual(attributes->canBeCollected, NO, @""); XCTAssertEqual(attributes->dynamic, YES, @""); XCTAssertEqual(attributes->memoryManagementPolicy, cas_propertyMemoryManagementPolicyAssign, @""); XCTAssertEqual(attributes->getter, @selector(untypedObject), @""); XCTAssertEqual(attributes->setter, @selector(setUntypedObject:), @""); XCTAssertTrue(attributes->ivar == NULL, @"untypedObject property should not have a backing ivar"); XCTAssertTrue(strlen(attributes->type) > 0, @"property type is missing from attributes"); NSUInteger size = 0; NSGetSizeAndAlignment(attributes->type, &size, NULL); XCTAssertTrue(size > 0, @"invalid property type %s, has no size", attributes->type); // cannot get class for type 'id' XCTAssertNil(attributes->objectClass, @""); free(attributes); } - (void)testGetPropertyAttributesForWeakObject { objc_property_t property = class_getProperty([RuntimeTestClass class], "weakObject"); NSLog(@"property attributes: %s", property_getAttributes(property)); cas_propertyAttributes *attributes = cas_copyPropertyAttributes(property); XCTAssertTrue(attributes != NULL, @"could not get property attributes"); XCTAssertEqual(attributes->readonly, NO, @""); XCTAssertEqual(attributes->nonatomic, YES, @""); XCTAssertEqual(attributes->weak, YES, @""); XCTAssertEqual(attributes->canBeCollected, NO, @""); XCTAssertEqual(attributes->dynamic, NO, @""); XCTAssertEqual(attributes->memoryManagementPolicy, cas_propertyMemoryManagementPolicyAssign, @""); XCTAssertEqual(attributes->getter, @selector(weakObject), @""); XCTAssertEqual(attributes->setter, @selector(setWeakObject:), @""); XCTAssertTrue(attributes->ivar == NULL, @"weakObject property should not have a backing ivar"); XCTAssertTrue(strlen(attributes->type) > 0, @"property type is missing from attributes"); NSUInteger size = 0; NSGetSizeAndAlignment(attributes->type, &size, NULL); XCTAssertTrue(size > 0, @"invalid property type %s, has no size", attributes->type); XCTAssertEqualObjects(attributes->objectClass, [NSObject class], @""); free(attributes); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASStylePropertySpec.m ================================================ // // CASStylePropertySpec.m // Classy // // Created by Jonas Budelmann on 16/10/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASStyleProperty.h" #import "XCTest+Spec.h" #import "CASLexer.h" NSArray *CASTokensFromString(NSString *string) { NSMutableArray *tokens = NSMutableArray.new; CASLexer *lexer = [[CASLexer alloc] initWithString:string]; while (lexer.peekToken && lexer.peekToken.type != CASTokenTypeEOS) { [tokens addObject:lexer.nextToken]; } return tokens; } SpecBegin(CASStyleProperty) - (void)testNotParseZeroUnits { NSArray *valueTokens = @[ [CASToken tokenOfType:CASTokenTypeRef value:@3], ]; CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block CGSize size = CGSizeZero; expect([prop transformValuesToCGSize:&size]).to.beFalsy(); expect(size).to.equal(CGSizeZero); __block UIEdgeInsets insets = UIEdgeInsetsZero; expect([prop transformValuesToUIEdgeInsets:&insets]).to.beFalsy(); expect(insets).to.equal(UIEdgeInsetsZero); __block CGRect rect = CGRectZero; expect([prop transformValuesToCGRect:&rect]).to.beFalsy(); expect(rect).to.equal(CGRectZero); __block CGPoint point = CGPointZero; expect([prop transformValuesToCGPoint:&point]).to.beFalsy(); expect(point).to.equal(CGPointZero); } - (void)testParseOneUnit { NSArray *valueTokens = CASTokensFromString(@"3"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block CGSize size = CGSizeZero; expect([prop transformValuesToCGSize:&size]).to.beTruthy(); expect(size).to.equal(CGSizeMake(3, 3)); __block UIEdgeInsets insets = UIEdgeInsetsZero; expect([prop transformValuesToUIEdgeInsets:&insets]).to.beTruthy(); expect(insets).to.equal(UIEdgeInsetsMake(3, 3, 3, 3)); __block CGRect rect = CGRectZero; expect([prop transformValuesToCGRect:&rect]).to.beFalsy(); expect(rect).to.equal(CGRectZero); __block CGPoint point = CGPointZero; expect([prop transformValuesToCGPoint:&point]).to.beTruthy(); expect(point).to.equal(CGPointMake(3, 3)); } - (void)testParseTwoUnits { NSArray *valueTokens = CASTokensFromString(@"3 34"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block CGSize size = CGSizeZero; expect([prop transformValuesToCGSize:&size]).to.beTruthy(); expect(size).to.equal(CGSizeMake(3, 34)); __block UIEdgeInsets insets = UIEdgeInsetsZero; expect([prop transformValuesToUIEdgeInsets:&insets]).to.beTruthy(); expect(insets).to.equal(UIEdgeInsetsMake(3, 34, 3, 34)); __block CGRect rect = CGRectZero; expect([prop transformValuesToCGRect:&rect]).to.beFalsy(); expect(rect).to.equal(CGRectZero); __block CGPoint point = CGPointZero; expect([prop transformValuesToCGPoint:&point]).to.beTruthy(); expect(point).to.equal(CGPointMake(3, 34)); } - (void)testParseFourUnits { NSArray *valueTokens = CASTokensFromString(@"1, 2, 3, 4"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block CGSize size = CGSizeZero; expect([prop transformValuesToCGSize:&size]).to.beFalsy(); expect(size).to.equal(CGSizeZero); __block UIEdgeInsets insets = UIEdgeInsetsZero; expect([prop transformValuesToUIEdgeInsets:&insets]).to.beTruthy(); expect(insets).to.equal(UIEdgeInsetsMake(1, 2, 3, 4)); __block CGRect rect = CGRectZero; expect([prop transformValuesToCGRect:&rect]).to.beTruthy(); expect(rect).to.equal(CGRectMake(1, 2, 3, 4)); __block CGPoint point = CGPointZero; expect([prop transformValuesToCGPoint:&point]).to.beFalsy(); expect(point).to.equal(CGPointZero); } - (void)testNamedColor { NSArray *valueTokens = CASTokensFromString(@"red"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor redColor]); } - (void)testWellFormedRGBColor { NSArray *valueTokens = CASTokensFromString(@"rgb(245, 215, 200)"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithRed:245/255.0 green:215/255.0 blue:200/255.0 alpha:1]); } - (void)testWellFormedRGBAColor { NSArray *valueTokens = CASTokensFromString(@"rgba(200, 215, 200, 0.5)"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithRed:200/255.0 green:215/255.0 blue:200/255.0 alpha:0.5]); } - (void)testMalformedFormedRGBAColor { NSArray *valueTokens = CASTokensFromString(@"rgba( 10 , 215 , 200 , 0.5 )"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithRed:10/255.0 green:215/255.0 blue:200/255.0 alpha:0.5]); } - (void)testRGBAWithoutBracesColor { NSArray *valueTokens = CASTokensFromString(@"rgba 10.1 215 200 0.5"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithRed:10.1/255.0 green:215/255.0 blue:200/255.0 alpha:0.5]); } - (void)testWellFormedHSLColor { NSArray *valueTokens = CASTokensFromString(@"hsl(200, 60%, 100%)"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithHue:200.0/360.0 saturation:60.0/100.0 brightness:100.0/100.0 alpha:1.0]); } - (void)testWellFormedHSLAColor { NSArray *valueTokens = CASTokensFromString(@"hsla(200, 60%, 100%, 0.5)"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithHue:200.0/360.0 saturation:60.0/100.0 brightness:100.0/100.0 alpha:0.5]); } - (void)testMalformedFormedHSLAColor { NSArray *valueTokens = CASTokensFromString(@"hsla( 200 , 60% , 100% , 0.5 )"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithHue:200.0/360.0 saturation:60.0/100.0 brightness:100.0/100.0 alpha:0.5]); } - (void)testHSLAWithoutBracesColor { NSArray *valueTokens = CASTokensFromString(@"hsla 200 60% 100% 0.5"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; UIColor *color = nil; [prop transformValuesToUIColor:&color]; expect(color).to.equal([UIColor colorWithHue:200.0/360.0 saturation:60.0/100.0 brightness:100.0/100.0 alpha:0.5]); } - (void)testResolveSimpleExpression { NSArray *valueTokens = CASTokensFromString(@"3 * 2 + 5"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; [prop resolveExpressions]; expect([prop.values componentsJoinedByString:@""]).to.equal(@"11"); } - (void)testResolveExpressionWithFunction { NSArray *valueTokens = CASTokensFromString(@"floor (4.5)* 2 +5"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; [prop resolveExpressions]; expect([prop.values componentsJoinedByString:@""]).to.equal(@"13"); } - (void)testResolveTwoTuple { NSArray *valueTokens = CASTokensFromString(@"(4.5,2)*5"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; [prop resolveExpressions]; expect([prop.values componentsJoinedByString:@""]).to.equal(@"(22.5,10)"); } - (void)testResolveTwoTupleForwards { NSArray *valueTokens = CASTokensFromString(@"5*(4.5,2)"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; [prop resolveExpressions]; expect([prop.values componentsJoinedByString:@""]).to.equal(@"(22.5,10)"); } - (void)testResolveQuadTuple { NSArray *valueTokens = CASTokensFromString(@"( 6, 10 12, 8 ) / 2"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; [prop resolveExpressions]; expect([prop.values componentsJoinedByString:@""]).to.equal(@"(3,5,6,4)"); } - (void)testSolveMixedExpression { NSArray *valueTokens = CASTokensFromString(@"tiger place 2 + 2 (1 + (2 * 3) 3 + 0.5) * 2 / 5"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; [prop resolveExpressions]; expect([prop.values componentsJoinedByString:@""]).to.equal(@"tiger place 4(2,1.4)"); } - (void)testImageFromMainBundle { NSArray *valueTokens = CASTokensFromString(@"test_image_1"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block UIImage *image = nil; expect([prop transformValuesToUIImage:&image]).to.beTruthy(); expect(image).toNot.beNil(); } - (void)testImageFromDocumentsDirectory { // First put the image in the documents directory NSString *imageBundlePath = [[NSBundle mainBundle] pathForResource:@"test_image_2" ofType:@"png"]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docsPath = [paths firstObject]; [[NSFileManager defaultManager] copyItemAtPath:imageBundlePath toPath:[docsPath stringByAppendingPathComponent:@"test_image_2.png"] error:nil]; // Test with no extension NSArray *valueTokens = CASTokensFromString(@"'documents://test_image_2.png'"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block UIImage *image = nil; expect([prop transformValuesToUIImage:&image]).to.beTruthy(); expect(image).toNot.beNil(); } - (void)testImageFromDocumentsDirectoryWithComplexPath { // First put the image in the documents directory NSString *imageBundlePath = [[NSBundle mainBundle] pathForResource:@"test_image_2" ofType:@"png"]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docsPath = [[paths firstObject] stringByAppendingPathComponent:@"test/extra"]; [[NSFileManager defaultManager] createDirectoryAtPath:docsPath withIntermediateDirectories:YES attributes:nil error:nil]; [[NSFileManager defaultManager] copyItemAtPath:imageBundlePath toPath:[docsPath stringByAppendingPathComponent:@"test_image_2.png"] error:nil]; // Test with no extension NSArray *valueTokens = CASTokensFromString(@"'documents:///test/extra/test_image_2.png'"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block UIImage *image = nil; expect([prop transformValuesToUIImage:&image]).to.beTruthy(); expect(image).toNot.beNil(); } - (void)testImageFromCachesDirectory { // First put the image in the documents directory NSString *imageBundlePath = [[NSBundle mainBundle] pathForResource:@"test_image_3" ofType:@"png"]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *docsPath = [paths firstObject]; [[NSFileManager defaultManager] createDirectoryAtPath:docsPath withIntermediateDirectories:YES attributes:nil error:nil]; [[NSFileManager defaultManager] copyItemAtPath:imageBundlePath toPath:[docsPath stringByAppendingPathComponent:@"test_image_3.png"] error:nil]; // Test with no extension NSArray *valueTokens = CASTokensFromString(@"'caches://test_image_3.png'"); CASStyleProperty *prop = [[CASStyleProperty alloc] initWithNameToken:nil valueTokens:valueTokens]; __block UIImage *image = nil; expect([prop transformValuesToUIImage:&image]).to.beTruthy(); expect(image).toNot.beNil(); } - (void)testFont { NSArray *valueTokens = CASTokensFromString(@"\"Palatino\" 24"); CASStyleProperty *prop = [[CASStyleProperty alloc]initWithNameToken:nil valueTokens:valueTokens]; __block UIFont *font = nil; expect([prop transformValuesToUIFont:&font]).to.beTruthy(); expect(font.familyName).to.equal(@"Palatino"); expect(font.pointSize).to.equal(24.0f); } - (void)testSystemFont { NSArray *valueTokens = CASTokensFromString(@"System 12"); CASStyleProperty *prop = [[CASStyleProperty alloc]initWithNameToken:nil valueTokens:valueTokens]; __block UIFont *font = nil; expect([prop transformValuesToUIFont:&font]).to.beTruthy(); expect(font.familyName).to.equal([UIFont systemFontOfSize:12].familyName); expect(font.pointSize).to.equal(12); } - (void)testSystemFontWeight { NSArray *valueTokens = CASTokensFromString(@"System-Medium 18"); CASStyleProperty *prop = [[CASStyleProperty alloc]initWithNameToken:nil valueTokens:valueTokens]; __block UIFont *font = nil; expect([prop transformValuesToUIFont:&font]).to.beTruthy(); expect([font.fontDescriptor.fontAttributes[UIFontDescriptorNameAttribute] rangeOfString:@"Medium"].location != NSNotFound).to.beTruthy(); expect(font.pointSize).to.equal(18); } - (void)testPreferredFontForTextStyle { NSArray *valueTokens = CASTokensFromString(@"body"); CASStyleProperty *prop = [[CASStyleProperty alloc]initWithNameToken:nil valueTokens:valueTokens]; __block UIFont *font = nil; if ([UIFont respondsToSelector:@selector(preferredFontForTextStyle:)]) { expect([prop transformValuesToUIFont:&font]).to.beTruthy(); expect(font).to.equal([UIFont preferredFontForTextStyle:@"UICTFontTextStyleBody"]); } else { expect([prop transformValuesToUIFont:&font]).to.beFalsy(); } } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASStyleSelectorSpec.m ================================================ // // CASStyleSelectorSpec.m // ClassyTests // // Created by Jonas Budelmann on 30/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "CASStyleSelector.h" #import "XCTest+Spec.h" #import "UIView+CASAdditions.h" #import "UIViewController+CASAdditions.h" #import "CASExampleView.h" #import "CASExampleViewController.h" /** * Test helper method for making sure we have created correct view hierarchy */ NSString * CASStringViewHierarchyFromView(UIView *view) { NSMutableString *viewHierarchy = NSMutableString.new; for (UIView *ancestor = view; ancestor != nil; ancestor = ancestor.superview) { if (ancestor.cas_styleClass.length) { [viewHierarchy insertString:[NSString stringWithFormat:@".%@", ancestor.cas_styleClass] atIndex:0]; } [viewHierarchy insertString:[NSString stringWithFormat:@" > %@", NSStringFromClass(ancestor.class)] atIndex:0]; } return [viewHierarchy stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" >"]]; } SpecBegin(CASStyleSelector) - (void)testSelectViewClass { CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UIView.class; expect(selector.stringValue).to.equal(@"UIView"); expect([selector shouldSelectItem:UIView.new]).to.beTruthy(); expect([selector shouldSelectItem:UISlider.new]).to.beFalsy(); selector.objectClass = UITabBar.class; expect(selector.stringValue).to.equal(@"UITabBar"); expect([selector shouldSelectItem:UITabBar.new]).to.beTruthy(); expect([selector shouldSelectItem:UINavigationBar.new]).to.beFalsy(); } - (void)testSelectViewWithIndirectSuperview { CASStyleSelector *parentSelector = CASStyleSelector.new; parentSelector.objectClass = UIControl.class; parentSelector.parent = YES; CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UISlider.class; selector.parentSelector = parentSelector; UIControl *control = UIControl.new; UISlider *slider = UISlider.new; [control addSubview:slider]; expect(selector.stringValue).to.equal(@"UIControl UISlider"); expect([selector shouldSelectItem:UIView.new]).to.beFalsy(); expect([selector shouldSelectItem:slider]).to.beTruthy(); //add view inbetween control and slider UIButton *button = UIButton.new; button.cas_styleClass = @"styleClassIrrelevantInThisCase"; [control addSubview:button]; [button addSubview:slider]; expect([selector shouldSelectItem:slider]).to.beTruthy(); } - (void)testSelectViewWithDirectSuperviewOnly { CASStyleSelector *parentSelector = CASStyleSelector.new; parentSelector.objectClass = UIControl.class; parentSelector.parent = YES; CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UISlider.class; selector.shouldSelectIndirectSuperview = NO; selector.parentSelector = parentSelector; UIControl *control = UIControl.new; UISlider *slider = UISlider.new; [control addSubview:slider]; expect(selector.stringValue).to.equal(@"UIControl > UISlider"); expect([selector shouldSelectItem:UIView.new]).to.beFalsy(); expect([selector shouldSelectItem:slider]).to.beTruthy(); //add view inbetween control and slider UIButton *button = UIButton.new; button.cas_styleClass = @"styleClassIrrelevantInThisCase"; [control addSubview:button]; [button addSubview:slider]; expect([selector shouldSelectItem:slider]).to.beFalsy(); } - (void)testSelectViewWithStyleClass { CASStyleSelector *selector = CASStyleSelector.new; selector.styleClass = @"big"; selector.objectClass = UIView.class; UIView *view = UIView.new; expect(selector.stringValue).to.equal(@"UIView.big"); expect([selector shouldSelectItem:view]).to.beFalsy(); view.cas_styleClass = @"big"; expect([selector shouldSelectItem:view]).to.beTruthy(); selector.objectClass = UITabBar.class; UITabBar *tabBar = UITabBar.new; expect(selector.stringValue).to.equal(@"UITabBar.big"); expect([selector shouldSelectItem:tabBar]).to.beFalsy(); tabBar.cas_styleClass = @"big"; expect([selector shouldSelectItem:tabBar]).to.beTruthy(); } - (void)testSelectViewWithSubclassMatch { CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UIControl.class; selector.shouldSelectSubclasses = YES; expect(selector.stringValue).to.equal(@"^UIControl"); expect([selector shouldSelectItem:UIControl.new]).to.beTruthy(); expect([selector shouldSelectItem:UIButton.new]).to.beTruthy(); expect([selector shouldSelectItem:UIView.new]).to.beFalsy(); expect([selector shouldSelectItem:UINavigationBar.new]).to.beFalsy(); } - (void)testSelectViewWithSubclassMatchAndStyleClass { CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UIControl.class; selector.shouldSelectSubclasses = YES; selector.styleClass = @"big"; expect(selector.stringValue).to.equal(@"^UIControl.big"); expect([selector shouldSelectItem:UIControl.new]).to.beFalsy(); expect([selector shouldSelectItem:UIButton.new]).to.beFalsy(); UIControl *control = UIControl.new; control.cas_styleClass = @"big"; expect([selector shouldSelectItem:control]).to.beTruthy(); UIButton *button = UIButton.new; button.cas_styleClass = @"big"; expect([selector shouldSelectItem:button]).to.beTruthy(); } - (void)testSelectViewWithComplexMixedMatchers { CASStyleSelector *parentSelector3 = CASStyleSelector.new; parentSelector3.objectClass = UIButton.class; parentSelector3.styleClass = @"top"; parentSelector3.parent = YES; CASStyleSelector *parentSelector2 = CASStyleSelector.new; parentSelector2.objectClass = UIView.class; parentSelector2.shouldSelectSubclasses = YES; parentSelector2.shouldSelectIndirectSuperview = NO; parentSelector2.parent = YES; parentSelector2.parentSelector = parentSelector3; CASStyleSelector *parentSelector1 = CASStyleSelector.new; parentSelector1.objectClass = UIControl.class; parentSelector1.styleClass = @"mid"; parentSelector1.parent = YES; parentSelector1.parentSelector = parentSelector2; CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UISlider.class; selector.shouldSelectIndirectSuperview = NO; selector.parentSelector = parentSelector1; expect(selector.stringValue).to.equal(@"UIButton.top > ^UIView UIControl.mid > UISlider"); // view heirarchy 1 UIButton *topButton = UIButton.new; topButton.cas_styleClass = @"top"; UIView *view = UIView.new; [topButton addSubview:view]; UIControl *midControl = UIControl.new; midControl.cas_styleClass = @"mid"; [view addSubview:midControl]; UISlider *slider = UISlider.new; [midControl addSubview:slider]; expect(CASStringViewHierarchyFromView(slider)).to.equal(@"UIButton.top > UIView > UIControl.mid > UISlider"); expect([selector shouldSelectItem:slider]).to.beTruthy(); // view heirarchy 2 [view removeFromSuperview]; expect(CASStringViewHierarchyFromView(slider)).to.equal(@"UIView > UIControl.mid > UISlider"); expect([selector shouldSelectItem:slider]).to.beFalsy(); // view heirarchy 3 UIButton *button = UIButton.new; [topButton addSubview:button]; CASExampleView *exampleView = CASExampleView.new; [button addSubview:exampleView]; [exampleView addSubview:view]; expect(CASStringViewHierarchyFromView(slider)).to.equal(@"UIButton.top > UIButton > CASExampleView > UIView > UIControl.mid > UISlider"); expect([selector shouldSelectItem:slider]).to.beTruthy(); } - (void)testSelectViewWithAlternativeParents { CASStyleSelector *parentSelector2 = CASStyleSelector.new; parentSelector2.objectClass = UIButton.class; parentSelector2.styleClass = @"top"; parentSelector2.parent = YES; CASStyleSelector *parentSelector1 = CASStyleSelector.new; parentSelector1.objectClass = CASExampleViewController.class; parentSelector1.shouldSelectIndirectSuperview = NO; parentSelector1.parent = YES; parentSelector1.parentSelector = parentSelector2; CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UISlider.class; selector.shouldSelectIndirectSuperview = YES; selector.parentSelector = parentSelector1; expect(selector.stringValue).to.equal(@"UIButton.top > CASExampleViewController UISlider"); // view heirarchy 1 UIButton *topButton = UIButton.new; topButton.cas_styleClass = @"top"; CASExampleViewController *viewController = CASExampleViewController.new; [topButton addSubview:viewController.view]; UIView *view = UIView.new; [viewController.view addSubview:view]; UISlider *slider = UISlider.new; [view addSubview:slider]; expect(viewController.view.cas_alternativeParent).to.beIdenticalTo(viewController); expect(CASStringViewHierarchyFromView(slider)).to.equal(@"UIButton.top > CASExampleView > UIView > UISlider"); expect([selector shouldSelectItem:slider]).to.beTruthy(); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASStylerSpec.m ================================================ // // CASRendererSpec.m // Classy // // Created by Jonas Budelmann on 16/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASStyler.h" #import "XCTest+Spec.h" #import "UIColor+CASAdditions.h" #import "CASStyleSelector.h" #import "UIView+CASAdditions.h" #import #import "CASExampleView.h" #import "UITextField+CASAdditions.h" #import "CASStyleNode.h" #import "UIDevice+CASMockDevice.h" @interface CASStyler () @property (nonatomic, strong) NSMutableArray *styleNodes; @end SpecBegin(CASStyler){ UIDevice *mockDevice; } - (void)setUp { mockDevice = mock(UIDevice.class); [UIDevice setMockDevice:mockDevice]; } - (void)tearDown { [UIDevice setMockDevice:nil]; } - (void)testGetViewDescriptor { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"UIView-Basic.cas" ofType:nil]; CASObjectClassDescriptor *descriptor = [styler objectClassDescriptorForClass:UISlider.class]; expect(descriptor.objectClass).to.equal(UISlider.class); expect(descriptor.parent.objectClass).to.equal(UIControl.class); expect(descriptor.parent.parent.objectClass).to.equal(UIView.class); expect(descriptor.parent.parent.parent).to.beNil(); descriptor = [styler objectClassDescriptorForClass:UIView.class]; expect(descriptor.objectClass).to.equal(UIView.class); expect(descriptor.parent).to.beNil(); } - (void)testSortSelectorsByPrecedence { NSError *error = nil; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Basic.cas" ofType:nil]; CASStyler *styler = CASStyler.new; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect([[styler.styleNodes[0] styleSelector] stringValue]).to.equal(@"UISlider"); expect([[styler.styleNodes[1] styleSelector] stringValue]).to.equal(@"UIButton UIControl[state:selected]"); expect([[styler.styleNodes[2] styleSelector] stringValue]).to.equal(@"UINavigationBar UIButton"); expect([[styler.styleNodes[3] styleSelector] stringValue]).to.equal(@"UIView.bordered"); expect([[styler.styleNodes[4] styleSelector] stringValue]).to.equal(@"UIControl.border"); } - (void)testSelectViewWithStyleClass { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Basic.cas" ofType:nil]; CASStyleSelector *selector = [styler.styleNodes[3] styleSelector]; expect([selector stringValue]).to.equal(@"UIView.bordered"); expect([selector shouldSelectItem:UIView.new]).to.beFalsy(); UIView *view = UIView.new; view.cas_styleClass = @"bordered"; expect([selector shouldSelectItem:view]).to.beTruthy(); } - (void)testSelectIndirectSuperview { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Properties-Basic.cas" ofType:nil]; CASStyleSelector *selector = [styler.styleNodes[2] styleSelector]; expect([selector stringValue]).to.equal(@"UINavigationBar UIButton"); expect([selector shouldSelectItem:UIButton.new]).to.beFalsy(); //direct superview UIButton *button = UIButton.new; button.cas_styleClass = @"large"; UINavigationBar *navigationBar = UINavigationBar.new; [navigationBar addSubview:button]; expect([selector shouldSelectItem:button]).to.beTruthy(); //indirect superview [button removeFromSuperview]; UIImageView *imageView = UIImageView.new; [imageView addSubview:button]; [navigationBar addSubview:imageView]; expect([selector shouldSelectItem:button]).to.beTruthy(); } - (void)testSelectSubclasses { CASStyleSelector *selector = CASStyleSelector.new; selector.objectClass = UIControl.class; selector.shouldSelectSubclasses = YES; expect([selector shouldSelectItem:UIControl.new]).to.equal(YES); expect([selector shouldSelectItem:UIView.new]).to.equal(NO); expect([selector shouldSelectItem:UIButton.new]).to.equal(YES); } - (void)testImport { CASStyler *styler = CASStyler.new; styler.filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Import-Base.cas" ofType:nil]; UIView *view = UIView.new; CASStyleNode *node = styler.styleNodes[0]; expect(node.styleSelector.stringValue).to.equal(@"UIView.two"); view.cas_styleClass = @"two"; [styler styleItem:view]; expect(view.backgroundColor).to.equal([UIColor purpleColor]); expect(view.layer.cornerRadius).to.equal(6); expect(view.layer.shadowOffset).to.equal(UIOffsetMake(5, 5)); node = styler.styleNodes[1]; expect(node.styleSelector.stringValue).to.equal(@"UIView.one"); view.cas_styleClass = @"one"; [styler styleItem:view]; expect(view.backgroundColor).to.equal([UIColor blueColor]); expect(view.layer.cornerRadius).to.equal(5); expect(view.layer.shadowOffset).to.equal(UIOffsetMake(6, 6)); expect(view.layer.shadowOpacity).to.equal(3); expect(view.layer.shadowRadius).to.equal(4); node = styler.styleNodes[2]; expect(node.styleSelector.stringValue).to.equal(@"UIView.base"); view.cas_styleClass = @"base"; [styler styleItem:view]; expect(view.backgroundColor).to.equal([UIColor redColor]); expect(view.layer.cornerRadius).to.equal(3); expect(view.layer.shadowOffset).to.equal(UIOffsetMake(4, 4)); expect(view.layer.shadowOpacity).to.equal(5); expect(view.layer.shadowRadius).to.equal(6); } - (void)testMediaQueriesOnPad { [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPad]; [given([mockDevice systemVersion]) willReturn:@"7.0.1"]; CASStyler *styler = CASStyler.new; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Media-Queries-styler.cas" ofType:nil]; NSError *error = nil; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect(styler.styleNodes).to.haveCountOf(0); } - (void)testMediaQueriesOnPhone { [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPhone]; [given([mockDevice systemVersion]) willReturn:@"7.0.1"]; CASStyler *styler = CASStyler.new; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Media-Queries-styler.cas" ofType:nil]; NSError *error = nil; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect(styler.styleNodes).to.haveCountOf(2); UILabel *view = UILabel.new; CASStyleNode *node = styler.styleNodes[0]; expect(node.styleSelector.stringValue).to.equal(@"UILabel.label2"); view.cas_styleClass = @"label2"; [styler styleItem:view]; expect(view.textColor).to.equal([UIColor cas_colorWithHex:@"#00f"]); expect(view.font).to.equal([UIFont fontWithName:@"HelveticaNeue" size:120]); } - (void)testMediaQueriesOnPhoneLessThan7 { [given([mockDevice userInterfaceIdiom]) willReturnInteger:UIUserInterfaceIdiomPhone]; [given([mockDevice systemVersion]) willReturn:@"6.1"]; CASStyler *styler = CASStyler.new; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Selectors-Media-Queries-styler.cas" ofType:nil]; NSError *error = nil; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect(styler.styleNodes).to.haveCountOf(2); UILabel *view = UILabel.new; CASStyleNode *node = styler.styleNodes[0]; expect(node.styleSelector.stringValue).to.equal(@"UILabel.label2"); view.cas_styleClass = @"label2"; [styler styleItem:view]; expect(view.textColor).to.equal([UIColor cas_colorWithHex:@"#0f0"]); expect(view.font).to.equal([UIFont fontWithName:@"HelveticaNeue" size:120]); } - (void)testPrecedence1 { CASStyler *styler = CASStyler.new; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Precedence-1.cas" ofType:nil]; NSError *error = nil; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect(styler.styleNodes).to.haveCountOf(5); UITextField *view = UITextField.new; [styler styleItem:view]; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(10, 10, 10, 10)); UIView *superview = UIView.new; [superview addSubview:view]; [styler styleItem:view]; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(5, 5, 5, 5)); view.cas_styleClass = @"twenty"; [styler styleItem:view]; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(20, 20, 20, 20)); UILabel *label = UILabel.new; [styler styleItem:label]; expect(label.textColor).to.equal([UIColor cas_colorWithHex:@"#f00"]); } - (void)testPrecedence2 { CASStyler *styler = CASStyler.new; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Precedence-2.cas" ofType:nil]; NSError *error = nil; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect(styler.styleNodes).to.haveCountOf(3); UITextField *view = UITextField.new; [styler styleItem:view]; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(10, 10, 10, 10)); UIView *superview = UIView.new; [superview addSubview:view]; [styler styleItem:view]; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(5, 5, 5, 5)); view.cas_styleClass = @"twenty"; [styler styleItem:view]; expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(20, 20, 20, 20)); } - (void)testVariablesInjection { CASStyler *styler = CASStyler.new; NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"Variables-Injection.cas" ofType:nil]; NSError *error = nil; styler.variables = @{ @"$filename" : @"'Injected-File.cas'", @"$namedColor" : @"purple", @"$hexColor" : @"#333", @"$bool" : @YES, @"$unit" : @200, @"$insets" : @"20, 15, 12, 10", }; [styler setFilePath:filePath error:&error]; expect(error).to.beNil(); expect(styler.styleNodes).to.haveCountOf(2); CASStyleNode *node = styler.styleNodes[0]; expect(node.styleSelector.stringValue).to.equal(@"UIView"); UIView *view2 = UIView.new; [styler styleItem:view2]; expect(view2.backgroundColor).to.equal([UIColor purpleColor]); node = styler.styleNodes[1]; expect(node.styleSelector.stringValue).to.equal(@"UITextField"); UITextField *view = UITextField.new; [styler styleItem:view]; expect(view.textColor).to.equal([UIColor cas_colorWithHex:@"#333"]); expect(view.cas_textEdgeInsets).to.equal(UIEdgeInsetsMake(20, 15, 12, 10)); expect(view.clearsOnBeginEditing).to.equal(YES); expect(view.minimumFontSize).to.equal(200); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/CASTokenSpec.m ================================================ // // CASTokenSpec.m // Classy // // Created by Jonas Budelmann on 24/09/13. // Copyright (c) 2013 cloudling. All rights reserved. // #import "CASToken.h" #import "XCTest+Spec.h" SpecBegin(CASToken) - (void)testReturnTokenName { expect([CASToken stringForType:CASTokenTypeUnknown]).to.equal(@"unknown"); expect([CASToken stringForType:CASTokenTypeIndent]).to.equal(@"indent"); expect([CASToken stringForType:CASTokenTypeOutdent]).to.equal(@"outdent"); expect([CASToken stringForType:CASTokenTypeEOS]).to.equal(@"EOS"); expect([CASToken stringForType:CASTokenTypeSemiColon]).to.equal(@"semicolon"); expect([CASToken stringForType:CASTokenTypeCarat]).to.equal(@"carat"); expect([CASToken stringForType:CASTokenTypeNewline]).to.equal(@"newline"); expect([CASToken stringForType:CASTokenTypeLeftSquareBrace]).to.equal(@"left square brace"); expect([CASToken stringForType:CASTokenTypeRightSquareBrace]).to.equal(@"right square brace"); expect([CASToken stringForType:CASTokenTypeLeftCurlyBrace]).to.equal(@"left curly brace"); expect([CASToken stringForType:CASTokenTypeRightCurlyBrace]).to.equal(@"right curly brace"); expect([CASToken stringForType:CASTokenTypeLeftRoundBrace]).to.equal(@"left round brace"); expect([CASToken stringForType:CASTokenTypeRightRoundBrace]).to.equal(@"right round brace"); expect([CASToken stringForType:CASTokenTypeColor]).to.equal(@"color"); expect([CASToken stringForType:CASTokenTypeString]).to.equal(@"string"); expect([CASToken stringForType:CASTokenTypeUnit]).to.equal(@"unit"); expect([CASToken stringForType:CASTokenTypeBoolean]).to.equal(@"boolean"); expect([CASToken stringForType:CASTokenTypeRef]).to.equal(@"ref"); expect([CASToken stringForType:CASTokenTypeOperator]).to.equal(@"operator"); expect([CASToken stringForType:CASTokenTypeSpace]).to.equal(@"space"); expect([CASToken stringForType:CASTokenTypeSelector]).to.equal(@"selector"); } - (void)testReturnTokenDescriptionWithValue { CASToken *token = [CASToken tokenOfType:CASTokenTypeColor]; token.value = @"#456456"; expect([token description]).to.equal(@"color #456456"); } - (void)testReturnTokenDescription { CASToken *token = [CASToken tokenOfType:CASTokenTypeColor]; expect([token description]).to.equal(@"color"); } - (void)testCodingAndDecoding { CASToken *tokenNewline = [CASToken tokenOfType:CASTokenTypeNewline]; CASToken *tokenColor = [CASToken tokenOfType:CASTokenTypeColor]; tokenColor.value = [UIColor colorWithWhite:0.5f alpha:0.5f]; CASToken *tokenString = [CASToken tokenOfType:CASTokenTypeString]; tokenString.value = @"A string"; NSArray *tokensArray = @[tokenColor, tokenNewline, tokenString]; NSData *serizlizedTokensArray = [NSKeyedArchiver archivedDataWithRootObject:tokensArray]; NSArray *tokensArrayDeserialized = [NSKeyedUnarchiver unarchiveObjectWithData:serizlizedTokensArray]; expect(tokensArray).to.equal(tokensArrayDeserialized); } SpecEnd ================================================ FILE: Tests/Specs/Unit Tests/UIView_CASAdditionsSpec.m ================================================ // // UIView_CASAdditionsSpec.m // ClassyTests // // Created by Jonas Budelmann on 18/11/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import "UIView+CASAdditions.h" #import "XCTest+Spec.h" @interface TestView : UIView @property (nonatomic, assign) NSInteger updateStylingCount; @property (nonatomic, assign) NSInteger setNeedsUpdateStylingCount; @end @implementation TestView - (void)cas_updateStyling { [super cas_updateStyling]; self.updateStylingCount++; } - (void)cas_setNeedsUpdateStyling { [super cas_setNeedsUpdateStyling]; self.setNeedsUpdateStylingCount++; } @end SpecBegin(UIView_CASAdditions) //test coalescing of styling calls - (void)testStyleUpdateCalledOnce { TestView *view = TestView.new; view.cas_styleClass = @"test"; UIWindow *window = UIWindow.new; window.hidden = NO; [window addSubview:view]; expect(view.updateStylingCount).to.equal(1); view.cas_styleClass = @"change"; expect(view.updateStylingCount).to.equal(1); expect(view.setNeedsUpdateStylingCount).to.equal(2); // Run the loop [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; expect(view.updateStylingCount).to.equal(2); expect(view.setNeedsUpdateStylingCount).to.equal(2); } SpecEnd ================================================ FILE: Tests/Specs/XCTest+Spec.h ================================================ // // Spec.h // ClassyTests // // Created by Jonas Budelmann on 18/10/13. // Copyright (c) 2013 Jonas Budelmann. All rights reserved. // #import #import // declare XCTestCase subclass interface and implementation #define SpecBegin(name) \ @interface name##Spec : XCTestCase @end \ @implementation name##Spec // close XCTestCase #define SpecEnd \ @end ================================================ FILE: Tests/Stylesheets/CustomView-Basic.cas ================================================ // You can set properties on your custom UIView subclasses. No config required CASExampleView { testCGFloat: 4.5; testBOOL true testNSUInteger 1000 testNSInteger -999 testInt 345 } ================================================ FILE: Tests/Stylesheets/Import-1.cas ================================================ @import Import-2.cas; $var3 = 3 $var4 = 4 UIView.one { background-color blue layer @{ corner-radius: $var5; shadow-offset: $var6; shadow-opacity: $var3; shadow-radius: $var4; } } ================================================ FILE: Tests/Stylesheets/Import-2.cas ================================================ $var5 = 5 $var6 = 6 UIView.two { background-color purple layer @{ corner-radius: $var6; shadow-offset: $var5; } } ================================================ FILE: Tests/Stylesheets/Import-Base.cas ================================================ @import "Import-1.cas" UIView.base { background-color red layer @{ corner-radius: $var3; shadow-offset: $var4; shadow-opacity: $var5; shadow-radius: $var6; } } ================================================ FILE: Tests/Stylesheets/Injected-File.cas ================================================ UIView { background-color $namedColor } ================================================ FILE: Tests/Stylesheets/Precedence-1.cas ================================================ UITextField.twenty { text-insets: 20 } UITextField { text-insets: 10 } UIView { UITextField { text-insets: 5 } } UILabel { text-color: blue } UILabel { text-color: red } ================================================ FILE: Tests/Stylesheets/Precedence-2.cas ================================================ ^UIView { UITextField { text-insets: 5 } } UITextField { text-insets: 10 } UITextField.twenty { text-insets: 20 } ================================================ FILE: Tests/Stylesheets/Properties-Args.cas ================================================ UIButton { background-color[state:selected] : #ffffff; font-name [ state : highlighted ] helvetica font-size[state: disabled] 14 } ================================================ FILE: Tests/Stylesheets/Properties-Basic.cas ================================================ // [state:selected] has no effect at present // may allow setting property arguments for all properties in a selector at once in future // instead of having todo on each property ie Properties-Args.cas UIButton UIControl[state:selected] { background-color:#ffffff; border-inset 1 } UIView.bordered, UIControl.border { font-color:#ffffff; border-width: 2; } // you can seperate selectors on newlines UISlider, UINavigationBar UIButton font-name helvetica size 40 50 text-color: #444 ================================================ FILE: Tests/Stylesheets/Properties-Nested.cas ================================================ //some properties have child properties. this is only used for text attributes at moment, but could be utilised for other things in the future. //to differentiate these from selectors you need to mark them with an @ symbol, `{` and `}` are optional UIButton background-color:#fff; border-width 1 > UINavigationBar amazing 23 embed @ font arial 15 paragraphStyle @{ maximumLineHeight 2 minimumLineHeight 4 } kern 5 stuff: "1 more thing" UISlider another 34 ================================================ FILE: Tests/Stylesheets/Selectors-Complex.cas ================================================ // [state:selected, coolness:alot] has no effect at present // may allow setting property arguments for all properties in a selector at once in future // instead of having todo on each property ie Properties-Args.cas UIButton[state:selected, coolness:alot].command , UIButton UIImageView.starImage { background-color:#ffffff; border-width 1 } UIView.bordered, UIView.panel { background-color:#ffffff; border-width: 2; } UISlider, UINavigationBar.videoNavBar UIButton[state:highlighted] { background-color #ffffff border-width: 3; } ================================================ FILE: Tests/Stylesheets/Selectors-Hierarchy.cas ================================================ // `>` means select only if UIButton is direct superview. // leaving this out means UIButton can be an indirect superview (ancestor) UIButton > UIImageView.starImage { background-color:#ffffff; } // `^` makes this selector match UIView and any class that inherits from UIView ^UIView>UINavigationBar background-color:#ffffff; background-color:#ffffff; UIView.bordered > UIView.panel { background-color:#ffffff; background-color:#ffffff; background-color:#ffffff; } ^UIView[state:selected] > UIImageView { background-color:#ffffff; background-color:#ffffff; background-color:#ffffff; background-color:#ffffff; } ================================================ FILE: Tests/Stylesheets/Selectors-Indentation.cas ================================================ // `{` and `}` are optional UIButton UIControl, UIButton UIImageView.starImage background-color:#ffffff; border-width 1 UIView.bordered, UIView.panel background-color:#ffffff; border-width: 2; border-thickness: 2; UISlider, UINavigationBar.videoNavBar UIButton background-color #ffffff border-width: 3; background-color #ffffff border-width: 3; ================================================ FILE: Tests/Stylesheets/Selectors-Media-Queries-styler.cas ================================================ @device iphone { UILabel.label2 { @media (version: < 7.0) { text-color: #0f0; } @media (version: >= 7.0) { text-color: #00f; } font: HelveticaNeue 120; } } ================================================ FILE: Tests/Stylesheets/Selectors-Media-Queries.cas ================================================ // nesting! use indentation or { } UIButton background-color1:#fff; @media ipad, (version:>= 6) background-color2:black; text-color:orange; @media (version:>=7.0.1) background-color3:blue; @device phone > UIControl amazing 23 UISlider @media phone @media (version: <= 6.1) { text-color: brown } hello: 61 UIView @device ipad { UINavigationBar > UIView { @media (version:5.0.1) { background-color:#eee } } border-color: #ddd } UITabBar.nice, UISegmentedControl.nice { UITextField { border-width 2 @device (version:>5) { &.nicer border-width 3 } } } UIView.size-query { always: 1; @media (screen-height: > 200) { heighty: 2; } @media (screen-width: > 100) and (version: >= 6.0) { combinedtrue: 3; } @media (version: < 6.0) and (screen-width: > 100) { combinedfalse: 4; } } ================================================ FILE: Tests/Stylesheets/Selectors-Messy.cas ================================================ // testing out messy formatting UIView { background-color:#fff } UIControl{ background-color:#ffffff border-width: 1; } UIView , UIButton,UITabBar{ border-width: 3; } UIView , UITabBar { } ================================================ FILE: Tests/Stylesheets/Selectors-Nested.cas ================================================ // nesting! use indentation or { } UIButton background-color:#fff; border-width 1 > UIControl amazing 23 UISlider another 34 UIView UINavigationBar > UIView { background-color:#eee } border-color: #ddd UITabBar.nice, UISegmentedControl.nice { UITextField { border-width 2 &.nicer border-width 3 } } UITableViewCell { normalBgColor $blue-color selectedBgColor $light-blue-color UILabel { font HelveticaNeue 14 text-alignment center } } ================================================ FILE: Tests/Stylesheets/UIAppearance-Basic.cas ================================================ // Support for all relevant UIAppearance setter methods // A few methods are not included // https://github.com/cloudkite/Classy/blob/master/Tests/UIAppearance-setters.md UIActivityIndicatorView { color: red } UIButton { content-edge-insets: 1 2 3 4 title-color yellow title-color[state:highlighted] #e3e3e3 title-color[state:disabled] #e4e4e4 title-color[state:selected] #e5e5e5 title-shadow-color[state:highlighted] #e7e7e7 title-shadow-color[state:disabled] #f4f4f4 title-shadow-color[state:normal] #e6e6e6 title-shadow-color[state:selected] #f5f5f5 //white space is flexible background-image [state:normal] test_image_1 background-image [state:highlighted] test_image_2 15 14 13 12 background-image[state:disabled] test_image_3 background-image[state:selected] test_image_4 } ^UIBarItem { title-text-attributes[state:highlighted] @{ font: HelveticaNeue 21 foreground-color: magenta background-color: red ligature: 6 kern: 0.6 strikethrough-style: single underline-style: double stroke-color: blue paragraph-style: @{ line-spacing: 20 paragraph-spacing: 15; line-break-mode:truncatingTail alignment:center; } shadow: @{ shadow-offset: 1,2 shadow-blur-radius: 10 shadow-color: purple } } } UIBarButtonItem { background-image[state:normal] test_image_1 background-image[state:highlighted] test_image_2 background-image[state:highlighted, barMetrics:landscape-phone] test_image_4 background-vertical-position-adjustment [barMetrics:default-prompt] 10 background-vertical-position-adjustment [barMetrics:landscape-phone] 20 title-position-adjustment 11 12 title-position-adjustment [barMetrics:landscape-phone] 21 22 backButton-background-image[state:highlighted] test_image_3 backButton-background-image[state:highlighted, barMetrics:landscape-phone] test_image_1 backButton-background-vertical-position-adjustment [barMetrics:default-prompt] 50 backButton-background-vertical-position-adjustment [barMetrics:landscape-phone] 60 backButton-title-position-adjustment 51 52 backButton-title-position-adjustment [barMetrics:landscape-phone] 61 62 } UINavigationBar { bar-tint-color blue shadow-image test_image_1 back-indicator-image test_image_4 backIndicatorTransitionMaskImage test_image_3 background-image[barMetrics:landscape-phone, barPosition:top] test_image_4 titleVerticalPositionAdjustment[barMetrics:default-prompt] 5 } UIPageControl { pageIndicatorTintColor: purple currentPageIndicatorTintColor: red } UIProgressView { progressImage test_image_1 trackImage test_image_2 } UIProgressView.tinted { progressTintColor: dark-gray trackTintColor: light-gray } UISearchBar { bar-tint-color brown background-image test_image_2 scopeBar-background-image test_image_3 searchField-background-position-adjustment (2, 3) * 10 searchText-position-adjustment 3 4 scopeBarButtonBackgroundImage[state:selected] test_image_1 scopeBarButtonDividerImage[leftSegmentState:disabled, rightSegmentState:selected] test_image_5 icon-position-adjustment[icon:clear] 5 6 icon-image[icon:bookmark, state:selected] test_image_2 } UISegmentedControl { background-image[state:disabled,barMetrics:landscape-phone] test_image_1 dividerImage[leftSegmentState:disabled, rightSegmentState:selected] test_image_5 contentPositionAdjustment[segmentType:left, barMetrics:landscape-phone] 1, 6 } UISlider.tinted { minimumTrackTintColor black maximumTrackTintColor purple thumbTintColor yellow } UISlider { minimum-track-image[state:normal] test_image_1 minimum-track-image[state:highlighted] test_image_2 maximum-track-image[state:normal] test_image_1 maximum-track-image[state:highlighted] test_image_2 thumb-image[state:normal] test_image_1 thumb-image[state:highlighted] test_image_2 } UIStepper { background-image[state:highlighted] test_image_1 dividerImage[leftSegmentState:disabled, rightSegmentState:selected] test_image_5 increment-image[state:disabled] test_image_2 decrement-image[state:disabled] test_image_3 } UISwitch.tinted { onTintColor : cyan thumbTintColor : green } UISwitch { onImage : test_image_1 offImage : test_image_2 } UITabBar { barTintColor : red selectedImageTintColor: orange backgroundImage: test_image_1 selectionIndicatorImage: test_image_2 shadowImage test_image_3 itemPositioning centered itemWidth 10 itemSpacing 5 barStyle black } UITabBarItem { titlePositionAdjustment 1, 2 } UITableView { separatorInset 1,2,3,4 sectionIndexColor blue sectionIndexBackgroundColor gray sectionIndexTrackingBackgroundColor orange } UITableViewCell { separatorInset 1,4,1,4 } UIToolbar { barTintColor magenta background-image[barMetrics:landscape-phone, toolbarPosition:top] test_image_4 shadow-image[barMetrics:landscape-phone, toolbarPosition:bottom] test_image_3 } UIView { background-color: #faf } ================================================ FILE: Tests/Stylesheets/UIKit-Basic.cas ================================================ // Support for common UIKit properties $border-width = 2; //look vars can be on same line $textInsets = 4 3 2 1; $defaultFontName = Avenir-Heavy; UIView { clips-to-bounds:YES; tint-color:blue; background-color:#A2A2A2; content-mode: aspectFill; layer: @{ border-width: $border-width; border-color: #A1A1A1; corner-radius: 7; shadow-color: red; shadow-offset: 1,3; shadow-opacity: 0.5; shadow-radius: 4; } } UITextField { font: $defaultFontName 12; text-color: #a0a0a0; text-alignment: right; content-vertical-alignment: bottom; background-image: test_image_5 1, 2, 3, 4; border-style:line; text-insets: $textInsets } ================================================ FILE: Tests/Stylesheets/UIKit-MultipleClasses.cas ================================================ UIView.class1 { background-color: red; } UIView.class2 { layer: @{ corner-radius: 2; } } UIView.class3 { background-color: green; } ================================================ FILE: Tests/Stylesheets/Variables-Basic.cas ================================================ $border-width = 2 // vars delimited by ; or by newline $textInsets = 4 3 2 1; $defaultFontName = Avenir-Heavy; // semi colon optional $another-var = 2000 UIView { layer: @{ border-width: $border-width shadow-radius: $another-var; } } UITextField.one { font: $defaultFontName 12; text-insets: $textInsets } UITextField.two { text-insets: 10 $another-var 20 30; } ================================================ FILE: Tests/Stylesheets/Variables-Injection.cas ================================================ @import $filename UITextField { text-color $hexColor clearsOnBeginEditing $bool text-insets $insets minimumFontSize $unit } ================================================ FILE: Tests/UIAppearance-setters.md ================================================ Extracted using technique described at https://gist.github.com/mattt/5135521 The information below was extracted from the iPhoneOS 11.1 SDK. ### UIActivityIndicatorView ```objective-c @property (nullable, readwrite, nonatomic, strong) UIColor *color NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UIBarButtonItem ```objective-c - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state style:(UIBarButtonItemStyle)style barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; - (void)setBackgroundVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setTitlePositionAdjustment:(UIOffset)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setBackButtonBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; - (void)setBackButtonTitlePositionAdjustment:(UIOffset)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; - (void)setBackButtonBackgroundVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; ``` ### UIBarItem ```objective-c - (void)setTitleTextAttributes:(nullable NSDictionary *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UIButton ```objective-c @property(nonatomic) UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero. On tvOS 10 or later, default is nonzero except for custom buttons. - (void)setTitleColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default if nil. use opaque white - (void)setTitleShadowColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil. use 50% black - (void)setBackgroundImage:(nullable UIImage *)image forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil ``` ### UINavigationBar ```objective-c @property(nonatomic,assign) UIBarStyle barStyle UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; @property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent @property (nonatomic, readwrite, assign) BOOL prefersLargeTitles UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos); @property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // default is nil @property(nullable,nonatomic,strong) UIImage *backIndicatorImage NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; @property(nullable,nonatomic,strong) UIImage *backIndicatorTransitionMaskImage NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; @property(nullable, nonatomic,strong) UIImage *shadowImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; @property(nullable,nonatomic,copy) NSDictionary *titleTextAttributes NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, copy) NSDictionary *largeTitleTextAttributes UI_APPEARANCE_SELECTOR API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos); - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setTitleVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UIPageControl ```objective-c @property(nullable, nonatomic,strong) UIColor *pageIndicatorTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; ``` ### UIProgressView ```objective-c @property(nonatomic, strong, nullable) UIColor* progressTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nonatomic, strong, nullable) UIColor* trackTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nonatomic, strong, nullable) UIImage* progressImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nonatomic, strong, nullable) UIImage* trackImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UIRefreshControl ```objective-c @property (nullable, nonatomic, strong) NSAttributedString *attributedTitle UI_APPEARANCE_SELECTOR; ``` ### UISearchBar ```objective-c @property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // default is nil @property(nullable, nonatomic,strong) UIImage *backgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic,strong) UIImage *scopeBarBackgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nonatomic) UIOffset searchFieldBackgroundPositionAdjustment NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nonatomic) UIOffset searchTextPositionAdjustment NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // Use UIBarMetricsDefaultPrompt to set a separate backgroundImage for a search bar with a prompt - (void)setSearchFieldBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setImage:(nullable UIImage *)iconImage forSearchBarIcon:(UISearchBarIcon)icon state:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setScopeBarButtonBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setScopeBarButtonDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setScopeBarButtonTitleTextAttributes:(nullable NSDictionary *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setPositionAdjustment:(UIOffset)adjustment forSearchBarIcon:(UISearchBarIcon)icon NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UISegmentedControl ```objective-c - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setTitleTextAttributes:(nullable NSDictionary *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setContentPositionAdjustment:(UIOffset)adjustment forSegmentType:(UISegmentedControlSegment)leftCenterRightOrAlone barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UISlider ```objective-c @property(nullable, nonatomic,strong) UIColor *minimumTrackTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic,strong) UIColor *maximumTrackTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic,strong) UIColor *thumbTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ``` ### UIStepper ```objective-c - (void)setBackgroundImage:(nullable UIImage*)image forState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; - (void)setDividerImage:(nullable UIImage*)image forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; - (void)setIncrementImage:(nullable UIImage *)image forState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; - (void)setDecrementImage:(nullable UIImage *)image forState:(UIControlState)state NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; ``` ### UISwitch ```objective-c @property(nullable, nonatomic, strong) UIColor *onTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, strong) UIColor *thumbTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, strong) UIImage *onImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, strong) UIImage *offImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; ``` ### UITabBar ```objective-c @property(nullable, nonatomic, strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // default is nil @property (nonatomic, readwrite, copy, nullable) UIColor *unselectedItemTintColor NS_AVAILABLE_IOS(10_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, strong) UIColor *selectedImageTintColor NS_DEPRECATED_IOS(5_0,8_0,"Use tintColor") UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; @property(nullable, nonatomic, strong) UIImage *backgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, strong) UIImage *selectionIndicatorImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property(nullable, nonatomic, strong) UIImage *shadowImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; @property(nonatomic) UITabBarItemPositioning itemPositioning NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; @property(nonatomic) CGFloat itemWidth NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; @property(nonatomic) CGFloat itemSpacing NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; @property(nonatomic) UIBarStyle barStyle NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; ``` ### UITabBarItem ```objective-c @property (nonatomic, readwrite, assign) UIOffset titlePositionAdjustment NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; @property (nonatomic, readwrite, copy, nullable) UIColor *badgeColor NS_AVAILABLE_IOS(10_0) UI_APPEARANCE_SELECTOR; - (void)setBadgeTextAttributes:(nullable NSDictionary *)textAttributes forState:(UIControlState)state NS_AVAILABLE_IOS(10_0) UI_APPEARANCE_SELECTOR; ``` ### UITableView ```objective-c @property (nonatomic) UIEdgeInsets separatorInset NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // allows customization of the frame of cell separators; see also the separatorInsetReference property. Use UITableViewAutomaticDimension for the automatic inset for that edge. @property (nonatomic, strong, nullable) UIColor *sectionIndexColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; // color used for text of the section index @property (nonatomic, strong, nullable) UIColor *sectionIndexBackgroundColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // the background color of the section index while not being touched @property (nonatomic, strong, nullable) UIColor *sectionIndexTrackingBackgroundColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; // the background color of the section index while it is being touched @property (nonatomic, strong, nullable) UIColor *separatorColor UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // default is the standard separator gray @property (nonatomic, copy, nullable) UIVisualEffect *separatorEffect NS_AVAILABLE_IOS(8_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // effect to apply to table separators ``` ### UITableViewCell ```objective-c @property (nonatomic) UIEdgeInsets separatorInset NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // allows customization of the separator frame @property (nonatomic) UITableViewCellFocusStyle focusStyle NS_AVAILABLE_IOS(9_0) UI_APPEARANCE_SELECTOR; ``` ### UIToolbar ```objective-c @property(nonatomic) UIBarStyle barStyle UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; // default is UIBarStyleDefault @property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent @property(nullable, nonatomic, strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // default is nil - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forToolbarPosition:(UIBarPosition)topOrBottom barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (void)setShadowImage:(nullable UIImage *)shadowImage forToolbarPosition:(UIBarPosition)topOrBottom NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; ``` ### UIView ```objective-c @property(nullable, nonatomic,copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is nil. Can be useful with the appearance proxy on custom UIView subclasses. ``` ================================================ FILE: script/coveralls.sh ================================================ #!/bin/bash source script/env.sh declare -r gcov_dir="${OBJECT_FILE_DIR_normal}/${CURRENT_ARCH}/" ## ====== generateGcov() { # doesn't set output dir to gcov... cd "${gcov_dir}" for file in ${gcov_dir}/*.gcda do gcov-4.2 "${file}" -o "${gcov_dir}" done cd - } copyGcovToProjectDir() { cp -r "${gcov_dir}" gcov } removeGcov() { rm -r gcov } main() { # generate + copy generateGcov copyGcovToProjectDir # post coveralls ${@+"$@"} # clean up removeGcov } main ${@+"$@"}