From d924063b020490b373557b591f5cd83107785feb Mon Sep 17 00:00:00 2001 From: iska Date: Mon, 30 Nov 2015 20:22:11 +0100 Subject: [PATCH] Add implementation for a DOM Token List https://dom.spec.whatwg.org/#interface-domtokenlist --- HTMLKit.xcodeproj/project.pbxproj | 18 ++++ HTMLKit/HTMLDOM.h | 1 + HTMLKit/HTMLDOMTokenList.h | 36 ++++++++ HTMLKit/HTMLDOMTokenList.m | 109 ++++++++++++++++++++++++ HTMLKit/HTMLKitDOMExceptions.h | 3 + HTMLKit/HTMLKitDOMExceptions.m | 2 + HTMLKitTests/HTMLKitDOMTokenListTests.m | 52 +++++++++++ 7 files changed, 221 insertions(+) create mode 100644 HTMLKit/HTMLDOMTokenList.h create mode 100644 HTMLKit/HTMLDOMTokenList.m create mode 100644 HTMLKitTests/HTMLKitDOMTokenListTests.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 3f3859e..b60e492 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -25,6 +25,12 @@ 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; }; + 62567F501C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 62567F511C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 62567F521C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */; }; + 62567F531C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */; }; + 62567F561C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */; }; + 62567F571C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */; }; 625A14B019C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; }; 625A14BA19C7829400AD0C32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 625A14B819C7829400AD0C32 /* InfoPlist.strings */; }; 625A14C519C7829400AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; }; @@ -362,6 +368,9 @@ 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitStringCategoryTests.m; sourceTree = ""; }; 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitOrderedDictionaryTests.m; sourceTree = ""; }; 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodesTests.m; sourceTree = ""; }; + 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDOMTokenList.h; sourceTree = ""; }; + 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDOMTokenList.m; sourceTree = ""; }; + 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMTokenListTests.m; sourceTree = ""; }; 625A14AC19C7829400AD0C32 /* HTMLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HTMLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 625A14AF19C7829400AD0C32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 625A14B219C7829400AD0C32 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -501,6 +510,8 @@ 62362A3B1A9FA70400301989 /* HTMLText.m */, 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */, 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, + 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */, + 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */, 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, 624717BA1B22009200B38302 /* HTMLTreeWalker.h */, @@ -551,6 +562,7 @@ isa = PBXGroup; children = ( 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, + 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */, ); name = Structures; sourceTree = ""; @@ -796,6 +808,7 @@ 62ECBEEC1C0B69FD00AF847B /* HTMLParseErrorToken.h in Headers */, 62ECBEED1C0B69FD00AF847B /* HTMLTagToken.h in Headers */, 62ECBEEE1C0B69FD00AF847B /* HTMLEOFToken.h in Headers */, + 62567F501C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */, 62ECBEEF1C0B69FD00AF847B /* HTMLParser.h in Headers */, 62ECBEF01C0B69FD00AF847B /* HTMLStackOfOpenElements.h in Headers */, 62ECBEF11C0B69FD00AF847B /* HTMLListOfActiveFormattingElements.h in Headers */, @@ -859,6 +872,7 @@ 62ECBFB01C0B6D3C00AF847B /* HTMLTreeWalker.h in Headers */, 62ECBFB11C0B6D3C00AF847B /* HTMLNodeTraversal.h in Headers */, 62ECBFB21C0B6D3C00AF847B /* HTMLNodeFilter.h in Headers */, + 62567F511C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */, 62ECBFB31C0B6D3C00AF847B /* HTMLKitDOMExceptions.h in Headers */, 62ECBFB41C0B6D3D00AF847B /* HTMLNamespaces.h in Headers */, 62ECBFB51C0B6D3D00AF847B /* HTMLQuirksMode.h in Headers */, @@ -1109,6 +1123,7 @@ 62ECBF451C0B6B7900AF847B /* CSSPseudoFunctionSelector.m in Sources */, 62ECBF461C0B6B7900AF847B /* CSSCombinatorSelector.m in Sources */, 62ECBF471C0B6B7A00AF847B /* CSSCompoundSelector.m in Sources */, + 62567F521C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1126,6 +1141,7 @@ 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, 624AB3181B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */, 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, + 62567F561C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */, 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */, @@ -1186,6 +1202,7 @@ 62ECBF8D1C0B6D2A00AF847B /* CSSPseudoFunctionSelector.m in Sources */, 62ECBF8E1C0B6D2A00AF847B /* CSSCombinatorSelector.m in Sources */, 62ECBF8F1C0B6D2A00AF847B /* CSSCompoundSelector.m in Sources */, + 62567F531C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1203,6 +1220,7 @@ 62ECBFD11C0B6E2E00AF847B /* HTMLTreeWalkerTests.m in Sources */, 62ECBFD21C0B6E2E00AF847B /* HTMLKitNodesTests.m in Sources */, 62ECBFD31C0B6E2E00AF847B /* HTMLKitMutationAlgorithmsTests.m in Sources */, + 62567F571C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */, 62ECBFD41C0B6E2E00AF847B /* HTMLKitStringCategoryTests.m in Sources */, 62ECBFD51C0B6E2E00AF847B /* HTMLKitOrderedDictionaryTests.m in Sources */, 62ECBFD61C0B6E2E00AF847B /* CSSSelectorTest.m in Sources */, diff --git a/HTMLKit/HTMLDOM.h b/HTMLKit/HTMLDOM.h index 20b494f..50eadbc 100644 --- a/HTMLKit/HTMLDOM.h +++ b/HTMLKit/HTMLDOM.h @@ -14,6 +14,7 @@ #import "HTMLComment.h" #import "HTMLText.h" #import "HTMLTemplate.h" +#import "HTMLDOMTokenList.h" #import "HTMLNodeIterator.h" #import "HTMLTreeWalker.h" #import "HTMLNodeFilter.h" diff --git a/HTMLKit/HTMLDOMTokenList.h b/HTMLKit/HTMLDOMTokenList.h new file mode 100644 index 0000000..4833c2f --- /dev/null +++ b/HTMLKit/HTMLDOMTokenList.h @@ -0,0 +1,36 @@ +// +// HTMLDOMTokenList.h +// HTMLKit +// +// Created by Iska on 30/11/15. +// Copyright © 2015 BrainCookie. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class HTMLElement; + +@interface HTMLDOMTokenList : NSObject + +@property (nonatomic, strong, readonly) HTMLElement *element; +@property (nonatomic, strong, readonly) NSString *attribute; + +- (instancetype)initWithElement:(HTMLElement *)element attribute:(NSString *)attribute value:(NSString *)value; + +- (NSUInteger)length; +- (BOOL)contains:(NSString *)token; +- (void)add:(NSArray *)tokens; +- (void)remove:(NSArray *)tokens; +- (BOOL)toggle:(NSString *)token; +- (void)replaceToke:(NSString *)token withToken:(NSString *)newToken; + +- (NSString *)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(NSString *)obj atIndexedSubscript:(NSUInteger)index; + +- (NSString *)stringify; + +@end + +NS_ASSUME_NONNULL_END diff --git a/HTMLKit/HTMLDOMTokenList.m b/HTMLKit/HTMLDOMTokenList.m new file mode 100644 index 0000000..e5f31f5 --- /dev/null +++ b/HTMLKit/HTMLDOMTokenList.m @@ -0,0 +1,109 @@ +// +// HTMLDOMTokenList.m +// HTMLKit +// +// Created by Iska on 30/11/15. +// Copyright © 2015 BrainCookie. All rights reserved. +// + +#import "HTMLDOMTokenList.h" +#import "HTMLElement.h" + +@interface HTMLDOMTokenList () +{ + HTMLElement *_element; + NSString *_attribute; + NSMutableOrderedSet *_tokens; +} +@end + +@implementation HTMLDOMTokenList +@synthesize element = _element; +@synthesize attribute = _attribute; + +#pragma mark - Init + +- (instancetype)initWithElement:(HTMLElement *)element attribute:(NSString *)attribute value:(NSString *)value +{ + self = [super init]; + if (self) { + _element = element; + _attribute = [attribute copy]; + _tokens = [NSMutableOrderedSet new]; + [self add:[value componentsSeparatedByString:@" "]]; + } + return self; +} + +#pragma mark - Access + +- (void)updateValue +{ + _element[_attribute] = self.stringify; +} + +- (NSUInteger)length +{ + return _tokens.count; +} + +- (BOOL)contains:(NSString *)token +{ + return [_tokens containsObject:token]; +} + +- (void)add:(NSArray *)tokens +{ + for (NSString *token in tokens) { + if (![token isEqualToString:@""]) { + [_tokens addObject:token]; + } + } + [self updateValue]; +} + +- (void)remove:(NSArray *)tokens +{ + for (NSString *token in tokens) { + [_tokens removeObject:token]; + } + [self updateValue]; +} + +- (BOOL)toggle:(NSString *)token +{ + if ([_tokens containsObject:token]) { + [_tokens removeObject:token]; + [self updateValue]; + return NO; + } else { + [_tokens addObject:token]; + [self updateValue]; + return YES; + } +} + +- (void)replaceToke:(NSString *)token withToken:(NSString *)newToken +{ + NSUInteger index = [_tokens indexOfObject:token]; + _tokens[index] = newToken; + [self updateValue]; +} + +- (NSString *)objectAtIndexedSubscript:(NSUInteger)index +{ + return _tokens[index]; +} + +- (void)setObject:(NSString *)obj atIndexedSubscript:(NSUInteger)index +{ + _tokens[index] = obj; + [self updateValue]; +} + +- (NSString *)stringify +{ + return [_tokens.array componentsJoinedByString:@" "]; +} + +@end diff --git a/HTMLKit/HTMLKitDOMExceptions.h b/HTMLKit/HTMLKitDOMExceptions.h index b8486b4..7d730c2 100644 --- a/HTMLKit/HTMLKitDOMExceptions.h +++ b/HTMLKit/HTMLKitDOMExceptions.h @@ -9,3 +9,6 @@ extern NSString * const HTMLKitHierarchyRequestError; extern NSString * const HTMLKitNotFoundError; extern NSString * const HTMLKitNotSupportedError; + +extern NSString * const HTMLKitSyntaxError; +extern NSString * const HTMLKitInvalidCharacterError; diff --git a/HTMLKit/HTMLKitDOMExceptions.m b/HTMLKit/HTMLKitDOMExceptions.m index f7fcacc..88210f6 100644 --- a/HTMLKit/HTMLKitDOMExceptions.m +++ b/HTMLKit/HTMLKitDOMExceptions.m @@ -11,3 +11,5 @@ NSString * const HTMLKitHierarchyRequestError = @"HierarchyRequestError"; NSString * const HTMLKitNotFoundError = @"NotFoundError"; NSString * const HTMLKitNotSupportedError = @"NotSupportedError"; +NSString * const HTMLKitSyntaxError = @"SyntaxError"; +NSString * const HTMLKitInvalidCharacterError = @"InvalidCharacterError"; diff --git a/HTMLKitTests/HTMLKitDOMTokenListTests.m b/HTMLKitTests/HTMLKitDOMTokenListTests.m new file mode 100644 index 0000000..8491e7d --- /dev/null +++ b/HTMLKitTests/HTMLKitDOMTokenListTests.m @@ -0,0 +1,52 @@ +// +// HTMLKitDOMTokenListTests.m +// HTMLKit +// +// Created by Iska on 30/11/15. +// Copyright © 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLElement.h" +#import "HTMLDOMTokenList.h" + +@interface HTMLKitDOMTokenListTests : XCTestCase + +@end + +@implementation HTMLKitDOMTokenListTests + +- (void)testTokenList +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"class": @"red"}]; + + HTMLDOMTokenList *list = element.classList; + + XCTAssertEqual(list.length, 1); + XCTAssertTrue([list contains:@"red"]); + XCTAssertEqualObjects(list.stringify, @"red"); + + [list add:@[@"green", @"blue"]]; + + XCTAssertEqual(list.length, 3); + XCTAssertTrue([list contains:@"green"]); + XCTAssertTrue([list contains:@"blue"]); + XCTAssertEqualObjects(list.stringify, @"red green blue"); + + XCTAssertFalse([list toggle:@"green"]); + XCTAssertFalse([list contains:@"green"]); + XCTAssertEqual(list.length, 2); + XCTAssertEqualObjects(list.stringify, @"red blue"); + + XCTAssertTrue([list toggle:@"green"]); + XCTAssertTrue([list contains:@"green"]); + XCTAssertEqual(list.length, 3); + XCTAssertEqualObjects(list.stringify, @"red blue green"); + + [list remove:@[@"blue", @"red"]]; + + XCTAssertEqual(list.length, 1); + XCTAssertEqualObjects(list.stringify, @"green"); +} + +@end