Files
HTMLKit/HTMLKit/CSSNthExpressionSelector.m
T

132 lines
3.3 KiB
Objective-C

//
// CSSNthExpressionSelector.m
// HTMLKit
//
// Created by Iska on 10/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSNthExpressionSelector.h"
#import "HTMLElement.h"
#import "HTMLNode+Private.h"
#pragma mark - Nth-Expression
const CSSNthExpression CSSNthExpressionOdd = (CSSNthExpression) {
.an = 2, .b = 1
};
const CSSNthExpression CSSNthExpressionEven = (CSSNthExpression) {
.an = 2, .b = 0
};
NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression)
{
if (expression.an == 0 && expression.b == 0) {
return @"invalid";
}
if (expression.an == 0) {
return [NSString stringWithFormat:@"%ld", (long)expression.b];
}
if (expression.b == 0) {
return [NSString stringWithFormat:@"%ldn", (long)expression.an];
}
return [NSString stringWithFormat:@"%ldn%+ld", (long)expression.an, (long)expression.b];
}
#pragma mark - Implementation
NSInteger computeIndex(NSEnumerator *enumerator, HTMLElement *element)
{
NSInteger index = 0;
for (HTMLNode *node in enumerator) {
if (node.nodeType != HTMLNodeElement) {
continue;
}
if ([node.asElement.tagName isEqualToString:element.tagName]) {
index++;
}
if (node == element) {
break;
}
}
return index;
}
@interface CSSNthExpressionSelector ()
{
NSString *_className;
CSSNthExpression _expression;
NSInteger (^ _computeIndex)(HTMLElement *);
}
@end
@implementation CSSNthExpressionSelector
@synthesize expression = _expression;
@synthesize className = _className;
+ (instancetype)nthChildSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-child" expression:expression block:^NSInteger(HTMLElement *element) {
return [element.parentElement indexOfChildElement:element] + 1;
}];
}
+ (instancetype)nthLastChildSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-last-child" expression:expression block:^NSInteger(HTMLElement *element) {
return element.parentElement.childElementsCount - [element.parentElement indexOfChildElement:element];
}];
}
+ (instancetype)nthOfTypeSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-of-type" expression:expression block:^NSInteger(HTMLElement *element) {
return computeIndex(element.parentElement.childNodes.array.objectEnumerator, element);
}];
}
+ (instancetype)nthLastOfTypeSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-last-of-type" expression:expression block:^NSInteger(HTMLElement *element) {
return computeIndex(element.parentElement.childNodes.array.reverseObjectEnumerator, element);
}];
}
- (instancetype)initWithClassName:(NSString *)className
expression:(CSSNthExpression)expression
block:(NSInteger (^)(HTMLElement *element))block
{
self = [super init];
if (self) {
_className = [className copy];
_expression = expression;
_computeIndex = [block copy];
}
return self;
}
- (BOOL)acceptElement:(HTMLElement *)element
{
NSInteger index = _computeIndex(element);
if (_expression.an == 0) {
return index == _expression.b;
} else {
NSInteger diff = (index - _expression.b);
return (diff * _expression.an >= 0) && (diff % _expression.an == 0);
}
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@":%@(%@)", self.className, NSStringFromNthExpression(self.expression)];
}
@end