2dfdeb22ca
Fixes #17
216 lines
5.1 KiB
Objective-C
216 lines
5.1 KiB
Objective-C
//
|
|
// HTMLElement.m
|
|
// HTMLKit
|
|
//
|
|
// Created by Iska on 05/10/14.
|
|
// Copyright (c) 2014 BrainCookie. All rights reserved.
|
|
//
|
|
|
|
#import "HTMLElement.h"
|
|
#import "HTMLParser.h"
|
|
#import "HTMLDocument.h"
|
|
#import "HTMLText.h"
|
|
#import "HTMLDOMTokenList.h"
|
|
#import "HTMLOrderedDictionary.h"
|
|
#import "NSString+HTMLKit.h"
|
|
#import "HTMLNode+Private.h"
|
|
|
|
@interface HTMLElement ()
|
|
{
|
|
HTMLOrderedDictionary *_attributes;
|
|
}
|
|
@end
|
|
|
|
@implementation HTMLElement
|
|
|
|
#pragma mark - Init
|
|
|
|
- (instancetype)init
|
|
{
|
|
return [self initWithTagName:@""];
|
|
}
|
|
|
|
- (instancetype)initWithTagName:(NSString *)tagName
|
|
{
|
|
return [self initWithTagName:tagName attributes:nil];
|
|
}
|
|
|
|
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes
|
|
{
|
|
return [self initWithTagName:tagName namespace:HTMLNamespaceHTML attributes:attributes];
|
|
}
|
|
|
|
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes
|
|
{
|
|
self = [super initWithName:tagName type:HTMLNodeElement];
|
|
if (self) {
|
|
_tagName = [tagName copy];
|
|
_attributes = nil;
|
|
if (attributes != nil) {
|
|
_attributes = [HTMLOrderedDictionary new];
|
|
[_attributes addEntriesFromDictionary:attributes];
|
|
}
|
|
_htmlNamespace = htmlNamespace;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - Special Attributes
|
|
|
|
- (NSMutableDictionary<NSString *,NSString *> *)attributes
|
|
{
|
|
if (_attributes == nil) {
|
|
_attributes = [HTMLOrderedDictionary new];
|
|
}
|
|
|
|
return _attributes;
|
|
}
|
|
|
|
- (NSString *)elementId
|
|
{
|
|
return self.attributes[@"id"] ?: @"";
|
|
}
|
|
|
|
- (void)setElementId:(NSString *)elementId
|
|
{
|
|
self.attributes[@"id"] = elementId;
|
|
}
|
|
|
|
- (NSString *)className
|
|
{
|
|
return self.attributes[@"class"] ?: @"";
|
|
}
|
|
|
|
- (void)setClassName:(NSString *)className
|
|
{
|
|
self.attributes[@"class"] = className;
|
|
}
|
|
|
|
- (HTMLDOMTokenList *)classList
|
|
{
|
|
return [[HTMLDOMTokenList alloc] initWithElement:self attribute:@"class" value:self.className];
|
|
}
|
|
|
|
#pragma mark - Attributes
|
|
|
|
- (BOOL)hasAttribute:(NSString *)name
|
|
{
|
|
return self.attributes[name] != nil;
|
|
}
|
|
|
|
- (NSString *)objectForKeyedSubscript:(NSString *)name;
|
|
{
|
|
return self.attributes[name];
|
|
}
|
|
|
|
- (void)setObject:(NSString *)value forKeyedSubscript:(NSString *)attribute
|
|
{
|
|
self.attributes[attribute] = value;
|
|
}
|
|
|
|
- (void)removeAttribute:(NSString *)name
|
|
{
|
|
[self.attributes removeObjectForKey:name];
|
|
}
|
|
|
|
- (NSString *)textContent
|
|
{
|
|
NSMutableString *content = [NSMutableString string];
|
|
for (HTMLNode *node in self.nodeIterator) {
|
|
if (node.nodeType == HTMLNodeText) {
|
|
[content appendString:[(HTMLText *)node data]];
|
|
}
|
|
}
|
|
return content;
|
|
}
|
|
|
|
- (void)setTextContent:(NSString *)textContent
|
|
{
|
|
HTMLText *node = [[HTMLText alloc] initWithData:textContent];
|
|
[self replaceAllChildNodesWithNode:node];
|
|
}
|
|
|
|
- (void)setInnerHTML:(NSString *)innerHTML
|
|
{
|
|
HTMLParser *parser = [[HTMLParser alloc] initWithString:innerHTML];
|
|
NSArray *fragmentNodes = [parser parseFragmentWithContextElement:self];
|
|
[self removeAllChildNodes];
|
|
[self appendNodes:fragmentNodes];
|
|
}
|
|
|
|
#pragma mark - NSCopying
|
|
|
|
- (id)copyWithZone:(NSZone *)zone
|
|
{
|
|
HTMLElement *copy = [super copyWithZone:zone];
|
|
copy->_tagName = [_tagName copy];
|
|
copy->_attributes = [_attributes copy];
|
|
copy->_htmlNamespace = _htmlNamespace;
|
|
return copy;
|
|
}
|
|
|
|
#pragma mark - Serialization
|
|
|
|
- (NSString *)outerHTML
|
|
{
|
|
NSMutableString *result = [NSMutableString string];
|
|
|
|
[result appendFormat:@"<%@", self.tagName];
|
|
[self.attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
|
|
NSMutableString *escaped = [value mutableCopy];
|
|
[escaped replaceOccurrencesOfString:@"&" withString:@"&" options:0 range:NSMakeRange(0, escaped.length)];
|
|
[escaped replaceOccurrencesOfString:@"0x00A0" withString:@" " options:0 range:NSMakeRange(0, escaped.length)];
|
|
[escaped replaceOccurrencesOfString:@"\"" withString:@""" options:0 range:NSMakeRange(0, escaped.length)];
|
|
|
|
[result appendFormat:@" %@=\"%@\"", key, escaped];
|
|
}];
|
|
|
|
[result appendString:@">"];
|
|
|
|
if ([self.tagName isEqualToAny:@"area", @"base", @"basefont", @"bgsound", @"br", @"col", @"embed",
|
|
@"frame", @"hr", @"img", @"input", @"keygen", @"link", @"menuitem", @"meta", @"param", @"source",
|
|
@"track", @"wbr", nil]) {
|
|
return result;
|
|
}
|
|
|
|
if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChild.nodeType == HTMLNodeText) {
|
|
HTMLText *textNode = (HTMLText *)self.firstChild;
|
|
if ([textNode.data hasPrefix:@"\n"]) {
|
|
[result appendString:@"\n"];
|
|
}
|
|
}
|
|
[result appendString:self.innerHTML];
|
|
[result appendFormat:@"</%@>", self.tagName];
|
|
|
|
return result;
|
|
}
|
|
|
|
#pragma mark - Description
|
|
|
|
- (NSString *)description
|
|
{
|
|
NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p <", self.class, self];
|
|
|
|
if (self.htmlNamespace == HTMLNamespaceMathML) {
|
|
[description appendString:@"math "];
|
|
} else if (self.htmlNamespace == HTMLNamespaceSVG) {
|
|
[description appendString:@"svg "];
|
|
}
|
|
|
|
[description appendString:self.tagName];
|
|
[self.attributes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
[description appendFormat:@" %@=\"%@\"", key, obj];
|
|
}];
|
|
|
|
[description appendString:@">>"];
|
|
|
|
return description;
|
|
}
|
|
|
|
- (NSString *)debugDescription
|
|
{
|
|
return self.description;
|
|
}
|
|
|
|
@end
|