diff --git a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate
index b4fb2a8..bc76c74 100644
Binary files a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate and b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/IJSVGExample/IJSVGExample/test.svg b/IJSVGExample/IJSVGExample/test.svg
index 1951576..da2a900 100644
--- a/IJSVGExample/IJSVGExample/test.svg
+++ b/IJSVGExample/IJSVGExample/test.svg
@@ -16,14 +16,30 @@
+
+
-
+
+
+
+
diff --git a/source/IJSVGStyleSheet.m b/source/IJSVGStyleSheet.m
index 8a43e47..7ad0348 100644
--- a/source/IJSVGStyleSheet.m
+++ b/source/IJSVGStyleSheet.m
@@ -10,6 +10,31 @@
#import "IJSVGStyle.h"
#import "IJSVGNode.h"
+@interface IJSVGStyleSheetSelectorWrapper : NSObject {
+
+ IJSVGStyleSheetSelector * selector;
+ IJSVGStyleSheetRule * rule;
+
+}
+
+@property (nonatomic, retain) IJSVGStyleSheetRule * rule;
+@property (nonatomic, retain) IJSVGStyleSheetSelector * selector;
+
+@end
+
+@implementation IJSVGStyleSheetSelectorWrapper
+
+@synthesize rule, selector;
+
+- (void)dealloc
+{
+ [rule release], rule = nil;
+ [selector release], selector = nil;
+ [super dealloc];
+}
+
+@end
+
@implementation IJSVGStyleSheet
- (void)dealloc
@@ -135,11 +160,32 @@
- (IJSVGStyle *)styleForNode:(IJSVGNode *)node
{
IJSVGStyle * style = [[[IJSVGStyle alloc] init] autorelease];
+ NSMutableArray * matchedRules = [[[NSMutableArray alloc] init] autorelease];
for(IJSVGStyleSheetRule * rule in _rules)
{
- if([rule matchesNode:node])
- style = [style mergedStyle:rule.style];
+ IJSVGStyleSheetSelector * matchedSelector = nil;
+ if([rule matchesNode:node selector:&matchedSelector]) {
+
+ // make a wrapper for the selector with the rule
+ IJSVGStyleSheetSelectorWrapper * wrapper = [[[IJSVGStyleSheetSelectorWrapper alloc] init] autorelease];
+ wrapper.rule = rule;
+ wrapper.selector = matchedSelector;
+
+ // add it to the array of matches
+ [matchedRules addObject:wrapper];
+ }
}
+
+ // now we have all the wrappers, we need to sort them
+ // by specificity
+ NSSortDescriptor * sort = [NSSortDescriptor sortDescriptorWithKey:@"selector.specificity" ascending:YES];
+ [matchedRules sortUsingDescriptors:@[sort]];
+
+ // combine the rule
+ for(IJSVGStyleSheetSelectorWrapper * wrapper in matchedRules) {
+ style = [style mergedStyle:wrapper.rule.style];
+ }
+
return style;
}
diff --git a/source/IJSVGStyleSheetRule.h b/source/IJSVGStyleSheetRule.h
index c34f1d5..85de67a 100644
--- a/source/IJSVGStyleSheetRule.h
+++ b/source/IJSVGStyleSheetRule.h
@@ -22,6 +22,7 @@
@property (nonatomic, retain) NSArray * selectors;
@property (nonatomic, retain) IJSVGStyle * style;
-- (BOOL)matchesNode:(IJSVGNode *)node;
+- (BOOL)matchesNode:(IJSVGNode *)node
+ selector:(IJSVGStyleSheetSelector **)matchedSelector;
@end
diff --git a/source/IJSVGStyleSheetRule.m b/source/IJSVGStyleSheetRule.m
index 5e50979..528bb70 100644
--- a/source/IJSVGStyleSheetRule.m
+++ b/source/IJSVGStyleSheetRule.m
@@ -20,12 +20,15 @@
}
- (BOOL)matchesNode:(IJSVGNode *)node
+ selector:(IJSVGStyleSheetSelector **)matchedSelector
{
// interate over each select and work out if
// it allows us to be applied
for(IJSVGStyleSheetSelector * selector in selectors) {
- if([selector matchesNode:node])
+ if([selector matchesNode:node]) {
+ *matchedSelector = selector;
return YES;
+ }
}
return NO;
}
diff --git a/source/IJSVGStyleSheetSelector.h b/source/IJSVGStyleSheetSelector.h
index 92b4c5d..96bd602 100644
--- a/source/IJSVGStyleSheetSelector.h
+++ b/source/IJSVGStyleSheetSelector.h
@@ -14,11 +14,14 @@
@interface IJSVGStyleSheetSelector : NSObject {
NSString * selector;
+ NSUInteger specificity;
@private
NSMutableArray * _rawSelectors;
}
+@property (nonatomic, assign) NSUInteger specificity;
+
- (id)initWithSelectorString:(NSString *)string;
- (BOOL)matchesNode:(IJSVGNode *)node;
diff --git a/source/IJSVGStyleSheetSelector.m b/source/IJSVGStyleSheetSelector.m
index f716e56..4cee59d 100644
--- a/source/IJSVGStyleSheetSelector.m
+++ b/source/IJSVGStyleSheetSelector.m
@@ -12,22 +12,22 @@
@implementation IJSVGStyleSheetSelector
+#define SPECIFICITY_TAG 1
+#define SPECIFICITY_CLASS 10
+#define SPECIFICITY_IDENTIFIER 100
+
+@synthesize specificity;
+
- (void)dealloc
{
[selector release], selector = nil;
[super dealloc];
}
-- (BOOL)combinatorIsGreedy:(IJSVGStyleSheetSelectorCombinator)combinator
+- (BOOL)isSiblingCombinator:(IJSVGStyleSheetSelectorCombinator)combinator
{
- return combinator == IJSVGStyleSheetSelectorCombinatorDescendant ||
- combinator == IJSVGStyleSheetSelectorCombinatorPrecededSibling;
-}
-
-- (BOOL)isAncestorOperator:(IJSVGStyleSheetSelectorCombinator)combinator
-{
- return combinator == IJSVGStyleSheetSelectorCombinatorDescendant ||
- combinator == IJSVGStyleSheetSelectorCombinatorDirectDescendant;
+ return combinator == IJSVGStyleSheetSelectorCombinatorNextSibling ||
+ combinator == IJSVGStyleSheetSelectorCombinatorPrecededSibling;
}
- (IJSVGStyleSheetSelectorCombinator)combinatorForUnichar:(unichar)aChar
@@ -48,10 +48,31 @@
selector = [string copy];
_rawSelectors = [[NSMutableArray alloc] init];
[self _compile];
+ [self _calculate];
}
return self;
}
+- (void)_calculate
+{
+ // calculate the specificity
+ // of the selector
+ for(IJSVGStyleSheetSelectorRaw * rawSelector in _rawSelectors) {
+
+ // 1 for a tag
+ if(rawSelector.tag != nil) {
+ self.specificity += SPECIFICITY_TAG;
+ }
+
+ // 100 for a id
+ if(rawSelector.identifier != nil)
+ self.specificity += SPECIFICITY_IDENTIFIER;
+
+ // 10 for a class
+ self.specificity += (rawSelector.classes.count*SPECIFICITY_CLASS);
+ }
+}
+
- (void)_compile
{
NSUInteger length = selector.length;
@@ -153,7 +174,8 @@
// add raw selector
if( i == length - 1 ) {
- [sels addObject:rawSelector];
+ if(rawSelector != nil)
+ [sels addObject:rawSelector];
rawSelector = nil;
}
@@ -226,62 +248,136 @@
- (BOOL)_matches:(IJSVGNode *)aNode
selector:(IJSVGStyleSheetSelectorRaw *)rawSelector
{
- IJSVGNode * prevNode = aNode;
IJSVGStyleSheetSelectorRaw * aSelector = rawSelector;
+ // loop until aSelector is nil
while(aSelector != nil) {
- IJSVGNode * workingNode = nil;
-
- // for adjacent node, the content to test against
- // is the previous sibling
- if(rawSelector.combinator == IJSVGStyleSheetSelectorCombinatorNextSibling ||
- rawSelector.combinator == IJSVGStyleSheetSelectorCombinatorPrecededSibling) {
- workingNode = [self _previousNode:prevNode];
+ // sibling, so + or ~
+ if([self isSiblingCombinator:aSelector.combinator]) {
+
+ // the +
+ if(aSelector.combinator == IJSVGStyleSheetSelectorCombinatorNextSibling) {
+
+ // straight forward again, find the previous sibling
+ // and match it against the next selector in the list
+ IJSVGNode * previousNode = [self _previousNode:aNode];
+ IJSVGStyleSheetSelectorRaw * s = [self _nextSelector:aSelector];
+
+ if(previousNode != nil && [self _match:previousNode selector:s]) {
+ // set the new starting selector and node
+ aSelector = s;
+ aNode = previousNode;
+ }
+
+ // didnt match previous element
+ else {
+ return NO;
+ }
+
+ }
+
+ // the ~
+ if(aSelector.combinator == IJSVGStyleSheetSelectorCombinatorPrecededSibling) {
+
+ IJSVGGroup * parentNode = (IJSVGGroup *)aNode.parentNode;
+
+ // no parent, just return no
+ if(parentNode == nil) {
+ return NO;
+ }
+
+ // grab the children
+ NSArray * nodes = parentNode.children;
+ NSInteger index = [nodes indexOfObject:aNode];
+
+ // doesnt contain the child
+ if(index == NSNotFound) {
+ return NO;
+ }
+
+ // find the next selector
+ IJSVGStyleSheetSelectorRaw * s = [self _nextSelector:aSelector];
+ BOOL found = NO;
+ for( NSUInteger i = index; index > 0; i-- ) {
+
+ // grab the child node
+ IJSVGNode * childNode = nodes[i];
+
+ // matches, huzzah!
+ if([self _match:childNode selector:s]) {
+ // set the new starting selector and node
+ found = YES;
+ aSelector = s;
+ aNode = childNode;
+ break;
+ }
+ }
+
+ // nothing found
+ if(found == NO) {
+ return NO;
+ }
+
+ }
+
}
- // for descedant combinators and child, the element
- // to test against is the parent node
+ // not a + or a ~
else {
- // find the parent
- IJSVGGroup * possibleParent = (IJSVGGroup *)prevNode.parentNode;
- if(possibleParent != nil) {
- workingNode = possibleParent;
- }
- }
-
- // no node - just kill
- if(workingNode == nil)
- return NO;
-
- // test the working node against the current selector
- if([self _match:workingNode
- selector:aSelector]) {
-
- // check for greedy
- IJSVGStyleSheetSelectorRaw * nSelector = [self _nextSelector:rawSelector];
-
- if([self combinatorIsGreedy:aSelector.combinator] &&
- nSelector.combinator != rawSelector.combinator &&
- (rawSelector.combinator == IJSVGStyleSheetSelectorCombinatorPrecededSibling
- && [self isAncestorOperator:nSelector.combinator]) == NO) {
- // just do a quick dirty check
- if([self _matches:workingNode selector:rawSelector]) {
- return YES;
+ // can up to the matching parent, if not found, nothing!
+ if(aSelector.combinator == IJSVGStyleSheetSelectorCombinatorDescendant) {
+ // go up the chain until we match a parent that
+ // matches the next selector
+ IJSVGStyleSheetSelectorRaw * s = [self _nextSelector:aSelector];;
+ IJSVGNode * p = aNode;
+ while(p != nil) {
+
+ // set p to current parentNode
+ p = p.parentNode;
+
+ // p must exist and match the selector
+ if(p != nil && [self _match:p selector:s]) {
+ // set the new starting selector and node
+ aSelector = s;
+ aNode = p;
+ break;
+ }
+
+ // no parent match found
+ else if(p == nil) {
+ return NO;
+ }
}
}
- // get the next selector
- aSelector = [self _nextSelector:aSelector];
- } else {
-
- // is its greedy, if we didnt match, just return NO
- if([self combinatorIsGreedy:aSelector.combinator] == NO) {
- return NO;
+ // > direct descedant
+ else if(aSelector.combinator == IJSVGStyleSheetSelectorCombinatorDirectDescendant) {
+
+ // grab parent
+ IJSVGGroup * parentNode = (IJSVGGroup *)aNode.parentNode;
+
+ // no parent, just return
+ if(parentNode == nil)
+ return NO;
+
+ // really straight forward, just check if the parent
+ // matches the next selector and... contains the node in question
+ IJSVGStyleSheetSelectorRaw * s = [self _nextSelector:aSelector];
+ if([self _match:parentNode selector:s] &&
+ [parentNode.children containsObject:aNode]) {
+ // set the new starting selector and node
+ aSelector = s;
+ aNode = parentNode;
+ }
+
+ // no match found
+ else {
+ return NO;
+ }
}
}
- prevNode = workingNode;
}
return YES;
}
@@ -289,10 +385,16 @@
- (BOOL)matchesNode:(IJSVGNode *)node
{
- IJSVGStyleSheetSelectorRaw * raw = _rawSelectors[0];
- if([self _match:node selector:raw] &&
- ([self _matches:node selector:raw] || _rawSelectors.count == 1 ))
+ IJSVGStyleSheetSelectorRaw * sel = _rawSelectors[0];
+ // return YES only if the first selector matches the node
+ // and the next selector is nil, or the next selector isnt nil
+ // and the node then goes up the tree and works itself out with the
+ // selectors in question
+ if([self _match:node selector:sel] &&
+ ([self _nextSelector:sel] == nil ||
+ [self _matches:node selector:sel])) {
return YES;
+ }
return NO;
}