forked from veritech/CSSApply
-
Notifications
You must be signed in to change notification settings - Fork 4
/
CSSSelectorTree.m
120 lines (106 loc) · 3.55 KB
/
CSSSelectorTree.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//
// CSSSelectorTree.m
// CSSSample
//
// Created by Sam Stewart on 7/16/11.
// Copyright 2011 Float:Right Ltd. All rights reserved.
//
/**
@class CSSSelectorTree
This custom data structure represents our css tree we inflate after parsing. In fact, it's not really a tree but more of a list of matching "paths".
An example will hopefully clarify this:
*/
#import "CSSSelectorTree.h"
#import "CSSSelector.h"
@implementation CSSSelectorTree
@dynamic score;
@synthesize nodes, rules, selector;
- (id)initWithSelector:(CSSSelector *)selector_arg {
self = [super init];
if (self) {
selector = [selector_arg retain];
}
return self;
}
/** Convenience method for breaking up multi level selector
into multiple smaller selectors wrapped in subtrees. Builds the subtrees in reverse order of specificity.*/
+ (NSArray*)subtreesFromSelector:(NSString *)selector {
NSArray *comps = [selector componentsSeparatedByString:@" "];
NSMutableArray *subtrees = [NSMutableArray arrayWithCapacity:20];
for (NSString *comp in comps) {
CSSSelector *selector = [[CSSSelector alloc] initWithSelectorStr:comp];
CSSSelectorTree *subtree = [[CSSSelectorTree alloc] initWithSelector:selector];
// make sure it's in reverse order.
[subtrees insertObject:subtree atIndex:0];
}
return subtrees;
}
+ (CSSSelectorTree*)chainSubtrees:(NSArray *)subtrees {
if (![subtrees count]) return nil;
//start with most specific and begin chaining
CSSSelectorTree *cur_parent = [subtrees objectAtIndex:0];
CSSSelectorTree *top_parent = cur_parent;
for (CSSSelectorTree *subtree in subtrees) {
if (subtree != cur_parent) {
[cur_parent.nodes addObject:subtree];
cur_parent = subtree; //move into subtree
}
}
return top_parent;
}
#pragma mark Accessors
- (void)sortNodes {
[nodes sortUsingComparator:(NSComparator)^(CSSSelector *a_sel, CSSSelector *b_sel) {
NSInteger a_score = [a_sel score];
NSInteger b_score = [b_sel score];
if (a_score < b_score)
return NSOrderedAscending;
else if(a_score > b_score)
return NSOrderedDescending;
else
return NSOrderedSame;
}];
}
/** Searches subnodes for partial matches for the selector you pass in.
Remember, only a few properties have to match for the entire selector to match.
@return Array of CSSSelectorTree nodes.
*/
- (NSArray*)find:(CSSSelector *)selector_arg {
NSMutableArray *results = [NSMutableArray arrayWithCapacity:20];
for (CSSSelectorTree *node in nodes) {
if ([node.selector doesMatchIntoSelector:selector_arg]) {
[results addObject:node];
}
}
return results;
}
- (BOOL)isLeaf {
return (nodes.count ? NO : YES);
}
- (NSString*)name {
// grab our selector entire selector string..
return selector.selector;
}
/** Takes the biggest child score to ensure we reflect the child nodes accurately.
Note: this may not be quite right. How do we know we "own" the biggest node?*/
- (NSInteger)score {
NSInteger max_score = 0;
for (CSSSelectorTree *node in nodes) {
NSInteger score = [node score];
max_score = (score > max_score ? score : max_score);
}
return max_score;
}
- (NSMutableArray*)nodes {
if (!nodes) {
nodes = [[NSMutableArray alloc] initWithCapacity:20];
}
return nodes;
}
- (void)dealloc {
[rules release], rules = nil;
[nodes release], nodes = nil;
[selector release], selector = nil;
[super dealloc];
}
@end