blob: 5a366d0cb159c4b2ecb78d48d1e7d54e24ba8750 [file] [log] [blame]
/**
* Copyright 2018 Alibaba Group
*
* 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 "EBWXModule.h"
#import <WeexSDK/WeexSDK.h>
#import "EBExpressionHandler.h"
#import <pthread/pthread.h>
#import <WeexPluginLoader/WeexPluginLoader.h>
#import "EBBindData.h"
#import "EBUtility+WX.h"
#import "EBWXUtils.h"
WX_PlUGIN_EXPORT_MODULE(bindingx, EBWXModule)
@interface EBWXModule ()
@property (nonatomic, strong) EBBindData *bindData;
@end
@implementation EBWXModule {
pthread_mutex_t mutex;
pthread_mutexattr_t mutexAttr;
}
@synthesize weexInstance;
WX_EXPORT_METHOD_SYNC(@selector(prepare:))
WX_EXPORT_METHOD_SYNC(@selector(bind:callback:tokenCallback:))
WX_EXPORT_METHOD_SYNC(@selector(bindAsync:callback:tokenCallback:))
WX_EXPORT_METHOD_SYNC(@selector(unbind:))
WX_EXPORT_METHOD_SYNC(@selector(unbindAll))
WX_EXPORT_METHOD_SYNC(@selector(supportFeatures))
WX_EXPORT_METHOD_SYNC(@selector(getComputedStyle:callback:))
WX_EXPORT_METHOD_SYNC(@selector(getComputedStyleAsync:callback:))
- (instancetype)init {
if (self = [super init]) {
pthread_mutexattr_init(&mutexAttr);
pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &mutexAttr);
_bindData = [EBBindData new];
}
return self;
}
- (void)dealloc {
[self unbindAll];
pthread_mutex_destroy(&mutex);
pthread_mutexattr_destroy(&mutexAttr);
}
- (void)prepare:(NSDictionary *)dictionary {
if (!dictionary) {
WX_LOG(WXLogFlagWarning, @"prepare params error, need json input");
return;
}
NSString *anchor = dictionary[@"anchor"];
NSString *eventType = dictionary[@"eventType"];
if ([WXUtility isBlankString:anchor] || [WXUtility isBlankString:eventType]) {
WX_LOG(WXLogFlagWarning, @"prepare binding params error");
return;
}
if (![EBEventHandlerFactory containsEvent:eventType]) {
WX_LOG(WXLogFlagWarning, @"prepare binding eventType error");
return;
}
__weak typeof(self) welf = self;
WXPerformBlockOnComponentThread(^{
// find sourceRef & targetRef
WXComponent *sourceComponent = [weexInstance componentForRef:anchor];
if (!sourceComponent && [EBEventHandlerFactory eventRequireSource:eventType]) {
WX_LOG(WXLogFlagWarning, @"prepare binding can't find component");
return;
}
pthread_mutex_lock(&mutex);
EBExpressionHandler *handler = [welf.bindData handlerForToken:anchor eventType:eventType];
if (!handler) {
// create handler for key
handler = [EBEventHandlerFactory createHandlerWithEvent:eventType source:sourceComponent];
[welf.bindData putHandler:handler forToken:anchor eventType:eventType];
}
pthread_mutex_unlock(&mutex);
});
}
- (NSDictionary *)bind:(NSDictionary *)dictionary
callback:(WXKeepAliveCallback)callback
tokenCallback:(WXKeepAliveCallback)tokenCallback {
if (!dictionary) {
WX_LOG(WXLogFlagWarning, @"bind params error, need json input");
return nil;
}
NSString *eventType = dictionary[@"eventType"];
NSArray *props = dictionary[@"props"];
NSString *token = dictionary[@"anchor"];
NSDictionary *exitExpression = dictionary[@"exitExpression"];
NSDictionary *options = dictionary[@"options"];
if ([WXUtility isBlankString:eventType] || !props || props.count == 0) {
WX_LOG(WXLogFlagWarning, @"bind params error");
callback(@{@"state":@"error",@"msg":@"bind params error"}, NO);
return nil;
}
if (![EBEventHandlerFactory containsEvent:eventType]) {
WX_LOG(WXLogFlagWarning, @"bind params handler error");
callback(@{@"state":@"error",@"msg":@"bind params handler error"}, NO);
return nil;
}
if ([WXUtility isBlankString:token]){
if ([EBEventHandlerFactory eventRequireSource:eventType]) {
WX_LOG(WXLogFlagWarning, @"bind params handler error");
callback(@{@"state":@"error",@"msg":@"anchor cannot be blank when type is pan or scroll"}, NO);
return nil;
} else {
token = [[NSUUID UUID] UUIDString];
}
}
__weak typeof(self) welf = self;
WXPerformBlockOnComponentThread(^{
// find sourceRef & targetRef
WXComponent *sourceComponent = nil;
NSString *instanceId = dictionary[@"instanceId"];
if (instanceId) {
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
sourceComponent = [instance componentForRef:token];
} else {
sourceComponent = [weexInstance componentForRef:token];
}
if (!sourceComponent && [EBEventHandlerFactory eventRequireSource:eventType]) {
WX_LOG(WXLogFlagWarning, @"bind can't find source component");
callback(@{@"state":@"error",@"msg":@"bind can't find source component"}, NO);
return;
}
NSMapTable<id, NSDictionary *> *targetExpression = [NSMapTable weakToStrongObjectsMapTable];
for (NSDictionary *targetDic in props) {
NSString *targetRef = targetDic[@"element"];
NSString *property = targetDic[@"property"];
NSDictionary *expression = targetDic[@"expression"];
NSString *instanceId = targetDic[@"instanceId"];
WXComponent *targetComponent = nil;
if (instanceId) {
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
targetComponent = [instance componentForRef:targetRef];
} else {
targetComponent = [weexInstance componentForRef:targetRef];
}
if (targetComponent) {
if ([targetComponent isViewLoaded]) {
WXPerformBlockOnMainThread(^{
[targetComponent.view.layer removeAllAnimations];
});
}
NSMutableDictionary *propertyDic = [[targetExpression objectForKey:targetComponent] mutableCopy];
if (!propertyDic) {
propertyDic = [NSMutableDictionary dictionary];
}
NSMutableDictionary *expDict = [NSMutableDictionary dictionary];
expDict[@"expression"] = [EBBindData parseExpression:expression];
if( targetDic[@"config"] )
{
expDict[@"config"] = targetDic[@"config"];
}
propertyDic[property] = expDict;
[targetExpression setObject:propertyDic forKey:targetComponent];
}
}
// find handler for key
pthread_mutex_lock(&mutex);
EBExpressionHandler *handler = [welf.bindData handlerForToken:token eventType:eventType];
if (!handler) {
// create handler for key
handler = [EBEventHandlerFactory createHandlerWithEvent:eventType source:sourceComponent];
[welf.bindData putHandler:handler forToken:token eventType:eventType];
}
[handler updateTargetExpression:targetExpression
options:options
exitExpression:[EBBindData parseExpression:exitExpression]
callback:^(id _Nonnull source, id _Nonnull result, BOOL keepAlive) {
callback(result,keepAlive);
}];
pthread_mutex_unlock(&mutex);
});
NSDictionary *ret = @{@"token":token};
if (tokenCallback) {
tokenCallback(ret, NO);
}
return ret;
}
- (void)bindAsync:(NSDictionary *)dictionary
callback:(WXKeepAliveCallback)callback
tokenCallback:(WXKeepAliveCallback)tokenCallback {
[self bind:dictionary
callback:callback
tokenCallback:tokenCallback];
}
- (void)unbind:(NSDictionary *)dictionary {
if (!dictionary) {
WX_LOG(WXLogFlagWarning, @"unbind params error, need json input");
return;
}
NSString* token = dictionary[@"token"];
NSString* eventType = dictionary[@"eventType"];
if ([WXUtility isBlankString:token] || [WXUtility isBlankString:eventType]) {
WX_LOG(WXLogFlagWarning, @"unbind params error");
return;
}
if (![EBEventHandlerFactory containsEvent:eventType]) {
WX_LOG(WXLogFlagWarning, @"unbind params handler error");
return;
}
pthread_mutex_lock(&mutex);
EBExpressionHandler *handler = [self.bindData handlerForToken:token eventType:eventType];
if (!handler) {
WX_LOG(WXLogFlagWarning, @"unbind can't find handler handler");
pthread_mutex_unlock(&mutex);
return;
}
[handler removeExpressionBinding];
[self.bindData removeHandler:handler forToken:token eventType:eventType];
pthread_mutex_unlock(&mutex);
}
- (void)unbindAll {
pthread_mutex_lock(&mutex);
[self.bindData unbindAll];
pthread_mutex_unlock(&mutex);
}
- (NSArray *)supportFeatures {
return EBsupportFeatures;
}
- (NSDictionary *)getComputedStyle:(NSString *)sourceRef
callback:(WXKeepAliveCallback)callback {
if (callback) {
// call async method when callback is not nil
[self getComputedStyleAsync:sourceRef callback:callback];
return nil;
}
__block NSDictionary *styles = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self fetchComputedStyle:sourceRef callback:^(NSDictionary *callbackStyles) {
styles = callbackStyles;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)));
return styles;
}
- (void)getComputedStyleAsync:(NSString *)sourceRef
callback:(WXKeepAliveCallback)callback {
[self fetchComputedStyle:sourceRef callback:^(NSDictionary *styles) {
if (callback) {
callback(styles, NO);
}
}];
}
- (void)fetchComputedStyle:(NSString *)sourceRef
callback:(void (^ _Nullable)(NSDictionary *))callback {
if (![sourceRef isKindOfClass:NSString.class] || [WXUtility isBlankString:sourceRef]) {
WX_LOG(WXLogFlagWarning, @"getComputedStyle params error");
callback(nil);
}
__block NSMutableDictionary *styles = [NSMutableDictionary new];
WXPerformBlockOnComponentThread(^{
// find sourceRef & targetRef
WXComponent *sourceComponent = [weexInstance componentForRef:sourceRef];
if (!sourceComponent) {
WX_LOG(WXLogFlagWarning, @"getComputedStyle can't find source component");
callback(nil);
}
NSDictionary* mapping = [EBWXUtils cssPropertyMapping];
for (NSString* key in mapping) {
id value = sourceComponent.styles[key];
if (value) {
if ([value isKindOfClass:NSString.class]) {
NSString *string = (NSString *)value;
if ([string hasSuffix:@"px"]) {
NSString *number = [string substringToIndex:(string.length-2)];
[styles setValue:@([number floatValue]) forKey:mapping[key]];
} else {
[styles setValue:string forKey:mapping[key]];
}
} else if([value isKindOfClass:NSNumber.class]) {
[styles setValue:value forKey:mapping[key]];
}
}
}
if (sourceComponent.styles[@"borderRadius"]) {
[styles setValue:sourceComponent.styles[@"borderRadius"] forKey:@"border-top-left-radius"];
[styles setValue:sourceComponent.styles[@"borderRadius"] forKey:@"border-top-right-radius"];
[styles setValue:sourceComponent.styles[@"borderRadius"] forKey:@"border-bottom-left-radius"];
[styles setValue:sourceComponent.styles[@"borderRadius"] forKey:@"border-bottom-right-radius"];
}
WXPerformBlockOnMainThread(^{
CALayer *layer = sourceComponent.view.layer;
layer = layer.presentationLayer ?: layer.modelLayer;
if (layer) {
styles[@"translateX"] = [EBUtility transformFactor:@"transform.translation.x" layer:layer];
styles[@"translateY"] = [EBUtility transformFactor:@"transform.translation.y" layer:layer];
styles[@"scaleX"] = [layer valueForKeyPath:@"transform.scale.x"];
styles[@"scaleY"] = [layer valueForKeyPath:@"transform.scale.y"];
styles[@"rotateX"] = [EBUtility radian2Angle:[layer valueForKeyPath:@"transform.rotation.x"]];
styles[@"rotateY"] = [EBUtility radian2Angle:[layer valueForKeyPath:@"transform.rotation.y"]];
styles[@"rotateZ"] = [EBUtility radian2Angle:[layer valueForKeyPath:@"transform.rotation.z"]];
styles[@"isRotateAngle"] = @"true";
styles[@"opacity"] = [layer valueForKeyPath:@"opacity"];
}
callback(styles);
});
});
}
@end
WX_PlUGIN_EXPORT_MODULE(binding, EBWXBindingModule)
@interface EBWXBindingModule : EBWXModule
@end
@implementation EBWXBindingModule
@end