mirror of
https://github.com/Tencent/Hippy.git
synced 2026-10-23 17:48:34 +08:00
568 lines
22 KiB
Objective-C
568 lines
22 KiB
Objective-C
/*!
|
|
* iOS SDK
|
|
*
|
|
* Tencent is pleased to support the open source community by making
|
|
* Hippy available.
|
|
*
|
|
* Copyright (C) 2019 THL A29 Limited, a Tencent company.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#import "HippyDomModel.h"
|
|
#import <UIKit/UIKit.h>
|
|
#import "HippyCSSPropsDefine.h"
|
|
#import "HippyLog.h"
|
|
#import "HippyUIManager.h"
|
|
|
|
// DOM Result JSON Key
|
|
NSString *const HippyDOMKeyRoot = @"root";
|
|
NSString *const HippyDOMKeyNodeId = @"nodeId";
|
|
NSString *const HippyDOMKeyBackendNodeId = @"backendNodeId";
|
|
NSString *const HippyDOMKeyNodeType = @"nodeType";
|
|
NSString *const HippyDOMKeyChildNodeCount = @"childNodeCount";
|
|
NSString *const HippyDOMKeyNodeName = @"nodeName";
|
|
NSString *const HippyDOMKeyBaseURL = @"baseURL";
|
|
NSString *const HippyDOMKeyDocumentURL = @"documentURL";
|
|
NSString *const HippyDOMKeyChildren = @"children";
|
|
NSString *const HippyDOMKeyLocalName = @"localName";
|
|
NSString *const HippyDOMKeyNodeValue = @"nodeValue";
|
|
NSString *const HippyDOMKeyParentId = @"parentId";
|
|
NSString *const HippyDOMKeyModel = @"model";
|
|
NSString *const HippyDOMKeyBoxModelContent = @"content";
|
|
NSString *const HippyDOMKeyBackendId = @"backendId";
|
|
NSString *const HippyDOMKeyFrameId = @"frameId";
|
|
NSString *const HippyDOMKeyNodeIds = @"nodeIds";
|
|
|
|
// Default Value
|
|
NSInteger const HippyDOMDefaultDocumentNodeId = -3;
|
|
NSInteger const HippyDOMDefaultDocumentChildNodeCount = 1;
|
|
NSString *const HippyDOMDefaultDocumentNodeName = @"#document";
|
|
NSString *const HippyDOMDefaultFrameID = @"main_frame";
|
|
|
|
// Node Prop
|
|
NSString *const HippyNodePropAttributes = @"attributes";
|
|
NSString *const HippyNodePropText = @"text";
|
|
|
|
typedef NS_ENUM(NSUInteger, HippyDOMNodeType) {
|
|
HippyDOMNodeTypeElementNode = 1,
|
|
HippyDOMNodeTypeAttributeNode = 2,
|
|
HippyDOMNodeTypeTextNode = 3,
|
|
HippyDOMNodeTypeCDataSectionNode = 4,
|
|
HippyDOMNodeTypeProcessingInstructionNode = 5,
|
|
HippyDOMNodeTypeCommentNode = 6,
|
|
HippyDOMNodeTypeDocumentNode = 7,
|
|
HippyDOMNodeTypeDocumentTypeNode = 8,
|
|
HippyDOMNodeTypeDocumentFragmentNode = 9
|
|
};
|
|
|
|
@interface HippyDomModel ()
|
|
|
|
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSNumber *> *backendIdMap; // key: nodeId value: backendId
|
|
|
|
@end
|
|
|
|
@implementation HippyDomModel
|
|
|
|
- (instancetype)init {
|
|
self = [super init];
|
|
if (self) {
|
|
self.backendIdMap = [NSMutableDictionary dictionary];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - DOM Protocol
|
|
- (BOOL)domGetDocumentJSONWithManager:(nullable HippyUIManager *)manager
|
|
completion:(void (^)(NSDictionary *rspObject))completion {
|
|
if (!completion) {
|
|
HippyLogWarn(@"DOM Model, getDocument error, completion is nil");
|
|
return NO;
|
|
}
|
|
if (!manager) {
|
|
HippyLogWarn(@"DOM Model, getDocument error, manager is nil");
|
|
completion(@{});
|
|
return NO;
|
|
}
|
|
HippyVirtualNode *rootNode = [manager nodeForHippyTag:[manager rootHippyTag]];
|
|
if (!rootNode) {
|
|
HippyLogWarn(@"DOM Model, getDocument error, root node is nil");
|
|
completion(@{});
|
|
return NO;
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.backendIdMap removeAllObjects];
|
|
NSMutableDictionary *rootDic = [NSMutableDictionary dictionary];
|
|
rootDic[HippyDOMKeyNodeId] = @(HippyDOMDefaultDocumentNodeId);
|
|
rootDic[HippyDOMKeyBackendNodeId] = @(HippyDOMDefaultDocumentNodeId);
|
|
rootDic[HippyDOMKeyNodeType] = @(HippyDOMNodeTypeDocumentFragmentNode);
|
|
rootDic[HippyDOMKeyChildNodeCount] = @(HippyDOMDefaultDocumentChildNodeCount);
|
|
rootDic[HippyDOMKeyBaseURL] = @"";
|
|
rootDic[HippyDOMKeyDocumentURL] = @"";
|
|
NSMutableArray *children = [NSMutableArray array];
|
|
for (HippyVirtualNode *childNode in rootNode.subNodes) {
|
|
[children addObject:[self nodeJSONWithVirtualNode:childNode nodeType:HippyDOMNodeTypeElementNode]];
|
|
}
|
|
|
|
rootDic[HippyDOMKeyChildren] = children;
|
|
NSMutableDictionary *resultDic = [NSMutableDictionary dictionary];
|
|
resultDic[HippyDOMKeyRoot] = rootDic;
|
|
completion(resultDic);
|
|
});
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)domGetBoxModelJSONWithNode:(nullable HippyVirtualNode *)node
|
|
manager:(nullable HippyUIManager *)manager
|
|
completion:(void (^)(NSDictionary *rspObject))completion {
|
|
if (!completion) {
|
|
HippyLogWarn(@"DOM Model, getBoxModel error, completion is nil");
|
|
return NO;
|
|
}
|
|
if (!node) {
|
|
HippyLogWarn(@"DOM Model, getBoxModel error, node is nil");
|
|
completion(@{});
|
|
return NO;
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
CGRect nodeFrame = [self windowFrameWithNode:node manager:manager];
|
|
NSMutableDictionary *boxModelDic = [NSMutableDictionary dictionary];
|
|
NSArray *border = [self assemblyBoxModelBorderWithFrame:nodeFrame];
|
|
NSArray *padding = [self assemblyBoxModelPaddingWithProps:node.props border:border];
|
|
NSArray *content = [self assemblyBoxModelContentWithProps:node.props padding:padding];
|
|
NSArray *margin = [self assemblyBoxModelMarginWithProps:node.props border:border];
|
|
boxModelDic[HippyDevtoolsCSSPropWidth] = @(nodeFrame.size.width);
|
|
boxModelDic[HippyDevtoolsCSSPropHeight] = @(nodeFrame.size.height);
|
|
boxModelDic[HippyDevtoolsCSSPropBorder] = border;
|
|
boxModelDic[HippyDevtoolsCSSPropPadding] = padding;
|
|
boxModelDic[HippyDOMKeyBoxModelContent] = content;
|
|
boxModelDic[HippyDevtoolsCSSPropMargin] = margin;
|
|
completion(@{HippyDOMKeyModel: boxModelDic});
|
|
});
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)domGetNodeForLocationWithManager:(nullable HippyUIManager *)manager
|
|
location:(CGPoint)location
|
|
completion:(void (^)(NSDictionary *rspObject))completion {
|
|
if (!completion) {
|
|
HippyLogWarn(@"DOM Model, getNodeForLocation error, completion is nil");
|
|
return NO;
|
|
}
|
|
if (!manager) {
|
|
HippyLogWarn(@"DOM Model, getNodeForLocation error, manager is nil");
|
|
completion(@{});
|
|
return NO;
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
NSMutableDictionary *resultDic = [NSMutableDictionary dictionary];
|
|
HippyVirtualNode *rootNode = [manager nodeForHippyTag:[manager rootHippyTag]];
|
|
if (!rootNode) {
|
|
HippyLogWarn(@"DOM Model, getNodeForLocation error, root node is nil");
|
|
completion(@{});
|
|
return;
|
|
}
|
|
HippyVirtualNode *hitNode = [self maxDepthAndMinAreaHitNodeWithLocation:location
|
|
node:rootNode
|
|
manager:manager];
|
|
if (!hitNode) {
|
|
completion(@{});
|
|
return;
|
|
}
|
|
resultDic[HippyDOMKeyBackendId] = hitNode.hippyTag;
|
|
resultDic[HippyDOMKeyFrameId] = HippyDOMDefaultFrameID;
|
|
resultDic[HippyDOMKeyNodeId] = hitNode.hippyTag;
|
|
completion(resultDic);
|
|
});
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)domGetNodeIdByPath:(NSString *)path
|
|
manager:(HippyUIManager *)manager
|
|
completion:(void (^)(NSDictionary * _Nonnull))completion {
|
|
if (!completion) {
|
|
HippyLogWarn(@"DOM Model, pushNodeByPathToFrontend error, completion is nil");
|
|
return NO;
|
|
}
|
|
if (!manager) {
|
|
HippyLogWarn(@"DOM Model, pushNodeByPathToFrontend error, manager is nil");
|
|
completion(@{});
|
|
return NO;
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
HippyVirtualNode *rootNode = [manager nodeForHippyTag:[manager rootHippyTag]];
|
|
if (!rootNode) {
|
|
HippyLogWarn(@"DOM Model, pushNodeByPathToFrontend error, root node is nil");
|
|
completion(@{});
|
|
return;
|
|
}
|
|
HippyVirtualNode *hitNode = [self nodeForPath:path rootNode:rootNode];
|
|
if (!hitNode) {
|
|
HippyLogWarn(@"DOM Model, pushNodeByPathToFrontend error, hitNode is nil");
|
|
completion(@{});
|
|
return;
|
|
}
|
|
NSMutableDictionary *resultDic = [NSMutableDictionary dictionary];
|
|
resultDic[HippyDOMKeyNodeId] = hitNode.hippyTag;
|
|
completion(resultDic);
|
|
});
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)domGetNodeIdsByBackendIds:(NSArray<NSNumber *> *)backendIds
|
|
completion:(void (^)(NSDictionary * _Nonnull))completion {
|
|
if (!completion) {
|
|
HippyLogWarn(@"DOM Model, pushNodesByBackendIdsToFrontend error, completion is nil");
|
|
return NO;
|
|
}
|
|
if (backendIds.count <= 0) {
|
|
HippyLogWarn(@"DOM Model, pushNodesByBackendIdsToFrontend error, backendIds is empty");
|
|
return NO;
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
NSMutableArray *nodeIds = [NSMutableArray array];
|
|
for (NSNumber *backendId in backendIds) {
|
|
NSNumber *nodeId = self.backendIdMap[backendId];
|
|
if (!nodeId) {
|
|
continue;
|
|
}
|
|
[nodeIds addObject:nodeId];
|
|
}
|
|
NSMutableDictionary *resultDic = [NSMutableDictionary dictionary];
|
|
resultDic[HippyDOMKeyNodeIds] = nodeIds;
|
|
completion(resultDic);
|
|
});
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark - private method
|
|
- (NSDictionary *)nodeJSONWithVirtualNode:(HippyVirtualNode *)node
|
|
nodeType:(HippyDOMNodeType)nodeType {
|
|
if (!node) {
|
|
return @{};
|
|
}
|
|
NSDictionary *tempNodeDic = [self assemblyNodeBasicJSON:node nodeType:nodeType];
|
|
NSMutableDictionary *nodeJSON = [NSMutableDictionary dictionaryWithDictionary:tempNodeDic];
|
|
NSMutableArray *childNodes = [NSMutableArray array];
|
|
NSString *nodeValue = [node.props objectForKey:HippyNodePropText];
|
|
if (nodeValue.length > 0) {
|
|
NSDictionary *textDic = [self assemblyNodeBasicJSON:node nodeType:HippyDOMNodeTypeTextNode];
|
|
NSMutableDictionary *textMutableDic = [NSMutableDictionary dictionaryWithDictionary:textDic];
|
|
textMutableDic[HippyDOMKeyChildNodeCount] = @(0);
|
|
textMutableDic[HippyDOMKeyChildren] = @[];
|
|
[childNodes addObject:textMutableDic];
|
|
}
|
|
for (HippyVirtualNode *childNode in node.subNodes) {
|
|
NSDictionary *childNodeJSON = [self nodeJSONWithVirtualNode:childNode nodeType:nodeType];
|
|
[childNodes addObject:childNodeJSON];
|
|
}
|
|
if (childNodes.count > 0) {
|
|
nodeJSON[HippyDOMKeyChildren] = childNodes;
|
|
}
|
|
|
|
return [nodeJSON copy];
|
|
}
|
|
|
|
- (NSDictionary *)assemblyNodeBasicJSON:(HippyVirtualNode *)node
|
|
nodeType:(HippyDOMNodeType)nodeType {
|
|
if (!node) {
|
|
return @{};
|
|
}
|
|
NSMutableDictionary *basicDictionary = [NSMutableDictionary dictionary];
|
|
NSNumber *nodeId = node.hippyTag;
|
|
if (nodeType == HippyDOMNodeTypeTextNode) {
|
|
nodeId = @(-nodeId.integerValue);
|
|
}
|
|
NSNumber *backendId = nodeId;
|
|
basicDictionary[HippyDOMKeyNodeId] = nodeId;
|
|
basicDictionary[HippyDOMKeyBackendNodeId] = backendId;
|
|
self.backendIdMap[nodeId] = backendId;
|
|
basicDictionary[HippyDOMKeyNodeType] = @(nodeType);
|
|
basicDictionary[HippyDOMKeyLocalName] = node.tagName;
|
|
basicDictionary[HippyDOMKeyNodeName] = node.tagName;
|
|
NSString *nodeValue = [node.props objectForKey:HippyNodePropText];
|
|
if (nodeValue.length > 0) {
|
|
basicDictionary[HippyDOMKeyNodeValue] = nodeValue;
|
|
}
|
|
NSNumber *parentId = @(0);
|
|
if (node.parent) {
|
|
parentId = node.parent.hippyTag;
|
|
}
|
|
basicDictionary[HippyDOMKeyParentId] = parentId;
|
|
basicDictionary[HippyDOMKeyChildNodeCount] = @(node.subNodes.count);
|
|
basicDictionary[HippyNodePropAttributes] = [self assemblyAttributeJSON:[node.props objectForKey:HippyNodePropAttributes]];
|
|
return [basicDictionary copy];
|
|
}
|
|
|
|
- (NSArray *)assemblyAttributeJSON:(NSDictionary *)attribute {
|
|
NSMutableArray<NSString *> *attributeJSONArray = [NSMutableArray arrayWithCapacity:[attribute count]];
|
|
for (NSString *key in attribute) {
|
|
NSString *value = [NSString stringWithFormat:@"%@", attribute[key]];
|
|
if (value.length <= 0) {
|
|
continue;
|
|
}
|
|
[attributeJSONArray addObject:key];
|
|
[attributeJSONArray addObject:value];
|
|
}
|
|
return [attributeJSONArray copy];
|
|
}
|
|
|
|
- (NSArray<NSNumber *> *)assemblyBoxModelBorderWithFrame:(CGRect)frame {
|
|
return @[
|
|
@(frame.origin.x),
|
|
@(frame.origin.y),
|
|
@(frame.origin.x + frame.size.width),
|
|
@(frame.origin.y),
|
|
@(frame.origin.x + frame.size.width),
|
|
@(frame.origin.y + frame.size.height),
|
|
@(frame.origin.x),
|
|
@(frame.origin.y + frame.size.height)
|
|
];
|
|
}
|
|
|
|
- (NSArray<NSNumber *> *)assemblyBoxModelPaddingWithProps:(NSDictionary *)props
|
|
border:(NSArray<NSNumber *> *)border {
|
|
if (props.count <= 0 || border.count <= 0) {
|
|
return @[];
|
|
}
|
|
NSInteger borderTop = 0;
|
|
NSInteger borderLeft = 0;
|
|
NSInteger borderRight = 0;
|
|
NSInteger borderBottom = 0;
|
|
NSNumber *borderWidth = props[HippyDevtoolsCSSPropBorderWidth];
|
|
if (borderWidth) {
|
|
borderTop = borderWidth.integerValue;
|
|
borderLeft = borderWidth.integerValue;
|
|
borderRight = borderWidth.integerValue;
|
|
borderBottom = borderWidth.integerValue;
|
|
}
|
|
NSNumber *borderTopWidth = props[HippyDevtoolsCSSPropBorderTopWidth];
|
|
if (borderTopWidth) {
|
|
borderTop = borderTopWidth.integerValue;
|
|
}
|
|
NSNumber *borderLeftWidth = props[HippyDevtoolsCSSPropBorderLeftWidth];
|
|
if (borderLeftWidth) {
|
|
borderLeft = borderLeftWidth.integerValue;
|
|
}
|
|
NSNumber *borderRightWidth = props[HippyDevtoolsCSSPropBorderRightWidth];
|
|
if (borderRightWidth) {
|
|
borderRight = borderRightWidth.integerValue;
|
|
}
|
|
NSNumber *borderBottomWidth = props[HippyDevtoolsCSSPropBorderBottomWidth];
|
|
if (borderBottomWidth) {
|
|
borderBottom = borderBottomWidth.integerValue;
|
|
}
|
|
|
|
return @[
|
|
@(border[0].integerValue + borderLeft),
|
|
@(border[1].integerValue + borderTop),
|
|
@(border[2].integerValue - borderRight),
|
|
@(border[3].integerValue + borderTop),
|
|
@(border[4].integerValue - borderRight),
|
|
@(border[5].integerValue - borderBottom),
|
|
@(border[6].integerValue + borderLeft),
|
|
@(border[7].integerValue - borderBottom)
|
|
];
|
|
}
|
|
|
|
- (NSArray<NSNumber *> *)assemblyBoxModelContentWithProps:(NSDictionary *)props
|
|
padding:(NSArray<NSNumber *> *)padding {
|
|
if (props.count <= 0 || padding.count <= 0) {
|
|
return @[];
|
|
}
|
|
NSInteger paddingTop = 0;
|
|
NSInteger paddingLeft = 0;
|
|
NSInteger paddingRight = 0;
|
|
NSInteger paddingBottom = 0;
|
|
NSNumber *propsPadding = props[HippyDevtoolsCSSPropPadding];
|
|
if (propsPadding) {
|
|
paddingTop = propsPadding.integerValue;
|
|
paddingLeft = propsPadding.integerValue;
|
|
paddingRight = propsPadding.integerValue;
|
|
paddingBottom = propsPadding.integerValue;
|
|
}
|
|
NSNumber *propsPaddingTop = props[HippyDevtoolsCSSPropPaddingTop];
|
|
if (propsPaddingTop) {
|
|
paddingTop = propsPaddingTop.integerValue;
|
|
}
|
|
NSNumber *propsPaddingLeft = props[HippyDevtoolsCSSPropPaddingLeft];
|
|
if (propsPaddingLeft) {
|
|
paddingLeft = propsPaddingLeft.integerValue;
|
|
}
|
|
NSNumber *propsPaddingRight = props[HippyDevtoolsCSSPropPaddingRight];
|
|
if (propsPaddingRight) {
|
|
paddingRight = propsPaddingRight.integerValue;
|
|
}
|
|
NSNumber *propsPaddingBottom = props[HippyDevtoolsCSSPropPaddingBottom];
|
|
if (propsPaddingBottom) {
|
|
paddingBottom = propsPaddingBottom.integerValue;
|
|
}
|
|
|
|
return @[
|
|
@(padding[0].integerValue + paddingLeft),
|
|
@(padding[1].integerValue + paddingTop),
|
|
@(padding[2].integerValue - paddingRight),
|
|
@(padding[3].integerValue + paddingTop),
|
|
@(padding[4].integerValue - paddingRight),
|
|
@(padding[5].integerValue - paddingBottom),
|
|
@(padding[6].integerValue + paddingLeft),
|
|
@(padding[7].integerValue - paddingBottom)
|
|
];
|
|
}
|
|
|
|
- (NSArray<NSNumber *> *)assemblyBoxModelMarginWithProps:(NSDictionary *)props
|
|
border:(NSArray<NSNumber *> *)border {
|
|
if (props.count <= 0 || border.count <= 0) {
|
|
return @[];
|
|
}
|
|
NSInteger marginTop = 0;
|
|
NSInteger marginLeft = 0;
|
|
NSInteger marginRight = 0;
|
|
NSInteger marginBottom = 0;
|
|
NSNumber *propsMargin = props[HippyDevtoolsCSSPropMargin];
|
|
if (propsMargin) {
|
|
marginTop = propsMargin.integerValue;
|
|
marginLeft = propsMargin.integerValue;
|
|
marginRight = propsMargin.integerValue;
|
|
marginBottom = propsMargin.integerValue;
|
|
}
|
|
NSNumber *propsMarginTop = props[HippyDevtoolsCSSPropMarginTop];
|
|
if (propsMarginTop) {
|
|
marginTop = propsMarginTop.integerValue;
|
|
}
|
|
NSNumber *propsMarginLeft = props[HippyDevtoolsCSSPropMarginLeft];
|
|
if (propsMarginLeft) {
|
|
marginLeft = propsMarginLeft.integerValue;
|
|
}
|
|
NSNumber *propsMarginRight = props[HippyDevtoolsCSSPropMarginRight];
|
|
if (propsMarginRight) {
|
|
marginRight = propsMarginRight.integerValue;
|
|
}
|
|
NSNumber *propsMarginBottom = props[HippyDevtoolsCSSPropMarginBottom];
|
|
if (propsMarginBottom) {
|
|
marginBottom = propsMarginBottom.integerValue;
|
|
}
|
|
|
|
return @[
|
|
@(border[0].integerValue - marginLeft),
|
|
@(border[1].integerValue - marginTop),
|
|
@(border[2].integerValue + marginRight),
|
|
@(border[3].integerValue - marginTop),
|
|
@(border[4].integerValue + marginRight),
|
|
@(border[5].integerValue + marginBottom),
|
|
@(border[6].integerValue - marginLeft),
|
|
@(border[7].integerValue + marginBottom)
|
|
];
|
|
}
|
|
|
|
- (HippyVirtualNode *)nodeForPath:(NSString *)path rootNode:(HippyVirtualNode *)rootNode {
|
|
if (path.length <= 0 || !rootNode) {
|
|
return nil;
|
|
}
|
|
NSArray<NSString *> *pathTokens = [path componentsSeparatedByString:@","];
|
|
if (pathTokens.count <= 0) {
|
|
return nil;
|
|
}
|
|
HippyVirtualNode *node = rootNode;
|
|
for (int index = 0; index < pathTokens.count - 1; index += 2) {
|
|
NSInteger childNumber = [pathTokens[index] integerValue];
|
|
if (childNumber >= node.subNodes.count) {
|
|
return nil;
|
|
}
|
|
if (index + 1 >= pathTokens.count) {
|
|
return nil;
|
|
}
|
|
NSString *childName = pathTokens[index + 1];
|
|
HippyVirtualNode *child = [node.subNodes objectAtIndex:childNumber];
|
|
if (!child || [child.tagName caseInsensitiveCompare:childName] != NSOrderedSame) {
|
|
return nil;
|
|
}
|
|
node = child;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
- (CGRect)windowFrameWithNode:(HippyVirtualNode *)node
|
|
manager:(HippyUIManager *)manager {
|
|
if (!node) {
|
|
HippyLogWarn(@"DOM Model, windowFrameWithNode, node is nil");
|
|
return CGRectZero;
|
|
}
|
|
if (!manager) {
|
|
HippyLogWarn(@"DOM Model, windowFrameWithNode, manager is nil");
|
|
return CGRectZero;
|
|
}
|
|
UIView *selfView = [manager viewForHippyTag:node.hippyTag];
|
|
if (!selfView) {
|
|
return CGRectZero;
|
|
}
|
|
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
|
|
CGRect nodeLocation = [selfView.superview convertRect:selfView.frame toView:window];
|
|
UIView *rootView = [manager viewForHippyTag:[manager rootHippyTag]];
|
|
if (rootView) {
|
|
CGRect rootLocation = [rootView.superview convertRect:rootView.frame toView:window];
|
|
nodeLocation = CGRectMake(nodeLocation.origin.x - rootLocation.origin.x,
|
|
nodeLocation.origin.y - rootLocation.origin.y,
|
|
nodeLocation.size.width, nodeLocation.size.height);
|
|
}
|
|
return nodeLocation;
|
|
}
|
|
|
|
- (HippyVirtualNode *)maxDepthAndMinAreaHitNodeWithLocation:(CGPoint)location
|
|
node:(HippyVirtualNode *)node
|
|
manager:(HippyUIManager *)manager {
|
|
if (!node || ![self isLocationHitNode:location node:node manager:manager]) {
|
|
return nil;
|
|
}
|
|
HippyVirtualNode *hitNode = nil;
|
|
for (HippyVirtualNode *childNode in node.subNodes) {
|
|
if (![self isLocationHitNode:location node:childNode manager:manager]) {
|
|
continue;
|
|
}
|
|
HippyVirtualNode *newHitNode = [self maxDepthAndMinAreaHitNodeWithLocation:location
|
|
node:childNode
|
|
manager:manager];
|
|
hitNode = [self smallerAreaNodeWithOldNode:hitNode newNode:newHitNode];
|
|
}
|
|
return hitNode != nil ? hitNode : node;
|
|
}
|
|
|
|
- (BOOL)isLocationHitNode:(CGPoint)location
|
|
node:(HippyVirtualNode *)node
|
|
manager:(HippyUIManager *)manager {
|
|
if (!node || !manager) {
|
|
return false;
|
|
}
|
|
CGRect windowFrame = [self windowFrameWithNode:node manager:manager];
|
|
BOOL isInTopOffset = (location.x >= windowFrame.origin.x) && (location.y >= windowFrame.origin.y);
|
|
BOOL isInBottomOffset = (location.x <= (windowFrame.origin.x + windowFrame.size.width)) &&
|
|
(location.y <= (windowFrame.origin.y + windowFrame.size.height));
|
|
return isInTopOffset && isInBottomOffset;
|
|
}
|
|
|
|
- (HippyVirtualNode *)smallerAreaNodeWithOldNode:(HippyVirtualNode *)oldNode
|
|
newNode:(HippyVirtualNode *)newNode {
|
|
if (!oldNode) {
|
|
return newNode;
|
|
}
|
|
if (!newNode) {
|
|
return oldNode;
|
|
}
|
|
double oldNodeArea = oldNode.frame.size.width * oldNode.frame.size.height;
|
|
double newNodeArea = newNode.frame.size.width * newNode.frame.size.height;
|
|
return oldNodeArea > newNodeArea ? newNode : oldNode;
|
|
}
|
|
|
|
@end
|