iska committed 2015-10-21 22:15:36 +02:00
1 parent bd84884bf7
commit 61d7c683fb
3 files changed
+319

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
//
// CSSStructuralPseudoSelector.h
// HTMLKit
//
// Created by Iska on 11/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
@class CSSSelector;
NS_ASSUME_NONNULL_BEGIN
extern CSSSelector * rootSelector();
extern CSSSelector * emptySelector();
extern CSSSelector * parentSelector();
extern CSSSelector * buttonSelector();
extern CSSSelector * checkboxSelector();
extern CSSSelector * fileSelector();
extern CSSSelector * headerSelector();
extern CSSSelector * imageSelector();
extern CSSSelector * inputSelector();
extern CSSSelector * linkSelector();
extern CSSSelector * passwordSelector();
extern CSSSelector * radioSelector();
extern CSSSelector * resetSelector();
extern CSSSelector * submitSelector();
extern CSSSelector * textSelector();
extern CSSSelector * enabledSelector();
extern CSSSelector * disabledSelector();
extern CSSSelector * checkedSelector();
extern CSSSelector * ltSelector();
extern CSSSelector * gtSelector();
extern CSSSelector * eqSelector();
NS_ASSUME_NONNULL_END
+271
View File
@@ -0,0 +1,271 @@
//
// CSSStructuralPseudoSelector.m
// HTMLKit
//
// Created by Iska on 11/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSStructuralPseudoSelectors.h"
#import "CSSSelectors.h"
#import "HTMLElement.h"
#import "NSString+HTMLKit.h"
#pragma mark - Elements
CSSSelector * rootSelector()
{
return namedBlockSelector(@"root", ^BOOL(HTMLElement * element) {
return !!element.parentElement;
});
}
CSSSelector * emptySelector()
{
return namedBlockSelector(@"empty", ^BOOL(HTMLElement * element) {
for (HTMLNode *child in element.childNodes) {
if (child.nodeType == HTMLNodeElement) {
return NO;
} else if (child.nodeType == HTMLNodeText && child.textContent.length > 0) {
return NO;
}
}
return YES;
});
}
CSSSelector * parentSelector()
{
return namedBlockSelector(@"parent", ^BOOL(HTMLElement * element) {
return element.childNodesCount > 0;
});
}
CSSSelector * buttonSelector()
{
return namedBlockSelector(@"button", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToString:@"button"]) {
return YES;
}
if ([element.tagName isEqualToString:@"input"] && [element[@"type"] isEqualToString:@"button"]) {
return YES;
}
return NO;
});
}
CSSSelector * checkboxSelector()
{
return namedBlockSelector(@"checkbox", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"checkbox"]) {
return YES;
}
return NO;
});
}
CSSSelector * fileSelector()
{
return namedBlockSelector(@"file", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"file"]) {
return YES;
}
return NO;
});
}
CSSSelector * headerSelector()
{
return namedBlockSelector(@"header", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
return YES;
}
return NO;
});
}
CSSSelector * imageSelector()
{
return namedBlockSelector(@"image", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"image"]) {
return YES;
}
return NO;
});
}
CSSSelector * inputSelector()
{
return namedBlockSelector(@"input", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToAny:@"button", @"input", @"select", @"textarea", nil]) {
return YES;
}
return NO;
});
}
CSSSelector * linkSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-link
return namedBlockSelector(@"root", ^BOOL(HTMLElement * element) {
if ([element hasAttribute:@"href"]) {
return [element.tagName isEqualToAny:@"a", @"area", @"link", nil];
}
return NO;
});
}
CSSSelector * passwordSelector()
{
return namedBlockSelector(@"password", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"password"]) {
return YES;
}
return NO;
});
}
CSSSelector * radioSelector()
{
return namedBlockSelector(@"radio", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"radio"]) {
return YES;
}
return NO;
});
}
CSSSelector * resetSelector()
{
return namedBlockSelector(@"reset", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"reset"]) {
return YES;
}
return NO;
});
}
CSSSelector * submitSelector()
{
return namedBlockSelector(@"submit", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToString:@"input"] && [element[@"type"] isEqualToString:@"submit"]) {
return YES;
}
if ([element.tagName isEqualToString:@"button"] && [element[@"type"] isEqualToString:@"submit"]) {
return YES;
}
return NO;
});
}
CSSSelector * textSelector()
{
return namedBlockSelector(@"text", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"text"]) {
return YES;
}
return NO;
});
}
#pragma mark - State
CSSSelector * enabledSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
CSSSelector *candiate = anyOf(@[
typeSelector(@"button"),
typeSelector(@"input"),
typeSelector(@"select"),
typeSelector(@"textarea"),
typeSelector(@"optgroup"),
typeSelector(@"option"),
typeSelector(@"menuitem"),
typeSelector(@"fieldset"),
]);
return namedPseudoSelector(@"enabled", allOf(@[candiate, nay(disabledSelector())]));
}
CSSSelector * disabledSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
CSSSelector *disabledAttribute = hasAttributeSelector(@"disabled");
// https://html.spec.whatwg.org/multipage/forms.html#concept-fieldset-disabled
CSSSelector *disabledFieldset = allOf(@[typeSelector(@"fieldset"), disabledAttribute]);
CSSSelector *firstLegend = allOf(@[typeSelector(@"legend"), firstOfTypeSelector()]);
CSSSelector *firstLegendDecendantDisabledFieldSet = allOf(@[firstLegend, descendantOfElementSelector(disabledFieldset)]);
// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
CSSSelector *disabledForm = anyOf(@[
allOf(@[
allOf(@[typeSelector(@"button"), disabledAttribute]),
allOf(@[typeSelector(@"input"), disabledAttribute]),
allOf(@[typeSelector(@"select"), disabledAttribute]),
allOf(@[typeSelector(@"textarea"), disabledAttribute])
]),
allOf(@[
descendantOfElementSelector(disabledFieldset),
nay(firstLegendDecendantDisabledFieldSet)
])
]);
// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
CSSSelector *disabledMenuItem = allOf(@[typeSelector(@"menuitem"), disabledAttribute]);
CSSSelector *disabledOptgroup = allOf(@[typeSelector(@"optgroup"), disabledAttribute]);
// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
CSSSelector *disabledOption = allOf(@[
typeSelector(@"option"),
anyOf(@[
disabledAttribute,
descendantOfElementSelector(disabledOptgroup)])
]);
return namedPseudoSelector(@"disabled",
anyOf(@[disabledOption, disabledOptgroup, disabledMenuItem, disabledForm, disabledFieldset]));
}
CSSSelector * checkedElementSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-checked
CSSSelector *candidate = anyOf(@[
typeSelector(@"input"),
typeSelector(@"option"),
typeSelector(@"menutitem")
]);
CSSSelector *hasAttribute = anyOf(@[
hasAttributeSelector(@"checked"),
hasAttributeSelector(@"selected")
]);
return namedPseudoSelector(@"checked", allOf(@[candidate, hasAttribute]));
}
#pragma mark - Positional
CSSSelector * ltSelector(NSUInteger index)
{
return namedBlockSelector(@"lt", ^BOOL(HTMLElement * _Nonnull element) {
return [element.parentElement indexOfChildNode:element] < index;
});
}
CSSSelector * gtSelector(NSUInteger index)
{
return namedBlockSelector(@"gt", ^BOOL(HTMLElement * _Nonnull element) {
return [element.parentElement indexOfChildNode:element] > index;
});
}
CSSSelector * eqSelector(NSInteger index)
{
return namedBlockSelector(@"eq", ^BOOL(HTMLElement * _Nonnull element) {
NSUInteger elementIndex = [element.parentElement indexOfChildNode:element];
if (index > 0) {
return elementIndex == index;
} else {
return elementIndex == element.parentElement.childNodesCount - index - 1;
}
});
}