From 7312ae647bc0cca842537ebeaa60cedc6a1948e8 Mon Sep 17 00:00:00 2001 From: Frank Slofstra Date: Wed, 18 Jan 2017 11:40:56 +0100 Subject: [PATCH] Updated to iOS SDK v2.1.8 Made some changes to be able to merge the iOS and tvOS projects in the future. --- .../JSONModel/JSONModel.h | 341 ---- .../JSONModel/JSONModel.m | 1386 ----------------- .../JSONModel/JSONModelClassProperty.h | 70 - .../JSONModel/JSONModelClassProperty.m | 59 - .../JSONModel/JSONModelError.h | 114 -- .../JSONModel/JSONModelError.m | 93 -- .../JSONModelTransformations/JSONKeyMapper.h | 102 -- .../JSONModelTransformations/JSONKeyMapper.m | 237 --- .../JSONValueTransformer.h | 220 --- .../JSONValueTransformer.m | 256 --- Spil/ActionHandler/SpilActionHandler.m | 191 ++- Spil/GameData/GameData.h | 22 +- Spil/GameData/GameData.m | 111 +- Spil/GameData/GameDataController.h | 1 - Spil/GameData/GameDataController.m | 44 +- Spil/GameData/bundles/Bundle.h | 17 +- Spil/GameData/bundles/Bundle.m | 69 +- Spil/GameData/bundles/BundleItem.h | 10 +- Spil/GameData/bundles/BundleItem.m | 14 +- Spil/GameData/bundles/BundlePrice.h | 10 +- Spil/GameData/bundles/BundlePrice.m | 14 +- Spil/GameData/currencies/Currency.h | 11 +- Spil/GameData/currencies/Currency.m | 25 +- Spil/GameData/items/Item.h | 11 +- Spil/GameData/items/Item.m | 23 +- Spil/GameData/shop/ShopEntry.h | 10 +- Spil/GameData/shop/ShopEntry.m | 22 +- Spil/GameData/shop/ShopPromotion.h | 14 +- Spil/GameData/shop/ShopPromotion.m | 47 +- Spil/GameData/shop/ShopTab.h | 14 +- Spil/GameData/shop/ShopTab.m | 35 +- Spil/HookBridge.h | 84 +- Spil/HookBridge.mm | 230 ++- Spil/PlayerData/Inventory/Inventory.h | 18 +- Spil/PlayerData/Inventory/Inventory.m | 66 +- Spil/PlayerData/Inventory/PlayerItem.h | 3 + Spil/PlayerData/Inventory/PlayerItem.m | 25 +- Spil/PlayerData/PlayerDataController.h | 10 +- Spil/PlayerData/PlayerDataController.m | 102 +- Spil/PlayerData/UserProfile.h | 13 +- Spil/PlayerData/UserProfile.m | 58 +- Spil/PlayerData/Wallet/PlayerCurrency.h | 8 +- Spil/PlayerData/Wallet/PlayerCurrency.m | 25 +- Spil/PlayerData/Wallet/Wallet.h | 18 +- Spil/PlayerData/Wallet/Wallet.m | 71 +- Spil/Spil.h | 267 +++- Spil/Spil.m | 393 ++++- Spil/UserHandler/SpilUserHandler.m | 8 +- Spil/Util/JsonUtil.h | 5 +- Spil/Util/JsonUtil.m | 23 + Spil/Util/NSArray+JSONModelExtensions.h | 15 - Spil/Util/NSArray+JSONModelExtensions.m | 20 - Spil/Util/NSString+Extensions.m | 2 +- SpilTV.xcodeproj/project.pbxproj | 64 - .../UserInterfaceState.xcuserstate | Bin 5643 -> 5659 bytes .../contents.xcworkspacedata | 0 .../UserInterfaceState.xcuserstate | Bin 0 -> 260242 bytes .../WorkspaceSettings.xcsettings | 0 .../xcdebugger/Breakpoints_v2.xcbkptlist | 0 .../WalletTestViewController.m | 10 +- .../UserInterfaceState.xcuserstate | Bin 259046 -> 0 bytes 61 files changed, 1700 insertions(+), 3431 deletions(-) delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModel.h delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModel.m delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.h delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.m delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelError.h delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelError.m delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.h delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.m delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.h delete mode 100755 Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.m delete mode 100644 Spil/Util/NSArray+JSONModelExtensions.h delete mode 100644 Spil/Util/NSArray+JSONModelExtensions.m rename {TestProjectWorkspace.xcworkspace => TVOS-Workspace.xcworkspace}/contents.xcworkspacedata (100%) create mode 100644 TVOS-Workspace.xcworkspace/xcuserdata/frankslofstra.xcuserdatad/UserInterfaceState.xcuserstate rename {TestProjectWorkspace.xcworkspace => TVOS-Workspace.xcworkspace}/xcuserdata/frankslofstra.xcuserdatad/WorkspaceSettings.xcsettings (100%) rename {TestProjectWorkspace.xcworkspace => TVOS-Workspace.xcworkspace}/xcuserdata/frankslofstra.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist (100%) delete mode 100644 TestProjectWorkspace.xcworkspace/xcuserdata/frankslofstra.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModel.h b/Spil/3rd_party_frameworks/JSONModel/JSONModel.h deleted file mode 100755 index 1525c47..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModel.h +++ /dev/null @@ -1,341 +0,0 @@ -// -// JSONModel.h -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import - -#import "JSONModelError.h" -#import "JSONValueTransformer.h" -#import "JSONKeyMapper.h" - -///////////////////////////////////////////////////////////////////////////////////////////// -#if TARGET_IPHONE_SIMULATOR -#define JMLog( s, ... ) NSLog( @"[%@:%d] %@", [[NSString stringWithUTF8String:__FILE__] \ -lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] ) -#else -#define JMLog( s, ... ) -#endif -///////////////////////////////////////////////////////////////////////////////////////////// - -#pragma mark - Property Protocols -/** - * Protocol for defining properties in a JSON Model class that should not be considered at all - * neither while importing nor when exporting JSON. - * - * @property (strong, nonatomic) NSString<Ignore>* propertyName; - * - */ -@protocol Ignore -@end - -/** - * Protocol for defining optional properties in a JSON Model class. Use like below to define - * model properties that are not required to have values in the JSON input: - * - * @property (strong, nonatomic) NSString<Optional>* propertyName; - * - */ -@protocol Optional -@end - -/** - * Protocol for defining index properties in a JSON Model class. Use like below to define - * model properties that are considered the Model's identifier (id). - * - * @property (strong, nonatomic) NSString<Index>* propertyName; - * - */ -@protocol Index -@end - -/** - * Make all objects Optional compatible to avoid compiler warnings - */ -@interface NSObject(JSONModelPropertyCompatibility) -@end - -// no longer used -__attribute__ ((deprecated)) -@protocol ConvertOnDemand -@end - -///////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - JSONModel protocol -/** - * A protocol describing an abstract JSONModel class - * JSONModel conforms to this protocol, so it can use itself abstractly - */ -@protocol AbstractJSONModelProtocol - -@required - /** - * All JSONModel classes should implement initWithDictionary: - * - * For most classes the default initWithDictionary: inherited from JSONModel itself - * should suffice, but developers have the option to also overwrite it if needed. - * - * @param dict a dictionary holding JSON objects, to be imported in the model. - * @param err an error or NULL - */ - -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError**)err; - - -/** - * All JSONModel classes should implement initWithData:error: - * - * For most classes the default initWithData: inherited from JSONModel itself - * should suffice, but developers have the option to also overwrite it if needed. - * - * @param data representing a JSON response (usually fetched from web), to be imported in the model. - * @param error an error or NULL - */ --(instancetype)initWithData:(NSData*)data error:(NSError**)error; - -/** - * All JSONModel classes should be able to export themselves as a dictionary of - * JSON compliant objects. - * - * For most classes the inherited from JSONModel default toDictionary implementation - * should suffice. - * - * @return NSDictionary dictionary of JSON compliant objects - * @exception JSONModelTypeNotAllowedException thrown when one of your model's custom class properties - * does not have matching transformer method in an JSONValueTransformer. - */ - -(NSDictionary*)toDictionary; - - /** - * Export a model class to a dictionary, including only given properties - * - * @param propertyNames the properties to export; if nil, all properties exported - * @return NSDictionary dictionary of JSON compliant objects - * @exception JSONModelTypeNotAllowedException thrown when one of your model's custom class properties - * does not have matching transformer method in an JSONValueTransformer. - */ - -(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames; -@end - -///////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - JSONModel interface -/** - * The JSONModel is an abstract model class, you should not instantiate it directly, - * as it does not have any properties, and therefore cannot serve as a data model. - * Instead you should subclass it, and define the properties you want your data model - * to have as properties of your own class. - */ -@interface JSONModel : NSObject - -/** @name Creating and initializing models */ - - /** - * Create a new model instance and initialize it with the JSON from a text parameter. The method assumes UTF8 encoded input text. - * @param string JSON text data - * @param err an initialization error or nil - * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON, - * or a property type in your model is not supported by JSONValueTransformer and its categories - * @see initWithString:usingEncoding:error: for use of custom text encodings - */ - -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err; - - /** - * Create a new model instance and initialize it with the JSON from a text parameter using the given encoding. - * @param string JSON text data - * @param encoding the text encoding to use when parsing the string (see NSStringEncoding) - * @param err an initialization error or nil - * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON, - * or a property type in your model is not supported by JSONValueTransformer and its categories - */ - -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err; - - -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err; - - -(instancetype)initWithData:(NSData *)data error:(NSError **)error; - -/** @name Exporting model contents */ - - /** - * Export the whole object to a dictionary - * @return dictionary containing the data model - */ - -(NSDictionary*)toDictionary; - - /** - * Export the whole object to a JSON data text string - * @return JSON text describing the data model - */ - -(NSString*)toJSONString; - - /** - * Export the whole object to a JSON data text string - * @return JSON text data describing the data model - */ - -(NSData*)toJSONData; - - /** - * Export the specified properties of the object to a dictionary - * @param propertyNames the properties to export; if nil, all properties exported - * @return dictionary containing the data model - */ - -(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames; - - /** - * Export the specified properties of the object to a JSON data text string - * @param propertyNames the properties to export; if nil, all properties exported - * @return JSON text describing the data model - */ - -(NSString*)toJSONStringWithKeys:(NSArray*)propertyNames; - - /** - * Export the specified properties of the object to a JSON data text string - * @param propertyNames the properties to export; if nil, all properties exported - * @return JSON text data describing the data model - */ - -(NSData*)toJSONDataWithKeys:(NSArray*)propertyNames; - -/** @name Batch methods */ - - /** - * If you have a list of dictionaries in a JSON feed, you can use this method to create an NSArray - * of model objects. Handy when importing JSON data lists. - * This method will loop over the input list and initialize a data model for every dictionary in the list. - * - * @param array list of dictionaries to be imported as models - * @return list of initialized data model objects - * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON, - * or a property type in your model is not supported by JSONValueTransformer and its categories - * @exception JSONModelInvalidDataException thrown when the input data does not include all required keys - * @see arrayOfDictionariesFromModels: - */ - +(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array __attribute__((deprecated("use arrayOfModelsFromDictionaries:error:"))); - +(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array error:(NSError**)err; - +(NSMutableArray*)arrayOfModelsFromData:(NSData*)data error:(NSError**)err; - +(NSMutableArray*)arrayOfModelsFromString:(NSString*)string error:(NSError**)err; - +(NSMutableDictionary*)dictionaryOfModelsFromDictionary:(NSDictionary*)dictionary error:(NSError**)err; - +(NSMutableDictionary*)dictionaryOfModelsFromData:(NSData*)data error:(NSError**)err; - +(NSMutableDictionary*)dictionaryOfModelsFromString:(NSString*)string error:(NSError**)err; - - /** - * If you have an NSArray of data model objects, this method takes it in and outputs a list of the - * matching dictionaries. This method does the opposite of arrayOfObjectsFromDictionaries: - * @param array list of JSONModel objects - * @return a list of NSDictionary objects - * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON, - * or a property type in your model is not supported by JSONValueTransformer and its categories - * @see arrayOfModelsFromDictionaries: - */ - +(NSMutableArray*)arrayOfDictionariesFromModels:(NSArray*)array; - +(NSMutableDictionary*)dictionaryOfDictionariesFromModels:(NSDictionary*)dictionary; - -/** @name Comparing models */ - - /** - * The name of the model's property, which is considered the model's unique identifier. - * You can define Index property by using the Index protocol: - * @property (strong, nonatomic) NSString<Index>* id; - */ - -(NSString*)indexPropertyName; - - /** - * Overridden NSObject method to compare model objects. Compares the <Index> property of the two models, - * if an index property is defined. - * @param object a JSONModel instance to compare to for equality - */ - -(BOOL)isEqual:(id)object; - - /** - * Comparison method, which uses the defined <Index> property of the two models, to compare them. - * If there isn't an index property throws an exception. If the Index property does not have a compare: method - * also throws an exception. NSString and NSNumber have compare: methods, and in case the Index property is - * a another custom class, the programmer should create a custom compare: method then. - * @param object a JSONModel instance to compare to - */ - -(NSComparisonResult)compare:(id)object; - -/** @name Validation */ - - /** - * Overwrite the validate method in your own models if you need to perform some custom validation over the model data. - * This method gets called at the very end of the JSONModel initializer, thus the model is in the state that you would - * get it back when initialized. Check the values of any property that needs to be validated and if any invalid values - * are encountered return NO and set the error parameter to an NSError object. If the model is valid return YES. - * - * NB: Only setting the error parameter is not enough to fail the validation, you also need to return a NO value. - * - * @param error a pointer to an NSError object, to pass back an error if needed - * @return a BOOL result, showing whether the model data validates or not. You can use the convenience method - * [JSONModelError errorModelIsInvalid] to set the NSError param if the data fails your custom validation - */ --(BOOL)validate:(NSError**)error; - -/** @name Key mapping */ - /** - * Overwrite in your models if your property names don't match your JSON key names. - * Lookup JSONKeyMapper docs for more details. - */ -+(JSONKeyMapper*)keyMapper; - -/** - * Sets a key mapper which affects ALL the models in your project. Use this if you need only one mapper to work - * with your API. For example if you are using the [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase] it is more - * likely that you will need to use it with ALL of your models. - * NB: Custom key mappers take precedence over the global key mapper. - * @param globalKeyMapper a key mapper to apply to all models in your project. - * - * Lookup JSONKeyMapper docs for more details. - */ -+(void)setGlobalKeyMapper:(JSONKeyMapper*)globalKeyMapper; - -/** - * Indicates whether the property with the given name is Optional. - * To have a model with all of its properties being Optional just return YES. - * This method returns by default NO, since the default behaviour is to have all properties required. - * @param propertyName the name of the property - * @return a BOOL result indicating whether the property is optional - */ -+(BOOL)propertyIsOptional:(NSString*)propertyName; - -/** - * Indicates whether the property with the given name is Ignored. - * To have a model with all of its properties being Ignored just return YES. - * This method returns by default NO, since the default behaviour is to have all properties required. - * @param propertyName the name of the property - * @return a BOOL result indicating whether the property is ignored - */ -+(BOOL)propertyIsIgnored:(NSString*)propertyName; - -/** - * Indicates the protocol name for an array property. - * Rather than using: - * @property (strong) NSArray* things; - * You can implement protocolForArrayProperty: and keep your property - * defined like: - * @property (strong) NSArray* things; - * @param propertyName the name of the property - * @return an NSString result indicating the name of the protocol/class - * that should be contained in this array property. Return nil to indicate - * no contained protocol. - */ -+(NSString*)protocolForArrayProperty:(NSString *)propertyName; - -/** - * Merges values from the given dictionary into the model instance. - * @param dict dictionary with values - * @param useKeyMapping if YES the method will use the model's key mapper and the global key mapper, if NO - * it'll just try to match the dictionary keys to the model's properties - */ -- (void)mergeFromDictionary:(NSDictionary *)dict useKeyMapping:(BOOL)useKeyMapping __attribute__((deprecated("use mergeFromDictionary:useKeyMapping:error:"))); -- (void)mergeFromDictionary:(NSDictionary *)dict useKeyMapping:(BOOL)useKeyMapping error:(NSError **)error; - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModel.m b/Spil/3rd_party_frameworks/JSONModel/JSONModel.m deleted file mode 100755 index c272879..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModel.m +++ /dev/null @@ -1,1386 +0,0 @@ -// -// JSONModel.m -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#if !__has_feature(objc_arc) -#error The JSONMOdel framework is ARC only, you can enable ARC on per file basis. -#endif - - -#import -#import - -#import "ShopEntry.h" -#import "JSONModel.h" -#import "JSONModelClassProperty.h" - -#pragma mark - associated objects names -static const char * kMapperObjectKey; -static const char * kClassPropertiesKey; -static const char * kClassRequiredPropertyNamesKey; -static const char * kIndexPropertyNameKey; - -#pragma mark - class static variables -static NSArray* allowedJSONTypes = nil; -static NSArray* allowedPrimitiveTypes = nil; -static JSONValueTransformer* valueTransformer = nil; -static Class JSONModelClass = NULL; - -#pragma mark - model cache -static JSONKeyMapper* globalKeyMapper = nil; - -#pragma mark - JSONModel implementation -@implementation JSONModel -{ - NSString* _description; -} - -#pragma mark - initialization methods - -+(void)load -{ - static dispatch_once_t once; - dispatch_once(&once, ^{ - // initialize all class static objects, - // which are common for ALL JSONModel subclasses - - @autoreleasepool { - allowedJSONTypes = @[ - [NSString class], [NSNumber class], [NSDecimalNumber class], [NSArray class], [NSDictionary class], [NSNull class], //immutable JSON classes - [NSMutableString class], [NSMutableArray class], [NSMutableDictionary class] //mutable JSON classes - ]; - - allowedPrimitiveTypes = @[ - @"BOOL", @"float", @"int", @"long", @"double", @"short", - //and some famous aliases - @"NSInteger", @"NSUInteger", - @"Block" - ]; - - valueTransformer = [[JSONValueTransformer alloc] init]; - - // This is quite strange, but I found the test isSubclassOfClass: (line ~291) to fail if using [JSONModel class]. - // somewhat related: https://stackoverflow.com/questions/6524165/nsclassfromstring-vs-classnamednsstring - // //; seems to break the unit tests - - // Using NSClassFromString instead of [JSONModel class], as this was breaking unit tests, see below - //http://stackoverflow.com/questions/21394919/xcode-5-unit-test-seeing-wrong-class - JSONModelClass = NSClassFromString(NSStringFromClass(self)); - } - }); -} - --(void)__setup__ -{ - //if first instance of this model, generate the property list - if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) { - [self __inspectProperties]; - } - - //if there's a custom key mapper, store it in the associated object - id mapper = [[self class] keyMapper]; - if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) { - objc_setAssociatedObject( - self.class, - &kMapperObjectKey, - mapper, - OBJC_ASSOCIATION_RETAIN // This is atomic - ); - } -} - --(id)init -{ - self = [super init]; - if (self) { - //do initial class setup - [self __setup__]; - } - return self; -} - --(instancetype)initWithData:(NSData *)data error:(NSError *__autoreleasing *)err -{ - //check for nil input - if (!data) { - if (err) *err = [JSONModelError errorInputIsNil]; - return nil; - } - //read the json - JSONModelError* initError = nil; - id obj = [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&initError]; - - if (initError) { - if (err) *err = [JSONModelError errorBadJSON]; - return nil; - } - - //init with dictionary - id objModel = [self initWithDictionary:obj error:&initError]; - if (initError && err) *err = initError; - return objModel; -} - --(id)initWithString:(NSString*)string error:(JSONModelError**)err { - JSONModelError* initError = nil; - id objModel = [self initWithString:string usingEncoding:NSUTF8StringEncoding error:&initError]; - if (initError && err) *err = initError; - return objModel; -} - --(id)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err -{ - //check for nil input - if (!string) { - if (err) *err = [JSONModelError errorInputIsNil]; - return nil; - } - - JSONModelError* initError = nil; - id objModel = [self initWithData:[string dataUsingEncoding:encoding] error:&initError]; - if (initError && err) *err = initError; - return objModel; - -} - --(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err -{ - //check for nil input - if (!dict) { - if (err) *err = [JSONModelError errorInputIsNil]; - return nil; - } - - //invalid input, just create empty instance - if (![dict isKindOfClass:[NSDictionary class]]) { - if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."]; - return nil; - } - - //create a class instance - self = [self init]; - if (!self) { - - //super init didn't succeed - if (err) *err = [JSONModelError errorModelIsInvalid]; - return nil; - } - - //check incoming data structure - if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) { - return nil; - } - - //import the data from a dictionary - if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) { - return nil; - } - - //run any custom model validation - if (![self validate:err]) { - return nil; - } - - //model is valid! yay! - return self; -} - --(JSONKeyMapper*)__keyMapper -{ - //get the model key mapper - return objc_getAssociatedObject(self.class, &kMapperObjectKey); -} - --(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err -{ - //check if all required properties are present - NSArray* incomingKeysArray = [dict allKeys]; - NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy; - NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray]; - - //transform the key names, if necessary - if (keyMapper || globalKeyMapper) { - - NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count]; - NSString* transformedName = nil; - - //loop over the required properties list - for (JSONModelClassProperty* property in [self __properties__]) { - - transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name; - - //check if exists and if so, add to incoming keys - id value; - @try { - value = [dict valueForKeyPath:transformedName]; - } - @catch (NSException *exception) { - value = dict[transformedName]; - } - - if (value) { - [transformedIncomingKeys addObject: property.name]; - } - } - - //overwrite the raw incoming list with the mapped key names - incomingKeys = transformedIncomingKeys; - } - - //check for missing input keys - if (![requiredProperties isSubsetOfSet:incomingKeys]) { - - //get a list of the missing properties - [requiredProperties minusSet:incomingKeys]; - - //not all required properties are in - invalid input - JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self.class, requiredProperties); - - if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties]; - return NO; - } - - //not needed anymore - incomingKeys= nil; - requiredProperties= nil; - - return YES; -} - --(NSString*)__mapString:(NSString*)string withKeyMapper:(JSONKeyMapper*)keyMapper importing:(BOOL)importing -{ - if (keyMapper) { - //custom mapper - NSString* mappedName = [keyMapper convertValue:string isImportingToModel:importing]; - if (globalKeyMapper && [mappedName isEqualToString: string]) { - mappedName = [globalKeyMapper convertValue:string isImportingToModel:importing]; - } - string = mappedName; - } else if (globalKeyMapper) { - //global keymapper - string = [globalKeyMapper convertValue:string isImportingToModel:importing]; - } - - return string; -} - --(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err -{ - //loop over the incoming keys and set self's properties - for (JSONModelClassProperty* property in [self __properties__]) { - - //convert key name to model keys, if a mapper is provided - NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name; - //JMLog(@"keyPath: %@", jsonKeyPath); - - //general check for data type compliance - id jsonValue; - @try { - jsonValue = [dict valueForKeyPath: jsonKeyPath]; - } - @catch (NSException *exception) { - jsonValue = dict[jsonKeyPath]; - } - - //check for Optional properties - if (isNull(jsonValue)) { - //skip this property, continue with next property - if (property.isOptional || !validation) continue; - - if (err) { - //null value for required property - NSString* msg = [NSString stringWithFormat:@"Value of required model key %@ is null", property.name]; - JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg]; - *err = [dataErr errorByPrependingKeyPathComponent:property.name]; - } - return NO; - } - - Class jsonValueClass = [jsonValue class]; - BOOL isValueOfAllowedType = NO; - - for (Class allowedType in allowedJSONTypes) { - if ( [jsonValueClass isSubclassOfClass: allowedType] ) { - isValueOfAllowedType = YES; - break; - } - } - - if (isValueOfAllowedType==NO) { - //type not allowed - JMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)); - - if (err) { - NSString* msg = [NSString stringWithFormat:@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)]; - JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg]; - *err = [dataErr errorByPrependingKeyPathComponent:property.name]; - } - return NO; - } - - //check if there's matching property in the model - if (property) { - - // check for custom setter, than the model doesn't need to do any guessing - // how to read the property's value from JSON - if ([self __customSetValue:jsonValue forProperty:property]) { - //skip to next JSON key - continue; - }; - - // 0) handle primitives - if (property.type == nil && property.structName==nil) { - - //generic setter - if (jsonValue != [self valueForKey:property.name]) { - [self setValue:jsonValue forKey: property.name]; - } - - //skip directly to the next key - continue; - } - - // 0.5) handle nils - if (isNull(jsonValue)) { - if ([self valueForKey:property.name] != nil) { - [self setValue:nil forKey: property.name]; - } - continue; - } - - - // 1) check if property is itself a JSONModel - if ([self __isJSONModelSubClass:property.type]) { - - //initialize the property's model, store it - JSONModelError* initErr = nil; - id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr]; - - if (!value) { - //skip this property, continue with next property - if (property.isOptional || !validation) continue; - - // Propagate the error, including the property name as the key-path component - if((err != nil) && (initErr != nil)) - { - *err = [initErr errorByPrependingKeyPathComponent:property.name]; - } - return NO; - } - if (![value isEqual:[self valueForKey:property.name]]) { - [self setValue:value forKey: property.name]; - } - - //for clarity, does the same without continue - continue; - - } else { - - // 2) check if there's a protocol to the property - // ) might or not be the case there's a built in transform for it - if (property.protocol) { - - //JMLog(@"proto: %@", p.protocol); - jsonValue = [self __transform:jsonValue forProperty:property error:err]; - if (!jsonValue) { - if ((err != nil) && (*err == nil)) { - NSString* msg = [NSString stringWithFormat:@"Failed to transform value, but no error was set during transformation. (%@)", property]; - JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg]; - *err = [dataErr errorByPrependingKeyPathComponent:property.name]; - } - return NO; - } - } - - // 3.1) handle matching standard JSON types - if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) { - - //mutable properties - if (property.isMutable) { - jsonValue = [jsonValue mutableCopy]; - } - - //set the property value - if (![jsonValue isEqual:[self valueForKey:property.name]]) { - [self setValue:jsonValue forKey: property.name]; - } - continue; - } - - // 3.3) handle values to transform - if ( - (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue)) - || - //the property is mutable - property.isMutable - || - //custom struct property - property.structName - ) { - - // searched around the web how to do this better - // but did not find any solution, maybe that's the best idea? (hardly) - Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]]; - - //JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName); - - //build a method selector for the property and json object classes - NSString* selectorName = [NSString stringWithFormat:@"%@From%@:", - (property.structName? property.structName : property.type), //target name - sourceClass]; //source name - SEL selector = NSSelectorFromString(selectorName); - - //check for custom transformer - BOOL foundCustomTransformer = NO; - if ([valueTransformer respondsToSelector:selector]) { - foundCustomTransformer = YES; - } else { - //try for hidden custom transformer - selectorName = [NSString stringWithFormat:@"__%@",selectorName]; - selector = NSSelectorFromString(selectorName); - if ([valueTransformer respondsToSelector:selector]) { - foundCustomTransformer = YES; - } - } - - //check if there's a transformer with that name - if (foundCustomTransformer) { - - //it's OK, believe me... -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - //transform the value - jsonValue = [valueTransformer performSelector:selector withObject:jsonValue]; -#pragma clang diagnostic pop - - if (![jsonValue isEqual:[self valueForKey:property.name]]) { - [self setValue:jsonValue forKey: property.name]; - } - - } else { - - // it's not a JSON data type, and there's no transformer for it - // if property type is not supported - that's a programmer mistake -> exception - @throw [NSException exceptionWithName:@"Type not allowed" - reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name] - userInfo:nil]; - return NO; - } - - } else { - // 3.4) handle "all other" cases (if any) - if (![jsonValue isEqual:[self valueForKey:property.name]]) { - [self setValue:jsonValue forKey: property.name]; - } - } - } - } - } - - return YES; -} - -#pragma mark - property inspection methods - --(BOOL)__isJSONModelSubClass:(Class)class -{ -// http://stackoverflow.com/questions/19883472/objc-nsobject-issubclassofclass-gives-incorrect-failure -#ifdef UNIT_TESTING - return [@"JSONModel" isEqualToString: NSStringFromClass([class superclass])]; -#else - return [class isSubclassOfClass:JSONModelClass]; -#endif -} - -//returns a set of the required keys for the model --(NSMutableSet*)__requiredPropertyNames -{ - //fetch the associated property names - NSMutableSet* classRequiredPropertyNames = objc_getAssociatedObject(self.class, &kClassRequiredPropertyNamesKey); - - if (!classRequiredPropertyNames) { - classRequiredPropertyNames = [NSMutableSet set]; - [[self __properties__] enumerateObjectsUsingBlock:^(JSONModelClassProperty* p, NSUInteger idx, BOOL *stop) { - if (!p.isOptional) [classRequiredPropertyNames addObject:p.name]; - }]; - - //persist the list - objc_setAssociatedObject( - self.class, - &kClassRequiredPropertyNamesKey, - classRequiredPropertyNames, - OBJC_ASSOCIATION_RETAIN // This is atomic - ); - } - return classRequiredPropertyNames; -} - -//returns a list of the model's properties --(NSArray*)__properties__ -{ - //fetch the associated object - NSDictionary* classProperties = objc_getAssociatedObject(self.class, &kClassPropertiesKey); - if (classProperties) return [classProperties allValues]; - - //if here, the class needs to inspect itself - [self __setup__]; - - //return the property list - classProperties = objc_getAssociatedObject(self.class, &kClassPropertiesKey); - return [classProperties allValues]; -} - -//inspects the class, get's a list of the class properties --(void)__inspectProperties -{ - //JMLog(@"Inspect class: %@", [self class]); - - NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary]; - - //temp variables for the loops - Class class = [self class]; - NSScanner* scanner = nil; - NSString* propertyType = nil; - - // inspect inherited properties up to the JSONModel class - while (class != [JSONModel class]) { - //JMLog(@"inspecting: %@", NSStringFromClass(class)); - - unsigned int propertyCount; - objc_property_t *properties = class_copyPropertyList(class, &propertyCount); - - //loop over the class properties - for (unsigned int i = 0; i < propertyCount; i++) { - - JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init]; - - //get property name - objc_property_t property = properties[i]; - const char *propertyName = property_getName(property); - p.name = @(propertyName); - - //JMLog(@"property: %@", p.name); - - //get property attributes - const char *attrs = property_getAttributes(property); - NSString* propertyAttributes = @(attrs); - NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","]; - - //ignore read-only properties - if ([attributeItems containsObject:@"R"]) { - continue; //to next property - } - - //check for 64b BOOLs - if ([propertyAttributes hasPrefix:@"Tc,"]) { - //mask BOOLs as structs so they can have custom converters - p.structName = @"BOOL"; - } - - scanner = [NSScanner scannerWithString: propertyAttributes]; - - //JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]); - [scanner scanUpToString:@"T" intoString: nil]; - [scanner scanString:@"T" intoString:nil]; - - //check if the property is an instance of a class - if ([scanner scanString:@"@\"" intoString: &propertyType]) { - - [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"] - intoString:&propertyType]; - - //JMLog(@"type: %@", propertyClassName); - p.type = NSClassFromString(propertyType); - p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound); - p.isStandardJSONType = [allowedJSONTypes containsObject:p.type]; - - //read through the property protocols - while ([scanner scanString:@"<" intoString:NULL]) { - - NSString* protocolName = nil; - - [scanner scanUpToString:@">" intoString: &protocolName]; - - if ([protocolName isEqualToString:@"Optional"]) { - p.isOptional = YES; - } else if([protocolName isEqualToString:@"Index"]) { - p.isIndex = YES; - objc_setAssociatedObject( - self.class, - &kIndexPropertyNameKey, - p.name, - OBJC_ASSOCIATION_RETAIN // This is atomic - ); - } else if([protocolName isEqualToString:@"Ignore"]) { - p = nil; - } else { - p.protocol = protocolName; - } - - [scanner scanString:@">" intoString:NULL]; - } - - } - //check if the property is a structure - else if ([scanner scanString:@"{" intoString: &propertyType]) { - [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet] - intoString:&propertyType]; - - p.isStandardJSONType = NO; - p.structName = propertyType; - - } - //the property must be a primitive - else { - - //the property contains a primitive data type - [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","] - intoString:&propertyType]; - - //get the full name of the primitive type - propertyType = valueTransformer.primitivesNames[propertyType]; - - if (![allowedPrimitiveTypes containsObject:propertyType]) { - - //type not allowed - programmer mistaken -> exception - @throw [NSException exceptionWithName:@"JSONModelProperty type not allowed" - reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name] - userInfo:nil]; - } - - } - - NSString *nsPropertyName = @(propertyName); - if([[self class] propertyIsOptional:nsPropertyName]){ - p.isOptional = YES; - } - - if([[self class] propertyIsIgnored:nsPropertyName]){ - p = nil; - } - - NSString* customProtocol = [[self class] protocolForArrayProperty:nsPropertyName]; - if (customProtocol) { - p.protocol = customProtocol; - } - - //few cases where JSONModel will ignore properties automatically - if ([propertyType isEqualToString:@"Block"]) { - p = nil; - } - - //add the property object to the temp index - if (p && ![propertyIndex objectForKey:p.name]) { - [propertyIndex setValue:p forKey:p.name]; - } - } - - free(properties); - - //ascend to the super of the class - //(will do that until it reaches the root class - JSONModel) - class = [class superclass]; - } - - //finally store the property index in the static property index - objc_setAssociatedObject( - self.class, - &kClassPropertiesKey, - [propertyIndex copy], - OBJC_ASSOCIATION_RETAIN // This is atomic - ); -} - -#pragma mark - built-in transformer methods -//few built-in transformations --(id)__transform:(id)value forProperty:(JSONModelClassProperty*)property error:(NSError**)err -{ - Class protocolClass = NSClassFromString(property.protocol); - if (!protocolClass) { - - //no other protocols on arrays and dictionaries - //except JSONModel classes - if ([value isKindOfClass:[NSArray class]]) { - @throw [NSException exceptionWithName:@"Bad property protocol declaration" - reason:[NSString stringWithFormat:@"<%@> is not allowed JSONModel property protocol, and not a JSONModel class.", property.protocol] - userInfo:nil]; - } - return value; - } - - //if the protocol is actually a JSONModel class - if ([self __isJSONModelSubClass:protocolClass]) { - - //check if it's a list of models - if ([property.type isSubclassOfClass:[NSArray class]]) { - - // Expecting an array, make sure 'value' is an array - if(![[value class] isSubclassOfClass:[NSArray class]]) - { - if(err != nil) - { - NSString* mismatch = [NSString stringWithFormat:@"Property '%@' is declared as NSArray<%@>* but the corresponding JSON value is not a JSON Array.", property.name, property.protocol]; - JSONModelError* typeErr = [JSONModelError errorInvalidDataWithTypeMismatch:mismatch]; - *err = [typeErr errorByPrependingKeyPathComponent:property.name]; - } - return nil; - } - - //one shot conversion - JSONModelError* arrayErr = nil; - value = [[protocolClass class] arrayOfModelsFromDictionaries:value error:&arrayErr]; - if((err != nil) && (arrayErr != nil)) - { - *err = [arrayErr errorByPrependingKeyPathComponent:property.name]; - return nil; - } - } - - //check if it's a dictionary of models - if ([property.type isSubclassOfClass:[NSDictionary class]]) { - - // Expecting a dictionary, make sure 'value' is a dictionary - if(![[value class] isSubclassOfClass:[NSDictionary class]]) - { - if(err != nil) - { - NSString* mismatch = [NSString stringWithFormat:@"Property '%@' is declared as NSDictionary<%@>* but the corresponding JSON value is not a JSON Object.", property.name, property.protocol]; - JSONModelError* typeErr = [JSONModelError errorInvalidDataWithTypeMismatch:mismatch]; - *err = [typeErr errorByPrependingKeyPathComponent:property.name]; - } - return nil; - } - - NSMutableDictionary* res = [NSMutableDictionary dictionary]; - - for (NSString* key in [value allKeys]) { - JSONModelError* initErr = nil; - id obj = [[[protocolClass class] alloc] initWithDictionary:value[key] error:&initErr]; - if (obj == nil) - { - // Propagate the error, including the property name as the key-path component - if((err != nil) && (initErr != nil)) - { - initErr = [initErr errorByPrependingKeyPathComponent:key]; - *err = [initErr errorByPrependingKeyPathComponent:property.name]; - } - return nil; - } - [res setValue:obj forKey:key]; - } - value = [NSDictionary dictionaryWithDictionary:res]; - } - } - - return value; -} - -//built-in reverse transformations (export to JSON compliant objects) --(id)__reverseTransform:(id)value forProperty:(JSONModelClassProperty*)property -{ - Class protocolClass = NSClassFromString(property.protocol); - if (!protocolClass) return value; - - //if the protocol is actually a JSONModel class - if ([self __isJSONModelSubClass:protocolClass]) { - - //check if should export list of dictionaries - if (property.type == [NSArray class] || property.type == [NSMutableArray class]) { - NSMutableArray* tempArray = [NSMutableArray arrayWithCapacity: [(NSArray*)value count] ]; - for (NSObject* model in (NSArray*)value) { - if ([model respondsToSelector:@selector(toDictionary)]) { - [tempArray addObject: [model toDictionary]]; - } else - [tempArray addObject: model]; - } - return [tempArray copy]; - } - - //check if should export dictionary of dictionaries - if (property.type == [NSDictionary class] || property.type == [NSMutableDictionary class]) { - NSMutableDictionary* res = [NSMutableDictionary dictionary]; - for (NSString* key in [(NSDictionary*)value allKeys]) { - id model = value[key]; - [res setValue: [model toDictionary] forKey: key]; - } - return [NSDictionary dictionaryWithDictionary:res]; - } - } - - return value; -} - -#pragma mark - custom transformations --(BOOL)__customSetValue:(id)value forProperty:(JSONModelClassProperty*)property -{ - if (!property.customSetters) - property.customSetters = [NSMutableDictionary new]; - - NSString *className = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:[value class]]); - - if (!property.customSetters[className]) { - //check for a custom property setter method - NSString* ucfirstName = [property.name stringByReplacingCharactersInRange:NSMakeRange(0,1) - withString:[[property.name substringToIndex:1] uppercaseString]]; - NSString* selectorName = [NSString stringWithFormat:@"set%@With%@:", ucfirstName, className]; - - SEL customPropertySetter = NSSelectorFromString(selectorName); - - //check if there's a custom selector like this - if (![self respondsToSelector: customPropertySetter]) { - property.customSetters[className] = [NSNull null]; - return NO; - } - - //cache the custom setter selector - property.customSetters[className] = selectorName; - } - - if (property.customSetters[className] != [NSNull null]) { - //call the custom setter - //https://github.com/steipete - SEL selector = NSSelectorFromString(property.customSetters[className]); - ((void (*) (id, SEL, id))objc_msgSend)(self, selector, value); - return YES; - } - - return NO; -} - --(BOOL)__customGetValue:(id*)value forProperty:(JSONModelClassProperty*)property -{ - if (property.getterType == kNotInspected) { - //check for a custom property getter method - NSString* ucfirstName = [property.name stringByReplacingCharactersInRange: NSMakeRange(0,1) - withString: [[property.name substringToIndex:1] uppercaseString]]; - NSString* selectorName = [NSString stringWithFormat:@"JSONObjectFor%@", ucfirstName]; - - SEL customPropertyGetter = NSSelectorFromString(selectorName); - if (![self respondsToSelector: customPropertyGetter]) { - property.getterType = kNo; - return NO; - } - - property.getterType = kCustom; - property.customGetter = customPropertyGetter; - - } - - if (property.getterType==kCustom) { - //call the custom getter - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - *value = [self performSelector:property.customGetter]; - #pragma clang diagnostic pop - return YES; - } - - return NO; -} - -#pragma mark - persistance --(void)__createDictionariesForKeyPath:(NSString*)keyPath inDictionary:(NSMutableDictionary**)dict -{ - //find if there's a dot left in the keyPath - NSUInteger dotLocation = [keyPath rangeOfString:@"."].location; - if (dotLocation==NSNotFound) return; - - //inspect next level - NSString* nextHierarchyLevelKeyName = [keyPath substringToIndex: dotLocation]; - NSDictionary* nextLevelDictionary = (*dict)[nextHierarchyLevelKeyName]; - - if (nextLevelDictionary==nil) { - //create non-existing next level here - nextLevelDictionary = [NSMutableDictionary dictionary]; - } - - //recurse levels - [self __createDictionariesForKeyPath:[keyPath substringFromIndex: dotLocation+1] - inDictionary:&nextLevelDictionary ]; - - //create the hierarchy level - [*dict setValue:nextLevelDictionary forKeyPath: nextHierarchyLevelKeyName]; -} - --(NSDictionary*)toDictionary -{ - return [self toDictionaryWithKeys:nil]; -} - --(NSString*)toJSONString -{ - return [self toJSONStringWithKeys:nil]; -} - --(NSData*)toJSONData -{ - return [self toJSONDataWithKeys:nil]; -} - -//exports the model as a dictionary of JSON compliant objects --(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames -{ - NSArray* properties = [self __properties__]; - NSMutableDictionary* tempDictionary = [NSMutableDictionary dictionaryWithCapacity:properties.count]; - - id value; - - //loop over all properties - for (JSONModelClassProperty* p in properties) { - - //skip if unwanted - if (propertyNames != nil && ![propertyNames containsObject:p.name]) - continue; - - //fetch key and value - NSString* keyPath = (self.__keyMapper||globalKeyMapper) ? [self __mapString:p.name withKeyMapper:self.__keyMapper importing:YES] : p.name; - value = [self valueForKey: p.name]; - - //JMLog(@"toDictionary[%@]->[%@] = '%@'", p.name, keyPath, value); - - if ([keyPath rangeOfString:@"."].location != NSNotFound) { - //there are sub-keys, introduce dictionaries for them - [self __createDictionariesForKeyPath:keyPath inDictionary:&tempDictionary]; - } - - //check for custom getter - if ([self __customGetValue:&value forProperty:p]) { - //custom getter, all done - [tempDictionary setValue:value forKeyPath:keyPath]; - continue; - } - - //export nil when they are not optional values as JSON null, so that the structure of the exported data - //is still valid if it's to be imported as a model again - if (isNull(value)) { - - if (p.isOptional) - { - [tempDictionary removeObjectForKey:keyPath]; - } - else - { - [tempDictionary setValue:[NSNull null] forKeyPath:keyPath]; - } - continue; - } - - //check if the property is another model - if ([value isKindOfClass:JSONModelClass]) { - - //recurse models - value = [(JSONModel*)value toDictionary]; - [tempDictionary setValue:value forKeyPath: keyPath]; - - //for clarity - continue; - - } else { - - // 1) check for built-in transformation - if (p.protocol) { - value = [self __reverseTransform:value forProperty:p]; - } - - // 2) check for standard types OR 2.1) primitives - if (p.structName==nil && (p.isStandardJSONType || p.type==nil)) { - - //generic get value - [tempDictionary setValue:value forKeyPath: keyPath]; - - continue; - } - - // 3) try to apply a value transformer - if (YES) { - - //create selector from the property's class name - NSString* selectorName = [NSString stringWithFormat:@"%@From%@:", @"JSONObject", p.type?p.type:p.structName]; - SEL selector = NSSelectorFromString(selectorName); - - BOOL foundCustomTransformer = NO; - if ([valueTransformer respondsToSelector:selector]) { - foundCustomTransformer = YES; - } else { - //try for hidden transformer - selectorName = [NSString stringWithFormat:@"__%@",selectorName]; - selector = NSSelectorFromString(selectorName); - if ([valueTransformer respondsToSelector:selector]) { - foundCustomTransformer = YES; - } - } - - //check if there's a transformer declared - if (foundCustomTransformer) { - - //it's OK, believe me... -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - value = [valueTransformer performSelector:selector withObject:value]; -#pragma clang diagnostic pop - - [tempDictionary setValue:value forKeyPath: keyPath]; - - } else { - - //in this case most probably a custom property was defined in a model - //but no default reverse transformer for it - @throw [NSException exceptionWithName:@"Value transformer not found" - reason:[NSString stringWithFormat:@"[JSONValueTransformer %@] not found", selectorName] - userInfo:nil]; - return nil; - } - } - } - } - - return [tempDictionary copy]; -} - -//exports model to a dictionary and then to a JSON string --(NSData*)toJSONDataWithKeys:(NSArray*)propertyNames -{ - NSData* jsonData = nil; - NSError* jsonError = nil; - - @try { - NSDictionary* dict = [self toDictionaryWithKeys:propertyNames]; - jsonData = [NSJSONSerialization dataWithJSONObject:dict options:kNilOptions error:&jsonError]; - } - @catch (NSException *exception) { - //this should not happen in properly design JSONModel - //usually means there was no reverse transformer for a custom property - NSLog(@"EXCEPTION: %@", exception.description); - return nil; - } - - return jsonData; -} - --(NSString*)toJSONStringWithKeys:(NSArray*)propertyNames -{ - return [[NSString alloc] initWithData: [self toJSONDataWithKeys: propertyNames] - encoding: NSUTF8StringEncoding]; -} - -#pragma mark - import/export of lists -//loop over an NSArray of JSON objects and turn them into models -+(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array -{ - return [self arrayOfModelsFromDictionaries:array error:nil]; -} - -+ (NSMutableArray *)arrayOfModelsFromData:(NSData *)data error:(NSError **)err -{ - id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:err]; - if (!json || ![json isKindOfClass:[NSArray class]]) return nil; - - return [self arrayOfModelsFromDictionaries:json error:err]; -} - -+ (NSMutableArray *)arrayOfModelsFromString:(NSString *)string error:(NSError **)err -{ - return [self arrayOfModelsFromData:[string dataUsingEncoding:NSUTF8StringEncoding] error:err]; -} - -// Same as above, but with error reporting -+(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array error:(NSError**)err -{ - //bail early - if (isNull(array)) return nil; - - //parse dictionaries to objects - NSMutableArray* list = [NSMutableArray arrayWithCapacity: [array count]]; - - for (id d in array) - { - if ([d isKindOfClass:NSDictionary.class]) - { - JSONModelError* initErr = nil; - id obj = [[self alloc] initWithDictionary:d error:&initErr]; - if (obj == nil) - { - // Propagate the error, including the array index as the key-path component - if((err != nil) && (initErr != nil)) - { - NSString* path = [NSString stringWithFormat:@"[%lu]", (unsigned long)list.count]; - *err = [initErr errorByPrependingKeyPathComponent:path]; - } - return nil; - } - - [list addObject: obj]; - } else if ([d isKindOfClass:NSArray.class]) - { - [list addObjectsFromArray:[self arrayOfModelsFromDictionaries:d error:err]]; - } else - { - // This is very bad - } - - } - - return list; -} - -+ (NSMutableDictionary *)dictionaryOfModelsFromString:(NSString *)string error:(NSError **)err -{ - return [self dictionaryOfModelsFromData:[string dataUsingEncoding:NSUTF8StringEncoding] error:err]; -} - -+ (NSMutableDictionary *)dictionaryOfModelsFromData:(NSData *)data error:(NSError **)err -{ - id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:err]; - if (!json || ![json isKindOfClass:[NSDictionary class]]) return nil; - - return [self dictionaryOfModelsFromDictionary:json error:err]; -} - -+ (NSMutableDictionary *)dictionaryOfModelsFromDictionary:(NSDictionary *)dictionary error:(NSError **)err -{ - NSMutableDictionary *output = [NSMutableDictionary dictionaryWithCapacity:dictionary.count]; - - for (NSString *key in dictionary.allKeys) - { - id object = dictionary[key]; - - if ([object isKindOfClass:NSDictionary.class]) - { - id obj = [[self alloc] initWithDictionary:object error:err]; - if (obj == nil) return nil; - output[key] = obj; - } - else if ([object isKindOfClass:NSArray.class]) - { - id obj = [self arrayOfModelsFromDictionaries:object error:err]; - if (obj == nil) return nil; - output[key] = obj; - } - else - { - *err = [JSONModelError errorInvalidDataWithTypeMismatch:@"Only dictionaries and arrays are supported"]; - return nil; - } - } - - return output; -} - -//loop over NSArray of models and export them to JSON objects -+(NSMutableArray*)arrayOfDictionariesFromModels:(NSArray*)array -{ - //bail early - if (isNull(array)) return nil; - - //convert to dictionaries - NSMutableArray* list = [NSMutableArray arrayWithCapacity: [array count]]; - - for (id object in array) { - - id obj = [object toDictionary]; - if (!obj) return nil; - - [list addObject: obj]; - } - return list; -} - -//loop over NSArray of models and export them to JSON objects with specific properties -+(NSMutableArray*)arrayOfDictionariesFromModels:(NSArray*)array propertyNamesToExport:(NSArray*)propertyNamesToExport; -{ - //bail early - if (isNull(array)) return nil; - - //convert to dictionaries - NSMutableArray* list = [NSMutableArray arrayWithCapacity: [array count]]; - - for (id object in array) { - - id obj = [object toDictionaryWithKeys:propertyNamesToExport]; - if (!obj) return nil; - - [list addObject: obj]; - } - return list; -} - -+(NSMutableDictionary *)dictionaryOfDictionariesFromModels:(NSDictionary *)dictionary -{ - //bail early - if (isNull(dictionary)) return nil; - - NSMutableDictionary *output = [NSMutableDictionary dictionaryWithCapacity:dictionary.count]; - - for (NSString *key in dictionary.allKeys) { - id object = dictionary[key]; - id obj = [object toDictionary]; - if (!obj) return nil; - output[key] = obj; - } - - return output; -} - -#pragma mark - custom comparison methods --(NSString*)indexPropertyName -{ - //custom getter for an associated object - return objc_getAssociatedObject(self.class, &kIndexPropertyNameKey); -} - --(BOOL)isEqual:(id)object -{ - //bail early if different classes - if (![object isMemberOfClass:[self class]]) return NO; - - if (self.indexPropertyName) { - //there's a defined ID property - id objectId = [object valueForKey: self.indexPropertyName]; - return [[self valueForKey: self.indexPropertyName] isEqual:objectId]; - } - - //default isEqual implementation - return [super isEqual:object]; -} - --(NSComparisonResult)compare:(id)object -{ - if (self.indexPropertyName) { - id objectId = [object valueForKey: self.indexPropertyName]; - if ([objectId respondsToSelector:@selector(compare:)]) { - return [[self valueForKey:self.indexPropertyName] compare:objectId]; - } - } - - //on purpose postponing the asserts for speed optimization - //these should not happen anyway in production conditions - NSAssert(self.indexPropertyName, @"Can't compare models with no property"); - NSAssert1(NO, @"The property of %@ is not comparable class.", [self class]); - return kNilOptions; -} - -- (NSUInteger)hash -{ - if (self.indexPropertyName) { - id val = [self valueForKey:self.indexPropertyName]; - - if (val) { - return [val hash]; - } - } - - return [super hash]; -} - -#pragma mark - custom data validation --(BOOL)validate:(NSError**)error -{ - return YES; -} - -#pragma mark - custom recursive description -//custom description method for debugging purposes --(NSString*)description -{ - NSMutableString* text = [NSMutableString stringWithFormat:@"<%@> \n", [self class]]; - - for (JSONModelClassProperty *p in [self __properties__]) { - - id value = ([p.name isEqualToString:@"description"])?self->_description:[self valueForKey:p.name]; - NSString* valueDescription = (value)?[value description]:@""; - - if (p.isStandardJSONType && ![value respondsToSelector:@selector(count)] && [valueDescription length]>60) { - - //cap description for longer values - valueDescription = [NSString stringWithFormat:@"%@...", [valueDescription substringToIndex:59]]; - } - valueDescription = [valueDescription stringByReplacingOccurrencesOfString:@"\n" withString:@"\n "]; - [text appendFormat:@" [%@]: %@\n", p.name, valueDescription]; - } - - [text appendFormat:@"", [self class]]; - return text; -} - -#pragma mark - key mapping -+(JSONKeyMapper*)keyMapper -{ - return nil; -} - -+(void)setGlobalKeyMapper:(JSONKeyMapper*)globalKeyMapperParam -{ - globalKeyMapper = globalKeyMapperParam; -} - -+(BOOL)propertyIsOptional:(NSString*)propertyName -{ - return NO; -} - -+(BOOL)propertyIsIgnored:(NSString *)propertyName -{ - return NO; -} - -+(NSString*)protocolForArrayProperty:(NSString *)propertyName -{ - return nil; -} - -#pragma mark - working with incomplete models -- (void)mergeFromDictionary:(NSDictionary *)dict useKeyMapping:(BOOL)useKeyMapping -{ - [self mergeFromDictionary:dict useKeyMapping:useKeyMapping error:nil]; -} - -- (void)mergeFromDictionary:(NSDictionary *)dict useKeyMapping:(BOOL)useKeyMapping error:(NSError **)error -{ - [self __importDictionary:dict withKeyMapper:(useKeyMapping)? self.__keyMapper:nil validation:NO error:error]; -} - -#pragma mark - NSCopying, NSCoding --(instancetype)copyWithZone:(NSZone *)zone -{ - return [NSKeyedUnarchiver unarchiveObjectWithData: - [NSKeyedArchiver archivedDataWithRootObject:self] - ]; -} - --(instancetype)initWithCoder:(NSCoder *)decoder -{ - NSString* json = [decoder decodeObjectForKey:@"json"]; - - JSONModelError *error = nil; - self = [self initWithString:json error:&error]; - if (error) { - JMLog(@"%@",[error localizedDescription]); - } - return self; -} - --(void)encodeWithCoder:(NSCoder *)encoder -{ - [encoder encodeObject:self.toJSONString forKey:@"json"]; -} - -+ (BOOL)supportsSecureCoding -{ - return YES; -} - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.h b/Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.h deleted file mode 100755 index 45d7aa5..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.h +++ /dev/null @@ -1,70 +0,0 @@ -// -// JSONModelClassProperty.h -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import - -enum kCustomizationTypes { - kNotInspected = 0, - kCustom, - kNo - }; - -typedef enum kCustomizationTypes PropertyGetterType; - -/** - * **You do not need to instantiate this class yourself.** This class is used internally by JSONModel - * to inspect the declared properties of your model class. - * - * Class to contain the information, representing a class property - * It features the property's name, type, whether it's a required property, - * and (optionally) the class protocol - */ -@interface JSONModelClassProperty : NSObject - -/** The name of the declared property (not the ivar name) */ -@property (copy, nonatomic) NSString* name; - -/** A property class type */ -@property (assign, nonatomic) Class type; - -/** Struct name if a struct */ -@property (strong, nonatomic) NSString* structName; - -/** The name of the protocol the property conforms to (or nil) */ -@property (copy, nonatomic) NSString* protocol; - -/** If YES, it can be missing in the input data, and the input would be still valid */ -@property (assign, nonatomic) BOOL isOptional; - -/** If YES - don't call any transformers on this property's value */ -@property (assign, nonatomic) BOOL isStandardJSONType; - -/** If YES - create a mutable object for the value of the property */ -@property (assign, nonatomic) BOOL isMutable; - -/** If YES - the value of this property determines equality to other models */ -@property (assign, nonatomic) BOOL isIndex; - -/** The status of property getter introspection in a model */ -@property (assign, nonatomic) PropertyGetterType getterType; - -/** a custom getter for this property, found in the owning model */ -@property (assign, nonatomic) SEL customGetter; - -/** custom setters for this property, found in the owning model */ -@property (strong, nonatomic) NSMutableDictionary *customSetters; - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.m b/Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.m deleted file mode 100755 index e63bc86..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelClassProperty.m +++ /dev/null @@ -1,59 +0,0 @@ -// -// JSONModelClassProperty.m -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import "JSONModelClassProperty.h" - -@implementation JSONModelClassProperty - --(NSString*)description -{ - //build the properties string for the current class property - NSMutableArray* properties = [NSMutableArray arrayWithCapacity:8]; - - if (self.isIndex) [properties addObject:@"Index"]; - if (self.isOptional) [properties addObject:@"Optional"]; - if (self.isMutable) [properties addObject:@"Mutable"]; - if (self.isStandardJSONType) [properties addObject:@"Standard JSON type"]; - if (self.customGetter) [properties addObject:[NSString stringWithFormat: @"Getter = %@", NSStringFromSelector(self.customGetter)]]; - - if (self.customSetters) - { - NSMutableArray *setters = [NSMutableArray array]; - - for (id obj in self.customSetters.allValues) - { - if (obj != [NSNull null]) - [setters addObject:obj]; - } - - [properties addObject:[NSString stringWithFormat: @"Setters = [%@]", [setters componentsJoinedByString:@", "]]]; - } - - NSString* propertiesString = @""; - if (properties.count>0) { - propertiesString = [NSString stringWithFormat:@"(%@)", [properties componentsJoinedByString:@", "]]; - } - - //return the name, type and additional properties - return [NSString stringWithFormat:@"@property %@%@ %@ %@", - self.type?[NSString stringWithFormat:@"%@*",self.type]:(self.structName?self.structName:@"primitive"), - self.protocol?[NSString stringWithFormat:@"<%@>", self.protocol]:@"", - self.name, - propertiesString - ]; -} - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelError.h b/Spil/3rd_party_frameworks/JSONModel/JSONModelError.h deleted file mode 100755 index 10c6b0f..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelError.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// JSONModelError.h -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import - -///////////////////////////////////////////////////////////////////////////////////////////// -typedef NS_ENUM(int, kJSONModelErrorTypes) -{ - kJSONModelErrorInvalidData = 1, - kJSONModelErrorBadResponse = 2, - kJSONModelErrorBadJSON = 3, - kJSONModelErrorModelIsInvalid = 4, - kJSONModelErrorNilInput = 5 -}; - -///////////////////////////////////////////////////////////////////////////////////////////// -/** The domain name used for the JSONModelError instances */ -extern NSString* const JSONModelErrorDomain; - -/** - * If the model JSON input misses keys that are required, check the - * userInfo dictionary of the JSONModelError instance you get back - - * under the kJSONModelMissingKeys key you will find a list of the - * names of the missing keys. - */ -extern NSString* const kJSONModelMissingKeys; - -/** - * If JSON input has a different type than expected by the model, check the - * userInfo dictionary of the JSONModelError instance you get back - - * under the kJSONModelTypeMismatch key you will find a description - * of the mismatched types. - */ -extern NSString* const kJSONModelTypeMismatch; - -/** - * If an error occurs in a nested model, check the userInfo dictionary of - * the JSONModelError instance you get back - under the kJSONModelKeyPath - * key you will find key-path at which the error occurred. - */ -extern NSString* const kJSONModelKeyPath; - -///////////////////////////////////////////////////////////////////////////////////////////// -/** - * Custom NSError subclass with shortcut methods for creating - * the common JSONModel errors - */ -@interface JSONModelError : NSError - -@property (strong, nonatomic) NSHTTPURLResponse* httpResponse; - -@property (strong, nonatomic) NSData* responseData; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorInvalidData = 1 - */ -+(id)errorInvalidDataWithMessage:(NSString*)message; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorInvalidData = 1 - * @param keys a set of field names that were required, but not found in the input - */ -+(id)errorInvalidDataWithMissingKeys:(NSSet*)keys; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorInvalidData = 1 - * @param mismatchDescription description of the type mismatch that was encountered. - */ -+(id)errorInvalidDataWithTypeMismatch:(NSString*)mismatchDescription; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorBadResponse = 2 - */ -+(id)errorBadResponse; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorBadJSON = 3 - */ -+(id)errorBadJSON; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorModelIsInvalid = 4 - */ -+(id)errorModelIsInvalid; - -/** - * Creates a JSONModelError instance with code kJSONModelErrorNilInput = 5 - */ -+(id)errorInputIsNil; - -/** - * Creates a new JSONModelError with the same values plus information about the key-path of the error. - * Properties in the new error object are the same as those from the receiver, - * except that a new key kJSONModelKeyPath is added to the userInfo dictionary. - * This key contains the component string parameter. If the key is already present - * then the new error object has the component string prepended to the existing value. - */ -- (instancetype)errorByPrependingKeyPathComponent:(NSString*)component; - -///////////////////////////////////////////////////////////////////////////////////////////// -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelError.m b/Spil/3rd_party_frameworks/JSONModel/JSONModelError.m deleted file mode 100755 index ffbf2fb..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelError.m +++ /dev/null @@ -1,93 +0,0 @@ -// -// JSONModelError.m -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import "JSONModelError.h" - -NSString* const JSONModelErrorDomain = @"JSONModelErrorDomain"; -NSString* const kJSONModelMissingKeys = @"kJSONModelMissingKeys"; -NSString* const kJSONModelTypeMismatch = @"kJSONModelTypeMismatch"; -NSString* const kJSONModelKeyPath = @"kJSONModelKeyPath"; - -@implementation JSONModelError - -+(id)errorInvalidDataWithMessage:(NSString*)message -{ - message = [NSString stringWithFormat:@"Invalid JSON data: %@", message]; - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorInvalidData - userInfo:@{NSLocalizedDescriptionKey:message}]; -} - -+(id)errorInvalidDataWithMissingKeys:(NSSet *)keys -{ - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorInvalidData - userInfo:@{NSLocalizedDescriptionKey:@"Invalid JSON data. Required JSON keys are missing from the input. Check the error user information.",kJSONModelMissingKeys:[keys allObjects]}]; -} - -+(id)errorInvalidDataWithTypeMismatch:(NSString*)mismatchDescription -{ - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorInvalidData - userInfo:@{NSLocalizedDescriptionKey:@"Invalid JSON data. The JSON type mismatches the expected type. Check the error user information.",kJSONModelTypeMismatch:mismatchDescription}]; -} - -+(id)errorBadResponse -{ - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorBadResponse - userInfo:@{NSLocalizedDescriptionKey:@"Bad network response. Probably the JSON URL is unreachable."}]; -} - -+(id)errorBadJSON -{ - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorBadJSON - userInfo:@{NSLocalizedDescriptionKey:@"Malformed JSON. Check the JSONModel data input."}]; -} - -+(id)errorModelIsInvalid -{ - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorModelIsInvalid - userInfo:@{NSLocalizedDescriptionKey:@"Model does not validate. The custom validation for the input data failed."}]; -} - -+(id)errorInputIsNil -{ - return [JSONModelError errorWithDomain:JSONModelErrorDomain - code:kJSONModelErrorNilInput - userInfo:@{NSLocalizedDescriptionKey:@"Initializing model with nil input object."}]; -} - -- (instancetype)errorByPrependingKeyPathComponent:(NSString*)component -{ - // Create a mutable copy of the user info so that we can add to it and update it - NSMutableDictionary* userInfo = [self.userInfo mutableCopy]; - - // Create or update the key-path - NSString* existingPath = userInfo[kJSONModelKeyPath]; - NSString* separator = [existingPath hasPrefix:@"["] ? @"" : @"."; - NSString* updatedPath = (existingPath == nil) ? component : [component stringByAppendingFormat:@"%@%@", separator, existingPath]; - userInfo[kJSONModelKeyPath] = updatedPath; - - // Create the new error - return [JSONModelError errorWithDomain:self.domain - code:self.code - userInfo:[NSDictionary dictionaryWithDictionary:userInfo]]; -} - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.h b/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.h deleted file mode 100755 index e022e5b..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.h +++ /dev/null @@ -1,102 +0,0 @@ -// -// JSONKeyMapper.h -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import - -typedef NSString* (^JSONModelKeyMapBlock)(NSString* keyName); - -/** - * **You won't need to create or store instances of this class yourself.** If you want your model - * to have different property names than the JSON feed keys, look below on how to - * make your model use a key mapper. - * - * For example if you consume JSON from twitter - * you get back underscore_case style key names. For example: - * - *
"profile_sidebar_border_color": "0094C2",
- * "profile_background_tile": false,
- * - * To comply with Obj-C accepted camelCase property naming for your classes, - * you need to provide mapping between JSON keys and ObjC property names. - * - * In your model overwrite the +(JSONKeyMapper*)keyMapper method and provide a JSONKeyMapper - * instance to convert the key names for your model. - * - * If you need custom mapping it's as easy as: - *
- * +(JSONKeyMapper*)keyMapper {
- *   return [[JSONKeyMapper alloc] initWithDictionary:@{@"crazy_JSON_name":@"myCamelCaseName"}];
- * }
- * 
- * In case you want to handle underscore_case, **use the predefined key mapper**, like so: - *
- * +(JSONKeyMapper*)keyMapper {
- *   return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
- * }
- * 
- */ -@interface JSONKeyMapper : NSObject - -/** @name Name converters */ -/** Block, which takes in a JSON key and converts it to the corresponding property name */ -@property (readonly, nonatomic) JSONModelKeyMapBlock JSONToModelKeyBlock; - -/** Block, which takes in a property name and converts it to the corresponding JSON key name */ -@property (readonly, nonatomic) JSONModelKeyMapBlock modelToJSONKeyBlock; - -/** Combined converter method -* @param value the source name -* @param importing YES invokes JSONToModelKeyBlock, NO - modelToJSONKeyBlock -* @return JSONKeyMapper instance -*/ --(NSString*)convertValue:(NSString*)value isImportingToModel:(BOOL)importing; - -/** @name Creating a key mapper */ - -/** - * Creates a JSONKeyMapper instance, based on the two blocks you provide this initializer. - * The two parameters take in a JSONModelKeyMapBlock block: - *
NSString* (^JSONModelKeyMapBlock)(NSString* keyName)
- * The block takes in a string and returns the transformed (if at all) string. - * @param toModel transforms JSON key name to your model property name - * @param toJSON transforms your model property name to a JSON key - */ --(instancetype)initWithJSONToModelBlock:(JSONModelKeyMapBlock)toModel - modelToJSONBlock:(JSONModelKeyMapBlock)toJSON; - -/** - * Creates a JSONKeyMapper instance, based on the mapping you provide - * in the map parameter. Use the JSON key names as keys, your JSONModel - * property names as values. - * @param map map dictionary, in the format:
@{@"crazy_JSON_name":@"myCamelCaseName"}
- * @return JSONKeyMapper instance - */ --(instancetype)initWithDictionary:(NSDictionary*)map; - -/** - * Creates a JSONKeyMapper, which converts underscore_case to camelCase and vice versa. - */ -+(instancetype)mapperFromUnderscoreCaseToCamelCase; - -+(instancetype)mapperFromUpperCaseToLowerCase; - -/** - * Creates a JSONKeyMapper based on a built-in JSONKeyMapper, with specific exceptions. - * Use the original JSON key names as keys, and your JSONModel property names as values. - */ -+ (instancetype)mapper:(JSONKeyMapper *)baseKeyMapper withExceptions:(NSDictionary *)exceptions; - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.m b/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.m deleted file mode 100755 index ee10794..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONKeyMapper.m +++ /dev/null @@ -1,237 +0,0 @@ -// -// JSONKeyMapper.m -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import "JSONKeyMapper.h" -#import - -@interface JSONKeyMapper() -@property (nonatomic, strong) NSMutableDictionary *toModelMap; -@property (nonatomic, strong) NSMutableDictionary *toJSONMap; -@property (nonatomic, assign) OSSpinLock lock; -@end - -@implementation JSONKeyMapper - --(instancetype)init -{ - self = [super init]; - if (self) { - //initialization - self.toModelMap = [NSMutableDictionary dictionary]; - self.toJSONMap = [NSMutableDictionary dictionary]; - } - return self; -} - --(instancetype)initWithJSONToModelBlock:(JSONModelKeyMapBlock)toModel - modelToJSONBlock:(JSONModelKeyMapBlock)toJSON -{ - self = [self init]; - - if (self) { - - __weak JSONKeyMapper* weakSelf = self; - - _JSONToModelKeyBlock = [^NSString* (NSString* keyName) { - - __strong JSONKeyMapper* strongSelf = weakSelf; - - //try to return cached transformed key - if (strongSelf.toModelMap[keyName]) { - return strongSelf.toModelMap[keyName]; - } - - //try to convert the key, and store in the cache - NSString* result = toModel(keyName); - - OSSpinLockLock(&strongSelf->_lock); - strongSelf.toModelMap[keyName] = result; - OSSpinLockUnlock(&strongSelf->_lock); - - return result; - - } copy]; - - _modelToJSONKeyBlock = [^NSString* (NSString* keyName) { - - __strong JSONKeyMapper *strongSelf = weakSelf; - - //try to return cached transformed key - if (strongSelf.toJSONMap[keyName]) { - return strongSelf.toJSONMap[keyName]; - } - - //try to convert the key, and store in the cache - NSString* result = toJSON(keyName); - - OSSpinLockLock(&strongSelf->_lock); - strongSelf.toJSONMap[keyName] = result; - OSSpinLockUnlock(&strongSelf->_lock); - - return result; - - } copy]; - - } - - return self; -} - --(instancetype)initWithDictionary:(NSDictionary *)map -{ - self = [super init]; - if (self) { - - NSDictionary *userToModelMap = [map copy]; - NSDictionary *userToJSONMap = [self swapKeysAndValuesInDictionary:map]; - - _JSONToModelKeyBlock = ^NSString *(NSString *keyName) { - NSString *result = [userToModelMap valueForKeyPath:keyName]; - return result ? result : keyName; - }; - - _modelToJSONKeyBlock = ^NSString *(NSString *keyName) { - NSString *result = [userToJSONMap valueForKeyPath:keyName]; - return result ? result : keyName; - }; - } - - return self; -} - -- (NSDictionary *)swapKeysAndValuesInDictionary:(NSDictionary *)dictionary -{ - NSMutableDictionary *swapped = [NSMutableDictionary new]; - - [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { - NSAssert([value isKindOfClass:[NSString class]], @"Expect keys and values to be NSString"); - NSAssert([key isKindOfClass:[NSString class]], @"Expect keys and values to be NSString"); - swapped[value] = key; - }]; - - return swapped; -} - --(NSString*)convertValue:(NSString*)value isImportingToModel:(BOOL)importing -{ - return !importing?_JSONToModelKeyBlock(value):_modelToJSONKeyBlock(value); -} - -+(instancetype)mapperFromUnderscoreCaseToCamelCase -{ - JSONModelKeyMapBlock toModel = ^ NSString* (NSString* keyName) { - - //bail early if no transformation required - if ([keyName rangeOfString:@"_"].location==NSNotFound) return keyName; - - //derive camel case out of underscore case - NSString* camelCase = [keyName capitalizedString]; - camelCase = [camelCase stringByReplacingOccurrencesOfString:@"_" withString:@""]; - camelCase = [camelCase stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[[camelCase substringToIndex:1] lowercaseString] ]; - - return camelCase; - }; - - JSONModelKeyMapBlock toJSON = ^ NSString* (NSString* keyName) { - - NSMutableString* result = [NSMutableString stringWithString:keyName]; - NSRange upperCharRange = [result rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]]; - - //handle upper case chars - while ( upperCharRange.location!=NSNotFound) { - - NSString* lowerChar = [[result substringWithRange:upperCharRange] lowercaseString]; - [result replaceCharactersInRange:upperCharRange - withString:[NSString stringWithFormat:@"_%@", lowerChar]]; - upperCharRange = [result rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]]; - } - - //handle numbers - NSRange digitsRange = [result rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]]; - while ( digitsRange.location!=NSNotFound) { - - NSRange digitsRangeEnd = [result rangeOfString:@"\\D" options:NSRegularExpressionSearch range:NSMakeRange(digitsRange.location, result.length-digitsRange.location)]; - if (digitsRangeEnd.location == NSNotFound) { - //spands till the end of the key name - digitsRangeEnd = NSMakeRange(result.length, 1); - } - - NSRange replaceRange = NSMakeRange(digitsRange.location, digitsRangeEnd.location - digitsRange.location); - NSString* digits = [result substringWithRange:replaceRange]; - - [result replaceCharactersInRange:replaceRange withString:[NSString stringWithFormat:@"_%@", digits]]; - digitsRange = [result rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet] options:kNilOptions range:NSMakeRange(digitsRangeEnd.location+1, result.length-digitsRangeEnd.location-1)]; - } - - return result; - }; - - return [[self alloc] initWithJSONToModelBlock:toModel - modelToJSONBlock:toJSON]; - -} - -+(instancetype)mapperFromUpperCaseToLowerCase -{ - JSONModelKeyMapBlock toModel = ^ NSString* (NSString* keyName) { - NSString*lowercaseString = [keyName lowercaseString]; - return lowercaseString; - }; - - JSONModelKeyMapBlock toJSON = ^ NSString* (NSString* keyName) { - - NSString *uppercaseString = [keyName uppercaseString]; - - return uppercaseString; - }; - - return [[self alloc] initWithJSONToModelBlock:toModel - modelToJSONBlock:toJSON]; - -} - -+ (instancetype)mapper:(JSONKeyMapper *)baseKeyMapper withExceptions:(NSDictionary *)exceptions -{ - NSArray *keys = exceptions.allKeys; - NSArray *values = [exceptions objectsForKeys:keys notFoundMarker:[NSNull null]]; - - NSDictionary *toModelMap = [NSDictionary dictionaryWithObjects:values forKeys:keys]; - NSDictionary *toJsonMap = [NSDictionary dictionaryWithObjects:keys forKeys:values]; - - JSONModelKeyMapBlock toModel = ^NSString *(NSString *keyName) { - if (!keyName) - return nil; - - if (toModelMap[keyName]) - return toModelMap[keyName]; - - return baseKeyMapper.JSONToModelKeyBlock(keyName); - }; - - JSONModelKeyMapBlock toJson = ^NSString *(NSString *keyName) { - if (!keyName) - return nil; - - if (toJsonMap[keyName]) - return toJsonMap[keyName]; - - return baseKeyMapper.modelToJSONKeyBlock(keyName); - }; - - return [[self alloc] initWithJSONToModelBlock:toModel modelToJSONBlock:toJson]; -} - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.h b/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.h deleted file mode 100755 index ddd5b5c..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.h +++ /dev/null @@ -1,220 +0,0 @@ -// -// JSONValueTransformer.h -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import - -///////////////////////////////////////////////////////////////////////////////////////////// - -#pragma mark - extern definitions -/** - * Boolean function to check for null values. Handy when you need to both check - * for nil and [NSNUll null] - */ -extern BOOL isNull(id value); - -///////////////////////////////////////////////////////////////////////////////////////////// - -#pragma mark - JSONValueTransformer interface -/** - * **You don't need to call methods of this class manually.** - * - * Class providing methods to transform values from one class to another. - * You are given a number of built-in transformers, but you are encouraged to - * extend this class with your own categories to add further value transformers. - * Just few examples of what can you add to JSONValueTransformer: hex colors in JSON to UIColor, - * hex numbers in JSON to NSNumber model properties, base64 encoded strings in JSON to UIImage properties, and more. - * - * The class is invoked by JSONModel while transforming incoming - * JSON types into your target class property classes, and vice versa. - * One static copy is create and store in the JSONModel class scope. - */ -@interface JSONValueTransformer : NSObject - -@property (strong, nonatomic, readonly) NSDictionary* primitivesNames; - -/** @name Resolving cluster class names */ -/** - * This method returns the umbrella class for any standard class cluster members. - * For example returns NSString when given as input NSString, NSMutableString, __CFString and __CFConstantString - * The method currently looksup a pre-defined list. - * @param sourceClass the class to get the umbrella class for - * @return Class - */ -+(Class)classByResolvingClusterClasses:(Class)sourceClass; - -#pragma mark - NSMutableString <-> NSString -/** @name Transforming to Mutable copies */ -/** - * Transforms a string value to a mutable string value - * @param string incoming string - * @return mutable string - */ --(NSMutableString*)NSMutableStringFromNSString:(NSString*)string; - -#pragma mark - NSMutableArray <-> NSArray -/** - * Transforms an array to a mutable array - * @param array incoming array - * @return mutable array - */ --(NSMutableArray*)NSMutableArrayFromNSArray:(NSArray*)array; - -#pragma mark - NSMutableDictionary <-> NSDictionary -/** - * Transforms a dictionary to a mutable dictionary - * @param dict incoming dictionary - * @return mutable dictionary - */ --(NSMutableDictionary*)NSMutableDictionaryFromNSDictionary:(NSDictionary*)dict; - -#pragma mark - NSSet <-> NSArray -/** @name Transforming Sets */ -/** - * Transforms an array to a set - * @param array incoming array - * @return set with the array's elements - */ --(NSSet*)NSSetFromNSArray:(NSArray*)array; - -/** - * Transforms an array to a mutable set - * @param array incoming array - * @return mutable set with the array's elements - */ --(NSMutableSet*)NSMutableSetFromNSArray:(NSArray*)array; - -/** - * Transforms a set to an array - * @param set incoming set - * @return an array with the set's elements - */ --(NSArray*)JSONObjectFromNSSet:(NSSet*)set; - -/** - * Transforms a mutable set to an array - * @param set incoming mutable set - * @return an array with the set's elements - */ --(NSArray*)JSONObjectFromNSMutableSet:(NSMutableSet*)set; - -#pragma mark - BOOL <-> number/string -/** @name Transforming JSON types */ -/** - * Transforms a number object to a bool number object - * @param number the number to convert - * @return the resulting number - */ --(NSNumber*)BOOLFromNSNumber:(NSNumber*)number; - -/** - * Transforms a number object to a bool number object - * @param string the string value to convert, "0" converts to NO, everything else to YES - * @return the resulting number - */ --(NSNumber*)BOOLFromNSString:(NSString*)string; - -/** - * Transforms a BOOL value to a bool number object - * @param number an NSNumber value coming from the model - * @return the result number - */ --(NSNumber*)JSONObjectFromBOOL:(NSNumber*)number; - -#pragma mark - string <-> number -/** - * Transforms a string object to a number object - * @param string the string to convert - * @return the resulting number - */ --(NSNumber*)NSNumberFromNSString:(NSString*)string; - -/** - * Transforms a number object to a string object - * @param number the number to convert - * @return the resulting string - */ --(NSString*)NSStringFromNSNumber:(NSNumber*)number; - -/** - * Transforms a string object to a nsdecimalnumber object - * @param string the string to convert - * @return the resulting number - */ --(NSDecimalNumber*)NSDecimalNumberFromNSString:(NSString*)string; - -/** - * Transforms a nsdecimalnumber object to a string object - * @param number the number to convert - * @return the resulting string - */ --(NSString*)NSStringFromNSDecimalNumber:(NSDecimalNumber*)number; - - -#pragma mark - string <-> url -/** @name Transforming URLs */ -/** - * Transforms a string object to an NSURL object - * @param string the string to convert - * @return the resulting url object - */ --(NSURL*)NSURLFromNSString:(NSString*)string; - -/** - * Transforms an NSURL object to a string - * @param url the url object to convert - * @return the resulting string - */ --(NSString*)JSONObjectFromNSURL:(NSURL*)url; - -#pragma mark - string <-> time zone - -/** @name Transforming NSTimeZone */ -/** - * Transforms a string object to an NSTimeZone object - * @param string the string to convert - * @return the resulting NSTimeZone object - */ -- (NSTimeZone *)NSTimeZoneFromNSString:(NSString*)string; - -/** - * Transforms an NSTimeZone object to a string - * @param timeZone the time zone object to convert - * @return the resulting string - */ -- (NSString *)JSONObjectFromNSTimeZone:(NSTimeZone *)timeZone; - -#pragma mark - string <-> date -/** @name Transforming Dates */ -/** - * The following two methods are not public. This way if there is a category on converting - * dates it'll override them. If there isn't a category the default methods found in the .m - * file will be invoked. If these are public a warning is produced at the point of overriding - * them in a category, so they have to stay hidden here. - */ - -//-(NSDate*)NSDateFromNSString:(NSString*)string; -//-(NSString*)JSONObjectFromNSDate:(NSDate*)date; - -#pragma mark - number <-> date - -/** - * Transforms a number to an NSDate object - * @param number the number to convert - * @return the resulting date - */ -- (NSDate*)NSDateFromNSNumber:(NSNumber*)number; - -@end diff --git a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.m b/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.m deleted file mode 100755 index 61aff8d..0000000 --- a/Spil/3rd_party_frameworks/JSONModel/JSONModelTransformations/JSONValueTransformer.m +++ /dev/null @@ -1,256 +0,0 @@ -// -// JSONValueTransformer.m -// -// @version 1.2 -// @author Marin Todorov (http://www.underplot.com) and contributors -// - -// Copyright (c) 2012-2015 Marin Todorov, Underplot ltd. -// This code is distributed under the terms and conditions of the MIT license. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - - -#import "JSONValueTransformer.h" - -#pragma mark - functions -extern BOOL isNull(id value) -{ - if (!value) return YES; - if ([value isKindOfClass:[NSNull class]]) return YES; - - return NO; -} - -@implementation JSONValueTransformer - --(id)init -{ - self = [super init]; - if (self) { - _primitivesNames = @{@"f":@"float", @"i":@"int", @"d":@"double", @"l":@"long", @"c":@"BOOL", @"s":@"short", @"q":@"long", - //and some famous aliases of primitive types - // BOOL is now "B" on iOS __LP64 builds - @"I":@"NSInteger", @"Q":@"NSUInteger", @"B":@"BOOL", - - @"@?":@"Block"}; - } - return self; -} - -+(Class)classByResolvingClusterClasses:(Class)sourceClass -{ - //check for all variations of strings - if ([sourceClass isSubclassOfClass:[NSString class]]) { - return [NSString class]; - } - - //check for all variations of numbers - if ([sourceClass isSubclassOfClass:[NSNumber class]]) { - return [NSNumber class]; - } - - //check for all variations of dictionaries - if ([sourceClass isSubclassOfClass:[NSArray class]]) { - return [NSArray class]; - } - - //check for all variations of arrays - if ([sourceClass isSubclassOfClass:[NSDictionary class]]) { - return [NSDictionary class]; - } - - //check for all variations of dates - if ([sourceClass isSubclassOfClass:[NSDate class]]) { - return [NSDate class]; - } - - //no cluster parent class found - return sourceClass; -} - -#pragma mark - NSMutableString <-> NSString --(NSMutableString*)NSMutableStringFromNSString:(NSString*)string -{ - return [NSMutableString stringWithString:string]; -} - -#pragma mark - NSMutableArray <-> NSArray --(NSMutableArray*)NSMutableArrayFromNSArray:(NSArray*)array -{ - return [NSMutableArray arrayWithArray:array]; -} - -#pragma mark - NSMutableDictionary <-> NSDictionary --(NSMutableDictionary*)NSMutableDictionaryFromNSDictionary:(NSDictionary*)dict -{ - return [NSMutableDictionary dictionaryWithDictionary:dict]; -} - -#pragma mark - NSSet <-> NSArray --(NSSet*)NSSetFromNSArray:(NSArray*)array -{ - return [NSSet setWithArray:array]; -} - --(NSMutableSet*)NSMutableSetFromNSArray:(NSArray*)array -{ - return [NSMutableSet setWithArray:array]; -} - --(id)JSONObjectFromNSSet:(NSSet*)set -{ - return [set allObjects]; -} - --(id)JSONObjectFromNSMutableSet:(NSMutableSet*)set -{ - return [set allObjects]; -} - -// -// 0 converts to NO, everything else converts to YES -// - -#pragma mark - BOOL <-> number/string --(NSNumber*)BOOLFromNSNumber:(NSNumber*)number -{ - if (isNull(number)) return [NSNumber numberWithBool:NO]; - return [NSNumber numberWithBool: number.intValue==0?NO:YES]; -} - --(NSNumber*)BOOLFromNSString:(NSString*)string -{ - if (string != nil && - ([string caseInsensitiveCompare:@"true"] == NSOrderedSame || - [string caseInsensitiveCompare:@"yes"] == NSOrderedSame)) { - return [NSNumber numberWithBool:YES]; - } - return [NSNumber numberWithBool: ([string intValue]==0)?NO:YES]; -} - --(NSNumber*)JSONObjectFromBOOL:(NSNumber*)number -{ - return [NSNumber numberWithBool: number.intValue==0?NO:YES]; -} - -#pragma mark - string/number <-> float --(float)floatFromObject:(id)obj -{ - return [obj floatValue]; -} - --(float)floatFromNSString:(NSString*)string -{ - return [self floatFromObject:string]; -} - --(float)floatFromNSNumber:(NSNumber*)number -{ - return [self floatFromObject:number]; -} - --(NSNumber*)NSNumberFromfloat:(float)f -{ - return [NSNumber numberWithFloat:f]; -} - -#pragma mark - string <-> number --(NSNumber*)NSNumberFromNSString:(NSString*)string -{ - return [NSNumber numberWithDouble:[string doubleValue]]; -} - --(NSString*)NSStringFromNSNumber:(NSNumber*)number -{ - return [number stringValue]; -} - --(NSDecimalNumber*)NSDecimalNumberFromNSString:(NSString*)string -{ - return [NSDecimalNumber decimalNumberWithString:string]; -} - --(NSString*)NSStringFromNSDecimalNumber:(NSDecimalNumber*)number -{ - return [number stringValue]; -} - -#pragma mark - string <-> url --(NSURL*)NSURLFromNSString:(NSString*)string -{ - // do not change this behavior - there are other ways of overriding it - // see: https://github.com/icanzilb/JSONModel/pull/119 - return [NSURL URLWithString:string]; -} - --(NSString*)JSONObjectFromNSURL:(NSURL*)url -{ - return [url absoluteString]; -} - -#pragma mark - string <-> date --(NSDateFormatter*)importDateFormatter -{ - static dispatch_once_t onceInput; - static NSDateFormatter* inputDateFormatter; - dispatch_once(&onceInput, ^{ - inputDateFormatter = [[NSDateFormatter alloc] init]; - [inputDateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]]; - [inputDateFormatter setDateFormat:@"yyyy-MM-dd'T'HHmmssZZZ"]; - }); - return inputDateFormatter; -} - --(NSDate*)__NSDateFromNSString:(NSString*)string -{ - string = [string stringByReplacingOccurrencesOfString:@":" withString:@""]; // this is such an ugly code, is this the only way? - return [self.importDateFormatter dateFromString: string]; -} - --(NSString*)__JSONObjectFromNSDate:(NSDate*)date -{ - static dispatch_once_t onceOutput; - static NSDateFormatter *outputDateFormatter; - dispatch_once(&onceOutput, ^{ - outputDateFormatter = [[NSDateFormatter alloc] init]; - [outputDateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]]; - [outputDateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZ"]; - }); - return [outputDateFormatter stringFromDate:date]; -} - -#pragma mark - number <-> date -- (NSDate*)NSDateFromNSNumber:(NSNumber*)number -{ - return [NSDate dateWithTimeIntervalSince1970:number.doubleValue]; -} - -#pragma mark - string <-> NSTimeZone - -- (NSTimeZone *)NSTimeZoneFromNSString:(NSString *)string { - return [NSTimeZone timeZoneWithName:string]; -} - -- (id)JSONObjectFromNSTimeZone:(NSTimeZone *)timeZone { - return [timeZone name]; -} - -#pragma mark - hidden transform for empty dictionaries -//https://github.com/icanzilb/JSONModel/issues/163 --(NSDictionary*)__NSDictionaryFromNSArray:(NSArray*)array -{ - if (array.count==0) return @{}; - return (id)array; -} - --(NSMutableDictionary*)__NSMutableDictionaryFromNSArray:(NSArray*)array -{ - if (array.count==0) return [[self __NSDictionaryFromNSArray:array] mutableCopy]; - return (id)array; -} - -@end diff --git a/Spil/ActionHandler/SpilActionHandler.m b/Spil/ActionHandler/SpilActionHandler.m index 5360b24..4d9efbd 100644 --- a/Spil/ActionHandler/SpilActionHandler.m +++ b/Spil/ActionHandler/SpilActionHandler.m @@ -7,7 +7,11 @@ // #import "SpilActionHandler.h" +#if TARGET_OS_IOS +#import "SpilWebViewController.h" +#endif #import "SpilEventTracker.h" +#import "SpilAdvertisementHandler.h" #import "SpilConfigHandler.h" #import "SpilPackageHandler.h" #import "GameDataController.h" @@ -18,7 +22,6 @@ #import "SpilUserHandler.h" #import "SpilError.h" #import "NotificationUtil.h" -#import "SpilAdvertisementHandler.h" @implementation SpilActionHandler @@ -61,9 +64,7 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID return; } - //NSLog(@"[SpilActionHandler] handleAction %@ type: %@, action: %@ callback: %@",action, action[@"type"], action[@"action"], callbackUID); - - // TODO: temp to handle the splash screen not available, is not needed anymore when slot handles it correctly through the action. + // Handle the splash screen not available, is not needed anymore when slot handles it correctly through the action. if([action objectForKey:@"action"] == nil ) { if ([action[@"name"] isEqualToString:@"requestSplashscreen"]) { [NotificationUtil send:@"splashScreenNotAvailable"]; @@ -71,38 +72,69 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID return; } - // Handle returned game data - /*if([action[@"name"] isEqualToString:@"requestGameData"] && [action[@"type"] isEqualToString:@"gameData"]) { - NSLog(@"[SpilActionHandler] Handle downloaded game data %@", [JsonUtil convertObjectToJson:action[@"data"]]); - NSDictionary *jsonConfig = action[@"data"]; - - [[GameDataController sharedInstance] processGameData:jsonConfig]; - - // Load the player data - [[PlayerDataController sharedInstance] requestPlayerData]; - } - - // Handle returned player data - if([action[@"name"] isEqualToString:@"requestPlayerData"] && [action[@"type"] isEqualToString:@"playerData"]) { - NSLog(@"[SpilActionHandler] Handle downloaded player data %@", [JsonUtil convertObjectToJson:action[@"data"]]); - - NSDictionary *jsonConfig = action[@"data"]; - NSError *error = nil; - Wallet *wallet = [[Wallet alloc] initWithDictionary:jsonConfig[@"wallet"] error:&error]; - Inventory *inventory = [[Inventory alloc] initWithDictionary:jsonConfig[@"inventory"] error:&error]; - [[PlayerDataController sharedInstance] processPlayerData:wallet withInventory:inventory]; + // Handle advertisement init + #if TARGET_OS_IOS + if([action[@"type"] isEqualToString:@"advertisement"]) { + if([action[@"action"] isEqualToString:@"init"]) { + NSDictionary *providers = action[@"data"][@"providers"]; + + @try { + if([providers valueForKey:@"DFP"] != nil) { + NSString *adUnitId = providers[@"DFP"][@"adUnitID"]; + NSLog(@"[SpilActionHandler] adUnitId in action %@", adUnitId); + NSDictionary *dfpOptions = @{@"adUnitId":[NSString stringWithFormat:@"%@",adUnitId], + @"targeting":providers[@"DFP"][@"targeting"]}; + NSLog(@"[SpilActionHandler] dfp options to pass %@", [JsonUtil convertObjectToJson:dfpOptions]); + + GoogleAdProvider *google = [[SpilAdvertisementHandler sharedInstance] getGoogleAdProvider]; + [google initialize:dfpOptions]; + } + } + @catch (NSException *exception) { + NSLog(@"[SpilActionHandler] advertisement DFP init Exception:%@",exception); + } + + // FYBER + @try { + if([providers valueForKey:@"Fyber"] != nil) { + NSString *appId = providers[@"Fyber"][@"appId"]; + NSString *securityToken = providers[@"Fyber"][@"securityToken"]; + NSDictionary *fyberOptions = @{@"appId":[NSString stringWithFormat:@"%@",appId], + @"securityToken":[NSString stringWithFormat:@"%@",securityToken], + @"targeting":providers[@"Fyber"][@"targeting"]}; + + FyberAdProvider *fyber = [[SpilAdvertisementHandler sharedInstance] getFyberAdProvider]; + [fyber initialize:fyberOptions]; + } + } + @catch (NSException *exception) { + NSLog(@"[SpilActionHandler] advertisement fyber init Exception:%@",exception); + } + + // CHARTBOOST + @try { + if([providers valueForKey:@"Chartboost"] != nil) { + NSString *appId = providers[@"Chartboost"][@"appId"]; + NSString *appSignature = providers[@"Chartboost"][@"appSignature"]; + BOOL parentalGate = false; + if (providers[@"Chartboost"][@"parentalGate"] != nil && [[providers[@"Chartboost"][@"parentalGate"] stringValue] isEqualToString:@"1"] ) { + parentalGate = true; + } + + NSDictionary *chartboostOptions = @{@"appId":[NSString stringWithFormat:@"%@",appId], + @"appSignature":[NSString stringWithFormat:@"%@",appSignature], + @"parentalGate":[NSNumber numberWithBool:parentalGate]}; + + ChartboostAdProvider *chartboost = [[SpilAdvertisementHandler sharedInstance] getChartboostAdProvider]; + [chartboost initialize:chartboostOptions]; + } + } + @catch (NSException *exception) { + NSLog(@"[SpilActionHandler] advertisement chartboost init Exception:%@",exception); + } + } } - // Handle updated player data - if([action[@"name"] isEqualToString:@"updatePlayerData"] && [action[@"type"] isEqualToString:@"playerData"]) { - NSLog(@"[SpilActionHandler] Handle downloaded player data %@", [JsonUtil convertObjectToJson:action[@"data"]]); - NSDictionary *jsonConfig = action[@"data"]; - NSError *error = nil; - Wallet *walletChanges = [[Wallet alloc] initWithDictionary:jsonConfig[@"wallet"] error:&error]; - Inventory *inventoryChanges = [[Inventory alloc] initWithDictionary:jsonConfig[@"inventory"] error:&error]; - [[PlayerDataController sharedInstance] processPlayerData:walletChanges withInventory:inventoryChanges]; - }*/ - // Show advertisements if([action[@"type"] isEqualToString:@"advertisement"]) { if([action[@"action"] isEqualToString:@"show"]) { @@ -111,6 +143,29 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID NSLog(@"[SpilActionHandler] action show advertisement %@", action[@"data"]); + NSString *provider = action[@"data"][@"provider"]; + if([action[@"data"][@"adType"] isEqualToString:@"rewardVideo"]) { + [[SpilAdvertisementHandler sharedInstance] checkRewardVideoAvailability:provider]; + } + if([action[@"data"][@"adType"] isEqualToString:@"interstitial"]) { + [[SpilAdvertisementHandler sharedInstance] showInterstitial:provider]; + } + } + } + @catch (NSException *exception) { + NSLog(@"[SpilActionHandler] advertisement show Exception:%@",exception); + } + } + } + #else + // Show advertisements for tvos + if([action[@"type"] isEqualToString:@"advertisement"]) { + if([action[@"action"] isEqualToString:@"show"]) { + @try { + if([action[@"data"] valueForKey:@"provider"] != nil) { + + NSLog(@"[SpilActionHandler] action show advertisement %@", action[@"data"]); + NSString *provider = action[@"data"][@"provider"]; if([action[@"data"][@"adType"] isEqualToString:@"rewardVideo"]) { // TODO: Currently app lovin is only supported @@ -127,6 +182,38 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID } } } + #endif + + + // Handle returned game data + if([action[@"name"] isEqualToString:@"requestGameData"] && [action[@"type"] isEqualToString:@"gameData"]) { + NSLog(@"[SpilActionHandler] Handle downloaded game data %@", [JsonUtil convertObjectToJson:action[@"data"]]); + NSDictionary *jsonConfig = action[@"data"]; + + [[GameDataController sharedInstance] processGameData:jsonConfig]; + + // Load the player data + [[PlayerDataController sharedInstance] requestPlayerData]; + } + + // Handle returned player data + if([action[@"name"] isEqualToString:@"requestPlayerData"] && [action[@"type"] isEqualToString:@"playerData"]) { + NSLog(@"[SpilActionHandler] Handle downloaded player data %@", [JsonUtil convertObjectToJson:action[@"data"]]); + + NSDictionary *jsonConfig = action[@"data"]; + Wallet *wallet = [[Wallet alloc] initWithDictionary:jsonConfig[@"wallet"]]; + Inventory *inventory = [[Inventory alloc] initWithDictionary:jsonConfig[@"inventory"]]; + [[PlayerDataController sharedInstance] processPlayerData:wallet withInventory:inventory]; + } + + // Handle updated player data + if([action[@"name"] isEqualToString:@"updatePlayerData"] && [action[@"type"] isEqualToString:@"playerData"]) { + NSLog(@"[SpilActionHandler] Handle downloaded player data %@", [JsonUtil convertObjectToJson:action[@"data"]]); + NSDictionary *jsonConfig = action[@"data"]; + Wallet *walletChanges = [[Wallet alloc] initWithDictionary:jsonConfig[@"wallet"]]; + Inventory *inventoryChanges = [[Inventory alloc] initWithDictionary:jsonConfig[@"inventory"]]; + [[PlayerDataController sharedInstance] processPlayerData:walletChanges withInventory:inventoryChanges]; + } // Handle returned game configurations if([action[@"type"] isEqualToString:@"gameConfig"]) { @@ -166,6 +253,25 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID } } + // webview action show + if([action[@"type"] isEqualToString:@"overlay"]) { + NSString *actionName = action[@"name"]; + if ([action[@"action"] isEqualToString:@"show"]) { + NSLog(@"[SpilActionHandler] matched action"); + if(hasCallbackUID) { + NSDictionary *data = action[@"data"]; + [[SpilActionHandler sharedInstance] showWebview:data withCallBackUID:callbackUID withActionName:actionName]; + return; + } + } else if ([action[@"action"] isEqualToString:@"notAvailable"]) { + if ([actionName isEqualToString:@"splashscreen"]) { + [NotificationUtil send:@"splashScreenNotAvailable"]; + } else if ([actionName isEqualToString:@"dailybonus"]) { + [NotificationUtil send:@"dailyBonusNotAvailable"]; + } + } + } + // handle daily login bonus if([action[@"type"] isEqualToString:@"reward"] && [action[@"action"] isEqualToString:@"collect"]) { NSDictionary *data = action[@"data"]; @@ -181,10 +287,10 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID if ([type isEqualToString:@"CURRENCY"]) { int currencyId = (int)[identifier integerValue]; - [[PlayerDataController sharedInstance] updateWallet:currencyId withDelta:[amount intValue] withReason:@"Daily Bonus From Client"]; + [[PlayerDataController sharedInstance] updateWallet:currencyId withDelta:[amount intValue] withReason:@"Daily Bonus From Client" withLocation:@"DailyBonus"]; } else if ([type isEqualToString:@"ITEM"]) { int itemId = (int)[identifier integerValue]; - [[PlayerDataController sharedInstance] updateInventoryWithItem:itemId withAmount:[amount intValue] withAction:@"add" withReason:@"Daily Bonus From Client"]; + [[PlayerDataController sharedInstance] updateInventoryWithItem:itemId withAmount:[amount intValue] withAction:@"add" withReason:@"Daily Bonus From Client" withLocation:@"DailyBonus"]; } else if ([type isEqualToString:@"EXTERNAL"]) { NSString *externalId = collectible[@"id"]; NSDictionary *externalItem = @{ @"externalId" : externalId, @@ -209,4 +315,17 @@ +(void)handleAction:(NSDictionary*)action withCallBackUID:(NSString*)callbackUID } } +-(void)showWebview:(NSDictionary*)data withCallBackUID:(NSString*)callbackUID withActionName:(NSString*)name { + #if TARGET_OS_IOS + SpilWebViewController *webview = [[SpilWebViewController alloc] init]; + webview.modalPresentationStyle = UIModalPresentationOverCurrentContext; + webview.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + [[[SpilActionHandler sharedInstance] topMostController] presentViewController:webview animated:YES completion:^() { + NSMutableDictionary *options = [[NSMutableDictionary alloc] initWithDictionary:data]; + [options setObject:callbackUID forKey:@"callbackUID"]; + [webview setupWebview:options withActionName:name]; + }]; + #endif +} + @end diff --git a/Spil/GameData/GameData.h b/Spil/GameData/GameData.h index 24c5e0a..3d95041 100644 --- a/Spil/GameData/GameData.h +++ b/Spil/GameData/GameData.h @@ -7,25 +7,25 @@ // #import -#import "JSONModel.h" #import "Item.h" #import "Currency.h" #import "Bundle.h" -#import "ShopEntry.h" #import "ShopTab.h" #import "ShopPromotion.h" -@protocol GameData -@end +@interface GameData : NSObject + +@property (strong, nonatomic) NSMutableArray *items; // Item +@property (strong, nonatomic) NSMutableArray *currencies; // Currency +@property (strong, nonatomic) NSMutableArray *bundles; // Bundle +@property (strong, nonatomic) NSMutableArray *shop; // ShopTab +@property (strong, nonatomic) NSMutableArray *promotions; // ShopPromotion -@interface GameData : JSONModel +-(id)initWithDictionary:(NSDictionary*)data; -@property (strong, nonatomic) NSMutableArray *items; -@property (strong, nonatomic) NSMutableArray *currencies; -@property (strong, nonatomic) NSMutableArray *bundles; -@property (strong, nonatomic) NSMutableArray *shop; -@property (strong, nonatomic) NSMutableArray *promotions; +-(NSString*)toJSONString; --(id)init; +-(NSString*)getShopJSONString; +-(NSString*)getPromotionsJSONString; @end diff --git a/Spil/GameData/GameData.m b/Spil/GameData/GameData.m index dc0fcf0..43d3e43 100644 --- a/Spil/GameData/GameData.m +++ b/Spil/GameData/GameData.m @@ -7,6 +7,7 @@ // #import "GameData.h" +#import "JsonUtil.h" @implementation GameData @@ -16,14 +17,116 @@ @implementation GameData @synthesize shop; @synthesize promotions; --(id)init { +-(id)initWithDictionary:(NSDictionary*)data { self = [super init]; + self.items = [NSMutableArray array]; + if (data[@"items"] != nil) { + for (NSDictionary *dict in data[@"items"]) { + Item *item = [[Item alloc] initWithDictionary:dict]; + [self.items addObject:item]; + } + } + + self.currencies = [NSMutableArray array]; + if (data[@"currencies"] != nil) { + for (NSDictionary *dict in data[@"currencies"]) { + Currency *currency = [[Currency alloc] initWithDictionary:dict]; + [self.currencies addObject:currency]; + } + } + + self.bundles = [NSMutableArray array]; + if (data[@"bundles"] != nil) { + for (NSDictionary *dict in data[@"bundles"]) { + Bundle *bundle = [[Bundle alloc] initWithDictionary:dict]; + [self.bundles addObject:bundle]; + } + } + + self.shop = [NSMutableArray array]; + if (data[@"shop"] != nil) { + for (NSDictionary *dict in data[@"shop"]) { + ShopTab *shopTab = [[ShopTab alloc] initWithDictionary:dict]; + [self.shop addObject:shopTab]; + } + } + + self.promotions = [NSMutableArray array]; + if (data[@"promotions"] != nil) { + for (NSDictionary *dict in data[@"promotions"]) { + ShopPromotion *shopPromotion = [[ShopPromotion alloc] initWithDictionary:dict]; + [self.promotions addObject:shopPromotion]; + } + } + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSString*)toJSONString { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + NSMutableArray *itemArray = [NSMutableArray array]; + if (self.items != nil) { + for (Item *item in self.items) { + [itemArray addObject:[item toJSONObject]]; + } + } + [rootDict setObject:itemArray forKey:@"items"]; + + NSMutableArray *currencyArray = [NSMutableArray array]; + if (self.currencies != nil) { + for (Currency *currency in self.currencies) { + [currencyArray addObject:[currency toJSONObject]]; + } + } + [rootDict setObject:currencyArray forKey:@"currencies"]; + + NSMutableArray *bundleArray = [NSMutableArray array]; + if (self.bundles != nil) { + for (Bundle *bundle in self.bundles) { + [bundleArray addObject:[bundle toJSONObject]]; + } + } + [rootDict setObject:bundleArray forKey:@"bundles"]; + + NSMutableArray *shopArray = [NSMutableArray array]; + if (self.shop != nil) { + for (ShopTab *shopTab in self.shop) { + [shopArray addObject:[shopTab toJSONObject]]; + } + } + [rootDict setObject:shopArray forKey:@"shop"]; + + NSMutableArray *promotionsArray = [NSMutableArray array]; + if (self.promotions != nil) { + for (ShopPromotion *shopPromotion in self.promotions) { + [promotionsArray addObject:[shopPromotion toJSONObject]]; + } + } + [rootDict setObject:promotionsArray forKey:@"promotions"]; + + return [JsonUtil convertObjectToJson:rootDict]; +} + +-(NSString*)getShopJSONString { + NSMutableArray *shopArray = [NSMutableArray array]; + if (self.shop != nil) { + for (ShopTab *shopTab in self.shop) { + [shopArray addObject:[shopTab toJSONObject]]; + } + } + return [JsonUtil convertObjectToJson:shopArray]; +} + +-(NSString*)getPromotionsJSONString { + NSMutableArray *promotionsArray = [NSMutableArray array]; + if (self.promotions != nil) { + for (ShopPromotion *shopPromotion in self.promotions) { + [promotionsArray addObject:[shopPromotion toJSONObject]]; + } + } + return [JsonUtil convertObjectToJson:promotionsArray]; } -@end \ No newline at end of file +@end diff --git a/Spil/GameData/GameDataController.h b/Spil/GameData/GameDataController.h index 6b59d83..23f9734 100644 --- a/Spil/GameData/GameDataController.h +++ b/Spil/GameData/GameDataController.h @@ -20,7 +20,6 @@ -(void)requestGameData; -(void)processGameData:(NSDictionary*)data; --(void)processGameData:(NSArray*)currencies withItems:(NSArray*)items withBundles:(NSArray*)bundles withShopTabs:(NSArray*)shopTabs withShopPromotions:(NSArray*)shopPromotions; -(GameData*)getGameData; -(Item*)getItem:(int)itemId; diff --git a/Spil/GameData/GameDataController.m b/Spil/GameData/GameDataController.m index e16f580..464a2c6 100644 --- a/Spil/GameData/GameDataController.m +++ b/Spil/GameData/GameDataController.m @@ -14,8 +14,6 @@ #import "SpilEventTracker.h" #import "Currency.h" #import "SpilError.h" -#import "NSArray+JSONModelExtensions.h" -#import "JsonModel.h" #import "Util.h" @implementation GameDataController @@ -37,31 +35,13 @@ -(void)requestGameData { } -(void)processGameData:(NSDictionary*)data { - NSError *error = nil; - NSMutableArray *currencies = [Currency arrayOfModelsFromDictionaries:data[@"currencies"] error:&error]; - NSMutableArray *items = [Item arrayOfModelsFromDictionaries:data[@"items"] error:&error]; - NSMutableArray *bundles = nil;//[Bundle arrayOfModelsFromDictionaries:data[@"bundles"] error:&error]; - NSMutableArray *shopTabs = [ShopTab arrayOfModelsFromDictionaries:data[@"shop"] error:&error]; - NSMutableArray *shopPromotions = [ShopPromotion arrayOfModelsFromDictionaries:data[@"promotions"] error:&error]; - [[GameDataController sharedInstance] processGameData:currencies withItems:items withBundles:bundles withShopTabs:shopTabs withShopPromotions:shopPromotions]; -} - --(void)processGameData:(NSArray*)currencies withItems:(NSArray*)items withBundles:(NSArray*)bundles withShopTabs:(NSArray*)shopTabs withShopPromotions:(NSArray*)shopPromotions { - GameData *gameData = [self getGameData]; - - if (gameData != nil) { - gameData.currencies = [currencies mutableCopy]; - gameData.items = [items mutableCopy]; - gameData.bundles = [bundles mutableCopy]; - gameData.shop = [shopTabs mutableCopy]; - gameData.promotions = [shopPromotions mutableCopy]; + GameData *gameData = [[GameData alloc] initWithDictionary:data]; - [self updateGameData:gameData]; + [self updateGameData:gameData]; - // Send a notification when the data is loaded - NSDictionary *userInfo = @{@"event" : @"gameDataAvailable"}; - [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; - } + // Send a notification when the data is loaded + NSDictionary *userInfo = @{@"event" : @"gameDataAvailable"}; + [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; } -(GameData*)getGameData { @@ -69,8 +49,8 @@ -(GameData*)getGameData { NSString *gameObjects = [defaults objectForKey:@"spilGameData"]; if(gameObjects != nil){ - NSError *error = nil; - return [[GameData alloc] initWithString:gameObjects error:&error]; + NSDictionary *data = [JsonUtil convertStringToObject:gameObjects]; + return [[GameData alloc] initWithDictionary:data]; } else { gameObjects = [self loadGameDataFromAssets]; @@ -78,8 +58,8 @@ -(GameData*)getGameData { [defaults setObject:gameObjects forKey:@"spilGameData"]; [defaults synchronize]; - NSError *error = nil; - return [[GameData alloc] initWithString:gameObjects error:&error]; + NSDictionary *data = [JsonUtil convertStringToObject:gameObjects]; + return [[GameData alloc] initWithDictionary:data]; } else { // Send a notification when the data failed to load NSDictionary *userInfo = @{@"event" : @"gameDataError", @"message" : [[SpilError LoadFailed:@"Game Object container is empty!"] toJson]}; @@ -192,9 +172,7 @@ -(NSString*)getShop { } } - //NSArray* jsonObjects = [ShopTab arrayOfDictionariesFromModels: [gameData shopTabs]]; - //return [JsonUtil convertObjectToJson:jsonObjects]; - return [[gameData shop] toJSONString]; + return [gameData getShopJSONString]; } -(NSString*)getShopPromotions { @@ -216,7 +194,7 @@ -(NSString*)getShopPromotions { } } - return [[gameData promotions] toJSONString]; + return [gameData getPromotionsJSONString]; } - (ShopPromotion*)getPromotion:(int)bundleId { diff --git a/Spil/GameData/bundles/Bundle.h b/Spil/GameData/bundles/Bundle.h index dff831b..e3d9c94 100644 --- a/Spil/GameData/bundles/Bundle.h +++ b/Spil/GameData/bundles/Bundle.h @@ -7,20 +7,21 @@ // #import -#import "JSONModel.h" #import "BundleItem.h" #import "BundlePrice.h" -@protocol Bundle -@end - -@interface Bundle : JSONModel +@interface Bundle : NSObject @property (assign, nonatomic) int id; @property (strong, nonatomic) NSString *name; -@property (strong, nonatomic) NSMutableArray *prices; // Array of BundlePrice's -@property (strong, nonatomic) NSMutableArray *items; // Array of BundleItem's +@property (strong, nonatomic) NSMutableArray *prices; // Array of BundlePrice's +@property (strong, nonatomic) NSMutableArray *items; // Array of BundleItem's + +-(id)initWithDictionary:(NSDictionary*)data; + +-(NSDictionary*)toJSONObject; --(id)init; +-(NSArray*)getItemsJSONArray; +-(NSArray*)getPricesJSONArray; @end diff --git a/Spil/GameData/bundles/Bundle.m b/Spil/GameData/bundles/Bundle.m index 0e47d92..03a5e62 100644 --- a/Spil/GameData/bundles/Bundle.m +++ b/Spil/GameData/bundles/Bundle.m @@ -15,14 +15,77 @@ @implementation Bundle @synthesize prices; @synthesize items; --(id)init { +-(id)initWithDictionary:(NSDictionary*)data { self = [super init]; + self.id = [data[@"id"] intValue]; + self.name = data[@"name"]; + + self.prices = [NSMutableArray array]; + if (data[@"prices"] != nil) { + for (NSDictionary *dict in data[@"prices"]) { + BundlePrice *bundlePrice = [[BundlePrice alloc] initWithDictionary:dict]; + [self.prices addObject:bundlePrice]; + } + } + + self.items = [NSMutableArray array]; + if (data[@"items"] != nil) { + for (NSDictionary *dict in data[@"items"]) { + BundleItem *bundleItem = [[BundleItem alloc] initWithDictionary:dict]; + [self.items addObject:bundleItem]; + } + } + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.id] forKey:@"id"]; + + if (self.name != nil) { + [rootDict setObject:self.name forKey:@"name"]; + } + + NSMutableArray *priceArray = [NSMutableArray array]; + if (self.prices != nil) { + for (BundlePrice *price in self.prices) { + [priceArray addObject:[price toJSONObject]]; + } + } + [rootDict setObject:priceArray forKey:@"prices"]; + + NSMutableArray *itemArray = [NSMutableArray array]; + if (self.items != nil) { + for (BundleItem *item in self.items) { + [itemArray addObject:[item toJSONObject]]; + } + } + [rootDict setObject:itemArray forKey:@"items"]; + + return rootDict; +} + +-(NSArray*)getItemsJSONArray { + NSMutableArray *itemArray = [NSMutableArray array]; + if (self.items != nil) { + for (BundleItem *item in self.items) { + [itemArray addObject:[item toJSONObject]]; + } + } + return itemArray; +} + +-(NSArray*)getPricesJSONArray { + NSMutableArray *priceArray = [NSMutableArray array]; + if (self.prices != nil) { + for (BundlePrice *price in self.prices) { + [priceArray addObject:[price toJSONObject]]; + } + } + return priceArray; } @end diff --git a/Spil/GameData/bundles/BundleItem.h b/Spil/GameData/bundles/BundleItem.h index 2622c45..e651630 100644 --- a/Spil/GameData/bundles/BundleItem.h +++ b/Spil/GameData/bundles/BundleItem.h @@ -7,16 +7,14 @@ // #import -#import "JSONModel.h" -@protocol BundleItem -@end - -@interface BundleItem : JSONModel +@interface BundleItem : NSObject @property (assign, nonatomic) int id; @property (assign, nonatomic) int amount; --(id)init; +-(id)initWithDictionary:(NSDictionary*)dict; + +-(NSDictionary*)toJSONObject; @end diff --git a/Spil/GameData/bundles/BundleItem.m b/Spil/GameData/bundles/BundleItem.m index 62dced2..25b296c 100644 --- a/Spil/GameData/bundles/BundleItem.m +++ b/Spil/GameData/bundles/BundleItem.m @@ -13,14 +13,22 @@ @implementation BundleItem @synthesize id; @synthesize amount; --(id)init { +-(id)initWithDictionary:(NSDictionary*)dict { self = [super init]; + self.id = [dict[@"id"] intValue]; + self.amount = [dict[@"amount"] intValue]; + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.id] forKey:@"id"]; + [rootDict setObject:[NSNumber numberWithInt:self.amount] forKey:@"amount"]; + + return rootDict; } @end diff --git a/Spil/GameData/bundles/BundlePrice.h b/Spil/GameData/bundles/BundlePrice.h index 18390dc..d013178 100644 --- a/Spil/GameData/bundles/BundlePrice.h +++ b/Spil/GameData/bundles/BundlePrice.h @@ -7,16 +7,14 @@ // #import -#import "JSONModel.h" -@protocol BundlePrice -@end - -@interface BundlePrice : JSONModel +@interface BundlePrice : NSObject @property (assign, nonatomic) int currencyId; @property (assign, nonatomic) int value; --(id)init; +-(id)initWithDictionary:(NSDictionary*)dict; + +-(NSDictionary*)toJSONObject; @end diff --git a/Spil/GameData/bundles/BundlePrice.m b/Spil/GameData/bundles/BundlePrice.m index 64aa765..e2be0c0 100644 --- a/Spil/GameData/bundles/BundlePrice.m +++ b/Spil/GameData/bundles/BundlePrice.m @@ -13,14 +13,22 @@ @implementation BundlePrice @synthesize currencyId; @synthesize value; --(id)init { +-(id)initWithDictionary:(NSDictionary*)dict { self = [super init]; + self.currencyId = [dict[@"currencyId"] intValue]; + self.value = [dict[@"value"] intValue]; + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.currencyId] forKey:@"currencyId"]; + [rootDict setObject:[NSNumber numberWithInt:self.value] forKey:@"value"]; + + return rootDict; } @end diff --git a/Spil/GameData/currencies/Currency.h b/Spil/GameData/currencies/Currency.h index 3c50589..d23136e 100644 --- a/Spil/GameData/currencies/Currency.h +++ b/Spil/GameData/currencies/Currency.h @@ -7,17 +7,16 @@ // #import -#import "JSONModel.h" -@protocol Currency -@end - -@interface Currency : JSONModel +@interface Currency : NSObject @property (assign, nonatomic) int id; @property (strong, nonatomic) NSString *name; @property (assign, nonatomic) int type; +@property (assign, nonatomic) int initialValue; + +-(id)initWithDictionary:(NSDictionary*)dict; --(id)init; +-(NSMutableDictionary*)toJSONObject; @end diff --git a/Spil/GameData/currencies/Currency.m b/Spil/GameData/currencies/Currency.m index c643ab5..9ce1f7b 100644 --- a/Spil/GameData/currencies/Currency.m +++ b/Spil/GameData/currencies/Currency.m @@ -13,15 +13,32 @@ @implementation Currency @synthesize id; @synthesize name; @synthesize type; +@synthesize initialValue; --(id)init { +-(id)initWithDictionary:(NSDictionary*)dict { self = [super init]; + self.id = [dict[@"id"] intValue]; + self.name = dict[@"name"]; + self.type = [dict[@"type"] intValue]; + self.initialValue = [dict[@"initialValue"] intValue]; + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSMutableDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.id] forKey:@"id"]; + + if (self.name != nil) { + [rootDict setObject:self.name forKey:@"name"]; + } + + [rootDict setObject:[NSNumber numberWithInt:self.type] forKey:@"type"]; + [rootDict setObject:[NSNumber numberWithInt:self.initialValue] forKey:@"initialValue"]; + + return rootDict; } -@end \ No newline at end of file +@end diff --git a/Spil/GameData/items/Item.h b/Spil/GameData/items/Item.h index 598cfff..1c10b06 100644 --- a/Spil/GameData/items/Item.h +++ b/Spil/GameData/items/Item.h @@ -7,17 +7,16 @@ // #import -#import "JSONModel.h" -@protocol Item -@end - -@interface Item : JSONModel +@interface Item : NSObject @property (assign, nonatomic) int id; @property (strong, nonatomic) NSString *name; @property (assign, nonatomic) int type; +@property (assign, nonatomic) int initialValue; + +-(id)initWithDictionary:(NSDictionary*)dict; --(id)init; +-(NSMutableDictionary*)toJSONObject; @end diff --git a/Spil/GameData/items/Item.m b/Spil/GameData/items/Item.m index 46943f0..5187019 100644 --- a/Spil/GameData/items/Item.m +++ b/Spil/GameData/items/Item.m @@ -13,15 +13,32 @@ @implementation Item @synthesize id; @synthesize name; @synthesize type; +@synthesize initialValue; --(id)init { +-(id)initWithDictionary:(NSDictionary*)dict { self = [super init]; + self.id = [dict[@"id"] intValue]; + self.name = dict[@"name"]; + self.type = [dict[@"type"] intValue]; + self.initialValue = [dict[@"initialValue"] intValue]; + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSMutableDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.id] forKey:@"id"]; + + if (self.name != nil) { + [rootDict setObject:self.name forKey:@"name"]; + } + + [rootDict setObject:[NSNumber numberWithInt:self.type] forKey:@"type"]; + [rootDict setObject:[NSNumber numberWithInt:self.initialValue] forKey:@"initialValue"]; + + return rootDict; } @end diff --git a/Spil/GameData/shop/ShopEntry.h b/Spil/GameData/shop/ShopEntry.h index bfd6138..bf2aa97 100644 --- a/Spil/GameData/shop/ShopEntry.h +++ b/Spil/GameData/shop/ShopEntry.h @@ -7,15 +7,15 @@ // #import -#import "JSONModel.h" -@protocol ShopEntry -@end - -@interface ShopEntry : JSONModel +@interface ShopEntry : NSObject @property (nonatomic, assign) int bundleId; @property (nonatomic, strong) NSString* label; @property (nonatomic, assign) int position; +-(id)initWithDictionary:(NSDictionary*)dict; + +-(NSDictionary*)toJSONObject; + @end diff --git a/Spil/GameData/shop/ShopEntry.m b/Spil/GameData/shop/ShopEntry.m index bc9ef53..c613c62 100644 --- a/Spil/GameData/shop/ShopEntry.m +++ b/Spil/GameData/shop/ShopEntry.m @@ -14,14 +14,28 @@ @implementation ShopEntry @synthesize label; @synthesize position; --(id)init { +-(id)initWithDictionary:(NSDictionary*)dict { self = [super init]; + self.bundleId = [dict[@"bundleId"] intValue]; + self.label = dict[@"label"]; + self.position = [dict[@"position"] intValue]; + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.bundleId] forKey:@"bundleId"]; + + if (self.label != nil) { + [rootDict setObject:self.label forKey:@"label"]; + } + + [rootDict setObject:[NSNumber numberWithInt:self.position] forKey:@"position"]; + + return rootDict; } -@end \ No newline at end of file +@end diff --git a/Spil/GameData/shop/ShopPromotion.h b/Spil/GameData/shop/ShopPromotion.h index bdb914e..1b616a1 100644 --- a/Spil/GameData/shop/ShopPromotion.h +++ b/Spil/GameData/shop/ShopPromotion.h @@ -8,20 +8,18 @@ #import #import "BundlePrice.h" -#import "JSONModel.h" -@protocol ShopPromotion -@end - -@interface ShopPromotion : JSONModel +@interface ShopPromotion : NSObject @property (nonatomic, assign) int bundleId; @property (nonatomic, assign) int amount; -@property (nonatomic, strong) NSMutableArray *prices; +@property (nonatomic, strong) NSMutableArray *prices; // BundlePrice @property (nonatomic, strong) NSString* discount; @property (nonatomic, strong) NSString* startDate; @property (nonatomic, strong) NSString* endDate; --(id)init; +-(id)initWithDictionary:(NSDictionary*)data; -@end \ No newline at end of file +-(NSDictionary*)toJSONObject; + +@end diff --git a/Spil/GameData/shop/ShopPromotion.m b/Spil/GameData/shop/ShopPromotion.m index 10fa189..31724d5 100644 --- a/Spil/GameData/shop/ShopPromotion.m +++ b/Spil/GameData/shop/ShopPromotion.m @@ -17,14 +17,53 @@ @implementation ShopPromotion @synthesize startDate; @synthesize endDate; --(id)init { +-(id)initWithDictionary:(NSDictionary*)data { self = [super init]; + self.bundleId = [data[@"bundleId"] intValue]; + self.amount = [data[@"amount"] intValue]; + self.discount = data[@"discount"]; + self.startDate = data[@"startDate"]; + self.endDate = data[@"endDate"]; + + self.prices = [NSMutableArray array]; + if (data[@"prices"] != nil) { + for (NSDictionary *dict in data[@"prices"]) { + BundlePrice *bundlePrice = [[BundlePrice alloc] initWithDictionary:dict]; + [self.prices addObject:bundlePrice]; + } + } + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.bundleId] forKey:@"bundleId"]; + [rootDict setObject:[NSNumber numberWithInt:self.amount] forKey:@"amount"]; + + if (self.discount != nil) { + [rootDict setObject:self.discount forKey:@"discount"]; + } + + if (self.startDate != nil) { + [rootDict setObject:self.startDate forKey:@"startDate"]; + } + + if (self.endDate != nil) { + [rootDict setObject:self.endDate forKey:@"endDate"]; + } + + NSMutableArray *priceArray = [NSMutableArray array]; + if (self.prices != nil) { + for (BundlePrice *price in self.prices) { + [priceArray addObject:[price toJSONObject]]; + } + } + [rootDict setObject:priceArray forKey:@"prices"]; + + return rootDict; } -@end \ No newline at end of file +@end diff --git a/Spil/GameData/shop/ShopTab.h b/Spil/GameData/shop/ShopTab.h index dd04cca..8dcb98b 100644 --- a/Spil/GameData/shop/ShopTab.h +++ b/Spil/GameData/shop/ShopTab.h @@ -7,18 +7,16 @@ // #import -#import "JSONModel.h" #import "ShopEntry.h" -@protocol ShopTab -@end - -@interface ShopTab : JSONModel +@interface ShopTab : NSObject @property (nonatomic, assign) int position; @property (nonatomic, strong) NSString* name; -@property (nonatomic, strong) NSMutableArray *entries; +@property (nonatomic, strong) NSMutableArray *entries; // ShopEntry --(id)init; +-(id)initWithDictionary:(NSDictionary*)data; -@end \ No newline at end of file +-(NSDictionary*)toJSONObject; + +@end diff --git a/Spil/GameData/shop/ShopTab.m b/Spil/GameData/shop/ShopTab.m index 3771c35..76d0920 100644 --- a/Spil/GameData/shop/ShopTab.m +++ b/Spil/GameData/shop/ShopTab.m @@ -14,14 +14,41 @@ @implementation ShopTab @synthesize name; @synthesize entries; --(id)init { +-(id)initWithDictionary:(NSDictionary*)data { self = [super init]; + self.position = [data[@"position"] intValue]; + self.name = data[@"name"]; + + self.entries = [NSMutableArray array]; + if (data[@"entries"] != nil) { + for (NSDictionary *dict in data[@"entries"]) { + ShopEntry *shopEntry = [[ShopEntry alloc] initWithDictionary:dict]; + [self.entries addObject:shopEntry]; + } + } + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.position] forKey:@"position"]; + + if (self.name != nil) { + [rootDict setObject:self.name forKey:@"name"]; + } + + NSMutableArray *entryArray = [NSMutableArray array]; + if (self.entries != nil) { + for (ShopEntry *entry in self.entries) { + [entryArray addObject:[entry toJSONObject]]; + } + } + [rootDict setObject:entryArray forKey:@"entries"]; + + return rootDict; } -@end \ No newline at end of file +@end diff --git a/Spil/HookBridge.h b/Spil/HookBridge.h index f137dd4..cf61394 100644 --- a/Spil/HookBridge.h +++ b/Spil/HookBridge.h @@ -26,26 +26,46 @@ extern "C" { void trackEventWithParamsNative(const char* eventName, const char* jsonStringParams); - void trackIAPPurchasedEvent(const char* skuId, const char* transactionId, const char* purchaseDate); + // --- Default events (Not used by the unity plugin, it uses the generic track event methods --- + + void trackMilestoneAchievedEvent(const char* name); + + void trackLevelStartEvent(const char* level, int score, int stars, int turns, bool customCreated, const char* creatorId); + + void trackLevelCompleteEvent(const char* level, int score, int stars, int turns, bool customCreated, const char* creatorId); + + void trackLevelFailed(const char* level, int score, int stars, int turns, bool customCreated, const char* creatorId); - void trackIAPRestoredEvent(const char* skuId, const char* originalTransactionId, const char* originalPurchaseDate); + void trackLevelUpEvent(const char* level, const char* objectId, const char* skillId); - void trackIAPFailedEvent(const char* skuId, const char* error); - - void trackWalletInventoryEvent(const char* currencyList, const char* itemsList, const char* reason, const char* location); + void trackEquipEvent(const char* equippedItem, const char* equippedTo); + + void trackUpgradeEvent(const char* upgradeId, const char* level, const char* reason, int iteration); + + void trackLevelCreateEvent(const char* levelId, const char* creatorId); + + void trackLevelDownloadEvent(const char* levelId, const char* creatorId, int rating); + + void trackLevelRateEvent(const char* levelId, const char* creatorId, int rating); - void trackMilestoneEvent(const char* name); + void trackEndlessModeStartEvent(); + + void trackEndlessModeEndEvent(int distance); - void trackLevelStartEvent(const char* level); + void trackPlayerDiesEvent(const char* level); + + void trackWalletInventoryEvent(const char* currencyList, const char* itemsList, const char* reason, const char* location); + + void trackIAPPurchasedEvent(const char* skuId, const char* transactionId, const char* purchaseDate); - void trackLevelCompleteEvent(const char* level, const char* score, const char* stars, const char* turns); + void trackIAPRestoredEvent(const char* skuId, const char* originalTransactionId, const char* originalPurchaseDate); - void trackLevelFailed(const char* level, const char* score, const char* turns); + void trackIAPFailedEvent(const char* skuId, const char* error); void trackTutorialCompleteEvent(); void trackTutorialSkippedEvent(); - + void trackRegisterEvent(const char* platform); void trackShareEvent(const char* platform); @@ -84,25 +104,27 @@ extern "C" { // --- Config --- - char* getConfigNative (); + char* getConfigNative(); - char* getConfigValueNative (const char* keyName); + char* getConfigValueNative(const char* keyName); // --- Packages & Promotions --- - void requestPackagesNative (); + void requestPackagesNative(); - char* getPackageNative (const char* keyName); + char* getPackageNative(const char* keyName); - char* getAllPackagesNative (); + char* getAllPackagesNative(); - char* getPromotionNative (const char* keyName); + char* getPromotionNative(const char* keyName); // --- ADS --- - void showMoreAppsNative (); + void showMoreAppsNative(); + + void requestRewardVideoNative(const char* rewardType); - void playRewardVideoNative (); + void playRewardVideoNative(); void devRequestAdNative(const char* providerName, const char* adTypeName, const bool parentalGate); @@ -118,23 +140,29 @@ extern "C" { // --- Game & Player data --- - void updatePlayerDataNative (); + void updatePlayerDataNative(); - char* getWalletNative (); + char* getWalletNative(); + + char* getSpilGameDataNative(); + + char* getInventoryNative(); + + void addCurrencyToWalletNative(int currencyId, int amount, char* reasonName, char* location); - char* getSpilGameDataNative (); + void subtractCurrencyFromWalletNative(int currencyId, int amount, char* reasonName, char* location); - char* getInventoryNative (); + void addItemToInventoryNative(int itemId, int amount, char* reasonName, char* location); - void addCurrencyToWalletNative (int currencyId, int amount, char* reasonName); + void subtractItemFromInventoryNative(int itemId, int amount, char* reasonName, char* location); - void subtractCurrencyFromWalletNative(int currencyId, int amount, char* reasonName); + void buyBundleNative(int itemId, char* reasonName, char* location); - void addItemToInventoryNative (int itemId, int amount, char* reasonName); + void resetPlayerDataNative(); - void subtractItemFromInventoryNative (int itemId, int amount, char* reasonName); + void resetInventoryNative(); - void consumeBundleNative (int itemId, char* reasonName); + void resetWalletNative(); // --- Customer support --- @@ -171,6 +199,6 @@ extern "C" { #endif -+ (void) sendMessage:(NSString*)messageName toObject:(NSString*)objectName withParameter:(NSString*)parameterString; ++ (void)sendMessage:(NSString*)messageName toObject:(NSString*)objectName withParameter:(NSString*)parameterString; @end diff --git a/Spil/HookBridge.mm b/Spil/HookBridge.mm index 6fef54a..0faecd1 100644 --- a/Spil/HookBridge.mm +++ b/Spil/HookBridge.mm @@ -9,6 +9,9 @@ #import "HookBridge.h" #import "SpilEventTracker.h" +#if TARGET_OS_IOS +#import "SpilNotificationHelper.h" +#endif #import "SpilActionHandler.h" #import "Spil.h" #import "SpilUserHandler.h" @@ -36,7 +39,6 @@ + (void) sendMessage:(NSString*)messageName toObject:(NSString*)objectName withP UnitySendMessage([objectName cStringUsingEncoding:NSUTF8StringEncoding], [messageName cStringUsingEncoding:NSUTF8StringEncoding], [parameterString cStringUsingEncoding:NSUTF8StringEncoding]); - //NSLog(@"[HookBridge] UnitySendMessage, object: %@ method: %@ data: %@", objectName, messageName, parameterString); } @catch (NSException *exception) { NSLog(@"[HookBridge] UnitySendMessage, exception:%@",exception.reason); @@ -109,6 +111,104 @@ void trackEventWithParamsNative(const char* eventName, const char* jsonStringPar [[SpilEventTracker sharedInstance] trackEvent:event withParameters:params]; } +// --- Default events (Not used by the unity plugin, it uses the generic track event methods --- + +void trackMilestoneAchievedEvent(const char* name) { + NSString *n = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackMilestoneAchievedEvent IN name: %@", n); + [Spil trackMilestoneAchievedEvent:n]; +} + +void trackLevelStartEvent(const char* level, int score, int stars, int turns, bool customCreated, const char* creatorId) { + NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; + NSString *ci = [NSString stringWithCString:creatorId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelStartEvent IN level: %@, score: %d, stars: %d, turns: %d, customCreated: %d, creatorId: %@", l, score, stars, turns, customCreated, ci); + [Spil trackLevelStartEvent:l score:score stars:stars turns:turns customCreated:customCreated creatorId:ci]; +} + +void trackLevelCompleteEvent(const char* level, int score, int stars, int turns, bool customCreated, const char* creatorId) { + NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; + NSString *ci = [NSString stringWithCString:creatorId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelCompleteEvent IN level: %@, score: %d, stars: %d, turns: %d, customCreated: %d, creatorId: %@", l, score, stars, turns, customCreated, ci); + [Spil trackLevelCompleteEvent:l score:score stars:stars turns:turns customCreated:customCreated creatorId:ci]; +} + +void trackLevelFailed(const char* level, int score, int stars, int turns, bool customCreated, const char* creatorId) { + NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; + NSString *ci = [NSString stringWithCString:creatorId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelFailed IN level: %@, score: %d, stars: %d, turns: %d, customCreated: %@, creatorId: %@", l, score, stars, turns, (customCreated ? @"YES" : @"NO"), ci); + [Spil trackLevelFailedEvent:l score:score stars:stars turns:turns customCreated:customCreated creatorId:ci]; +} + +void trackLevelUpEvent(const char* level, const char* objectId, const char* skillId) { + NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; + NSString *o = [NSString stringWithCString:objectId encoding:NSUTF8StringEncoding]; + NSString *s = [NSString stringWithCString:skillId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelUpEvent IN level: %@, objectId: %@, skillId: %@", l, o, s); + [Spil trackLevelUpEvent:l objectId:o skillId:s]; +} + +void trackEquipEvent(const char* equippedItem, const char* equippedTo) { + NSString *i = [NSString stringWithCString:equippedItem encoding:NSUTF8StringEncoding]; + NSString *t = [NSString stringWithCString:equippedTo encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackEquipEvent IN equippedItem: %@, equippedTo: %@", i, t); + [Spil trackEquipEvent:i equippedTo:t]; +} + +void trackUpgradeEvent(const char* upgradeId, const char* level, const char* reason, int iteration) { + NSString *u = [NSString stringWithCString:upgradeId encoding:NSUTF8StringEncoding]; + NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; + NSString *r = [NSString stringWithCString:reason encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackUpgradeEvent IN upgradeId: %@, level: %@, reason: %@, iteration: %d", u, l, r, iteration); + [Spil trackUpgradeEvent:u level:l reason:r iteration:iteration]; +} + +void trackLevelCreateEvent(const char* levelId, const char* creatorId) { + NSString *l = [NSString stringWithCString:levelId encoding:NSUTF8StringEncoding]; + NSString *c = [NSString stringWithCString:creatorId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelCreateEvent IN levelId: %@, creatorId: %@", l, c); + [Spil trackLevelCreateEvent:l creatorId:c]; +} + +void trackLevelDownloadEvent(const char* levelId, const char* creatorId, int rating) { + NSString *l = [NSString stringWithCString:levelId encoding:NSUTF8StringEncoding]; + NSString *c = [NSString stringWithCString:creatorId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelDownloadEvent IN levelId: %@, creatorId: %@, rating: %d", l, c, rating); + [Spil trackLevelDownloadEvent:l creatorId:c rating:rating]; +} + +void trackLevelRateEvent(const char* levelId, const char* creatorId, int rating) { + NSString *l = [NSString stringWithCString:levelId encoding:NSUTF8StringEncoding]; + NSString *c = [NSString stringWithCString:creatorId encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackLevelRateEvent IN levelId: %@, creatorId: %@, rating: %d", l, c, rating); + [Spil trackLevelRateEvent:l creatorId:c rating:rating]; +} + +void trackEndlessModeStartEvent() { + NSLog(@"[HookBridge] trackEndlessModeStartEvent"); + [Spil trackEndlessModeStartEvent]; +} + +void trackEndlessModeEndEvent(int distance) { + NSLog(@"[HookBridge] trackEndlessModeEndEvent IN distance: %d", distance); + [Spil trackEndlessModeEndEvent:distance]; +} + +void trackPlayerDiesEvent(const char* level) { + NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackPlayerDiesEvent IN level: %@", l); + [Spil trackPlayerDiesEvent:l]; +} + +void trackWalletInventoryEvent(const char* currencyList, const char* itemsList, const char* reason, const char* location) { + NSString *currency = [NSString stringWithCString:currencyList encoding:NSUTF8StringEncoding]; + NSString *items = [NSString stringWithCString:itemsList encoding:NSUTF8StringEncoding]; + NSString *r = [NSString stringWithCString:reason encoding:NSUTF8StringEncoding]; + NSString *l = [NSString stringWithCString:location encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] trackWalletInventoryEvent IN itemsList: %@, itemsList: %@, reason: %@, location: %@", currency, items, r, l); + [Spil trackWalletInventoryEvent:r location:l currencyList:currency itemList:items]; +} + void trackIAPPurchasedEvent(const char* skuId, const char* transactionId, const char* purchaseDate) { NSString *sku = [NSString stringWithCString:skuId encoding:NSUTF8StringEncoding]; NSString *transaction = [NSString stringWithCString:transactionId encoding:NSUTF8StringEncoding]; @@ -132,44 +232,6 @@ void trackIAPFailedEvent(const char* skuId, const char* error) { [Spil trackIAPFailedEvent:sku error:e]; } -void trackWalletInventoryEvent(const char* currencyList, const char* itemsList, const char* reason, const char* location) { - NSString *currency = [NSString stringWithCString:currencyList encoding:NSUTF8StringEncoding]; - NSString *items = [NSString stringWithCString:itemsList encoding:NSUTF8StringEncoding]; - NSString *r = [NSString stringWithCString:reason encoding:NSUTF8StringEncoding]; - NSString *l = [NSString stringWithCString:location encoding:NSUTF8StringEncoding]; - NSLog(@"[HookBridge] trackWalletInventoryEvent IN itemsList: %@, itemsList: %@, reason: %@, location: %@", currency, items, r, l); - [Spil trackWalletInventoryEvent:currency itemsList:items reason:r location:l]; -} - -void trackMilestoneEvent(const char* name) { - NSString *n = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; - NSLog(@"[HookBridge] trackMilestoneEvent IN name: %@", n); - [Spil trackMilestoneEvent:n]; -} - -void trackLevelStartEvent(const char* level) { - NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; - NSLog(@"[HookBridge] trackLevelStartEvent IN level: %@", l); - [Spil trackLevelStartEvent:l]; -} - -void trackLevelCompleteEvent(const char* level, const char* score, const char* stars, const char* turns) { - NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; - NSString *s = [NSString stringWithCString:score encoding:NSUTF8StringEncoding]; - NSString *st = [NSString stringWithCString:stars encoding:NSUTF8StringEncoding]; - NSString *t = [NSString stringWithCString:turns encoding:NSUTF8StringEncoding]; - NSLog(@"[HookBridge] trackLevelCompleteEvent IN level: %@, score: %@, stars: %@, turns: %@", l, s, st, t); - [Spil trackLevelCompleteEvent:l score:s stars:st turns:t]; -} - -void trackLevelFailed(const char* level, const char* score, const char* turns) { - NSString *l = [NSString stringWithCString:level encoding:NSUTF8StringEncoding]; - NSString *s = [NSString stringWithCString:score encoding:NSUTF8StringEncoding]; - NSString *t = [NSString stringWithCString:turns encoding:NSUTF8StringEncoding]; - NSLog(@"[HookBridge] trackLevelFailed IN level: %@, score: %@, turns: %@", l, s, t); - [Spil trackLevelFailed:l score:s turns:t]; -} - void trackTutorialCompleteEvent() { NSLog(@"[HookBridge] trackTutorialCompleteEvent"); [Spil trackTutorialCompleteEvent]; @@ -201,7 +263,13 @@ void trackInviteEvent(const char* platform) { // --- App flow --- void applicationDidFinishLaunchingWithOptions(const char* launchOptions) { - + NSString *launchOptionsString = [NSString stringWithCString:launchOptions encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] applicationDidFinishLaunchingWithOptions: %@", launchOptionsString); + + NSData *data = [launchOptionsString dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *params = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + + [Spil application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:params]; } void applicationDidEnterBackground() { @@ -219,11 +287,16 @@ void applicationDidBecomeActive() { // --- Push Notifications --- void disableAutomaticRegisterForPushNotificationsNative() { - + NSLog(@"[HookBridge] registerForPushNotificationsNative"); + + [Spil disableAutomaticRegisterForPushNotifications]; } void registerForPushNotifications() { NSLog(@"[HookBridge] registerForPushNotifications"); + #if TARGET_OS_IOS + [SpilNotificationHelper registerPushNotifications]; + #endif } void setPushNotificationKey(const char* pushKey){ @@ -370,6 +443,12 @@ void showMoreAppsNative () { [Spil showMoreApps]; } +void requestRewardVideoNative(const char* rewardType) { + NSString *r = [NSString stringWithCString:rewardType encoding:NSUTF8StringEncoding]; + NSLog(@"[HookBridge] requestRewardVideoNative IN name: %@", r); + [Spil requestRewardVideo:r]; +} + void playRewardVideoNative () { NSLog(@"[HookBridge] playRewardVideoNative"); @@ -419,16 +498,6 @@ void closedParentalGateNative(const bool pass) { // -- Game & Player data --- -// TODO: will be exposed later when the user profile is actually fully implemented -/*char* getUserProfileNative () { - if([[SpilEventTracker sharedInstance] getDebug]) { - NSLog(@"[HookBridge] getUserProfileNative"); - } - - NSString *jsonString = [Spil getUserProfile]; - return cStringCopy([jsonString UTF8String]); -}*/ - void updatePlayerDataNative () { if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { NSLog(@"[HookBridge] updatePlayerData"); @@ -464,63 +533,92 @@ void updatePlayerDataNative () { return cStringCopy([jsonString UTF8String]); } -void addCurrencyToWalletNative(int currencyId, int amount, char* reasonName) { +void addCurrencyToWalletNative(int currencyId, int amount, char* reasonName, char* locationName) { if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { NSLog(@"[HookBridge] addCurrencyToWallet"); } NSString *reason = [NSString stringWithCString:reasonName encoding:NSUTF8StringEncoding]; - [Spil addCurrencyToWallet:currencyId withAmount:amount withReason:reason]; + NSString *location = [NSString stringWithCString:locationName encoding:NSUTF8StringEncoding]; + [Spil addCurrencyToWallet:currencyId withAmount:amount withReason:reason withLocation:location]; } -void subtractCurrencyFromWalletNative(int currencyId, int amount, char* reasonName) { +void subtractCurrencyFromWalletNative(int currencyId, int amount, char* reasonName, char* locationName) { if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { NSLog(@"[HookBridge] subtractCurrencyFromWallet"); } NSString *reason = [NSString stringWithCString:reasonName encoding:NSUTF8StringEncoding]; - [Spil subtractCurrencyFromWallet:currencyId withAmount:amount withReason:reason]; + NSString *location = [NSString stringWithCString:locationName encoding:NSUTF8StringEncoding]; + [Spil subtractCurrencyFromWallet:currencyId withAmount:amount withReason:reason withLocation:location]; } -void addItemToInventoryNative(int itemId, int amount, char* reasonName) { +void addItemToInventoryNative(int itemId, int amount, char* reasonName, char* locationName) { if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { NSLog(@"[HookBridge] addItemToInventory"); } NSString *reason = [NSString stringWithCString:reasonName encoding:NSUTF8StringEncoding]; - [Spil addItemToInventory:itemId withAmount:amount withReason:reason]; + NSString *location = [NSString stringWithCString:locationName encoding:NSUTF8StringEncoding]; + [Spil addItemToInventory:itemId withAmount:amount withReason:reason withLocation:location]; } -void subtractItemFromInventoryNative(int itemId, int amount, char* reasonName) { +void subtractItemFromInventoryNative(int itemId, int amount, char* reasonName, char* locationName) { if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { NSLog(@"[HookBridge] subtractItemToInventory"); } NSString *reason = [NSString stringWithCString:reasonName encoding:NSUTF8StringEncoding]; - [Spil subtractItemFromInventory:itemId withAmount:amount withReason:reason]; + NSString *location = [NSString stringWithCString:locationName encoding:NSUTF8StringEncoding]; + [Spil subtractItemFromInventory:itemId withAmount:amount withReason:reason withLocation:location]; } -void consumeBundleNative(int itemId, char* reasonName) { +void buyBundleNative(int itemId, char* reasonName, char* locationName) { if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { - NSLog(@"[HookBridge] consumeBundle"); + NSLog(@"[HookBridge] buyBundleNative"); } NSString *reason = [NSString stringWithCString:reasonName encoding:NSUTF8StringEncoding]; - [Spil consumeBundle:itemId withReason:reason]; + NSString *location = [NSString stringWithCString:locationName encoding:NSUTF8StringEncoding]; + [Spil buyBundle:itemId withReason:reason withLocation:location]; +} + +void resetPlayerDataNative () { + [Spil resetPlayerData]; +} + +void resetInventoryNative () { + [Spil resetInventory]; +} + +void resetWalletNative () { + [Spil resetWallet]; } // --- Customer support --- void showHelpCenterNative() { - + if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { + NSLog(@"[HookBridge] showHelpCenterNative"); + } + + [Spil showHelpCenter]; } void showContactCenterNative() { - + if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { + NSLog(@"[HookBridge] showContactCenterNative"); + } + + [Spil showContactCenter]; } void showHelpCenterWebviewNative() { - + if([[SpilEventTracker sharedInstance] getAdvancedLoggingEnabled]) { + NSLog(@"[HookBridge] showHelpCenterWebviewNative"); + } + + [Spil showHelpCenterWebview]; } // --- Web --- @@ -610,7 +708,7 @@ void setPublicGameStateNative(const char* publicData) { void getOtherUsersGameStateNative(const char* provider, const char* userIdsJsonArray) { NSString *providerString = [NSString stringWithCString:provider encoding:NSUTF8StringEncoding]; NSString *uidsString = [NSString stringWithCString:userIdsJsonArray encoding:NSUTF8StringEncoding]; - NSLog(@"[HookBridge] setPublicGameStateNative IN userIdsJsonArray: %@", uidsString); + NSLog(@"[HookBridge] getOtherUsersGameStateNative IN userIdsJsonArray: %@", uidsString); NSArray *userIds = [JsonUtil convertStringToObject:uidsString]; [Spil getOtherUsersGameState:providerString userIds:userIds]; diff --git a/Spil/PlayerData/Inventory/Inventory.h b/Spil/PlayerData/Inventory/Inventory.h index af93096..5f679ae 100644 --- a/Spil/PlayerData/Inventory/Inventory.h +++ b/Spil/PlayerData/Inventory/Inventory.h @@ -8,18 +8,20 @@ #import #import "PlayerItem.h" -#import "JSONModel.h" -@protocol Inventory -@end - -@interface Inventory : JSONModel +@interface Inventory : NSObject -@property (strong, nonatomic) NSMutableArray *items; +@property (strong, nonatomic) NSMutableArray *items; // PlayerItem @property (nonatomic) int offset; @property (strong, nonatomic) NSString* logic; -(id)init; +-(id)initWithDictionary:(NSDictionary*)data; + +-(NSDictionary*)toJSONObject; +-(NSString*)toJSONString; + +-(NSArray*)getItemsJSONArray; -(void)updateItem:(PlayerItem*)item; @@ -27,4 +29,6 @@ -(PlayerItem*)getItem:(int)id; -@end \ No newline at end of file +-(void)reset; + +@end diff --git a/Spil/PlayerData/Inventory/Inventory.m b/Spil/PlayerData/Inventory/Inventory.m index 47d45f4..517ca96 100644 --- a/Spil/PlayerData/Inventory/Inventory.m +++ b/Spil/PlayerData/Inventory/Inventory.m @@ -8,6 +8,7 @@ #import "Inventory.h" #import "GameDataController.h" +#import "JsonUtil.h" @implementation Inventory @@ -21,6 +22,57 @@ -(id)init { return self; } +-(id)initWithDictionary:(NSDictionary*)data { + self = [super init]; + + self.offset = [data[@"offset"] intValue]; + self.logic = data[@"logic"]; + + self.items = [NSMutableArray array]; + if (data[@"items"] != nil) { + for (NSDictionary *dict in data[@"items"]) { + PlayerItem *playerItem = [[PlayerItem alloc] initWithDictionary:dict]; + [self.items addObject:playerItem]; + } + } + + return self; +} + +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.offset] forKey:@"offset"]; + + if (self.logic != nil) { + [rootDict setObject:self.logic forKey:@"logic"]; + } + + NSMutableArray *itemArray = [NSMutableArray array]; + if (self.items != nil) { + for (PlayerItem *item in self.items) { + [itemArray addObject:[item toJSONObject]]; + } + } + [rootDict setObject:itemArray forKey:@"items"]; + + return rootDict; +} + +-(NSArray*)getItemsJSONArray { + NSMutableArray *itemArray = [NSMutableArray array]; + if (self.items != nil) { + for (PlayerItem *item in self.items) { + [itemArray addObject:[item toJSONObject]]; + } + } + return itemArray; +} + +-(NSString*)toJSONString { + return [JsonUtil convertObjectToJson:[self toJSONObject]]; +} + -(void)updateItem:(PlayerItem*)item { if([items count ] < 1){ for(int i = 0; i < [items count]; i++){ @@ -59,15 +111,21 @@ -(NSArray*)getUpdatedItems { PlayerItem* item = (PlayerItem*)[items objectAtIndex:i]; if(item.delta != 0) { - [result addObject:[item toDictionary]]; + [result addObject:[item toJSONObject]]; } } return result; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(void)reset { + for(int i = 0; i < items.count; i++){ + PlayerItem* item = (PlayerItem*)[items objectAtIndex:i]; + if (item.amount != 0) { + item.delta -= (item.amount - item.initialValue); + item.amount = item.initialValue; + } + } } -@end \ No newline at end of file +@end diff --git a/Spil/PlayerData/Inventory/PlayerItem.h b/Spil/PlayerData/Inventory/PlayerItem.h index c3269dc..eb12094 100644 --- a/Spil/PlayerData/Inventory/PlayerItem.h +++ b/Spil/PlayerData/Inventory/PlayerItem.h @@ -19,5 +19,8 @@ -(id)init; -(id)initWithItem:(Item*)item; +-(id)initWithDictionary:(NSDictionary*)dict; + +-(NSDictionary*)toJSONObject; @end diff --git a/Spil/PlayerData/Inventory/PlayerItem.m b/Spil/PlayerData/Inventory/PlayerItem.m index 122a9ff..39099ff 100644 --- a/Spil/PlayerData/Inventory/PlayerItem.m +++ b/Spil/PlayerData/Inventory/PlayerItem.m @@ -24,14 +24,29 @@ -(id)initWithItem:(Item*)item { self = [super init]; [ClassUtil copyParent:item intoChild:self]; - self.amount = 0; - self.delta = 0; + self.amount = item.initialValue; + self.delta = item.initialValue; + self.initialValue = item.initialValue; return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(id)initWithDictionary:(NSDictionary*)dict { + self = [super initWithDictionary:dict]; + + self.amount = [dict[@"amount"] intValue]; + self.delta = [dict[@"delta"] intValue]; + + return self; +} + +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [super toJSONObject]; + + [rootDict setObject:[NSNumber numberWithInt:self.amount] forKey:@"amount"]; + [rootDict setObject:[NSNumber numberWithInt:self.delta] forKey:@"delta"]; + + return rootDict; } -@end \ No newline at end of file +@end diff --git a/Spil/PlayerData/PlayerDataController.h b/Spil/PlayerData/PlayerDataController.h index c1bda06..0f527c7 100644 --- a/Spil/PlayerData/PlayerDataController.h +++ b/Spil/PlayerData/PlayerDataController.h @@ -28,11 +28,15 @@ -(NSString*)getWallet; -(NSString*)getInventory; --(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)reason; --(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NSString*)action withReason:(NSString*)reason; --(void)updateInventoryWithBundle:(int)bundleId withReason:(NSString*)reason; +-(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)reason withLocation:(NSString*)location; +-(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NSString*)action withReason:(NSString*)reason withLocation:(NSString*)location; +-(void)updateInventoryWithBundle:(int)bundleId withReason:(NSString*)reason withLocation:(NSString*)location; -(void)updateUserProfile:(UserProfile*)userProfile; -(NSString*)loadPlayerDataFromAssets; +-(void)resetPlayerData; +-(void)resetInventory; +-(void)resetWallet; + @end diff --git a/Spil/PlayerData/PlayerDataController.m b/Spil/PlayerData/PlayerDataController.m index a461fa9..b6ce60c 100644 --- a/Spil/PlayerData/PlayerDataController.m +++ b/Spil/PlayerData/PlayerDataController.m @@ -18,6 +18,7 @@ #import "BundleItem.h" #import "GameData.h" #import "SpilError.h" +#import "JsonUtil.h" @implementation PlayerDataController @@ -53,7 +54,7 @@ -(void)requestPlayerData { } -(void)updatePlayerData { - [self sendUpdatePlayerDataEvent:[self getUserProfile] withBundle:nil withReason:@"update"]; + [self sendUpdatePlayerDataEvent:[self getUserProfile] withBundle:nil withReason:@"update" withLocation:@""]; } //Exposed to SDK @@ -144,9 +145,9 @@ -(void)processPlayerData:(Wallet*)updatedWallet withInventory:(Inventory*)update if(previousWalletOffset < updatedWallet.offset || previousInventoryOffset < updatedInventory.offset) { // Send update notification NSMutableDictionary *updatedData = [NSMutableDictionary dictionary]; - NSArray* itemObjects = [PlayerItem arrayOfDictionariesFromModels:updatedInventory.items]; + NSArray* itemObjects = [updatedInventory getItemsJSONArray]; [updatedData setObject:itemObjects forKey:@"items"]; - NSArray* currencyObjects = [PlayerCurrency arrayOfDictionariesFromModels:updatedWallet.currencies]; + NSArray* currencyObjects = [updatedWallet getCurrenciesJSONArray]; [updatedData setObject:currencyObjects forKey:@"currencies"]; NSDictionary *userInfo = @{@"event" : @"playerDataUpdated", @"reason" : @"Server Update", @"updatedData" : updatedData}; [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; @@ -166,17 +167,21 @@ -(UserProfile*)getUserProfile { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *userProfileString = [defaults objectForKey:@"spilUserProfile"]; - NSError *error = nil; + bool newInstall = false; UserProfile *userProfile = nil; if(userProfileString != nil) { - userProfile = [[UserProfile alloc] initWithString:userProfileString error:&error]; + NSDictionary *jsonData = [JsonUtil convertStringToObject:userProfileString]; + userProfile = [[UserProfile alloc] initWithDictionary:jsonData]; } else { userProfileString = [self loadPlayerDataFromAssets]; if(userProfileString != nil){ [defaults setObject:userProfileString forKey:@"spilUserProfile"]; [defaults synchronize]; - userProfile = [[UserProfile alloc] initWithString:userProfileString error:&error]; + + newInstall = true; + NSDictionary *jsonData = [JsonUtil convertStringToObject:userProfileString]; + userProfile = [[UserProfile alloc] initWithDictionary:jsonData]; } else { // Send a notification when the data failed to load NSDictionary *userInfo = @{@"event" : @"playerDataError", @"message" : [[SpilError LoadFailed:@"User Profile is empty!"] toJson]}; @@ -188,13 +193,14 @@ -(UserProfile*)getUserProfile { if (userProfile != nil) { // Check for missing arrays if (userProfile.wallet.currencies == nil) { - userProfile.wallet.currencies = [NSMutableArray array]; + userProfile.wallet.currencies = [NSMutableArray array]; // PlayerCurrency } if (userProfile.inventory.items == nil) { - userProfile.inventory.items = [NSMutableArray array]; + userProfile.inventory.items = [NSMutableArray array]; // PlayerItem } // Add missing gamedata currencies + bool sendUpdateAfterProcessing = false; NSArray *currencies = [[GameDataController sharedInstance] getGameData].currencies; if (currencies != nil) { for (Currency *currency in currencies) { @@ -207,8 +213,15 @@ -(UserProfile*)getUserProfile { } if (foundCurrency != nil) { foundCurrency.name = currency.name; + foundCurrency.initialValue = currency.initialValue; + if (newInstall == true) { + foundCurrency.delta = currency.initialValue; + foundCurrency.currentBalance = currency.initialValue; + sendUpdateAfterProcessing = true; + } } else { [userProfile.wallet.currencies addObject:[[PlayerCurrency alloc] initWithCurrency:currency]]; + sendUpdateAfterProcessing = true; } } } @@ -226,11 +239,22 @@ -(UserProfile*)getUserProfile { } if (foundItem != nil) { foundItem.name = item.name; + foundItem.initialValue = item.initialValue; + if (newInstall == true) { + foundItem.delta = item.initialValue; + foundItem.amount = item.initialValue; + sendUpdateAfterProcessing = true; + } } else { [userProfile.inventory.items addObject:[[PlayerItem alloc] initWithItem:item]]; + sendUpdateAfterProcessing = true; } } } + + if (sendUpdateAfterProcessing) { + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:@"initialValue" withLocation:@"sdk"]; + } } return userProfile; @@ -288,7 +312,7 @@ -(void)clientWalletUpdateAllowedTimerFinished:(NSTimer*)timer { } //Exposed to SDK - Case 1 (see Gist https://gist.github.com/sebastian24/392bd6a37d6c09c4bec9a13cb0e1bf3a ) --(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)reason { +-(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)reason withLocation:(NSString*)location { // Check for existing profile UserProfile *userProfile = [self getUserProfile]; if(userProfile == nil){ @@ -320,7 +344,7 @@ -(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)r currency.currentBalance += delta; // Send the data - [self sendUpdatePlayerDataEvent:userProfile withBundle: nil withReason:reason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle: nil withReason:reason withLocation:location]; currency.delta = 0; @@ -332,13 +356,13 @@ -(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)r [clientWalletUpdateAllowedTimer invalidate]; clientWalletUpdateAllowedTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(clientWalletUpdateAllowedTimerFinished:) userInfo:nil repeats:NO]; } else if (![latestWalletReason isEqualToString:reason]) { - [self sendUpdatePlayerDataEvent:userProfile withBundle: nil withReason:latestWalletReason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle: nil withReason:latestWalletReason withLocation:location]; // Adjust the values currency.delta = delta; currency.currentBalance += delta; - [self sendUpdatePlayerDataEvent:userProfile withBundle: nil withReason:reason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle: nil withReason:reason withLocation:location]; currency.delta = 0; // Save to shared prefs @@ -363,7 +387,7 @@ -(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)r NSMutableDictionary *updatedData = [NSMutableDictionary dictionary]; [updatedData setObject:@[] forKey:@"items"]; - NSMutableDictionary *dict = [[currency toDictionary] mutableCopy]; + NSMutableDictionary *dict = [[currency toJSONObject] mutableCopy]; [dict setObject:[NSNumber numberWithInt:delta] forKey:@"delta"]; [updatedData setObject:@[dict] forKey:@"currencies"]; @@ -375,7 +399,7 @@ -(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)r currency.currentBalance += delta; // Send the changes to the server - [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:reason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:reason withLocation:location]; // Revert the changed values because it should be validated by the server first before setting it on the client side currency.delta -= delta; @@ -384,7 +408,7 @@ -(void)updateWallet:(int)currencyId withDelta:(int)delta withReason:(NSString*)r } //Exposed to SDK - Case 2 (see Gist https://gist.github.com/sebastian24/392bd6a37d6c09c4bec9a13cb0e1bf3a ) --(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NSString*)action withReason:(NSString*)reason { +-(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NSString*)action withReason:(NSString*)reason withLocation:(NSString*)location { // Check user profile UserProfile *userProfile = [self getUserProfile]; if(userProfile == nil){ @@ -421,7 +445,7 @@ -(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NS item.amount += delta; // Send the data - [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:reason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:reason withLocation:location]; item.delta = 0; // Save to shared prefs @@ -430,7 +454,7 @@ -(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NS // Send notification NSMutableDictionary *updatedData = [NSMutableDictionary dictionary]; - NSMutableDictionary *dict = [[item toDictionary] mutableCopy]; + NSMutableDictionary *dict = [[item toJSONObject] mutableCopy]; [dict setObject:[NSNumber numberWithInt:delta] forKey:@"delta"]; [updatedData setObject:@[dict] forKey:@"items"]; @@ -443,7 +467,7 @@ -(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NS item.amount += delta; // Send the changes to the server - [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:reason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:reason withLocation:location]; // Revert the changed values because it should be validated by the server first before setting it on the client side item.delta -= delta; @@ -452,7 +476,7 @@ -(void)updateInventoryWithItem:(int)itemId withAmount:(int)amount withAction:(NS } //Exposed to SDK - Case 3 (see Gist https://gist.github.com/sebastian24/392bd6a37d6c09c4bec9a13cb0e1bf3a ) --(void)updateInventoryWithBundle:(int)bundleId withReason:(NSString*)reason { +-(void)updateInventoryWithBundle:(int)bundleId withReason:(NSString*)reason withLocation:(NSString*)location { // Check user profile UserProfile *userProfile = [self getUserProfile]; if(userProfile == nil){ @@ -520,7 +544,7 @@ -(void)updateInventoryWithBundle:(int)bundleId withReason:(NSString*)reason { } // Send the updated profile to the backend - [self sendUpdatePlayerDataEvent:userProfile withBundle: bundle withReason:reason]; + [self sendUpdatePlayerDataEvent:userProfile withBundle: bundle withReason:reason withLocation:location]; // Reset the deltas for (BundlePrice *bundlePrice in bundle.prices) { @@ -545,9 +569,9 @@ -(void)updateInventoryWithBundle:(int)bundleId withReason:(NSString*)reason { // Send notification NSMutableDictionary *updatedData = [NSMutableDictionary dictionary]; - NSArray* itemObjects = [PlayerItem arrayOfDictionariesFromModels:bundle.items]; + NSArray* itemObjects = [bundle getItemsJSONArray]; [updatedData setObject:itemObjects forKey:@"items"]; - NSArray* currencyObjects = [PlayerCurrency arrayOfDictionariesFromModels:bundle.prices]; + NSArray* currencyObjects = [bundle getPricesJSONArray]; [updatedData setObject:currencyObjects forKey:@"currencies"]; NSDictionary *userInfo = @{@"event" : @"playerDataUpdated", @"reason" : reason, @"updatedData" : updatedData}; [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; @@ -577,7 +601,7 @@ -(NSString*)loadPlayerDataFromAssets { return @""; } --(void)sendUpdatePlayerDataEvent:(UserProfile*)userProfile withBundle:(Bundle*)bundle withReason:(NSString*)reason { +-(void)sendUpdatePlayerDataEvent:(UserProfile*)userProfile withBundle:(Bundle*)bundle withReason:(NSString*)reason withLocation:(NSString*)location { NSMutableDictionary *requestData = [NSMutableDictionary dictionary]; // Add wallet @@ -610,8 +634,40 @@ -(void)sendUpdatePlayerDataEvent:(UserProfile*)userProfile withBundle:(Bundle*)b } // Add update metadata + [requestData setObject:location forKey:@"location"]; [requestData setObject:reason forKey:@"reason"]; [[SpilEventTracker sharedInstance] trackEvent:@"updatePlayerData" withParameters:requestData]; } +-(void)resetPlayerData { + UserProfile *userProfile = [self getUserProfile]; + [userProfile.inventory reset]; + [userProfile.wallet reset]; + [self updateUserProfile:userProfile]; + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:@"Reset" withLocation:@""]; + + NSDictionary *userInfo = @{@"event" : @"playerDataUpdated", @"reason" : @"Reset"}; + [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; +} + +-(void)resetInventory { + UserProfile *userProfile = [self getUserProfile]; + [userProfile.inventory reset]; + [self updateUserProfile:userProfile]; + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:@"Reset" withLocation:@""]; + + NSDictionary *userInfo = @{@"event" : @"playerDataUpdated", @"reason" : @"Reset"}; + [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; +} + +-(void)resetWallet { + UserProfile *userProfile = [self getUserProfile]; + [userProfile.wallet reset]; + [self updateUserProfile:userProfile]; + [self sendUpdatePlayerDataEvent:userProfile withBundle:nil withReason:@"Reset" withLocation:@""]; + + NSDictionary *userInfo = @{@"event" : @"playerDataUpdated", @"reason" : @"Reset"}; + [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; +} + @end diff --git a/Spil/PlayerData/UserProfile.h b/Spil/PlayerData/UserProfile.h index 07746d5..3995b66 100644 --- a/Spil/PlayerData/UserProfile.h +++ b/Spil/PlayerData/UserProfile.h @@ -9,12 +9,8 @@ #import #import "Wallet.h" #import "Inventory.h" -#import "JSONModel.h" -@protocol UserProfile -@end - -@interface UserProfile : JSONModel +@interface UserProfile : NSObject @property (strong, nonatomic) NSString *userID; @property (strong, nonatomic) NSString *facebookID; @@ -27,6 +23,9 @@ @property (strong, nonatomic) Wallet *wallet; @property (strong, nonatomic) Inventory *inventory; --(id)init; +-(id)initWithDictionary:(NSDictionary*)dict; -@end \ No newline at end of file +-(NSDictionary*)toJSONObject; +-(NSString*)toJSONString; + +@end diff --git a/Spil/PlayerData/UserProfile.m b/Spil/PlayerData/UserProfile.m index 28b68bc..062c3dc 100644 --- a/Spil/PlayerData/UserProfile.m +++ b/Spil/PlayerData/UserProfile.m @@ -7,6 +7,7 @@ // #import "UserProfile.h" +#import "JsonUtil.h" @implementation UserProfile @@ -15,19 +16,68 @@ @implementation UserProfile @synthesize playGamesID; @synthesize username; @synthesize gender; +@synthesize age; @synthesize country; @synthesize wallet; @synthesize inventory; --(id)init { +-(id)initWithDictionary:(NSDictionary*)dict { self = [super init]; + self.userID = dict[@"userID"]; + self.facebookID = dict[@"facebookID"]; + self.playGamesID = dict[@"playGamesID"]; + self.username = dict[@"username"]; + self.gender = dict[@"gender"]; + self.age = dict[@"age"]; + self.country = dict[@"country"]; + + self.wallet = [[Wallet alloc] initWithDictionary:dict[@"wallet"]]; + self.inventory = [[Inventory alloc] initWithDictionary:dict[@"inventory"]]; + return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + if (self.userID != nil) { + [rootDict setObject:self.userID forKey:@"userID"]; + } + + if (self.facebookID != nil) { + [rootDict setObject:self.facebookID forKey:@"facebookID"]; + } + + if (self.playGamesID != nil) { + [rootDict setObject:self.playGamesID forKey:@"playGamesID"]; + } + + if (self.username != nil) { + [rootDict setObject:self.username forKey:@"username"]; + } + + if (self.gender != nil) { + [rootDict setObject:self.gender forKey:@"gender"]; + } + + if (self.age != nil) { + [rootDict setObject:self.age forKey:@"age"]; + } + + if (self.country != nil) { + [rootDict setObject:self.country forKey:@"country"]; + } + + [rootDict setObject:[self.wallet toJSONObject] forKey:@"wallet"]; + [rootDict setObject:[self.inventory toJSONObject] forKey:@"inventory"]; + + return rootDict; +} + +-(NSString*)toJSONString { + return [JsonUtil convertObjectToJson:[self toJSONObject]]; } -@end \ No newline at end of file +@end diff --git a/Spil/PlayerData/Wallet/PlayerCurrency.h b/Spil/PlayerData/Wallet/PlayerCurrency.h index ef734cd..562e370 100644 --- a/Spil/PlayerData/Wallet/PlayerCurrency.h +++ b/Spil/PlayerData/Wallet/PlayerCurrency.h @@ -9,17 +9,15 @@ #import #import "Currency.h" -@protocol PlayerCurrency - -@end - @interface PlayerCurrency : Currency @property (nonatomic) int currentBalance; @property (nonatomic) int delta; -(id)init; - -(id)initWithCurrency:(Currency*)currency; +-(id)initWithDictionary:(NSDictionary*)dict; + +-(NSDictionary*)toJSONObject; @end diff --git a/Spil/PlayerData/Wallet/PlayerCurrency.m b/Spil/PlayerData/Wallet/PlayerCurrency.m index e7bfd62..403fc9f 100644 --- a/Spil/PlayerData/Wallet/PlayerCurrency.m +++ b/Spil/PlayerData/Wallet/PlayerCurrency.m @@ -24,14 +24,29 @@ -(id)initWithCurrency:(Currency*)currency { self = [super init]; [ClassUtil copyParent:currency intoChild:self]; - self.currentBalance = 0; - self.delta = 0; + self.currentBalance = currency.initialValue; + self.initialValue = currency.initialValue; + self.delta = currency.initialValue; return self; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; +-(id)initWithDictionary:(NSDictionary*)dict { + self = [super initWithDictionary:dict]; + + self.currentBalance = [dict[@"currentBalance"] intValue]; + self.delta = [dict[@"delta"] intValue]; + + return self; +} + +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [super toJSONObject]; + + [rootDict setObject:[NSNumber numberWithInt:self.currentBalance] forKey:@"currentBalance"]; + [rootDict setObject:[NSNumber numberWithInt:self.delta] forKey:@"delta"]; + + return rootDict; } -@end \ No newline at end of file +@end diff --git a/Spil/PlayerData/Wallet/Wallet.h b/Spil/PlayerData/Wallet/Wallet.h index b33a711..35aee78 100644 --- a/Spil/PlayerData/Wallet/Wallet.h +++ b/Spil/PlayerData/Wallet/Wallet.h @@ -8,19 +8,21 @@ #import #import "PlayerCurrency.h" -#import "JSONModel.h" #import "Bundle.h" -@protocol Wallet -@end - -@interface Wallet : JSONModel +@interface Wallet : NSObject -@property (strong, nonatomic) NSMutableArray *currencies; +@property (strong, nonatomic) NSMutableArray *currencies; // PlayerCurrency @property (nonatomic) int offset; @property (strong, nonatomic) NSString* logic; -(id)init; +-(id)initWithDictionary:(NSDictionary*)data; + +-(NSDictionary*)toJSONObject; +-(NSString*)toJSONString; + +-(NSArray*)getCurrenciesJSONArray; -(PlayerCurrency*)getCurrency:(int)id; @@ -28,4 +30,6 @@ -(BOOL)hasEnoughCurrencyForBundle:(Bundle*)bundle; -@end \ No newline at end of file +-(void)reset; + +@end diff --git a/Spil/PlayerData/Wallet/Wallet.m b/Spil/PlayerData/Wallet/Wallet.m index ebd52d2..6c34fd9 100644 --- a/Spil/PlayerData/Wallet/Wallet.m +++ b/Spil/PlayerData/Wallet/Wallet.m @@ -10,6 +10,7 @@ #import "GameDataController.h" #import "PlayerDataController.h" #import "UserProfile.h" +#import "JsonUtil.h" @implementation Wallet @@ -20,9 +21,61 @@ @implementation Wallet -(id)init { self = [super init]; + return self; } +-(id)initWithDictionary:(NSDictionary*)data { + self = [super init]; + + self.offset = [data[@"offset"] intValue]; + self.logic = data[@"logic"]; + + self.currencies = [NSMutableArray array]; + if (data[@"currencies"] != nil) { + for (NSDictionary *dict in data[@"currencies"]) { + PlayerCurrency *playerCurrency = [[PlayerCurrency alloc] initWithDictionary:dict]; + [self.currencies addObject:playerCurrency]; + } + } + + return self; +} + +-(NSDictionary*)toJSONObject { + NSMutableDictionary *rootDict = [NSMutableDictionary dictionary]; + + [rootDict setObject:[NSNumber numberWithInt:self.offset] forKey:@"offset"]; + + if (self.logic != nil) { + [rootDict setObject:self.logic forKey:@"logic"]; + } + + NSMutableArray *currencyArray = [NSMutableArray array]; + if (self.currencies != nil) { + for (PlayerCurrency *currency in self.currencies) { + [currencyArray addObject:[currency toJSONObject]]; + } + } + [rootDict setObject:currencyArray forKey:@"currencies"]; + + return rootDict; +} + +-(NSArray*)getCurrenciesJSONArray { + NSMutableArray *currencyArray = [NSMutableArray array]; + if (self.currencies != nil) { + for (PlayerCurrency *currency in self.currencies) { + [currencyArray addObject:[currency toJSONObject]]; + } + } + return currencyArray; +} + +-(NSString*)toJSONString { + return [JsonUtil convertObjectToJson:[self toJSONObject]]; +} + -(PlayerCurrency*)getCurrency:(int)id { for(int i = 0; i < [self.currencies count]; i++){ PlayerCurrency *playerCurrency = [self.currencies objectAtIndex:i]; @@ -34,10 +87,6 @@ -(PlayerCurrency*)getCurrency:(int)id { return nil; } -+(BOOL)propertyIsOptional:(NSString*)propertyName { - return YES; -} - -(NSArray*)getUpdatedCurrencies { NSMutableArray *result = [NSMutableArray array]; @@ -45,7 +94,7 @@ -(NSArray*)getUpdatedCurrencies { PlayerCurrency* currency = (PlayerCurrency*)[currencies objectAtIndex:i]; if(currency.delta != 0) { - [result addObject:[currency toDictionary]]; + [result addObject:[currency toJSONObject]]; } } @@ -63,4 +112,14 @@ -(BOOL)hasEnoughCurrencyForBundle:(Bundle*)bundle { return true; } -@end \ No newline at end of file +-(void)reset { + for(int i = 0; i < currencies.count; i++){ + PlayerCurrency* currency = (PlayerCurrency*)[currencies objectAtIndex:i]; + if (currency.currentBalance != 0) { + currency.delta -= (currency.currentBalance - currency.initialValue); + currency.currentBalance = currency.initialValue; + } + } +} + +@end diff --git a/Spil/Spil.h b/Spil/Spil.h index 9c49487..5259eec 100644 --- a/Spil/Spil.h +++ b/Spil/Spil.h @@ -8,11 +8,15 @@ #import "JsonUtil.h" #import "HookBridge.h" +#if TARGET_OS_IOS +#import "GAI.h" +#endif -#define SDK_VERSION @"2.1.4" +#define SDK_VERSION @"2.1.8" @class Spil; @class UserProfile; +@class SpilEventTracker; @protocol SpilDelegate @@ -118,7 +122,6 @@ /** * Get the Spil user id - * */ +(NSString*)getSpilUserId; @@ -132,6 +135,14 @@ #pragma mark App flow +/** + * Forwarding Delegate method to let the Spil framework know when the app was launched + * + * @param application Delegate application to be passed + * @param launchOptions Dictionary with launch options + */ ++(void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions; + /** * Forwarding Delegate method to let the Spil framework know when the app went to the background * @@ -146,64 +157,171 @@ */ +(void)applicationDidBecomeActive:(UIApplication *)application; -#pragma mark Event tracking - -/** - * @param skuId The product identifier of the item that was purchased - * @param transactionId The transaction identifier of the item that was purchased (also called orderId) - * @param purchaseDate The date and time that the item was purchased - */ -+(void)trackIAPPurchasedEvent:(NSString*)skuId transactionId:(NSString*)transactionId purchaseDate:(NSString*)purchaseDate; - /** - * @param skuId The product identifier of the item that was purchased - * @param originalTransactionId For a transaction that restores a previous transaction, the transaction identifier of the original transaction. - * Otherwise, identical to the transaction identifier - * @param originalPurchaseDate For a transaction that restores a previous transaction, the date of the original transaction + * Handle remote notification packages + * + * @param Application Reference to the UIApplication object + * @param userInfo Reference to the push notification payload */ -+(void)trackIAPRestoredEvent:(NSString*)skuId originalTransactionId:(NSString*)originalTransactionId originalPurchaseDate:(NSString*)originalPurchaseDate; ++(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo; -/** - * @param skuId The product identifier of the item that was purchased - * @param error Error description or error code - */ -+(void)trackIAPFailedEvent:(NSString*)skuId error:(NSString*)error; +#pragma mark Event tracking /** - * @param currencyList A list containing the currency objects that have been changed with the event. - * {@link com.spilgames.spilsdk.models.tracking.TrackingCurrency} - * @param itemsList A list containing the item objects that have been changed with the event. - * {@link com.spilgames.spilsdk.models.tracking.TrackingItem} - * @param reason The reason for which the wallet or the inventory has been updated - * A list of default resons can be found here: {@link com.spilgames.spilsdk.playerdata.PlayerDataUpdateReasons} - * @param location The location where the event occurred (ex.: Shop Screen, End of the level Screen) + * Track a milstone achieved event + * + * @param name The name of the milestone */ -+(void)trackWalletInventoryEvent:(NSString*)currencyList itemsList:(NSString*)itemsList reason:(NSString*)reason location:(NSString*)location; ++(void)trackMilestoneAchievedEvent:(NSString*)name; /** - * @param name The name of the milestone + * Track a level start + * + * @param level The name of the level + * @param score The score at the start of the level + * @param stars The stars at the start of the level + * @param turns The turns at the start of the level + * @param customCreated Indictating if the level was custom created + * @param creatorId The id of the creator of the level */ -+(void)trackMilestoneEvent:(NSString*)name; ++(void)trackLevelStartEvent:(NSString*)level score:(double)score stars:(int)stars turns:(int)turns customCreated:(bool)customCreated creatorId:(NSString*)creatorId; /** + * Track a level complete + * * @param level The name of the level + * @param score The final score the player achieves at the end of the level + * @param stars The # of stars (or any other rating system) the player achieves at the end of the level + * @param turns The # of moves/turns taken to complete the level + * @param customCreated Indictating if the level was custom created + * @param creatorId The id of the creator of the level */ -+(void)trackLevelStartEvent:(NSString*)level; ++(void)trackLevelCompleteEvent:(NSString*)level score:(double)score stars:(int)stars turns:(int)turns customCreated:(bool)customCreated creatorId:(NSString*)creatorId; /** + * Track a level fail + * * @param level The name of the level * @param score The final score the player achieves at the end of the level * @param stars The # of stars (or any other rating system) the player achieves at the end of the level * @param turns The # of moves/turns taken to complete the level + * @param customCreated Indictating if the level was custom created + * @param creatorId The id of the creator of the level */ -+(void)trackLevelCompleteEvent:(NSString*)level score:(NSString*)score stars:(NSString*)stars turns:(NSString*)turns; ++(void)trackLevelFailedEvent:(NSString*)level score:(double)score stars:(int)stars turns:(int)turns customCreated:(bool)customCreated creatorId:(NSString*)creatorId; /** + * Track a level up + * * @param level The name of the level * @param score The final score the player achieves at the end of the level - * @param turns The # of moves/turns taken to complete the level + * @param objectId The level up object identifier + * @param skillId The skill id + */ ++(void)trackLevelUpEvent:(NSString*)level objectId:(NSString*)objectId skillId:(NSString*)skillId; + +/** + * Track an item equip + * + * @param equippedItem The id of the equipped item + * @param equippedTo The id of were the item will be equipped to + */ ++(void)trackEquipEvent:(NSString*)equippedItem equippedTo:(NSString*)equippedTo; + +/** + * Track an item upgrade + * + * @param upgradeId The id of the upgraded item + * @param level The level in which the upgrade happened + * @param reason The upgrade reason + * @param iteration The upgrade iteration + */ ++(void)trackUpgradeEvent:(NSString*)upgradeId level:(NSString*)level reason:(NSString*)reason iteration:(int)iteration; + +/** + * Track a level create event + * + * @param levelId The id of the created level + * @param creatorId The id of the level creator + */ ++(void)trackLevelCreateEvent:(NSString*)levelId creatorId:(NSString*)creatorId; + +/** + * Track a download event + * + * @param levelId The id of the downloaded level + * @param creatorId The id of the level creator + * @param rating The level rating + */ ++(void)trackLevelDownloadEvent:(NSString*)levelId creatorId:(NSString*)creatorId rating:(int)rating; + +/** + * Track a level rate event + * + * @param levelId The id of the rated level + * @param creatorId The id of the level creator + * @param rating The level rating */ -+(void)trackLevelFailed:(NSString*)level score:(NSString*)score turns:(NSString*)turns; ++(void)trackLevelRateEvent:(NSString*)levelId creatorId:(NSString*)creatorId rating:(int)rating; + +/** + * Track the start of an endless level + */ ++(void)trackEndlessModeStartEvent; + +/** + * Track the end of an endless mode level + * + * @param level The distance completed in the endless level + */ ++(void)trackEndlessModeEndEvent:(int)distance; + +/** + * Track a player dies event + * + * @param level The name of the level in which the player dies + */ ++(void)trackPlayerDiesEvent:(NSString*)level; + +/** + * Track a wallet/inventory update + * + * @param reason The reason for which the wallet or the inventory has been updated + * A list of default resons can be found here: {@link com.spilgames.spilsdk.playerdata.PlayerDataUpdateReasons} + * @param location The location where the event occurred (ex.: Shop Screen, End of the level Screen) + * @param currencyList A list containing the currency objects that have been changed with the event. + * {@link com.spilgames.spilsdk.models.tracking.TrackingCurrency} + * @param itemsList A list containing the item objects that have been changed with the event. + * {@link com.spilgames.spilsdk.models.tracking.TrackingItem} + */ ++(void)trackWalletInventoryEvent:(NSString*)reason location:(NSString*)location currencyList:(NSString*)currencyList itemList:(NSString*)itemsList; + +/** + * Track a successful iap + * + * @param skuId The product identifier of the item that was purchased + * @param transactionId The transaction identifier of the item that was purchased (also called orderId) + * @param purchaseDate The date and time that the item was purchased + */ ++(void)trackIAPPurchasedEvent:(NSString*)skuId transactionId:(NSString*)transactionId purchaseDate:(NSString*)purchaseDate; + +/** + * Track a restored iap + * + * @param skuId The product identifier of the item that was purchased + * @param originalTransactionId For a transaction that restores a previous transaction, the transaction identifier of the original transaction. + * Otherwise, identical to the transaction identifier + * @param originalPurchaseDate For a transaction that restores a previous transaction, the date of the original transaction + */ ++(void)trackIAPRestoredEvent:(NSString*)skuId originalTransactionId:(NSString*)originalTransactionId originalPurchaseDate:(NSString*)originalPurchaseDate; + +/** + * Track a failed iap + * + * @param skuId The product identifier of the item that was purchased + * @param error Error description or error code + */ ++(void)trackIAPFailedEvent:(NSString*)skuId error:(NSString*)error; /** * Track the completion of a tutorial @@ -211,24 +329,32 @@ +(void)trackTutorialCompleteEvent; /** + * Track a skipped tutorial + * * Track the skipping of a tutorial */ +(void)trackTutorialSkippedEvent; /** + * Track a register + * * @param platform The platform for which the registration occurred (ex.: Facebook) */ +(void)trackRegisterEvent:(NSString*)platform; /** + * Track a share + * * @param platform The platform for which the share occurred (ex.: Facebook) */ +(void)trackShareEvent:(NSString*)platform; /** + * Track an invite + * * @param platform The platform for which the invite occurred (ex.: Facebook) */ -+(void) trackInviteEvent:(NSString*)platform; ++(void)trackInviteEvent:(NSString*)platform; /** * Track a basic named event @@ -282,6 +408,24 @@ */ +(void)sendMessage:(NSString*)messageName toObject:(NSString*)objectName withString:(NSString*)parameterString; +#pragma mark Push notifications + +/** + * Disable the automated registration message for push notifications + * Should be called before [Spil start] + */ ++(void)disableAutomaticRegisterForPushNotifications; + +/** + * Helper function to register for push notifications + */ ++(void)registerPushNotifications; + +/** + * Helper function to forward the app delegate listener on the deviceToken + */ ++(void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken; + #pragma mark Config /** @@ -343,6 +487,13 @@ */ +(void)showMoreApps; +/** + * Request a reward video + * + * @param rewardType The expected reward type (optional) + */ ++(void)requestRewardVideo:(NSString*)rewardType; + /** * Show the last requested reward video */ @@ -391,7 +542,7 @@ +(NSString*)getWallet; /** - * Returns the player data as json + * Returns the configured game data as json */ +(NSString*)getSpilGameData; @@ -416,7 +567,7 @@ * @param amount Amount to add * @param reason The add reason */ -+(void)addCurrencyToWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason; ++(void)addCurrencyToWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location; /** * Subtract currency from the wallet @@ -424,7 +575,7 @@ * @param amount Amount to subtract * @param reason The subtract reason */ -+(void)subtractCurrencyFromWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason; ++(void)subtractCurrencyFromWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location; /** * Add item to the inventory @@ -432,7 +583,7 @@ * @param amount Amount to add * @param reason The add reason */ -+(void)addItemToInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason; ++(void)addItemToInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location; /** * Subtract item to from the inventory @@ -440,14 +591,46 @@ * @param amount Amount to subtract * @param reason The subtract reason */ -+(void)subtractItemFromInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason; ++(void)subtractItemFromInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location; /** * Uses the bundle and will add the items to the inventory and subtract the currency from the wallet * @param bundleId Id of the bundle * @param reason The bundle reason */ -+(void)consumeBundle:(int)bundleId withReason:(NSString*)reason; ++(void)buyBundle:(int)bundleId withReason:(NSString*)reason withLocation:(NSString*)location; + +/** + * Resets all the player data + */ ++(void)resetPlayerData; + +/** + * Resets the inventory data + */ ++(void)resetInventory; + +/** + * Resets the wallet data + */ ++(void)resetWallet; + +#pragma mark Customer support + +/** + * Shows the help center + */ ++(void)showHelpCenter; + +/** + * Shows the contact center + */ ++(void)showContactCenter; + +/** + * Shows the help center webview version + */ ++(void)showHelpCenterWebview; #pragma mark Web diff --git a/Spil/Spil.m b/Spil/Spil.m index 2ec7014..d5be084 100644 --- a/Spil/Spil.m +++ b/Spil/Spil.m @@ -8,9 +8,17 @@ #import "Spil.h" #import "SpilEventTracker.h" +#if TARGET_OS_IOS +#import "SpilNotificationHelper.h" +#import +#import +#import "CustomerSupportHandler.h" +#endif +#import "SpilAdvertisementHandler.h" #import "SpilAnalyticsHandler.h" #import "SpilConfigHandler.h" #import "SpilPackageHandler.h" +#import "SpilAdvertisementHandler.h" #import "UserProfile.h" #import "GameDataController.h" #import "PlayerDataController.h" @@ -20,8 +28,6 @@ #import "SpilUserHandler.h" #import "SpilActionHandler.h" #import "NSString+Extensions.h" -#import "AppLovinAdProvider.h" -#import "SpilAdvertisementHandler.h" // C classes #include "HookBridge.h" @@ -31,6 +37,18 @@ @import StoreKit; @import SystemConfiguration; @import MediaPlayer; +#if TARGET_OS_IOS +@import CoreTelephony; +@import EventKit; +@import EventKitUI; +@import MessageUI; +@import Social; +@import WebKit; +@import AssetsLibrary; +@import Twitter; +@import Accounts; +@import CoreMotion; +#endif @import CoreGraphics; @import CoreLocation; @import AdSupport; @@ -116,6 +134,9 @@ -(void)spilNotificationHandler:(NSNotification *)notification { // When a new config is loaded also try to init other services again [[SpilAnalyticsHandler sharedInstance] initializeAnalyticsProviders]; + #if TARGET_OS_IOS + [[CustomerSupportHandler sharedInstance] initialize]; + #endif // Forward to unity [Spil sendMessage:@"ConfigUpdated" toObject:@"SpilSDK" withString:@""]; @@ -171,8 +192,12 @@ -(void)spilNotificationHandler:(NSNotification *)notification { // Forward to unity NSMutableDictionary *data = [NSMutableDictionary dictionary]; [data setObject:userInfo[@"reason"] forKey:@"reason"]; - [data setObject:userInfo[@"updatedData"][@"currencies"] forKey:@"currencies"]; - [data setObject:userInfo[@"updatedData"][@"items"] forKey:@"items"]; + if (userInfo[@"updatedData"][@"currencies"] != nil) { + [data setObject:userInfo[@"updatedData"][@"currencies"] forKey:@"currencies"]; + } + if (userInfo[@"updatedData"][@"items"] != nil) { + [data setObject:userInfo[@"updatedData"][@"items"] forKey:@"items"]; + } [Spil sendMessage:@"PlayerDataUpdated" toObject:@"SpilSDK" withString:[JsonUtil convertObjectToJson:data]]; } @@ -311,12 +336,16 @@ +(void)startUsingUnity:(BOOL)usingUnity{ [[SpilEventTracker sharedInstance] isUnity:usingUnity]; - // Start the tracker, no need to pass an appid yet + // Start the tracker, no need to pass an app id yet [[SpilEventTracker sharedInstance] startWithAppId:@""]; [self updateAppSettingsMenu]; [self setAdvancedLoggingEnabled:NO]; + + if (disableAutoPushNotificationRegistration == false) { + [self registerPushNotifications]; + } } +(NSString*)getSpilUserId { @@ -362,78 +391,221 @@ +(void)updateAppSettingsMenu{ #pragma mark Event tracking -+(void)trackIAPPurchasedEvent:(NSString*)skuId transactionId:(NSString*)transactionId purchaseDate:(NSString*)purchaseDate { ++(void)trackMilestoneAchievedEvent:(NSString*)name { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"skuId"] = skuId; - params[@"transactionId"] = transactionId; - params[@"purchaseDate"] = purchaseDate; - [[SpilEventTracker sharedInstance] trackEvent:@"iapPurchased" withParameters:params]; + if (name != nil) { + params[@"name"] = name; + } + [[SpilEventTracker sharedInstance] trackEvent:@"milestoneAchieved" withParameters:params]; } -+(void)trackIAPRestoredEvent:(NSString*)skuId originalTransactionId:(NSString*)originalTransactionId originalPurchaseDate:(NSString*)originalPurchaseDate { ++(void)trackLevelStartEvent:(NSString*)level score:(double)score stars:(int)stars turns:(int)turns customCreated:(bool)customCreated creatorId:(NSString*)creatorId { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"skuId"] = skuId; - params[@"originalTransactionId"] = originalTransactionId; - params[@"originalPurchaseDate"] = originalPurchaseDate; - [[SpilEventTracker sharedInstance] trackEvent:@"iapRestored" withParameters:params]; + if (level != nil) { + params[@"level"] = level; + } + params[@"score"] = [NSNumber numberWithInt:score]; + params[@"stars"] = [NSNumber numberWithInt:stars]; + params[@"turns"] = [NSNumber numberWithInt:turns]; + params[@"customCreated"] = [NSNumber numberWithBool:customCreated]; + if (creatorId != nil) { + params[@"creatorId"] = creatorId; + } + [[SpilEventTracker sharedInstance] trackEvent:@"levelStart" withParameters:params]; } -+(void)trackIAPFailedEvent:(NSString*)skuId error:(NSString*)error { ++(void)trackLevelCompleteEvent:(NSString*)level score:(double)score stars:(int)stars turns:(int)turns customCreated:(bool)customCreated creatorId:(NSString*)creatorId { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"skuId"] = skuId; - params[@"error"] = error; - [[SpilEventTracker sharedInstance] trackEvent:@"iapFailed" withParameters:params]; + if (level != nil) { + params[@"level"] = level; + } + params[@"score"] = [NSNumber numberWithDouble:score]; + params[@"stars"] = [NSNumber numberWithInt:stars]; + params[@"turns"] = [NSNumber numberWithInt:turns]; + params[@"customCreated"] = [NSNumber numberWithBool:customCreated]; + if (creatorId != nil) { + params[@"creatorId"] = creatorId; + } + [[SpilEventTracker sharedInstance] trackEvent:@"levelComplete" withParameters:params]; } -+(void)trackWalletInventoryEvent:(NSString*)currencyList itemsList:(NSString*)itemsList reason:(NSString*)reason location:(NSString*)location { ++(void)trackLevelFailedEvent:(NSString*)level score:(double)score stars:(int)stars turns:(int)turns customCreated:(bool)customCreated creatorId:(NSString*)creatorId { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - NSArray *currencies = [JsonUtil convertStringToObject:currencyList]; - params[@"wallet"] = @{@"currencies": currencies}; - NSArray *items = [JsonUtil convertStringToObject:itemsList]; - params[@"items"] = items; - params[@"reason"] = reason; - params[@"location"] = location; - params[@"trackingOnly"] = [NSNumber numberWithBool:true]; - [[SpilEventTracker sharedInstance] trackEvent:@"updatePlayerData" withParameters:params]; + if (level != nil) { + params[@"level"] = level; + } + params[@"score"] = [NSNumber numberWithDouble:score]; + params[@"stars"] = [NSNumber numberWithInt:stars]; + params[@"turns"] = [NSNumber numberWithInt:turns]; + params[@"customCreated"] = [NSNumber numberWithBool:customCreated]; + if (creatorId != nil) { + params[@"creatorId"] = creatorId; + } + [[SpilEventTracker sharedInstance] trackEvent:@"levelFailed" withParameters:params]; } -+(void)trackMilestoneEvent:(NSString*)name { ++(void)trackLevelUpEvent:(NSString*)level objectId:(NSString*)objectId skillId:(NSString*)skillId { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"name"] = name; - [[SpilEventTracker sharedInstance] trackEvent:@"milestoneAchieved" withParameters:params]; + if (level != nil) { + params[@"level"] = level; + } + if (objectId != nil) { + params[@"objectId"] = objectId; + } + if (skillId != nil) { + params[@"skillId"] = skillId; + } + [[SpilEventTracker sharedInstance] trackEvent:@"levelUp" withParameters:params]; } -+(void)trackLevelStartEvent:(NSString*)level { ++(void)trackEquipEvent:(NSString*)equippedItem equippedTo:(NSString*)equippedTo { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"level"] = level; - [[SpilEventTracker sharedInstance] trackEvent:@"levelStart" withParameters:params]; + if (equippedItem != nil) { + params[@"equippedItem"] = equippedItem; + } + if (equippedTo != nil) { + params[@"equippedTo"] = equippedTo; + } + [[SpilEventTracker sharedInstance] trackEvent:@"equip" withParameters:params]; } -+(void)trackLevelCompleteEvent:(NSString*)level score:(NSString*)score stars:(NSString*)stars turns:(NSString*)turns { ++(void)trackUpgradeEvent:(NSString*)upgradeId level:(NSString*)level reason:(NSString*)reason iteration:(int)iteration { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"level"] = level; - if(score != nil) { - params[@"score"] = score; + if (upgradeId != nil) { + params[@"upgradeId"] = upgradeId; } - if(stars != nil) { - params[@"stars"] = stars; + if (level != nil) { + params[@"level"] = level; } - if(turns != nil) { - params[@"turns"] = turns; + if (reason != nil) { + params[@"reason"] = reason; } - [[SpilEventTracker sharedInstance] trackEvent:@"levelComplete" withParameters:params]; + params[@"iteration"] = [NSNumber numberWithInt:iteration]; + [[SpilEventTracker sharedInstance] trackEvent:@"upgrade" withParameters:params]; } -+(void)trackLevelFailed:(NSString*)level score:(NSString*)score turns:(NSString*)turns { ++(void)trackLevelCreateEvent:(NSString*)levelId creatorId:(NSString*)creatorId { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"level"] = level; - if(score != nil) { - params[@"score"] = score; + if (levelId != nil) { + params[@"levelId"] = levelId; } - if(turns != nil) { - params[@"turns"] = turns; + if (creatorId != nil) { + params[@"creatorId"] = creatorId; } - [[SpilEventTracker sharedInstance] trackEvent:@"levelFailed" withParameters:params]; + [[SpilEventTracker sharedInstance] trackEvent:@"levelCreate" withParameters:params]; +} + ++(void)trackLevelDownloadEvent:(NSString*)levelId creatorId:(NSString*)creatorId rating:(int)rating { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (levelId != nil) { + params[@"levelId"] = levelId; + } + if (creatorId != nil) { + params[@"creatorId"] = creatorId; + } + params[@"rating"] = [NSNumber numberWithInt:rating]; + [[SpilEventTracker sharedInstance] trackEvent:@"levelDownload" withParameters:params]; +} + ++(void)trackLevelRateEvent:(NSString*)levelId creatorId:(NSString*)creatorId rating:(int)rating { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (levelId != nil) { + params[@"levelId"] = levelId; + } + if (creatorId != nil) { + params[@"creatorId"] = creatorId; + } + params[@"rating"] = [NSNumber numberWithInt:rating]; + [[SpilEventTracker sharedInstance] trackEvent:@"levelRate" withParameters:params]; +} + ++(void)trackEndlessModeStartEvent { + [[SpilEventTracker sharedInstance] trackEvent:@"endlessModeStart"]; +} + ++(void)trackEndlessModeEndEvent:(int)distance { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + params[@"distance"] = [NSNumber numberWithInt:distance]; + [[SpilEventTracker sharedInstance] trackEvent:@"endlessModeEnd" withParameters:params]; +} + ++(void)trackPlayerDiesEvent:(NSString*)level { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (level != nil) { + params[@"level"] = level; + } + [[SpilEventTracker sharedInstance] trackEvent:@"playerDies" withParameters:params]; +} + ++(void)requestRewardVideo:(NSString*)rewardType { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (rewardType != nil) { + params[@"rewardType"] = rewardType; + } + [[SpilEventTracker sharedInstance] trackEvent:@"requestRewardVideo" withParameters:params]; +} + ++(void)trackWalletInventoryEvent:(NSString*)reason location:(NSString*)location currencyList:(NSString*)currencyList itemList:(NSString*)itemsList { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (reason != nil) { + params[@"reason"] = reason; + } + if (location != nil) { + params[@"location"] = location; + } + if (currencyList != nil) { + NSArray *currencies = [JsonUtil convertStringToObject:currencyList]; + params[@"wallet"] = @{@"currencies": currencies}; + } else { + params[@"wallet"] = @{}; + } + if (itemsList != nil) { + NSArray *items = [JsonUtil convertStringToObject:itemsList]; + params[@"inventory"] = @{@"items": items}; + } else { + params[@"inventory"] = @{}; + } + params[@"trackingOnly"] = [NSNumber numberWithBool:true]; + [[SpilEventTracker sharedInstance] trackEvent:@"updatePlayerData" withParameters:params]; +} + ++(void)trackIAPPurchasedEvent:(NSString*)skuId transactionId:(NSString*)transactionId purchaseDate:(NSString*)purchaseDate { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (skuId != nil) { + params[@"skuId"] = skuId; + } + if (transactionId != nil) { + params[@"transactionId"] = transactionId; + } + if (purchaseDate != nil) { + params[@"purchaseDate"] = purchaseDate; + } + [[SpilEventTracker sharedInstance] trackEvent:@"iapPurchased" withParameters:params]; +} + ++(void)trackIAPRestoredEvent:(NSString*)skuId originalTransactionId:(NSString*)originalTransactionId originalPurchaseDate:(NSString*)originalPurchaseDate { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (skuId != nil) { + params[@"skuId"] = skuId; + } + if (originalTransactionId != nil) { + params[@"originalTransactionId"] = originalTransactionId; + } + if (originalPurchaseDate != nil) { + params[@"originalPurchaseDate"] = originalPurchaseDate; + } + [[SpilEventTracker sharedInstance] trackEvent:@"iapRestored" withParameters:params]; +} + ++(void)trackIAPFailedEvent:(NSString*)skuId error:(NSString*)error { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (skuId != nil) { + params[@"skuId"] = skuId; + } + if (error != nil) { + params[@"error"] = error; + } + [[SpilEventTracker sharedInstance] trackEvent:@"iapFailed" withParameters:params]; } +(void)trackTutorialCompleteEvent { @@ -446,19 +618,25 @@ +(void)trackTutorialSkippedEvent { +(void)trackRegisterEvent:(NSString*)platform { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"platform"] = platform; + if (platform != nil) { + params[@"platform"] = platform; + } [[SpilEventTracker sharedInstance] trackEvent:@"register" withParameters:params]; } +(void)trackShareEvent:(NSString*)platform { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"platform"] = platform; + if (platform != nil) { + params[@"platform"] = platform; + } [[SpilEventTracker sharedInstance] trackEvent:@"share" withParameters:params]; } +(void)trackInviteEvent:(NSString*)platform { NSMutableDictionary *params = [NSMutableDictionary dictionary]; - params[@"platform"] = platform; + if (platform != nil) { + params[@"platform"] = platform; + } [[SpilEventTracker sharedInstance] trackEvent:@"invite" withParameters:params]; } @@ -503,6 +681,11 @@ +(void)applicationDidBecomeActive:(UIApplication *)application{ // Request a advertisement init [self trackEvent:@"advertisementInit"]; + // Initialize the customer support handler + #if TARGET_OS_IOS + [[CustomerSupportHandler sharedInstance] initialize]; + #endif + // Request the game and player data [self requestGameData]; @@ -510,8 +693,25 @@ +(void)applicationDidBecomeActive:(UIApplication *)application{ [[SpilUserHandler sharedInstance] getMyGameState]; // Initialize app lovin + #if TARGET_OS_TV AppLovinAdProvider *appLovin = [[SpilAdvertisementHandler sharedInstance] getAppLovinAdProvider]; [appLovin initialize:nil]; + #endif +} + ++(void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + #if TARGET_OS_IOS + NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; + if (userInfo != nil) { + [SpilNotificationHelper application:application didReceiveRemoteNotification:userInfo didLaunchApp:true]; + } + #endif +} + ++(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{ + #if TARGET_OS_IOS + [SpilNotificationHelper application:application didReceiveRemoteNotification:userInfo didLaunchApp:false]; + #endif } #pragma mark Send message @@ -546,6 +746,24 @@ + (void) sendMessage:(NSString*)messageName toObject:(NSString*)objectName withS } +#pragma mark Push notifications + ++(void)disableAutomaticRegisterForPushNotifications { + disableAutoPushNotificationRegistration = true; +} + ++(void)registerPushNotifications { + #if TARGET_OS_IOS + [SpilNotificationHelper registerPushNotifications]; + #endif +} + ++(void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { + #if TARGET_OS_IOS + [SpilNotificationHelper didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; + #endif +} + #pragma mark Config +(NSDictionary*)getConfig{ @@ -581,6 +799,9 @@ +(void)requestPackages{ #pragma mark Ads +(void)showMoreApps{ + #if TARGET_OS_IOS + [[SpilAdvertisementHandler sharedInstance] showMoreApps:@"chartboost"]; + #endif } +(void)playRewardVideo { @@ -588,14 +809,20 @@ +(void)playRewardVideo { } +(BOOL)isAdProviderInitialized:(NSString*)identifier { - return false; + return [[SpilAdvertisementHandler sharedInstance] isAdProviderInitialized:identifier]; } +(void)showToastOnVideoReward:(BOOL)enabled{ - + #if TARGET_OS_IOS + FyberAdProvider *fyber = [[SpilAdvertisementHandler sharedInstance] getFyberAdProvider]; + [fyber setShouldShowToastOnReward:enabled]; + #endif } +(void)closedParentalGate:(BOOL)pass{ + #if TARGET_OS_IOS + [[SpilAdvertisementHandler sharedInstance] closedParentalGate:pass]; + #endif } #pragma mark UserData & GameData @@ -636,24 +863,56 @@ +(NSString*)getShopPromotions { return [[GameDataController sharedInstance] getShopPromotions]; } -+(void)addCurrencyToWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason { - [[PlayerDataController sharedInstance] updateWallet:currencyId withDelta:amount withReason:reason]; ++(void)addCurrencyToWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location { + [[PlayerDataController sharedInstance] updateWallet:currencyId withDelta:amount withReason:reason withLocation:location]; +} + ++(void)subtractCurrencyFromWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location { + [[PlayerDataController sharedInstance] updateWallet:currencyId withDelta:-amount withReason:reason withLocation:location]; +} + ++(void)addItemToInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location { + [[PlayerDataController sharedInstance] updateInventoryWithItem:itemId withAmount:amount withAction:@"add" withReason:reason withLocation:location]; +} + ++(void)subtractItemFromInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason withLocation:(NSString*)location { + [[PlayerDataController sharedInstance] updateInventoryWithItem:itemId withAmount:amount withAction:@"subtract" withReason:reason withLocation:location]; } -+(void)subtractCurrencyFromWallet:(int)currencyId withAmount:(int)amount withReason:(NSString*)reason { - [[PlayerDataController sharedInstance] updateWallet:currencyId withDelta:-amount withReason:reason]; ++(void)buyBundle:(int)bundleId withReason:(NSString*)reason withLocation:(NSString*)location { + [[PlayerDataController sharedInstance] updateInventoryWithBundle:bundleId withReason:reason withLocation:location]; } -+(void)addItemToInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason { - [[PlayerDataController sharedInstance] updateInventoryWithItem:itemId withAmount:amount withAction:@"add" withReason:reason]; ++(void)resetPlayerData { + [[PlayerDataController sharedInstance] resetPlayerData]; +} + ++(void)resetInventory { + [[PlayerDataController sharedInstance] resetInventory]; +} + ++(void)resetWallet { + [[PlayerDataController sharedInstance] resetWallet]; +} + +#pragma mark Customer support + ++(void)showHelpCenter { + #if TARGET_OS_IOS + [[CustomerSupportHandler sharedInstance] showHelpCenter]; + #endif } -+(void)subtractItemFromInventory:(int)itemId withAmount:(int)amount withReason:(NSString*)reason { - [[PlayerDataController sharedInstance] updateInventoryWithItem:itemId withAmount:amount withAction:@"subtract" withReason:reason]; ++(void)showContactCenter { + #if TARGET_OS_IOS + [[CustomerSupportHandler sharedInstance] showContactCenter]; + #endif } -+(void)consumeBundle:(int)bundleId withReason:(NSString*)reason { - [[PlayerDataController sharedInstance] updateInventoryWithBundle:bundleId withReason:reason]; ++(void)showHelpCenterWebview { + #if TARGET_OS_IOS + [[CustomerSupportHandler sharedInstance] showHelpCenterWebview]; + #endif } #pragma mark Web @@ -706,6 +965,7 @@ +(void)devRequestAd:(NSString*)provider withAdType:(NSString*)adType withParenta /*if ([adType isEqualToString:@"banner"]) { [Spil showBanner]; } else {*/ + [[SpilAdvertisementHandler sharedInstance] requestAd:provider withAdType:adType withParentalGate:parentalGate]; //} } @@ -718,6 +978,9 @@ +(void)devShowInterstitial:(NSString*)adProvider { } +(void)devShowMoreApps:(NSString*)adProvider { + #if TARGET_OS_IOS + [[SpilAdvertisementHandler sharedInstance] showMoreApps:adProvider]; + #endif } /*+(void)showBanner { diff --git a/Spil/UserHandler/SpilUserHandler.m b/Spil/UserHandler/SpilUserHandler.m index bb49161..5e524e2 100644 --- a/Spil/UserHandler/SpilUserHandler.m +++ b/Spil/UserHandler/SpilUserHandler.m @@ -173,8 +173,10 @@ -(NSString*)getPrivateGameState { -(void)setPublicGameState:(NSString*)publicData { if ([self getExternalUserRequestData] == nil) { - NSDictionary *userInfo = @{@"event" : @"gameStateError", @"message" : [[SpilError PublicGameStateOperationFailed:@"User id not set!"] toJson]}; - [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; + if (![publicData isEqualToString:@""]) { + NSDictionary *userInfo = @{@"event" : @"gameStateError", @"message" : [[SpilError PublicGameStateOperationFailed:@"User id not set!"] toJson]}; + [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfo]; + } return; } @@ -237,4 +239,4 @@ -(void)friendsGameStateLoaded:(NSDictionary*)gameStates provider:(NSString*)prov [[NSNotificationCenter defaultCenter] postNotificationName:@"spilNotificationHandler" object:nil userInfo:userInfoPrivate]; } -@end \ No newline at end of file +@end diff --git a/Spil/Util/JsonUtil.h b/Spil/Util/JsonUtil.h index 69df2d0..3b14b2f 100644 --- a/Spil/Util/JsonUtil.h +++ b/Spil/Util/JsonUtil.h @@ -3,7 +3,7 @@ // Spil // // Util class to convert foundation objects to and from json. -// It does not support serialization, use JSONModel instead. +// It does not support serialization. // // Created by Frank Slofstra on 23/05/16. // Copyright © 2016 Spil Games. All rights reserved. @@ -15,5 +15,6 @@ +(id)convertStringToObject:(NSString*) jsonString; +(NSString*)convertObjectToJson:(id) object; ++(NSString*)convertObjectToReadableJson:(id) object; -@end \ No newline at end of file +@end diff --git a/Spil/Util/JsonUtil.m b/Spil/Util/JsonUtil.m index bc53af8..368a05c 100644 --- a/Spil/Util/JsonUtil.m +++ b/Spil/Util/JsonUtil.m @@ -42,4 +42,27 @@ +(NSString*)convertObjectToJson:(id) object { } } ++(NSString*)convertObjectToReadableJson:(id) object { + if (object == nil) { + return nil; + } + + if ([object isKindOfClass:[NSNumber class]]) { + return [object stringValue]; + } + + @try { + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:&error]; + if (jsonData) { + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } else { + return nil; + } + } @catch (NSException *exception) { + NSLog(@"JsonUtil EXCEPTION: %@", exception.description); + return nil; + } +} + @end diff --git a/Spil/Util/NSArray+JSONModelExtensions.h b/Spil/Util/NSArray+JSONModelExtensions.h deleted file mode 100644 index 9e5b8cd..0000000 --- a/Spil/Util/NSArray+JSONModelExtensions.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NSArray+JSONModelExtensions.h -// Spil -// -// Created by Frank Slofstra on 16/06/16. -// Copyright © 2016 Spil Games. All rights reserved. -// - -#import - -@interface NSArray (JSONModelExtensions) - --(NSString*)toJSONString; - -@end diff --git a/Spil/Util/NSArray+JSONModelExtensions.m b/Spil/Util/NSArray+JSONModelExtensions.m deleted file mode 100644 index 59a0547..0000000 --- a/Spil/Util/NSArray+JSONModelExtensions.m +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSArray+JSONModelExtensions.m -// Spil -// -// Created by Frank Slofstra on 16/06/16. -// Copyright © 2016 Spil Games. All rights reserved. -// - -#import "NSArray+JSONModelExtensions.h" - -@implementation NSArray (JSONModelExtensions) - -- (NSString*)toJSONString { - NSMutableArray* jsonObjects = [NSMutableArray new]; - for ( id obj in self ) - [jsonObjects addObject:[obj toJSONString]]; - return [NSString stringWithFormat:@"[%@]", [jsonObjects componentsJoinedByString:@","]]; -} - -@end diff --git a/Spil/Util/NSString+Extensions.m b/Spil/Util/NSString+Extensions.m index 9c98faf..55323f5 100644 --- a/Spil/Util/NSString+Extensions.m +++ b/Spil/Util/NSString+Extensions.m @@ -10,7 +10,7 @@ @implementation NSString (Extensions) -//#pragma GCC diagnostic ignored "-Wundeclared-selector" +#pragma GCC diagnostic ignored "-Wundeclared-selector" - (NSString *)urlencode { NSMutableString *output = [NSMutableString string]; const unsigned char *source = (const unsigned char *)[self UTF8String]; diff --git a/SpilTV.xcodeproj/project.pbxproj b/SpilTV.xcodeproj/project.pbxproj index 0bedc41..f834d32 100644 --- a/SpilTV.xcodeproj/project.pbxproj +++ b/SpilTV.xcodeproj/project.pbxproj @@ -43,24 +43,12 @@ 140956CA1DF5491700256F07 /* ALSwiftHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 140956AF1DF5491700256F07 /* ALSwiftHeaders.h */; }; 140956CB1DF5491700256F07 /* ALTargetingData.h in Headers */ = {isa = PBXBuildFile; fileRef = 140956B01DF5491700256F07 /* ALTargetingData.h */; }; 140956CC1DF5491700256F07 /* libAppLovinTVOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 140956B11DF5491700256F07 /* libAppLovinTVOS.a */; }; - 1435C8851D659224000EAA9F /* NSArray+JSONModelExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1435C8811D659224000EAA9F /* NSArray+JSONModelExtensions.h */; }; - 1435C8861D659224000EAA9F /* NSArray+JSONModelExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1435C8821D659224000EAA9F /* NSArray+JSONModelExtensions.m */; }; 1435C8871D659224000EAA9F /* NSString+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1435C8831D659224000EAA9F /* NSString+Extensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1435C8881D659224000EAA9F /* NSString+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1435C8841D659224000EAA9F /* NSString+Extensions.m */; }; 1435C88B1D6592FE000EAA9F /* Util.h in Headers */ = {isa = PBXBuildFile; fileRef = 1435C8891D6592FE000EAA9F /* Util.h */; }; 1435C88C1D6592FE000EAA9F /* Util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1435C88A1D6592FE000EAA9F /* Util.m */; }; 14828BB31DA274FC00414EA7 /* NotificationUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 14828BB11DA274FC00414EA7 /* NotificationUtil.h */; }; 14828BB41DA274FC00414EA7 /* NotificationUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 14828BB21DA274FC00414EA7 /* NotificationUtil.m */; }; - 14C815691DEECB6C0046BFAE /* JSONModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 14C8155E1DEECB6C0046BFAE /* JSONModel.h */; }; - 14C8156A1DEECB6C0046BFAE /* JSONModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C8155F1DEECB6C0046BFAE /* JSONModel.m */; }; - 14C8156B1DEECB6C0046BFAE /* JSONModelClassProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 14C815601DEECB6C0046BFAE /* JSONModelClassProperty.h */; }; - 14C8156C1DEECB6C0046BFAE /* JSONModelClassProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C815611DEECB6C0046BFAE /* JSONModelClassProperty.m */; }; - 14C8156D1DEECB6C0046BFAE /* JSONModelError.h in Headers */ = {isa = PBXBuildFile; fileRef = 14C815621DEECB6C0046BFAE /* JSONModelError.h */; }; - 14C8156E1DEECB6C0046BFAE /* JSONModelError.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C815631DEECB6C0046BFAE /* JSONModelError.m */; }; - 14C8156F1DEECB6C0046BFAE /* JSONKeyMapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 14C815651DEECB6C0046BFAE /* JSONKeyMapper.h */; }; - 14C815701DEECB6C0046BFAE /* JSONKeyMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C815661DEECB6C0046BFAE /* JSONKeyMapper.m */; }; - 14C815711DEECB6C0046BFAE /* JSONValueTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 14C815671DEECB6C0046BFAE /* JSONValueTransformer.h */; }; - 14C815721DEECB6C0046BFAE /* JSONValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C815681DEECB6C0046BFAE /* JSONValueTransformer.m */; }; CC21F1FC1CD8820A00EC9020 /* SpilAppStoreHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = CC21F1FA1CD8820A00EC9020 /* SpilAppStoreHandler.h */; }; CC21F1FD1CD8820A00EC9020 /* SpilAppStoreHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = CC21F1FB1CD8820A00EC9020 /* SpilAppStoreHandler.m */; }; CC21F2001CD8D16000EC9020 /* AppStoreRequestData.h in Headers */ = {isa = PBXBuildFile; fileRef = CC21F1FE1CD8D16000EC9020 /* AppStoreRequestData.h */; }; @@ -174,24 +162,12 @@ 140956AF1DF5491700256F07 /* ALSwiftHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALSwiftHeaders.h; sourceTree = ""; }; 140956B01DF5491700256F07 /* ALTargetingData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALTargetingData.h; sourceTree = ""; }; 140956B11DF5491700256F07 /* libAppLovinTVOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libAppLovinTVOS.a; sourceTree = ""; }; - 1435C8811D659224000EAA9F /* NSArray+JSONModelExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+JSONModelExtensions.h"; path = "Spil/Util/NSArray+JSONModelExtensions.h"; sourceTree = SOURCE_ROOT; }; - 1435C8821D659224000EAA9F /* NSArray+JSONModelExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+JSONModelExtensions.m"; path = "Spil/Util/NSArray+JSONModelExtensions.m"; sourceTree = SOURCE_ROOT; }; 1435C8831D659224000EAA9F /* NSString+Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+Extensions.h"; path = "Spil/Util/NSString+Extensions.h"; sourceTree = SOURCE_ROOT; }; 1435C8841D659224000EAA9F /* NSString+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+Extensions.m"; path = "Spil/Util/NSString+Extensions.m"; sourceTree = SOURCE_ROOT; }; 1435C8891D6592FE000EAA9F /* Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util.h; sourceTree = ""; }; 1435C88A1D6592FE000EAA9F /* Util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Util.m; sourceTree = ""; }; 14828BB11DA274FC00414EA7 /* NotificationUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationUtil.h; sourceTree = ""; }; 14828BB21DA274FC00414EA7 /* NotificationUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationUtil.m; sourceTree = ""; }; - 14C8155E1DEECB6C0046BFAE /* JSONModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONModel.h; sourceTree = ""; }; - 14C8155F1DEECB6C0046BFAE /* JSONModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONModel.m; sourceTree = ""; }; - 14C815601DEECB6C0046BFAE /* JSONModelClassProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONModelClassProperty.h; sourceTree = ""; }; - 14C815611DEECB6C0046BFAE /* JSONModelClassProperty.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONModelClassProperty.m; sourceTree = ""; }; - 14C815621DEECB6C0046BFAE /* JSONModelError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONModelError.h; sourceTree = ""; }; - 14C815631DEECB6C0046BFAE /* JSONModelError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONModelError.m; sourceTree = ""; }; - 14C815651DEECB6C0046BFAE /* JSONKeyMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONKeyMapper.h; sourceTree = ""; }; - 14C815661DEECB6C0046BFAE /* JSONKeyMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONKeyMapper.m; sourceTree = ""; }; - 14C815671DEECB6C0046BFAE /* JSONValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONValueTransformer.h; sourceTree = ""; }; - 14C815681DEECB6C0046BFAE /* JSONValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONValueTransformer.m; sourceTree = ""; }; 14E5D4AE1DEED750000AE2EA /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; CC21F1FA1CD8820A00EC9020 /* SpilAppStoreHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpilAppStoreHandler.h; sourceTree = ""; }; CC21F1FB1CD8820A00EC9020 /* SpilAppStoreHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpilAppStoreHandler.m; sourceTree = ""; }; @@ -385,36 +361,10 @@ isa = PBXGroup; children = ( 140956941DF5491700256F07 /* AppLovin */, - 14C8155D1DEECB6C0046BFAE /* JSONModel */, ); path = 3rd_party_frameworks; sourceTree = ""; }; - 14C8155D1DEECB6C0046BFAE /* JSONModel */ = { - isa = PBXGroup; - children = ( - 14C8155E1DEECB6C0046BFAE /* JSONModel.h */, - 14C8155F1DEECB6C0046BFAE /* JSONModel.m */, - 14C815601DEECB6C0046BFAE /* JSONModelClassProperty.h */, - 14C815611DEECB6C0046BFAE /* JSONModelClassProperty.m */, - 14C815621DEECB6C0046BFAE /* JSONModelError.h */, - 14C815631DEECB6C0046BFAE /* JSONModelError.m */, - 14C815641DEECB6C0046BFAE /* JSONModelTransformations */, - ); - path = JSONModel; - sourceTree = ""; - }; - 14C815641DEECB6C0046BFAE /* JSONModelTransformations */ = { - isa = PBXGroup; - children = ( - 14C815651DEECB6C0046BFAE /* JSONKeyMapper.h */, - 14C815661DEECB6C0046BFAE /* JSONKeyMapper.m */, - 14C815671DEECB6C0046BFAE /* JSONValueTransformer.h */, - 14C815681DEECB6C0046BFAE /* JSONValueTransformer.m */, - ); - path = JSONModelTransformations; - sourceTree = ""; - }; CC21F1F91CD8820900EC9020 /* AppStoreHandler */ = { isa = PBXGroup; children = ( @@ -547,8 +497,6 @@ CCCB2FE61D337D0600B7AD16 /* Extensions */ = { isa = PBXGroup; children = ( - 1435C8811D659224000EAA9F /* NSArray+JSONModelExtensions.h */, - 1435C8821D659224000EAA9F /* NSArray+JSONModelExtensions.m */, 1435C8831D659224000EAA9F /* NSString+Extensions.h */, 1435C8841D659224000EAA9F /* NSString+Extensions.m */, ); @@ -628,7 +576,6 @@ CC21F1FC1CD8820A00EC9020 /* SpilAppStoreHandler.h in Headers */, CC95D5D71CEB5901002E0B8D /* GameData.h in Headers */, CC95D5C71CEB51FA002E0B8D /* PlayerCurrency.h in Headers */, - 1435C8851D659224000EAA9F /* NSArray+JSONModelExtensions.h in Headers */, 140956C31DF5491700256F07 /* ALNativeAdLoadDelegate.h in Headers */, 140956C21DF5491700256F07 /* ALNativeAd.h in Headers */, 140956B81DF5491700256F07 /* ALAdType.h in Headers */, @@ -645,15 +592,12 @@ 140956C81DF5491700256F07 /* ALSdk.h in Headers */, CCE301DE1D115DF9009C6988 /* ShopEntry.h in Headers */, 140956BB1DF5491700256F07 /* ALAdView.h in Headers */, - 14C815711DEECB6C0046BFAE /* JSONValueTransformer.h in Headers */, 14828BB31DA274FC00414EA7 /* NotificationUtil.h in Headers */, - 14C8156F1DEECB6C0046BFAE /* JSONKeyMapper.h in Headers */, 140956921DF5490C00256F07 /* SpilAdvertisementHandler.h in Headers */, CC95D5CB1CEB5329002E0B8D /* Inventory.h in Headers */, CC95D5E31CEB5C4B002E0B8D /* Bundle.h in Headers */, 140956C51DF5491700256F07 /* ALNativeAdService.h in Headers */, CCCB2FE41D337CE400B7AD16 /* SpilError.h in Headers */, - 14C815691DEECB6C0046BFAE /* JSONModel.h in Headers */, CC95D5BF1CEB4F32002E0B8D /* UserProfile.h in Headers */, 140956CA1DF5491700256F07 /* ALSwiftHeaders.h in Headers */, CC95D5F21CEC4859002E0B8D /* GameDataController.h in Headers */, @@ -672,11 +616,9 @@ CCE301E01D115DF9009C6988 /* ShopTab.h in Headers */, CCC684891CEC9CA0005C6A15 /* PlayerDataController.h in Headers */, CCE301DC1D115DF9009C6988 /* ShopPromotion.h in Headers */, - 14C8156B1DEECB6C0046BFAE /* JSONModelClassProperty.h in Headers */, CC95D5CF1CEB54A7002E0B8D /* PlayerItem.h in Headers */, 140956BF1DF5491700256F07 /* ALEventTypes.h in Headers */, CC95D5DF1CEB5B88002E0B8D /* Currency.h in Headers */, - 14C8156D1DEECB6C0046BFAE /* JSONModelError.h in Headers */, 140956B31DF5491700256F07 /* ALAdDisplayDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -763,13 +705,10 @@ CC95D5E81CEB5CF7002E0B8D /* BundleItem.m in Sources */, CCCB30221D33849000B7AD16 /* SpilPackageHandler.m in Sources */, CC95D5D01CEB54A7002E0B8D /* PlayerItem.m in Sources */, - 14C8156C1DEECB6C0046BFAE /* JSONModelClassProperty.m in Sources */, CC95D5F31CEC4859002E0B8D /* GameDataController.m in Sources */, 1409568F1DF5490C00256F07 /* AppLovinAdProvider.m in Sources */, CC5147A11CF317F7005EE8A8 /* JsonUtil.m in Sources */, CC95D5C01CEB4F32002E0B8D /* UserProfile.m in Sources */, - 1435C8861D659224000EAA9F /* NSArray+JSONModelExtensions.m in Sources */, - 14C8156E1DEECB6C0046BFAE /* JSONModelError.m in Sources */, CC2431FA1D361FE1006B813D /* SpilAnalyticsHandler.m in Sources */, CC95D5C81CEB51FA002E0B8D /* PlayerCurrency.m in Sources */, CCCB2FE11D337C7200B7AD16 /* SpilUserHandler.m in Sources */, @@ -784,9 +723,7 @@ CCCB300D1D337F8400B7AD16 /* SpilConfigHandler.m in Sources */, CC95D5E41CEB5C4B002E0B8D /* Bundle.m in Sources */, CCE301DF1D115DF9009C6988 /* ShopEntry.m in Sources */, - 14C815721DEECB6C0046BFAE /* JSONValueTransformer.m in Sources */, 1094857B1BBD7DE3009D1B46 /* HookBridge.mm in Sources */, - 14C815701DEECB6C0046BFAE /* JSONKeyMapper.m in Sources */, 109485621BBD7D7F009D1B46 /* Spil.m in Sources */, 1435C88C1D6592FE000EAA9F /* Util.m in Sources */, CCCB2FE51D337CE400B7AD16 /* SpilError.m in Sources */, @@ -795,7 +732,6 @@ 140956931DF5490C00256F07 /* SpilAdvertisementHandler.m in Sources */, CC9C5DF51CF5801F007951EF /* ClassUtil.m in Sources */, CC95D5C41CEB5091002E0B8D /* Wallet.m in Sources */, - 14C8156A1DEECB6C0046BFAE /* JSONModel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SpilTV.xcodeproj/project.xcworkspace/xcuserdata/frankslofstra.xcuserdatad/UserInterfaceState.xcuserstate b/SpilTV.xcodeproj/project.xcworkspace/xcuserdata/frankslofstra.xcuserdatad/UserInterfaceState.xcuserstate index 68bc3a2f5372edb21d010609c8deed20ce56cadc..47cfacd889d80e936c203c69d6c5c23b748d386b 100644 GIT binary patch delta 2654 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 z.{ff-IMOL;1xhpBaGMr+>VGrr&}x}x)`efWjn=}SNQ^DmR|Y)xIxDm+_LJzG=t zGgUuR_hJ6N^B7J&%5feA;j|ohUZ=IfzD#=`XHNT$UO01_Gp9Lo+CV%P(}ppeQH;Ty zrp?D(d{;7@ww+z<NH z&yN3^(404D%iFxeyL6&A?*HE+-1EPS+~P?P&Pas!pOKafIA=yy@==h&=zc~CVsO?B z-OrGH#&qPIv4*W2;uL4m;S3$lxW;|VamG`g2ciG@Je(OpHr!+8E7+Zx<~XxI<~DN* zbMXE%<(|2e6>MNL+t|Tw%y8!QAe@zs+~{Q1tLS2u4t(!4oaOyzeMc{xGwT=rW+3)u z)=-=~Yb5q`);ydy%dX7w{Z?Q$P}g{yYB>tz7rhI-Ok-0oF~UTGn(h@ zdCs2a%z0)puMN(a*Pf0zYu@L4iH_!Vr3XFvnSU6EbLP!t4)a;WYVdYZ4N`R*{^9p;<${O{1m z{CUK27H7|Q_JRnK5lL#&;j9JDT9AkQ_)J*fGhsnd%Hr$=O)!@QZ_pNdwZK^mKISvN zpbOv7jo$phP{uNW$xLGg3t7T)R;3Btp9)yb$l7!@> zM6N|qWG5FoTcoo^I$KndO4zMMGB2`Qi`vnij&#CqEpmrN?y#sUc56{j?A9XB;v#)5 zGP^}Hv0IB|Uu3ryEk*W4vM-W-(N=b_i^H7ZDvyG2v7K71!^Me6N(wTNnXJgZSoX!T zFD^m_^t`w^Z_pO=UEG29FyF;r@D=8}xI4OE{1ZA~JcJ3D>*CqW!+aN;@8Y$r$Mdjw z3)|U=&KIBJ3J-&DNkBaGy+q$j^zFOt;gU?~dr3BOk{i3Zq%>7%$g8}D>`P={BKwjL zu$xO{U(y-7xkTqn?8_4Svc!J)-h8-ZEV3_|%rsr?_GXFBmmEOPOZ2?t z2DiC~{7dCuD*saXmnJ3|dS6?vd^(o%iM2SH_T?4*(`HU-|r8X{mI`9U^r7)fV(YQ!#Xx% zKFjvvp34ps$8k<_3HM$0A_$i!A}Q|YcLc)aX|Y$!Wm|4O%kxrzXli2)%k9kaaZJZu zm)ncw_F%a^SnizV&RKqnvs~jQchK*0{VsnTgexL&-U?@}@ZKw8C_{NXcPpw=kA^g% z8L!Y1{jbo~3SF(}iCL`pjlP)43f-*G&5BXji526SgsxVY(TWwA%L@IhILZl5bB>GL z;w}$(#8aLJ;mS-@L!Oo9v(o;p{FVMVYvp9@-O3rvW**B~g^pI5%}TRbX&+YZL02nv zw9GS;CNzvmIImV33_s}FM&xmVkz)n_@+4eZeB7eTlt8M;}M5!u&d zBPX)2k$sKqYf4fYovksuHT7x1TfEOld`f4!V4v3fz>mnk#(dZO&OpX6jYX^`7JIbD zJlAYvKc0a#aUA0$I$v`Inb*cg&udeW7Bls`D&gAPfP~ z`C6T?)%jYTukA%2e#UdKb~qE6%Q9B727Ry9_gX!#wI6Hk$6C49%Dq;e`ef2`cGa>s5#-?2N{%@NLEpJN{d;X3=gPVRMyNQ(UH zttS6ho;z#buD=v`Pa$6PX2ZBultzKuq*4jBlo($7|sMHGmRO{ zW(mt#h2Gb#V*_%pJC40rcb5k|<{2-8aD8Hu5sBW{>wSHC*t~O^?F~wlvs9ggbUo{4)@Xb`lms- zLEnDIDfBx|;fCa-BsE#cgPu23peDNBP@hKVe1onx=z2ptWZxkB2H7`!N)LXeKZ6*; zzl>xOQ~8gX%t7`II^VFK9mH`Kxi?(q8uD+Dd&B>8zX-yO0r5zH&NoJpj~FVU>y0(2 zjm|gfdZVs4zD8@_MD~sLWaF3UdE@U4#An{dVT@ojQ<#pt8)qZ$MqO`Q$yWArnk!uA z7I(2D8|}uX2;|(P>rJ}er0Y#~V^enWQ;Nz|L)V+?pzBTcdXv50r0Y#@(3ZD&pD*~9 z-x$C@3}rZWWYc8q^`;rjVlMO9i5=LiyUm3ujjlH9XtT36>tu6#I-;A+_HeU)Hh)bw z%y#p4{D8AJJA1P;H|uD#S#Ea5=0z-J1smARHq3VOZuX+5E$PTjLte!mZTSFaZTX(w z{KT*P!659?7Q3{?Y_^PMEbh6*xm!H1Teh&BT{vgU0Zw2(Th4KT%Ulh@tqCbgCER!G zCv@j$2I7pZ&e%GRiMZodciif%t?syW3CpnuTQ}lvTaVz3t@d^69dy0*5l@3~TYM5> z2e#RPZ7E4j7V=UIS+>=t9(vi~TRIm9W>ase~lYo>ee^9aw{UfKQnRk%;j`;w87oD@agee&*;cb~lbY9jAGdH1zI z-hHppnl21yD!Vb?eP+AwCeMO!zh3t1Wxq4_N0J)z+^?Vg&fA}xd=#JrRj5Zx+R>hl zcn0@>$d~A6e>ZyY9Y63JgR$@XXR(-NtYi)Auz!9ZINWch`}d=#{qpWN%L7qF<9-L) z;LHP`(48Oo8D|_YlLP%2%5X+u9tXx_9tZ5vfhDYEC(bw!$8k(8z>Up$pjkLsz-MEglEq;e@0j8~HK!!$l}g43(&gyodFDxGwc+foJsa z2iU>G-_x6)_?18Kj2<4szl>lsW0}Sx?Bd}a9OMYcILR60JS^v7-5$Qn10Dt8k&t9$ zAP=%0k@bkIN6JthS&!)Uh-c@B-8j;iro2u^KBp&k;)tC%VkeIDV*q+RVkeHsdBjc} z(eIHd%wZ*)(Cv}qoIC9&lOIgk;%=YLO4xp!_ z-v6i>9(~L+^l~f$vpg1wevYNZT#scU7ez78V|6gUV=ZWjIUZ|+Ssv@ehkVRu$a_pz z$8>c}N5_UC?=g9g$$L!RV>6NWn7qeUAn&oYtYbSeA3Mo4%=4Ie9(&4*AUqzTqvI(^ zMH`Tfq2$WjAk4Yn9Nk0}pr;@p#i7|S$fFq?TSWEE@C(aDW$VH+2M@Ra$TilP>cd5!jb$~Sb! z8K-{0E}iO2KL+5;Q$w&@r`++>bUbUP*5GcZoOQ}sr?zu|!yM%}r*PL(SGgaArz1#4 zB&kV&Sej9qp0*jLc_bKJz7C(G}gF`4M@~{L5s_^^CcmG1oIoSdO`#*~C_4KC=s% z&%|+#+dScU5S|T)M?zAPhV;mNR_?QMpUp>cD&o04Ywyp#%4@tqTi)XXKE|$`^&Fg) z{j9#v{({{&JCZSsXA=58tM9Y=KD(HutiXPrmH+I1PI3)9arO~Uc@c!?LXwh#RLFf! z?sIaVvm@tH{d{KfP=X3nrW!S=LsMGN5}lvd z`S~}I|GeDiyJIiT|IBanr62O2AI>QBeqQhACo+>|tYbGvIEJpzpF#ff@}HOgy!_`M z@EG0uJ0{@;xi4fSFGZ32LMilpp#pMWko!V?8qx&)U(ox7_xPNi$a_KF3-Vr&_rd_= z^>!u$cp#;56sB$QAA)>jhaa{6DKbxfl?IOc$Hejoz5=Mg3fK z&PDURXucQC_oDe;G~bKndvQ6=ylB1`V=>>0=6mrx`njl|i~6}}z8B5+lKEaT-%I9u z$$T&A=~5~(lba&w=aMX!>Y$%XjcCg2w4ohu(}8#Slpg#_e>_K*hVU;V(a)u+{D-+- z($l4R$bM-(yD-~JGGDsHRb;;;yT2I|UV0vcmvwYGf&?T&=F3^hkGWp1jD5T;_hq>+ z%YC^8_VKdZm*1c*Z}C2!`^!J@8-3}A*0E~jAk4%U$G}wWWF*N9behNUSz!@>lInA*vTt)@`|ii?qVmeJmM+(zG_FV zrbNeA^HGq(6r&^+sX}#RzbgCHdNijUAJK(x=uS`cef2l=eO2FA`!k5a$ba=emarCk zadi`0*}*}Ma13|2>JC@Wat+z9+3Rb`k@;F0(vyi?==@p%qA7wqTq{Qnnqoh$+3Ra^ zUu#cCKH+n|m+GC#a zG6=6nkbp>1la35zCJOnl%Y0qt>$R~P*Y$k;RbJx_I`AGJp!@5eBLDSn{6s%SFp()t zXC`x4$_niEb=_ZI&qj7}nDgA=Hurdl=kkWmZ`hL?K1*&SL;f47ko!g+N>G8yRHG(! zXi5uOBL5BfZ@kI7e1@)X$n5Vbg*WDz29($8}4vpHumO5EZdOzhW)s4 zj*DF3IuCe^-fz4J!kZ!Pa5Fn{-PGsJZ*lfbXWtykM4WL`PdDeYh^08|<_0#il^y8o zrn7IJ!Cu~U)~yHo&!XFZ(hq&zmie}>ZhHo9+o9WA*p6;)`wY5$6wmK%^SLeiZP{;M!fxGu z8iaS^lN$Zp(a)Wnxz~<-WU$ zwX8$;cMos^`*HUk4|&4#AiNilgyf_|_xI8v|GkpPcCQoWd(V9DIs2Y7?=5Bx&bVjJ z_ssd8v+gMJ8gR}45;bjosPfRi*ao&CB-Oq-O?wj@fd=#V*`toBt{=&j;@Pzer7dsM0e5`pjt@V?E(H@x}y@$+? zI`bXBF__WVpGOm!!gS`d2)p}8=0~fL`H_Af>G#nYZt@73{SCPAvCNNUew+fCAItnW zJu*MeLLSWYaT#hN>tk6T%lfz_uOsVYGkx5N5BQi*k@fLxboW?Sk8fbEPxRw&$%Rk! z@gyhCc%qjl(b$_O&U#Xwid3N*dV2B-`gx*{C;IUB=fWrU^htMm@-xnS(wD#J&p<|? zqbGCG$&*cNWe2<2$1zT#qbKLM$Ysp;iOf%vkOtj6)y-4$e43pC6rw2R`ZR{p$o;e) zdV2a6@ADCQc`Ex;*`Lb%)DAxVk)QaL-;w$07^Y#KPgfJm1~#*e{g~;~IAnh+`_t20 z;T~rDEE#%!mJzdjrtfF^ein_HK9l!ZN#uQ2mTEMj6&?7P&-j9`=t?j8@C&+r))!qr z8-mQwrm_${KU>RsHlgoldVY2g&(5=>oZu98@p*1a(3*GnoNsZ)^8x(BP=+&#$>`^~ z=k)n3<}#lZtixQNJLkD>p6llM8SLP5JNW!I_jtf#od3cz@FE3zdSM@5*vA)odZDKm zdh+)N!xxpPN_A>cho;E<;yrZrLRT+z^+H!KdZ4Qpx_a?Dx_Z%%{`|`X=CBmGU#!NQ zUzqcYo$O&hI(y+RFOFh2UtCA_m+?qWO6=xKyZO>?zRX2l3g8Ye-Qi_1$|3v9CcMTQ zwB>Eg`=xombcdJj@X~(1{DyAyM(&p*n8*~SGZXn=%KuXSm-4?f^Ox({$Sw|}_m?-& z`%AsQe8`g^B8W#ql8}r@QjwYbl%NWAX+UF|@d|J97VprJPJF-@^x!83Fq~10Wdf6# z%{&&em}RVB13Nj&IWBUA>)hfo&v+R`gdy=sNJ=u1o8pwG5>=@|ZJN-WS7}9S+8}$_ zneX_8-}#fj8OR7mGYw^zM(rk`GMc)OF#NEh{4DoA#;SjBUZ7N^=x7*`#6X@ zMCd-^1gE&n9rPV9F?J+gB&kV9Hgb{&_lV~n@d_btyie%Cc>cqj<2g6pP7V>rahwzH zEZ4Y+^Wy0%-b3^iKY~=~Dt*-% zIl%^WoL~?8IfSkg=s3Xz%rwDOZXkaG9Vd(+8JWmMUJ78A35!ye3RFhl32RV`#;MD`?++=;5w0Q-^XO+MfYzM?BV_zpXg$c`kk zBZ>5#NZ*O%Pc)KA%w`3;PP7@>6Um-P_C&f)bR5|eo#8we(RHFHK}6zsq#`p}$w6-N zQH+w5M)t(ACzd_2{YcytT_=8z&yYK@+==B*EO+8Q$emd3#C_?9-V^`Jc>ZGvYgx}G zwz7kR9N`!zIE~&DU*mBQkt9B;$wD@Al85{h$1|Cv4CSauW$K{kByXeVBp)Mp61kJe zokY(`dLeg`pZSeH&~uW}$eUyVi&@4>*06=`crKIdVL$RGIf=YU9t06d&UmPWmK>NG5Z#gd{=sWU?odIaw6;JDJXtRTY!-9TcQSn^({r+QY{Y&i+lDM8}ajj?{5vLv$Re<47Gx>Nrxz zkvfjlaioqTbsVYV$e#SpK=d4`=SV$A>N#=>)6sL}Z1fzt0NEoqu^X8qFL0S_+~f{V zc@adU4AFN=eW!GXl&Q%^0ZLPa>eQkx4S0oCw8n0yY=`_Qb)E7HzUNnTo$@dGGl-Fl zVLTI&-`|*yNGW$pou^#S9^#NYK`Po?`*!&%5Nt^^UO(-4Ij zr;ec-&Pi=AQ`^hbEwGoVb(Fe29kG|Gb(Q)fx?skse`X*fFx%9gq0~A`t)tX)n9m}X zpsUm?*~nf_a*3)GFyAzKN|TtRq$LN@=qQcMX>^oEM`>hFBYPT|)4Y!P zrfJLDyo1bXx}u{r{TPZ)(u`s(6Ywmhnaw;Fu$ZOn=V=g;HVGxENG;4Gt#i`qCT%A^ z9UfKf)vJ1rL$A%VyHwzbd#<A?Y-Srf(RG%6xI>l`T;et_f{3U@ z$Q_k}RLCDCca+>wxyefbbRHE$CG;Gn=P0`oC3{pG+97+C>`}5uea07bLGMwzj*>Y_ z=BTkuU^3H~$3pB%)G}7$4pHt9WyhmVB6F0kqi!I3l+L4`2maqG#3KQTahI&_kToZT zC`WZ_QI`fZrWLJu6WO!2rvo4J72oj(gBZfUjART``Hxx5VLtL_UBy=R<9W<_h3nkn zE)RGaL}ZIV-fX(hmXzeACkI6+LwR(a&5mUAEM{v+6Poi1@@Lb1Ho3FuJlmK2z;E=W z9|O>Nwo!~l=h-G9f41q!oozK+ILJxPaGp!(JDZ-fJ>&_`co{@w53wWJb(-BCWcT@- z{YU;}FyqlrcKu|Z%K{d&hIMRYGuzmKv$H!p`!(+Kh^N@k9L~z2qa5}#hyBcvhV*14 zCxs|Sb>zum#yQM5M`Lu9!;Ev7aSk)i(Vh-`%-4KRUv!jXD8tcJj zH)l&;=M7}e`3c|9hd=q7fedCCBYVyE+*;56ruJCEFXH8d4J$XkuL#}q(<(1a_5sfUmo%!cfP_DqXdnx2l;xTt9)}= zg){RV*LJ)O=-@n*v1y?7-Ri-7o)j9$eQkx4QPzB z3bf`;+R>g4e9Tw$#ykrAMSlh{gfW;+0r?6{VLCHdh`tJJV?T$8<2dpcxXd+favR+h zaGwJ1Q7|d87tD#d6fA;$Di}iD0e}*3tm9(f^rwUf!qb}qW@_76|MK^C_J~(1<-eN zQS4K+Sx1|7bTwp;mOWbb=w|3W`aM453uKS(N)Kd@mOWbb=)Uwr|Iz<4nfa_>HL+|! z{%HB5<&Ty>`UuB3!A0%`5rsnZTqp&p(03ubQYZ(x$%i`>a)(0tFH{ft3iV_nGg-!Z zoK;9ig>+QNYzv)5SB1>B&>imckS9C~A`0uOa7wb`oWcbuOfgDQkt$TDCUtOr;pV(S z2R^48-|{{7v+z&Y&%*jCJP30xY|e#;GXa?kFJc|r*~MNCaG2AaLq~WyNGj({EU4nGJ?^JLpMdHFbA_K;=CeDSk6i|;>;rEQN$TV^ixDXMIP~# z7ePePM3_xcvni^lqG?D+Hq4`FIdoIB7IkSrV_MOgH)%(EbXD|Yy741_GYpxF%3M_D zqB0lNO;MSP%3O3IOIU`kitgYbXVFQ~o5)>M?xK%^h+-k}k-M0#iX|tKXqxjDzu@d* zBk`;ib4IaMtYtl$*vdYfRm{#7JIV>{R54u@b7rxZK}7MyB*PiSQ=^;Wx+$KMJlLt? zcB;6pikCuH#T)WEIw{@}b1eQLpYS#MDgG_r(F}v`8 zTB0;&Qo`PrsD+u7Xh0*HqJt9lwuEy_IIl!^ynhM%Q{p%J(vP9o)e_DvF^2J&O$oCp zv6wZOMG5_s&`$|FRN^FOxXKM~bC(C0P06&#RkA*vadt^(mo$r#!>|V>?LkRaDS3{IT;V#mc+4}*C&oU+#3v#8iqTh$zG8}@uNZyB=qpC9 z7=6X)D@I>2`ig0azGB|MjAFXblOK>fMrScPiy6Q_=q%=6>{X0=#7to>GRN4dnBD9{ zXE8_6S(O`Vt?WSGrS)A}-=#gnrBCAy{u^cyr5^?nW#S`qnN*}D1DVN9J_@4uGDUHRGVV}D z<}%G`M<+hS^Hk<@zT{iJr#F4jc^TQu*p)K6E@L;!%wax@Sjq}Eu$gVhUS>C*`7%d2 z$8DbQJcuY85RZhUBn|1wi2loFMgFofm#vJ;c$*LS61|uE4Lz5WyWCKQGYYxO$z5&+vzUwi%jvzG-plRc z80WdfRc@g3a!+_3M3mQg`3Mq_2-(ZqmGb$~v;T%&MENq|%88JBl%dFPeChutji-15$?kdP!KM`smMqq7QGadw4LRK{mPg__i%KF+Gp zlGk~Iw!Fpre1RRU@GF1t7yXf|!brw2o{3CBcNNy7mkMTCF&CbTish(5Q=C)LTq>GN zMLSioBko%9bIhmWS9GO2Kk_&KVm1|LGY>me(N0xdj`J#RVk_I(#UA3g$ZcK(5tR~= z6wh%bGpVGTO1i0(gWTk$0MVFHC9|odpGx|vq@PN1SCYGu+?6`sGJzjLuIqAEPrMBD|;R) zdmbtmMCQuXs81twU0L?ZvR9V5vaT!Zy7GH`z(>ej`8$4NFr%?=mCdvA6s9vDpFfqC zqVLM9kiYU4_Hl~KT;nEpxR1`Ogv7^gR!M^VRnlQEsuaUsRH;Z6s^dAX(wJtvg6viF zUgZrspzkW(u@_Z-M(!$o>BmrpGYZ{T8IOBZnTh;WWUk`#vdTe@aEz0j;VL({&0Ta~ zrD`VfA#>G|lt%WdvR9S4YF!$j_o_{4j?7i{UG-CX(1&06oj>`TVT?fU zRmU=cNzB52SB=F!SB>L1x~_Vb3+TM+Js$Fe=RriZfOsSz4Z5zDfz0&cFPvL#0RQ0q zs$J(M?q1FNRj*DHn$d!m$W?s~^H{**AfiS#3Q>mgcwTB$r3SU}^EJ${Mk`wLCU4Q6 z{@A4&cD<%-HEYtA4>9AKpYtVO(-kwWDR0ex8HxGU9EYB2+RvKiTXQL^Sj&1gvLBxT zwUUvJT;wO3BA8{ZQk2CVYRO*fHQuHJ@9{p}_>rHnbG78D)en1K>qQVz+xyq{Ox8|| zzH3L39p}}~gPv+Tt9D)VQ2Ske!u!Hi`}e!3;S660gv%nQ2S*NQ6~a3 zu9F!3)yYZ@a+8mO6s8!?t>cb$Ix>J2Tn!@X%2?N3>)OS-W>NP5XE@6_>{wlA*L8MX zXV-OhU1!$|Nss&0bGLf>tf$X<&a0=xdR3@SE$Y*VrZnd*zF;up@flOkXH31B*sXeY ztDfDeXO{J1If1P8u3=B>-9(Q1GSrVxB9aqHYV=<}hL+g5`sP>vBR-`wUt^!@dmiie z#60VpXZ^nX#c0Mc5!vd?R{voT(I6lNcBnx{vS9xkpg@9E7? z{K6o{GJ(lVV+ONv{|2jB%X&7lnQiRAZZxe7rBbU|;8+^f+D zCgL8Ac5{TIK}6$tm`P(ZX2aRQI?5>U7webz~-S}}3(WD9u zd7U=6ZIaGB(pevo)+ku4ZyIldG9r&E#r!fGb?*CU>~cBc39k|K4jv^Z&JUm*G{NTf2Z~ zXN6LCrv++2-JnKG-6;uH(&EM4O3_kWihGgbK?)_fTYw~xK!}ncF&qM+1gO$;?{m(+ zu6yqvtTo>;p68wG`}al1)pT5~Bs#425oW12ibZ&KwLI(;@i ztvp02&-Pah;&rD+mv-k}&*Ki+dxDPcpvyC0Zk&b&=^Cs>`J~hjrtu6?TnaeGNzP#3I`*w&-#YfK<1W;>5`XbP%P3DEBn#Wh(JHm8rsee8?w!&X>qBswVDIlzpS* z870psokcaHCGJs_Y@^(xDEBC;8$J1fz6@Y6!x+J6#xaqfnZ^uc9yN~z{K{`EVlT;?zS4Z^PjuI4&Qaua&~`VQ{qJ|5&@ z%J3LZQh{f9o|mY|YrM(ZyvqlC%x6?3f*RB&in=tQF->VfYueF~E__c9dh;Xw8N^VA zGm5cHU@}wrg;~sHK8sjF3@cd8IySO}Say;?B6~?8o&6l(FnJu|IHxG2hznfePyXS* zAp9oeTCV3tZsB(B;$9x06s38TCn(R;JjaW?!mGT&TfD>je8i`GK{cvVi#mKmeHzh( z=Cqn3^kX1H_=%B>VLX$V!gOXbhiDeEn58Ud6>HhRX11||cy_ageWZ~| zHiyWifMcBGG{wlX?tPR&K6PbK*Y0&cqya6kSKSdzME7-_Ti1DYcX5LA=%${W>&drX zIcnqk_2g4;Fryhu9Leaten3fX2Bp8@QVd$R!*#?W z=Z4O0=-h_RZS*kaZd9H~zNRiZZ`6+g%wiEbY^0k;nPlNPjXbB3=QO^T(%7M~=QQ@5 z#-7u-8-3}|96Y1(Z)A{zeH#B0gx_9E2_ENJo+paNG+{UsaL2ygL>vkHj`P3$Hwc?J zyU9~LLk*nY#Qkp4i-G8{Ni<7YMl$wjasV?lHA7P~G?i=93V6SodQMZ%Y3eyme?*T> z_1N@RRE@;IjOOL2!Y6!2BU;f0b2RtfHJ?ZfI&Qv!BNTEj2wPmw?cB-BROW5;(!wk)`lH(x zEAU>nkVOmMZ*eRLTi(cB+{5d<#|Jc}9UU0PICRrerY+aA5t+1JleMteFk8=1CR!fw*Y!2N0+at)86+t$xf7jv|3jvQLcp|u=Z>!bA+wvodL z%+W?KZG69tOxt`;O=>fk(Ri=hti|`*Y{&d<{`Woex4jkbTiXYaL0jK%Tb1u|C))O7 zJ~1puhHZ}^!*cP7B3FX2eLw|X;uY-P-tO(2 zGaT8s_g=L3UbOdKwD(@LKTR>0g0O?{ba)H*sKXD~xx-KvptBAuILvWQ24TmWxSM-< z8GUusSI365q%{+n!7O4)#69k4?~d+XM|ZE2ygTWulfF8AgZH?T&O6z+lYKkcx0CO7 z^4(6p+ez1*b=~8w-_FI@x6A#M#ra)6q8gEO zq9?sEcb7#h=1LHLSAvp!Mh$8q)9+?7n|z${o!z=VjxM`Ck7suE%&wl<)f`>z($y~C zU&}4rMs@7}eLXzu`(If?5qAFm?;z|}ju+5zw-$IOx^>2U-Bw}OZvO;fchBu^*6!|5 z_jh?8op#r0cb#@mAceFb>|viCcW@WAap!t8VlwvcF^2-qa5e~gK8c-rzK9umnxUr| zdd|n)?zx;3oaaIi_A1L$=(v|V*~^{m)ttH5uh&xYky|gh_4eG}Z{R-l?#nQS6Gt*B zLHNT}+`vtI$d^Q5zaK_3mMz%xhuuNg=SJ+;=N@Vz<39H6_(tjM&@Y((ud$EPQpxsojJrjL(DV8JVW}U zt05!t{UNd%BCDbPedsGxVgwU$zlO?s=y}|eVQ=#>pW-vad}f%>4D*>`J~PZ`eyWJi z{qzo#n91xQ9A1LkxPy-Lpcm#GZqDK69AV!P_8np05uLEd2s@96Bbk&S9C;sQu+zw< zw8P#b*RYk?ARMLBQ92!UJGE&*BcfT#GA?o@2uBBeKvk+?uhI4zZLiUC8GV|!m+v>>wb)t(b(xYZ|njt2jMv9j0^dShBRg|t1$a`|31DX zHxhy0%JJW@nk{S#!U+#x?+K41y9oms%we2A;bag_EX5O)qaMv@!F*y^9)y#w39C--yod)IW?(`ohHk0vJ5B7W%4Ed2*RJA<5gbg2h8>JP;~b5 z0S*P>6tho}^IGM(_Y|5oHI=y z)Ao}`K@d)Vi;wt(U$Fo5h1lnp>$sk;(8Vvh_+=%Vko64rW`?Y1JcQk6*nNiGXMBg< zW;k<(EN84^1J0XK#Cha4qc{j>UWMPYnfKyZGfPn#8O${Q%<{-$rafl9i#s^;V?LuQ z5!ArBGoz?Wee5~YnKMrX;jFvye>Y}%S7yyd*0Xdr+itTd}zKbfzoa=|vxoagKk2aPHOUcCOv$I%n>k+{67m z#KXLY-sgJOT+f*68FTwFfHAl$bG-+1=Q1Df!`vmw<70x z-ypkro;A-i=6S|E&zQHI4S3c(_jO)g5Ju}K`YE2}1ztu^(XS(;=qi{yT4&KZi`H4R zoT6nET@(96%PLw{(XxvEmS(i14ejZKPNQ`iZLVndIJz(PjvkDDqer0Q=*j3edL`zJ zwsZ7Bj&eK*=a=9%?%*}t-}&z0eCN$~&ipROalZWKFGNoBlS#pE#{5hC5rhlMV($gd zVCDs8UC<8STOg|ivRZJ0^IQnRg)j03GFjM>9_Vo4TDIZ-ExMLlFyEptsD*qM*=Nxl z=5dzG{27G5en3^KF^QSX<`mBS^|rmmTq?_@IYAin4fSb6 z6PnYCwsgQfkMYjLcxPf{71IazJ;pO*oE`HMBN@YZ%p4=D82iP@B1UF0(daQ|F-uv_ zD&!usfz8N2W(V=?#;h^>NW*?H*&M>2F$Ek$CNXC?$M48G<`4enN)Rr)ifbsr4cyFa z+{q}$BIjk3(aAD*ZrLp6GM_~(A%+#KW*r;ZLM%H;z&pH5w#($YES>!vK!3~fIKpw< zi)Dorae+&?N6Y@Rw#$`h35X`bUnbie#n-rz0X;e9^h z6Ke4lQPf3u%k92gU(07R53?^nfq9q9c)5&MJjM&Wgfmt+V?`Uf)05u(h;CQNW`#ai z#IXyzt-J~Qtkl6u9jyGAn$)Ha-!Pi#%s>|_b+IxV*{#&~%F~<;!c}%(^*r`n^)i)c zO*eYbi$1Kx-CDIBU9Hm9sw+XbTDGg*ht;=o2eMgR1KF&W&FZglM^;ZmHmhZ`dJb6} zLpJ^fZ@5~QYsw<~HL_VFn>7__#rMc&jcnHZfIGBiE3#Q5n>F$L8-#0bKsIY-vsTw@ zKcqUcSu2~hdRjY@smNxnY}V>&?S76To3*l8tEY8kcox~Llg&Ckt!qhFWV22->-4m4 zIa`p;I@zq#)4G3xaD7Q+vtBmq^|by2B9YB{*{s*o`VmY)HtS`xUQg>YIf87~%Vxcv zHk9TWWV1mw8}ziH1>Yf?4YJvwrwz;4jBGZ@W`mwK{2hcFuSYf;WwTLF8{a1a*=&@} zMm=pD&d*buC9>Hno2`1S=2ldB|p~Y_{rYTPZ3an{Be$rl)O9>4a>y$!43Lw*AHiWV1~++w`>Uk06Y_ z7TLtgCRR_e?@$%l#L6aCPq9Oph-_kI6RW4#RC19`tZZWSwEZEyDQ(^A2khB$3TQayY{|e&-@)-T7A# z#@)jsm@)2gp5#s5Mh^atWf=DX=8cm{+*jxzPBw9^Xp33n+>f}COu~$DI*oJR<75=K zhIQyDPDgR(jnhN?)!fZ}Jji1>KVIkY6?qM_#G57F9P#hdfEKi-9nOv)$Pk9{6Xu8? z!!OL@Hdz<$hkl zdjQ2djd8?-soG*!_CbiMyuAclrU-~nd zshDw>On1q2*IZ<}YbD#*K|H%L>#k(7FypQq^3eINKlz(~_%8@|hsbodOn2YSeLP4h z%)0ws)?Iz1qE>103Qo zX54**^ArbR;#FLOToZ5LX3Uys#zZqF$~mz#ukb3b^9FD64(>o=1T~OtViezC?nHAZ znme%#eHp+Y%%5ofMDr(3VH)0}ME4@my+~ZnD%P?dcOfwscQ?`9O+3NBL6{UE+oY?x z4)-GI9?YI(_M~z=#k0J?%T(lTWS%5*e`hmHGH;U1lNuuTB(o=(J*f*_>5jRRhBBN{ zjAcC2naLdHVcw)oY+)Dy7PB$qo(0Hvk9_y+B!NWsl7dUkGT(b0cOvt>kMaa&-upDqA@jYjWA43Gkon%)L?QFNb!k9j+S7?X^kX1H z_zCx9??RSi?!D&TE9bq*q>@esSsV<)D(nDOYnHCAo=PF;~h1l%h0ckznxf~F40N2L8s$Ej;l4_6Cx2ZvG%%2*C`BNLvhW7NL5B;!Hs-05(mZjPybs|6G zo}|XGf>o@>Ua9s<-9-|~*eTUcsr#`@>Jg4}k}E-&b`@814faa2SK7TiKq>5!Rsp-D zy@o#0?2zU?OY_^7_7T<4Nt#a5+@Umkq}d~_Bc1pT{iN9?%`R#BN%Omw=H8^)B`unT zEM_Uoafi~}n>6<(Z96+Tz+rN+OPXEM?2=Z@iGt8Xf{$y++7MW)xu$we8F@MHE&Lj7XOUORsFa8a}Oxb73 zKJx)eQJP12f^xio`7N;hsnY3W#&$#CzxDA=_e+Bntzx%SkGBV#^ z6LaqWim$1M8TYqA{`-5;hkgvitow&C4m0lmnW@CE0(WHpYSyulT_llADjDp@o!@_i zv7!ktWWuZYS<-9&RH#Kjrp_8pXL5!nLo?? zS)&-s1SVtdtm#Cvkj4CloU`IcU>D}kGJn<~aw(*U3%D~`fAV(_W}7wJjM-+)c1N<^ zk?hJ;!Ea3Vdwj?z)S?beX@Qxu+tHEE^upZP=FT2~oU^C#3p1F_JeISHSaxFO>_qmm zk8I4Hor}AYeVM=bhkpbAueG_F+qjd`l%Xt-;f^18nM%A)W!~mvKEs_qP>m+I;|Jt_ z!2LPUmJamhM@BFj_vXMv>~lc=2j()LMaciaHg+KY1M%!;4+l6*9`4P72hScjBOY56btD zJPx^khu*_?5BaXYJsKX`ihFV>l?<{ti1+kRK1V4=2ZzjhNdAZAe^~y9ucIV4@;Guo z{63L1p(_KB<6$`-9>)aM;M~LJJ1n!q<~w{k2y;9q$L=}z^8gP~9=`)Qa>$WGjvR6- z@d?h!smWK^J*O^BX+?JiF_<9?V-nGLZ*ugOvlMqR$6d@>%LaC%yPSQbkx4d(a36DI zlJidx=ISiB6wjlNT=ywg2f0yvgZ*>OlG_|Je6B=%Jtmt!YO`y5QV`9yqUHB%>LNo(gnSFqL1J$!y%20{Ik}so(_WIHH3i z74TjivFDK~%wiF%h{e4*Qou1zat56q`JIdW!QaUBs7#MugZ+-)z>|3X(PyyNQN143 z>ruTP)$38c9@Xnn`ySQn(a)$#1l4IkBXoVVSr8t(jyrL0{4LDznCy=E_hWK7)`3pw zvDT;LLNJR`@#>$#cR&{yGo$gj{lUg(||x(|gf z@(Qm~nJT=8yHFTOZK9})jtiS$m%^6lxzHYk_9(PRp*;$9Q8*U66i#9;3t7xkRwBbf z85VA27kfz|ooo(~OFri)<}x}e{4WU4hFnVt?&5wP;t}+6_DL%6G`cza7P>jBo3na3 z`#HKftK+lva5v6+zt6UyEgk60cl2W@!?D|0yPf@+Y0O|Yc09YE3qg3!T|aj>rFoPm zD34jrJ%>AU&OH7uV0i9hKBFo%`HHVG)42u=LI39pID_XG*}dpi?!n$g_Aat_k#mZi zQ{dhW2!#3tj2PbYdwC z!s2_8V{tj`Q~WZO(PObQiaoQ~GmG8JV$UqrU9o2tcccfS7>iwsCo`2_n1#-Y=d*|< z*sFLW_9@f{NIz)lvcp&m;1T*{{FWYXwG@x_nGgr-sg>j%S%ft zs~nC~5DXy@3Naui#L9R(V^<%;s8B^^Nw}+6ZleUWK+oJD@k9x1n9o zZfHOB4)g(Z2>J**4t)xJ37v&5Lf=AHq3h5M=sxr-^c(a$48a0e2#a7bEP7&06wMMfZHNEoR=Mk5mt5}At3Mdl&%kp;+$ z$V8Nt zkw1_>kq0P*!YBh}qs`D5l#hx~9U6+7a!9c1C-nebByW z9y$=sM+cz==t#5ztwgKPYIGDj8l8YnL?@vHN}^NHMd)I53Az+rhCYwJg04W(AC@G(*tEg6}NEJg=LM@A<`4@OT$FGg=hA4Vx-1fz@*W|T8VGAbCAj4H-# z#vI0TjJb??jQNZOjD?IvjKz#4jF%a8jFpVFjCG7HjIE4qjMo`&Gu~kwV0_3p&iI^h zo^gfoBjYyXHzvX~FilJ|)5^3lT}(IA$4p_SG1Hlu%q(VGW;Zc}ncJ9eFn2L`GxspxWgcW6Vjg84V}8v1l=&s|9P>Q$0`m&NOUM$j#4IIC%QCRySw@zHWo0>8UY3uQ!V0poSnXKtS=p?vtQ=Mk zR$o>gs~@X?6=Drz4QG|IDp;df<5=Ta6IhE`&$C`&z07)rwUV`pwT`u(^(yN%);8Ab ztT$OZS-V+#Snse7u-<2V!1{!Bf_0L0iuDESOV)YTCDs+z_pEEIA6R!;zp(DH?z8@2 z{fQwM6JudG#>WJh5R+kYOo1se1D1d#VGhiRc`+ZBhGk+|SSPGA)&=W|7S?*b?jo>?Q1FY#p{9+kkDvHe=hc*Reg=0qkAu zAa(>hhMmAZ$If6^uy3)e*mu~E*iGywHpXtoZqCNpe72Y^VJp~5wwA498`$w|Gdq!O zW82wob~3vqJCmKoZpCiRZqLqUcV*|WyRi$|MeGo}m_39&lwHCe#x7@%WLK~&*`wKG z*fs14?1}7I>}T1t*>l*>vFEZEvzM^fu-CHJvDdRVus5=a4I=9oSB?ioEJGS zabD)U!dbzo<}H&1+-A+1HE-6UnXs9(nXH+- znL1`|YF74uxo+?a(ii(;0=JRkE?%*!!#F)L%%#;l9k z6!U7#)|hQEZ^XPAvnytI%>I~nV&02+Kjv`Ek(iHTj>nve`84LsnA0)mV$R20j`=#~ zYRq>rKgL{-xfSzs%r7zbVt$YLg9q~v9*c+Zn(>39ZSJkQKa z>AWB>i`RoiPxFejn|#mi`SdikJq0!h*!W1@rrrFc*A*N zUOBIdSIw*8jpa?`P2xSno5GvHo5`EQdycn&w~)7#w~Y59?ua38dx0bh&w~4of zx0Sbp_Xh86-Y(ug-hSRe-g~?cd53w&cpvjl@=o!-;C;zE%R9%r#JkM^ z2A_`4z-Qxg@cH-xdiK{Eqyt{2YD{ zeoua1eja}yKc8R7FX9j7m+(jM%lH-iO8#j682)(v1U|{1%%8@e&VQCan?H{~pTC&D zg#QA6IsX;@3jQkoYW{lu2L5aO&HUH-+xa{BZ}Io=_wo<$-{pV6Kg9otf0X|T{{;Ut z{^$I!_-FVR_!s%#@UQT{=U?OB;NRrm=HKDp=l{z8lm9?~3K#;mfFo!jh!F?`LV;8u z6Q~4gL98H7U=$<>k^~lkL*Nv61YSWvkSfR!v=p=!v=L+rItaQ5x(adyJp_FOeFXyq z0|kQxg@Pf1p@LGu2*F4}gw-50I|aK1dj#(Y4hY^Cd>}X?_(*VE@QL74!DoWgg0BSU z1s4Qg3%(J2C-`1)U2sG2v*5Pip5VUV55b>8M2HG8AzRp7*h0t`3WO4&RHziHggRlY zFkWaBCJK{;cA-Ok|2=57h7ycoFMTm$c!bHtP%|*C~FA|F+B85mP(u#Bzl$L;EM|&XVy?KEm?y@?BC%L37c0aXu~w`X8^k8DS!@;C#4fR0>=UPm z)5PiGOmUXDt+<`IqqvheN8C-^Q`}3OC+;WC7Y`B_i9_NN@i1|jI4rIdSBb}nYs3@8 z6UCFo&xogsXNYHu=ZNQv7l@aLmx`B*Ulgwp*NRt**N8WWH;Ollw}`ikcZlB-zb)P? z-Y0%nd{BHy{Gs@$_?Y;F_@wxA@fYGV;ulE@@-iCUtO#7Xp$1c^yvkys^8iA&;@_#~;4G)YTIrlgIe zt)zpbqok`ON76&mQ_@$GCmATomlR5hBts=7k`aTY$uY^t zlH-z3Bqt;%C8s2xN-jz+NiIvimV6_*BKcNwRdQQ$M{-y4i{zflDefC(w5RpX_mB$w5v2n+D+PB znk(%g?I|sg4we>5!_so;NNI(%Qd%XgmXgxR(r2Voq*JBSq|>DfrHiDCrAwqsrOTwx zOJ9(#k*<}lldhL;kZzQ2lD;Z^OZv8Smvpyuk94nepLDJNz|B(JEE_$cD;FWW!{`Wus+d zWHqv}vT3sEvKg|Ovc<9`vZb`$l#}_O0xy?6&NV?5^w=IVxw!nR1pKle6U3pKt4iV zCJ)QY<>TcOCcn0m8MEp z1yvnX9aWuFeN=r_d8&S@A*!LO5>>TolxnnUjB1K%s%n~Qfoh>@k!rE3R#m53samDl zqS~t3rg~kqUA05?hU!h#yQ+h#_f+qzPN+_*PN^=aE~+l6E~{>+ZmND#-BSIjdZ32X zu)2jhM$J>>YL!~8)~L1WB(+6tRom17b*ef|ovse5Gt@2Bnd;8!F6yr89Cd&70QEq1 zzIwR2R6RmnqaLdsryj4Kpq{9nq$bp})pOL(spqPft6x;Vq<&ewLA_DEN&Tw&E%n># zUFzNHL+TIJht;2{KUaUD{!;yo`ilBn^;Pw4^&RzH^)Kps>igKH42SVqtci(W=*2TtMO@4G=5DROa%1rbrXg6l;cPhH6SQ z)tXV7(V8)uDVnL8X`1Ppg_=d0#hN9WI?YPWD$N$nR?Rle>zaL<{hD_)2Qr^!X>l!IE6@tHBCS{(tBup@ zwFa$2>(simZf%CPr8ZNWrR}2as?E{%*ACDQ)aGl4YfH5wv}M|{+HuCbOkI|)i>|9K zN7qd^KsQjAuN$r_)s4`V>Bj2D>Bj43=w|9>>6Yl0>XzxA*R9sA(XG|3({0!7(7mC1 zQ@2z1mhNrcF5L&ZL%I)jhjmAEAL)+jj_FS8zS5o1UDbW3`(Aff_lxeH?tUyYmKBS| z3S&jF;@H^OxLAFxA=VM=jCIAjV>4n~#%9KLj_nfLH8v-8b_Im7%*qgCG#r_fdXY7MGC=QN8;?Ou=93IDyQ^#rIv~jvPOPn>%78i(1 zjZ2G5kIRnh5Z5uTQ(T|8zHxbR#c@O8hQ?LJRmY8r8y)vd+?2Sfans@!#4U_l6t_68 zHm)viW!&btEpc1p_QdUt+ZT5v?xVP)abL!rj{7R^+qkQ7-^JaDyBqgQ+&w)*&(yQ@ zm|mzC>BV}9K2ERK8}tsnQ}5CT^%?q>`b>RieHVRKeU84ret>?UK3_jbU!WhXFVu(i z<@%BO3jIX=Bt4-g^^^6_=%?tX>gVeh=oji=(XY_g>g)8c={M`Q=(pG$hD z(jV0y)1TIVr9Y#;s{c;^z5cHL7yUi`eFM|LGGGRwL1YjcBnGKLW{?{c2BRUtU@{~d zJO;19XJ}<;ZD?a?YiMU^Z^$-uF!VC?HuN#{HRKuk8TuOr7)lJo48skh4Py*7hOvfe zhUtbGhM9)Nh9!ojhLwg@hSi2OhHZw|4ciSn4DT2Y7~VA;G#od4VmM(qX*gy0)bN?% zbHioB*M@HlR}8ldKO1fv?!?3KNIV+Pi^t>n@q&0wyf$7J9~*Ctx5eAz9r0=L>G8q% zjQEc6o#H#kcZtu7?-$=cen9-d`26@m@dfcC;>+U0@#Esh$4`i#89yuj+4!aL%i^Dp zUmd?Der^1^`0epK;@^lr5dUuc!T96xpTwVtKNo*K{zCl4_#fl1$KQy*8UK6yAMt-0 zxyEM3=EfFAxlv(M8WW5rquH2f^csD}6k}^+8)I8zcVn)xhjEaxz&O}gW(*t4jU$Z} z#!6$AvD!!)CmWwJPBG3i&NnVFzHEHOxWZU#eAW1xakFukakp`gaj)^P@rdyw<5A<6 z#?!{HjAx8jjo%r+H(oRTV!UU(Z~QfZm4GF%6F3Rt1WAH4L6%@hh)*ykBqX>Kk`p`$ z-h`}#Rtc>W+9Y&K=$??9&?8|`LP5gdgu;YyLV3c-gb4`~6DB1*n=m_JPQr5uFC;8a zcroFng!Ks<5;i7mO4yn3R>Io}?rP=lx_-|I+!||I+;3~`kL}g{Y*nlLro>7 zVWv^0(WWt`8q-wMG}Cm`LenDCV$%{+ooS_Mm1(tUt7)6*b<;l6e$zXq1EzOP2Tkvp z-Zz~voiv>?eQLUBx@5X+x?#F$`pI<5^rz{888XA>7Umc;&y1V-W`S8~7MXSCSaY0N zZ?>BqW~bR@4w^H}EzOZszXhTyws8kh#D-*j#1~o6F53%@fQM&6CW8 zdA50u`8o4k^K$cx=9kPbn>Uy@nm3tun%^?NZQf=6zFU((>zcF7i ze{25PeA|2{5l%!B(L_cfp2$xWBnlI?iMqtt#JEITqCL@(=uAvc3?^nIc1-M)*g3IF zVqRju#Quo`5=#2vMSeLjmaaH2##I1?j5?@c; zp142pox}r)#}YqIJf8ST;@QM=iRTk9B>s^2W8(G18;Lg)e@eWS_;cceBq#|^YLOI^ z#7j~psgl%5nxw>}q$EpHN|HY*kd&I#E~$M|c2b9=UP--^`Xu#D3MCaM4M`fBRFzbn zG%9Iy(lbd@lBOojPg;<)FlkZJilo}4SCd{#+MKi{X?N0|q`gUpla3^Plyo%d%cRpu zUnQMMx|;M|()USslYU9MmvrC4w6H9gg>4a8#1@Gq&Z4&%Eb$hjCBb5{m@OWQ*W$CJ zSXx`!SlU|JS=w8&EgdYqEWIs#EJc=(rPxwoskBsC2n%VMY)~2 z4eOiMoz}Ok?^)lseqcRhJ!SpW`kD1}>lfB9t*5P5tlwI%T5ntLSnt{p8){?Nm^Qvm zU=!LzHk~cj7H8Aj>^6tZX>-|vwhUWKTc)kEt&6Rzt)H#GZGdf{ZJ2Git<*NgR%07$ z8)uttn_-)2TWnimTWVWrTV-2q+iKfpd)>C(w%_)S?SSoF+sC%!wohzlZRc#~Z5M1m z*nYHKx81P)X8Yathn-{R+MC&%+gsRU>^!^DuClA`W_zMN$?mhK*!}i^J=LCOPqzo{ z9qb+Lo$Q_MeeHSne)j(Mq4pB{F#9O`X!{s@jeV+pnti%`p?#5kv3-fX&c4#V%D&pZ z)xORCx_zI0zx^HiQTs9b$M&!6XY6O~=j`9xui1aF|7gE&|JDAR{dWi3!EtaM%^YHf z+@Wx299oCoVQ_dIK1Yhf?+7?j9chkqN2Vjo(bmz<(caO?(cO{j= zDjb!LDo3?rlw-7GtYd;>qGPgShGV8|{t&N!#undG!Mtxl)Y=S*?7bY?oUoUNR#o!QRL&I0FPXQ8vm8FCgohd75i zOPs@;WzH&RwR5a8IA zx)NL_m)Vu*N^)6TR+r6Xce!23t`t|$mEmgb>g4L|>f-9^>h0>|>gyWdDsY8d!(78% zrLIw~(XKJB8rN9YMAvh!xvqJx`K|@7g|0=ew_Lkj`&xS!=>yGQ5>o?b*ZrIImV{Wdyg&TJZ-4eIlt#WJKdbiP?=uUC_ z-2r#1JI$T$4!T>r+qgTpySsDUJ={Ir1Kb1MgWW^iL)|6rN_UmJ+C9oW);-Jptb4Y5 zj{7ytMnZ%*Epyd!yM@~-5)$?qf|O#UGGaPraQ^b52)boYsE6+L4MbFosZ$008e)Qb*{Oq~wx$pVi^T3OEnO?THnK#DE_lmqyufnVL z>b!cd(QEcvymqh4>+z;|Q@ufNrnj}XowtLxvp2_^>+R+3>+SE&_YU@kyhFXiy=C5! z-YV~C?^y2yFX4U0JIy=OJKHIR?;GB?yt}>o zya&ARc@KGyc#nBM@t*R2?mg{2>%HK;?7ias&ijM+hWD2Dj`yDTH}9W5*vIf;KCZ8Y z5BCXu5}(|s@@ai>zIdO>m*lhgoW5kA&lm8e`&#;1`P%xjeVu$=ecgRMeSLiWd;@(2 zz9Qcc-!R_@U%9W+H_BJz8}FOso9vtFo8f!b_ndFOZ;@}Q?*-pWz7@WezBRt}zD>T( zzHPo8zMZ~ZzP-M8dZW=KI0-qwl)!hVQ2DC*Lo=d%izX zm?^9jEQOtdr|?tEDTygbDV7v#iY>*S;z)6(xKi9H$tfu*{*?5TRw=Dh+NX3%$w}#v z(kG>V%Ak~@l#-NTDZ^7LQ>s#`Q+`UhlX5rZ*OcE<9{3?Y!_V|{{9J#GpXV3)MShuI z?pOOY{y4whpWrw7Eq<%t>38|PexEAJEXdP%1$PRQ3bPMzj^a~UOh6IKNN&^*vs=(O5xWJ^q)WEdBtiW@D1%YLO z=L0VVRt8oFHU>5ab_CuG>A+yI2rgXa3*jza53<0;Jd($z|VpEfd{Ej zDkGJf+B{W|DomB8s#CS8@u}uid#WeZpPG@HmD)ZvJGFCaZfdX8{;7jfi&INd!>N_2 zHK~(QiPR~nvr=cLE=XONx-|8r)K^khr>;rekh&#xTk6i#-Khss52hYYJ(Buy>Zhrn zrGAlmF7Q}~JD7Gj?ReU$w9{#4(=Mldopv?tdfHEEzogwu`#l{=XQek!k4YD$%hEOJx^!cD zV!9(eIXxvkn4Xc|Dm^>BV|usr?&-bK`={rphtf;Z!|B!OHR+Smr>0L!pOro@eL?!N z^cU0X(pRN#NZ**gIeka^&h)+M2htCvA5K4>ek%QR`kC|#=~vRfPrsRdEB#*j??EKU z4mJ-8g0i4Is0iAE_Mju^47!5uU~A@Mn*}*x%`N0LjCBdb^<-r$&tAeY8Yl3To>w;T?TZ7wz zZwGe;cLxs!KMI}-o(Y}}eiQsLcqjNr@Xz3b3@n3}Aj7b?p2AMH6V?oA} zjHMaNGhWPCkx`qmI%7@7hK!9Fn=`g#Y|q${@m5bmS#@dYNr(e+p=MBXhzALJcgeL4 z57kuWfq(0}gL)YXOA9J1>!21;41o}69fU)CfY%j|C69RuqLQLPZS-fjO>@xwNpVT2A|Ka+;hBY1pn>Y~E9T?!1}u7n!=-~8;g^pG z8v|>CNq(EQ4yVWCb6VTB@wTxhw`~PNau8KnJuYB5LEMs@?C`SlapkyiPPfBj(OJRh z2^QTrr`HKS`CaY_6HY{A>xA63fTzs}-U-M9c_AN^l1GcR3^1h+{cETaNI$cpqM)V@ z4Dk2qlG`=XAI6EwDGh2588<@dP!P(1T0)sn7SsxA4Yh&VLhT3vAtXeEn2-=sLPp35 z1)(HV8=-6I&t6Pu-zhLJj1mA!ZW`NjRB=eI%Sk;3V9JgxgZWlw?*` zg5I@)vZ^*kB~{^y%1#9};p(b<90#n*9U30prC?OakOJ^#0C+%;$*c$!L&sbFxZ6o;bB9t@R2#`RDkR0M^f zVrU376e@v+LBk0xp(A36I6_Yth%fpML~MWOs$!0e)eiXw`i6oYA=f^FN2ZWW|I=T;9M0Y>N1 zpCS`+A(+uRR9sM9S{0#~W}>eCQ4Te|vI=aK?xFIEP$l@KpbE4ef<4BMGN`dQDA>{G z)Jmry5}FJ>1FX{&Xeu-fnx2mfD#GC^S|KP>x=`rj(jlR;lIlttF>0%Z##GU7WaSkl zBMK^N9(hHi5fEV}GCqT5LbCvXXQA298~|hrG!L2&Er15~0WFcGMHQj4UgZVg@2Y-< z)VgI=J!;AUL|sw65zCCUP8I-D)0D)1;fj(dxd2ic5yW94@l$9Kv=~5J0xgA>LC-@k zK+B z`xF8e*?^d6MF)sxSA|Bj4pYn78Vpu$RYi4SRdq#(HW;r!?p4qVs1~Y&RuWdiNw^6w z!8-}9fz|@-*Fo!{4S5gY^^9Ob*a$n}0J>E~>q9$WT7WEyONQ8nRffxO25oy@gEr^m zDuB;J&H>;hWkVv1)P7MN=-Itf->Q-kU|7M3a>7pp2q+&97Xu6ecDuc2CD;Y_;);T@;gzM~ z;>xOu0((?umG=6DEA92bsz!CqwKsxje*|0m+J6R^?dfhOUp&>$im?2aG-(R%k8<*25C zeOd3QPD5Yi;|Tz%21RXRe4E;VI_M0A(?1P2NkYFYB z6LbsunP@|_CEBfo?f?b;g=h~HcnB4qnVWzEX1*$v3!G=!kjSsCOF+~TE-L_*3Rq-u z)3KpauscFUe|_FA6dn<(s;Hsm^#>)dKZ$J0HblZm7=}3@n1c}*g&8mtX2BTDCOQxu ziB3dkq6^WL$XN?>;bw4iU`5u!JQyds5#5P?zlb|o_I1DV>cOQYg%JxDB~c0M zAi*kF1*>5VtR-@Z9z;)~*D5#`jsqu{2BJ672SliSDgMiHX!jFcu}9QOfXMkTuj&KV zRznLa3IO9Px)+oU2@PmpQWVN8EzPd1tfpiX|CdR*^-^j&OP{&t?su0g$#eURb69lF zWXFU)VgNCa$R`H<#WB!+AZk$?8R+PvM%K9g5!@kScMJZR-HlpUV}on!SW;!9H6_Yu zTW+|zq7e9>qR>gW2hMF|5KV;iVcmTyXnlppIR)7nLp#%?XnQ;{39g$ev;YhLQ zlHJJ@CPeio1P_CZtKedI2s{)nA%+pdiBe+3DtI_t3hji;h%%xIcq)P@?$aq$Tpy{5 zfrV;_lo}$fJlc*>p;+JUB}0Zr21mhE!(%Bhqu|l-7`TQA6XnE6qGABftbuYWbg85#&V?7$ z!Smqx#AsqXF)2#)B6ukzsDl^7ONcQ9(CcOJ^Tb$U9FaEwEc0-SQvp@49u&5n3x<@H zR8<#+8kntbIc6S2{V%%a3V3CcHCMr_i3!9+BJUy3^)2Vnb7(Ev0B;=Fpf8b>Y9+jZ zN~)quz6x)q=zNVJYoVP$*c6>0R;mn_hJg74yPpzibntd~2V`8af+&tY*a^Q)&3=oR zQVZ`Qrv4q5_EB8g55Gf9BW6D2(n0uxCKMflKP09TGXOoPN)tfe z!dHp8#5^J|N8fVR{wC}G2;cn6&|6^W0wPb}GCeY|L56n`2&7Z+FYrD1KKv{E8~i)` z2mB}efLKHGm{j35Y#Fc2ofLNJ7laEOu=tR`MH>~~SWuq=4 z?n%pnd}?@kxTLHKY)RD<21TR@?7}|r-&F)01Jdj5Gf)<z7$ctq>*1irAX$M8t{sL9~Oo5I2&Hcn~k*LsE#f#5!U>Kz2MNLx zkd}a6Jo57zu)UipB{ERhMpU9f$drksvRDx7fz+XES!oSjWO~f7_Mie)08&sef{IKZ zGom4Krq`kBUym8pimLI2>R~AwjTu4JD1bpKqmwkcfcDn~qAnpq2G9_yp|zGRoA3ebm6$R-nLF zP+l1-YF81iE(ds0EPHfVZXs0(%n4VPP|-06q8~eo+M$_MAafmD4Z`Focc{Wj!-}1$ zot#@$Qwj9T5T9kAw?G`)QulE zz@y&Ng_|abW)CR~SA<%DxF{c|N(Bw;HB5{m8cioFBO!UmP|XRX9Aw0UkdYvujD)PM z!qsI}#O{bAL@E)mnfVQ_FhXN)sA?rrjf|qQsYhRd%5!woNn{Mv45>lJBIA(ppd3l_ z7{F;@LM5@6I7aM00ftRPCLu&$5Y<&iiV4KN{w3kc+@j%h1%k$AGV%;?Rt?zD>#am4 zBU2*fPWp3nuTMi}f$R#Ij?6%267LWPh<8^a&myytImAKYL*fXL2SQ1pP+?FV#Jl9? zfT(>4RWpo;yH}UowqXB$3h-KpEJ79|OOU0=GUR#W1!OshJR{Sn90kx4`8}5^iw^)F zK-yCp9zwl}u1*{x-p5(Qd&CF5a%(^-Z3F;U2-XHF+5=R%BGXSIuON#6tQANtvJ_10 zJ+!1Ml;wbMlsH6Fz6x0#U9b*W1>z6{)Nzn?&{SjtvTe}LF0&NWGpX`C z2#XKXvo|5Hf*M-`-! ztwx~_sz>9aQ4(q-ZV-7hFT_3K{u*dKnhCtW7Hth_LEZOPpoq;$7zXs# z>tICqt-`b}01qCCv~^ay2DVUv7kEc|NlK+DHKAfB zpeJP@%*-kc7Y>h{;WUX@DSsOsnGIF|dPvow>sO01s|(s4GOk3sqB&?c;y2=V;*XVR zE@VJ^5`R)BU=T$DrR5Pk8+HRFi+tP(p0_U;70L$rYDqz9X-)KftDK&6_C>*~KeULD zMn@fO#)Jy1sYs?x*{G6=aKyG}7J-wEO5nt)!aL=~sbfO4ABxtZ{fP&}mB$vp2Rg?1 zii1 zD3bytIRY(1!x2LQB*`J+79<=)u|fw(202HAY_h`jTQfo)z?%-?8V_l_fgg^d(uKyO zB0D&%CbEJs6pd6H&U68{LH`dPwr_t2cc@&g3p4NC^BN@5e zZjkzbY9M_oj81MGjiXc1*_6DdA?MH;=uC7L`YZ{Gv!_XzPr?Ec7Lu@N4LS#X4xI}G zHXqWGu$Y7`By6R2pNrmolm&@uWhCyW)2~MpGAaw9B^52$J!*2PIJiObO;*hc6@zfK zVFnF^Ixfm>)tM4b^&<&mbpO48z6gvEx}1b1wdhMEEd3iV4EDk*$XJKgp({yPM#Azs zbTzt$gcT&Lp_KD6$3wj+arWpHg|-m|Ri2gTCg@G{H4;`5;H+B(KB8OEP2eL5tEq>@ zIe8IBHJWOVh|YWy1r?q;XdAkTaDhQ}U=lqAjE|1lhrSzG9eoEq5Lph0L`NPE_&~whTJD%+e1h7JNhSx$|!A+*P;(d z*xn!u2Et%auYeqMwG3e291mZy8C>v+0j!#{Ho`wjcW_!!Y~WOIM2M1nJ>3i*gWmw2 z0k)mH0X##@kTmg=3^_vuXl5uFKn5NX_SR9hG-5>o1aTy~q5M`SQ1=NH(QXUW5&Cp* zcvJ73+E#=|G=!8;JqfXW9*IF;IFR8gQ|KU1gs`j*vO8kh4Jenwi8>FJCh38#~A5R`AhWV$d(&zws% znc0fbw#m$PjP@kll7us(fE$`G=;`Gxn@sP_$e}pdh0&FSTaj?DjWD! z?*pi^G2I#i^hw+BwvpFwGNRxA@dUW!F9*Pnx(M4C>P(;V22VU~kBYG`V=!b~$H-&! zWAtYXU<_pBGX^mVNEomun}j=%a7PmEM8cg(xC;q)T?ZK$MT`)mm@$Mglu^PMMg=7~ zB)^8_k0<%FNd8`ue~#pzr&L(#I=HKB8?FE+DwX}|CMmjX z2Ns||g0Kpl7JyhO()a-Cn@tvom_{mTtY0G^cXTSK1ZnRGkRJko4^L3(WwSsHqbRa4 z@UEa{0?r~p${%$ybT1c>nv8ZnP0x%d9HV@zW)Pq@#TdmH%^1U|VT@&rV~l4^U`%98 zBH?Z%+?|ATNw^0I_axz7B;1>X`;c&763*MeAQ_Vx&oHJirZT26rZZ+RW-?}xa6b|* zC*iM2q#KEATNaS6ptN0WkUQ?zW9HqilIs3J-Uovn1ME-4(|DI6Y7 z&!|5G17Y9aEtf-mF&I4|ANMsG8ByV4P>aZ_2E8w#iboFBz=2N9;4tVE`YX!+YG$A8 zED*Fb0rnRvA0F6&wt~MK2)e^URrQsU#{DgiIRuPy4TOFV8COqo1cV!S2C56BzH#KQ z^fnpuFWA}$MpTKGGG3s}$}-0DB;22b2i7r`GhQU&K_om3_=ktbl8jpF2$F;cfUt9n zbquw+twmJtnROHpeI;lj0dcp_=k$7Aj$}vUiEHSI`F}UD*x@hqy4(&&6s`4)O%z%i z7#m3#n1aD|j8_>TJ}D$&aQxn5Y&blkPeqt+)Y3=HB={?mATYLr(_h98#v6<`89PZB zU_69`L0DfxwQGO`g0YKwzZ)zEen_}DvL;xzkl^Vv=+mV7h2EtGzDf^`jH(|SUFb0V zJ~AS@3N;b%yADhW>~ z;h7{1BKkShY9gkXDPc;PGNzoVU@Dm^rkbf?YMDA_EEB|kb4hp!2?Ir~BjI%<3{>NF z5`K$>_mS{H68?~cKPKT*Bn(mm;0PLNBQt^4Mo$AnZEFUHD(s~tgCoCy#w=SwW9SwdI2N<=vM zA#0aVRiw&d?_2_IdX#}8D&XQ;1ve#hBQtQ zWPp?D9L}j8+!U#yG*Ywk@wHE{RZnKtlTB7$>yGV1rRA+aH78Wj71SN6@U$)H#}8Fd zIm*P6@`55;S;=5~{na6GkI<1sJ!tF{N6^?mmyb_>dhDNOPjt@7EXt|?t!<$Ss_;SI z_K9Fex2`hFnH8Yq!34q7yjmuRr{+_cnLK*(9Zet_PQW8UAagWm`(utF;ROWm6muMC z4W}B!vzZf^6Pc6fdln5zS`P3;U8+ZbAcg>yzeOaxkc1ci_hKc`6&hNQns}A&0cAeJ z1ZIgUnl|9~R|;1$r!uEe2d95K>hV40^|=Of29s*&V}jswX)P1PpUeIUPv=rRokxP! zy(T->>5y0eR-wC-i@r)ULv&-z;CJ0!f}pV0aq zMeF+{ys;^*hnYucS^?)kO&jd{|8C3+h`WC1yJ-u$Mu|JlJV6mh@63}V44Tnsi$>kS zV17=*TbdH^74r;Dz%~-z8X@3+956>V;P{-?uSO}l$h=HZbcuww*D^syzvG{X>ncU< zcO?8qQ)+)?UZ<%Ab&faxUDbR#Z`C*w2eei%T8AAS8m0AT<{gUG+a&yUE%Po3+JOIo zulkju^*0jU-IUe`EQqFcFA497(E7g*@hO5Q)O;Rb)Dz5NvM`F^h;?PLNqB#~y0V%9 zqFBvI(Bji?1a08loMHKDbqZGQ^bxEuMS8@Ku)q=3g(&Hj z6zNqYe6cC%V^}pb>6b|uI9d?K{J)aL0n)0Aw)E?JBTCvt)=Y4O$C|_lxM* z)>PIs)^yek68?sSza?Sd_OFrfk0gA9gnuI8pGo)*3IDQzHH-BuYc^{R>p9k3);!jH z)&dsD)b5iAlSE=jL`NbCB;q6y4~YawB$GrsG%16#meCpRm3%z(^cilWcF*Y5;6{&Z zk@lZbaQy*X=V+8&vtFWMU(Ls}pB{F^s$`cHhi#Gnh*0D8>*)2r&&T`z8|#OvsNT5B zXaPM^nxq=;9)-7-hWA50Ui@_M9w`x1#5KLt{X}MNqJdw}$GbcoaH=*;9n*so#wP;V zN&~r>k9U1KAQ1}x6_htBVLem7SZN)u~N)une_EB&}q9N;#CzNcd0c zf^2+Yc!aHh>hZT#g6`A`(5w;pSVDjOg>^{ef(||LG>WcdeZ@M%I!huDiNGX+P@UD_ zyqR_3VXHLjGQ6OE{Ymu8Z>a}YNdzT8AH~U28eigddqL|Y_(rsbJ~7shte+?bUuWH5-6RnfiC`qcUdg(}`k8f`L^veU zj6_=eR~bxYcdXw?giA44(5h`#TH7!SxXTLAu|?2P z5RX^P(b1+2tQFRd5!F zCK54|Na9Lt05%ZICy^u)v62Yro_w4V{AUak76SVQD<%=k|IsjE!%(yi8;+Hd2#~rx z%7$;{0S1 zaYsQ;{YRXiiOm5Z{%WzX=diihJQDGeh>t}4Q5Xv!8WUPymr_8Nk>Gl3{cc$P7@*D+ zu(z7njaRUh6kRK@TC9#lQb{C@MA9j>U5%}w=n9fZ%m1phH&JxFN+KCiI=0l)K^uwW z>06TK=Xj`(&5AIxf9$w|fZehQJTmIP3*lp|%b{G2vyNBJ!e#L$xk?thY zgG73fNFNf(Ba!|jGLS?Dk;vfn*dN%R*aJ4ihS>-kWi!|y%PS(0NhGq6M3$4tDiV30 zM9z@NEfQrnN%Yv{H2;zDpAy$sxP zjdo%-%Fft48lj#~eX#!zg6;nsMj{%c-ud{Lr(@ASzzE!2t?x>$jI>%)Umo-X2FYo3 zKv)aDm+U|E^;{3pc>)p|8i{`Sc*WBp@vn2SMna;#G3p73=xIbi+iB&~A@UD|^Up9b z(U^cf*3nOg$-idFL$UlzEwR$rfG?CB`gGX*1HSx?SX?yt!hF2?>A?Trm=U?1Q`uNW z=p2&mVF$q1cd)%|A3KHZCy@||fLLY-i40xEPG$Sq>1^P=OTb4G8BQW)l!IvKmH^+P z3Hlr!CLbW5s{b+*a1jyoq(!ddP`CH89?AtIwe)uoQq3d}TQsOHimX-zWupor-%j-K zMZ~AE+ki-g4ZPzpf|r*U@>sott*$C}z1wDYU{i%`HgIXBwd~F$G9rQ{b%mf~8E8uE zT3uBNzGQ;}*t?{tYN)v4%~o>S``!#!HJ5Bdk++GZ`>#P6X7`F$uvD@6zTMW|B7VU zV;7a`S;NS+H?l4La68O&cx;v%DQlt2IkSW7cCLb}fAIv&nAZikyqP zWp@n8ub7ozJ$`tU!g<_s1JI$VdqHEz1>7R;Vs0@pM=r#tu|aq7 zf6j2YoP+x$uuPm4qZ7E2TSnOJD$p%z4ZB6Zc0G4h}Pi^IKgSN&(0rMl|ORk8PPF6 zk|*eMclu92fib( z;~AWw?q<;40=iY8!^_$2WP&|g96XOJ!J~8NR$PhVes%eytMlhi8ycA}Z{j;eSK=uu9HM{e z!ejiqk?+cP<1=~mz1#u1J3)6B=vL#Q=Pi6TpF>t+HR#rW?wn-Pv9N6Dg252Nqj!vi!b2qpj!(%yn(I*9UhGJpu7K0-pRXoH}B!SWK%YPZX@U( z0Nt~od+x9~+C6&j>~q)SE1&$1a0b5bV)ys*`H(wYPLI>;cKh(Yj6d~4zQET)@P!(e z&+CkEMxPEYEIZ-*^8NV!`~ZF+KZqa958;RM!}#I+2!13#ibqG%gP?l|bPt2>5zsvf zx=o;a40Ml!?g`Ls2HlgOdkS<I8Zd-H!a^^l0Uj?RMB|<$o$$F z`2&0ti)Kxmlke=8KX357qUpYg`F>x1W!2oG2~Kx@$*}xM!{$U;r<4yxc|W2Uqkoys zSGFJp7V)$A*%2}K0_a|hNddl&pOZui@MrUIU@4$~oWFp-kiUpu6cKaiVR#vIAAs&d zO)gr!c{fP{^WFLfe6B<^N>=Mz{X zVPg}vFo8AV92MQDwfwzFw(x%bAtJsu@EiFDc;sP>QhWlsPeJ!tBmXe}Apar_w|1+zo>A2nh0k+I$vn1DgJr>MIzgf$G$ok+4dU$E*i)D>--!1oBUh++x%7@ zw|NKXz5(60p!*JV--GT4(EW(UG5;RFoqwPIfd4SUaX*1>C+L0#-7lcq)kMl2rp7V9 zL(4dhvdEA8Pfb{4YJ!|Hc2q{|mZ5@K@0N3A%ml!$N`)feS3?_96@Q8&;fOR+C>JiZ(6~L_tOt5+u<5 zbr38hbQWwVt%NQ@SD~AbDOdz6==Ou|Z_xb%x_^P`08Ga_g)AXk$PsddJmeil2Mhy@ z9vCAqY!k+5Kg+jA@2z((FTZm+mgFDT;6Sa|3r!=p!x8YfojyN#oWq6Q@m_wn%Ng)^ zQ91e|c9U=dS=SSRG2ps3vzvqh!5-x*wcRAR1?)Z9D0l>~;1m47@W2Sbh`>l_Hwk{> zRN*w#9gGBw42;sR^gq3wN0?jaCk!Br(H|Jo!7#>9VGPO^VVE#n7$J-lMhRyKqk%C4 zlL1U;V7dU)6_{?oWZor=6~+nUg$cq$Y-^1CVFAVpOcpTNz@X*WghLKL(bD5|Y7f)W zXaU1fw+m&;1)Q-(FW?jAl2(!~e{@kR1hgCIz2YkCaE{_|V!8*-D^qtk9YiXkl9{IzHen)8fzmnv+rSiH zQ-{jxDy%`&s?(XLsv;7ALO${^$v-3L=nYi3P{6daM&Tl1k#MoF7?>Ww^aSQ;V6YeW z0%3`;SXfGq<+1nyFee>ImUkcAXVCnTnsU;UP}@1CHY!n`#_5$Mu#siEO7#}eH z!~Re!R+sQ(3-*HWoPe$hm2Y4|12)aW03iMHE2tMp2od9l(G`6|c!%)To5EYd+rn00 zP6p-_U`_?*v_|1wVVkg3*bdD9fa#5&0MqY3WiAT()8?SR6i6#4;Va>5U`_|7@HXKa z;agz(0Mj=OZu?2tsb;QS!msKNb_;(KZrdaLA?y|Y6#f$S3HyOTJv0EAfxrv`W-u^A zfEh}-?Oze!2oQB5@rt19mKg@ja9~CNGZL6l|3A1*G_}BOVpp+S6K*SNaC@*9w#yq~ zI5Df)-idkA3)p}nqvL9^yLg0nq?igY2Z#g3LE>O>h&WUnCJq-z zh$F>O;u+#-af~=t94C$!Cx{cpNn(*WS)3xCDNYrG;#p#`SR$5+Au%kLiPOaCV!1d& zoGDg_m131xEzS~Wi#1}cSSQwtbHusgJaN8ww%8z^BQ6ln70(kFiXfgZULam5UL-CO zFBTVzmxz~&OT^2>rQ+q{72=iRRpK)7YVjKJTJbvZdhrJFMsc~gLR=}{B;G9EBCZl| z6>k%77aPSp#5;}WiL1pm;@#ps;#%=uah-UdxL&+p+#qfg9}piD9}*uH9}yoFH;Ipl zkBd);o5d%^r^KhlE#focv*L5&^WqEQi{eY-%i=5ItKw_o>*5>Yo8nvI+u~O79r0ap zoA{o%U3_2sK>SerNc>p*MEq3zO#EE@Li|$vO8i>fA$}u%D}E<_Fa99@DE=hw6n_?f z5qF8dioc1!i@U`=;veE(@lWwDai6$f{9F7-{8#EAb(C}xBk3iBWRzHmlXyvxL`jll z$s~1>%uQ>CDEmQ*a2 zNTpIp3QJ|uG-MNsFY5rNz=E(xuW8=`v}lbh&hebft8av`o5Mx<YH5vhw{(xRR=QVOC*3Elm+qG~NE@XG zqz9#kq=%(Pq(`Mq(qq!&(i75V=}GA+>1k<;^o;bZ^qllOFlfz=1!g=j6M-oLW(qJ< zfjJA95@15Wpa_}{%nV>EfT;pz7BDrypdB{{n0dgQ4a_;foD0lCV9p2TLSPmFvly65 zfw>Hr%YnHPm}S6R0}N{T8-Q63%t~Nx24)p7w*k`#*vF7r4b0tu9qF()A+sKs4Zu7A z%tOFD0?a009tUPKFi!#Yv}2wH1_8ea%*(*M3e4-kyb0JYka-7~ZNO{?<^y0p0_GE7 zJ_F_pV7>xo2Qc3P^F1&>0=DF1egWoJV15T?4={Uy`3so+!2ARH4xrb89(!gPLC=9+ z0KEiy6X?x=&F}PGL7xeFE9kR8p9A_l&>sQ%qd?yiuqB=TIMAN}*oIEu3-or-J3;RT zy%+R;(4P$YQvsXD=}!lJAJF#${Q%Gp0{sxs4+H%O(2oNBXwZ)Z{dmw%1bq?cr+|Jc z=&?1^*Pt%}eF*erpq~!<8KAEKeHG|ufxZUxb)cUE`gx!~8}#RZ{#?*6#D-s>zYz3` zK))FDmxBH>&|ePvD?z^u^w)s?I?&$$`sIMFpY%6_eii6%1AQat?*#p7(BBREwSeuH z^y@*t0rU@m{vp8TO8QNJosIOHLH`u!w}Adx&_56Q7eW6r=wAi>>!5!V^lyXy9nfzB z{dUlQ0Q!$W{|V?n1N|3(4UP0WK>sc1zX$z~px+7lUqJsW=zjKp5Bh(= z&;blOFzCTx1Oo>K0SpotOkgkrb`~;p1w$qntYF9jLk<}7z;FZ@jsinZFdPGhWiY%7hS$OH zCK%oZ!#iNu28Qim_y7zaf#DM{dewBWcewTJjd!#?4z0#l3U(!Bl zzx22CkMytHLGCE)WJcD@2H7aHGAHx0Ad9jj%d$!CB%9?7xwG6w?kabaGi8fxm2GmC zoGs_bxpJP|T|PoSQa(!VA@`JzmXDE-m5-B;mrsySluwfL?*+lZVSA6K z@}2Ts@@jdFe7Ag$yjH$fUMJrtub1zaH^>|12jmCkhvbLlN90H4P4Z*%oX8B3^ zDfwx6i~NlIto)q(y!?XvqWqHlviyqts{ES#y8MRxru>%tw!Bq-M}Ak{Cch_dm*1B^ zkUx|^l0TL|kw29`lRuZgkiV3_lE0RB$lu7{%HPS~%Rk6J%0J0F<)7tWa%rn<>*|F)lw-;@<(ayhjxZf*I?B|;)YEjd=@`?orsGVKRBoDKnrW&qRhp_y)nL3CjCX=@9T*=1<7O~E560KQ_%0aV z2jk~p{0fXefbmx_{spWKSOHiQu$jQxfXxB+2w;x`wg6ZkV7e$f5ZIx>js$iBV1hO~ z71%Ja(}Ar3b{4SnfL#deV!(`H_8P!^V0I<2tAJe%>^;EV2kb_`bXayXuulW~BCu}) zyA{}Nz-s+*!bdfSV57EZ}N^n+x38z?};m0Cy2^mjEVX zaMuEN18^&WTLoMraI1k^3*36(9s%xg;GP2R8Q@+4?hW9!0{0$p9|HFYa9;qo1Gt^Q z?E>yk;PwOG0eC&|9Ppih?*bUC=Cgs%1O6!Bj{*J!;GMvGfj=4eKEMwEehBa*0Ari{ zIN&D%e zuLb@A;2#J6Nx(P`|2*(70skuSZvw_#`1gSS5cp4k{{r|Oz<&??Pr&~I{BOYj0sKC| z0D{1PAb=o)kO4whKu5cf144HYjsoEr5KaK07YGgz+#vWsI2nY~KsX(Qejp42VHgM_ z0UfZyco2#}C;_1igenkbgHR8`d=M6Zun>d`Kv)FAr64Q?;aU)G0AVEvjUcQA;XV*H zg77d1n?TqM!WIyo2jO)P-UeYC2=9aNF$kXn`Ur&YK==uS-5~4*VIK(pfY=d41Be`m zc!M*6*crr35N#mlf_MaoJwZGc#1lZw2hk3qAH-8Z>_js|f&h(#by z1+fIgG7x8gSOsDYh;u-k58^o>E(GyH5Ep~E1jH*qyc)#oK)eyel_0JHu@S`8Ag%>* zJ%}4Yd>F*XK->)C77(8Y@g)%70`XlCw}bc*h@XM@Er>sY_zQ@?fw&LEe?ekEVnGr? z>I70}kTOBa0x1uqBSGp3(s3Z21d<&jH%LB^P64SmNPR%+57HozhJiE+q_H4P0BJHv zL6D|_Gy|kckY%7^Ed2T>;WnAYBX64Ir%qX%$GfgLD^2_kgqx zq=!J-1k%$WJqywcAiWII>ma=i(l(Gj0O@0pJ_G4XkbVGZCrH17v~&GUI(gS7Qsh<} zd72r^;L!E0JUUT}UHT%$X$7P3aY}4+Us+o~+slm{gLQ}WqlABj4)lb7P#cU?!w`66exMUe_sqO1`TX=93gmkH9P(hWU?mCD`GzAHD&D z@4tuDKrf&`F}J%>R?>l$6+i}>MjqZ6G_Idg z)t&Hrugb(Rm(npYH9HM%Zn?BzOigJ)VW_r1iN9b2Zt|#z+Ow~Qw2VT+9PQbuEdyEK z*}V zq&9lXtI)jESL489AHG6-<-D{)NnBR14s9jP;B!W!)5J&KZzrV`1<@bW1JTyg%(S3+ zL9AzWc`bQGu(G6LKK87xC2ug%{=K~`XzdDeKFXNU;j+`Pp>^0@76_F3efDFNf2jzn zLB+MxPjmTwE{Dq>2spidr#s+txV;JgB>HVvsXtWe3@5h3Hr+!vr#w|m{t}1XTUO#} zj>+xu1-y2r*Y3v$R04Ld-Urb)su8?I5!9z~keohWNwG8WlqiSW>u`A-9-rIp4Y=Jd zhXXs>H#^*Tki4NlAY7t4NUu{&b5kyEYu&2!t*=F;fE5|_vq-H&6E0Kff~^$M{KLe) zLtfH0@Fon!szH2>dpOEp>_MvD>g{wA4MFp;)R|3D>x1wiiawaCfFo3@PUFXPhy~b1 zHzidO3iwJF1{%e4vohK9M$ov8s(ui^&;$|h%O46t5aKqR+Q$2!ezzv6?Meyp_-=GjN0m| zMC*JHMR9S^JTWyCF(WW)TNTt!ubx$aZwbxnkFAO4tG8hv9qy8#Ih5vbu@|~XUwHZ? z7JXKsFckejHFT#Abm%4N^Lkl_l0ly>EOm3*mIiDxs z*49F9f5_u1OVmQ0Xw5fs#UZ#1CLFsKk6|ZT*3Dd%#yX^F*18;JUXN-WXkj;VbC;lK6poP+HQ>+;|8j6Mf~Rwt)5%QV&P#wwV<{^L1&?QFDp4 zdpfq5IZ7pVeU0*(m|B+6vTo+4pt&w}um|?3kYi1BcoWMp(IV5y! zlt`61zU^IsmBxuB)#^?3Q89aK(0oy9>vHIhGCtU@#&#;jc6-n~KQ(NvH2Rer<$*7j zoeC+QJA&q0Qpc0r33l*SBl5fQ04#Cd){Yyj=^E`cfTF%DXuh&7P`CD&4j)}OxL?8W z(TF+v{DMl~52Y~I90EIM)-2LjARKvBR1un}Q4&8>^Vujm^gTgyFtrtmpMgZ-++13# z#K1T@>b>nH22@~*D46?#=Ce{;f|$O-(8YWz1JQFt-hRdYEF_+RACgyr)hi#Q!``2& zaf7!zXIW_oJ$DHgzvixn0Q%+}&V+wLeRkw-==Hh1s=HyQ5XH1HXudqnrO;Gstv%(V z!?WwrM>7oHYgB}|dg00`$Oqf&CO$<#=VD#>b(bOd2 zmeSJd`l>oL#>*(iXHs_4#&yPs>N;#!Nt>S%j}X3dNcN@q$7+lADvIE_lyB#;ojUkP zSf}gg;4cKtxBdrPlyqz24zx&2thUemVZ|lkiX?utPAe$xm(q%3w*DFrYe|MacGWRk zMRB~6vMZ}~rtRRuQB7_lDx2#LisH4Pc}AMM*qm9D@vsWO-4wtZDaVZBr>KeRKGFD# z9Qc-2;yw!Gtv0?Mj>4!Yd6KdvC=XC5TT@kZ$sOhajI4SBKCj2&47i7OR}75@w}M=elKXQNRzWQRY**Y z1yM8w$sLT`z|;_)p%C6rV;?;jRJJ#dM>pF?r$=iau{);6=Tz4y=T`>t_M zDp8Z+E32v_{u&+f zUVS*sp@k5;RwhTM-4y;$?aaLcim`=2X~BTPKA0eYLb|THv|61>)afsZ^yf5GRgxIJ z`fiO~!90ncnwT5L>Gb#=c9p97mtxwLa<|?CTgVXyN8J0+G}dd@QxLzUY@Z$&MADq5 zL@jFODUjW5Ca6Rn`#x0qM-f_nv0@Jf%pt-3oy66YG!Rkby+R;s4a-TXIx8?9mpF@waSGQ z&Gq<|aa>(_MZt(M*j)u5q&l(xyt;4|wc}L-9;*zf@AzL?hv87Uq7ILC+9OUftT-s_ zhi{Zeo-jufwhAXyP5h+Ig}M18ikE3a!AWEmx)v0_w6a&>iVC$Ta8MA2R7C;$Thq*1 zaQeb1OA;=zW-rCW2F(vfIp^>+i3+hAV-CKGG`K26+=A801M4c&$b1T&3!m!x;x*q> z7ASRv6V-9Ez=;c+yn39?z2W$6Zva!Y-KzCHonjK(!MCOoTm2~xxeX6MLTDs%p==Ty zF%O|2It9(eQD#e3Oc8ddu1a)hsXkF3ypeR=j5chh-^5+qL|Ch~Vk|||B~><6!Y*YV zXK^z&ZRP{f4%#?9p^(e3V$(?!Q@4~~XpAcmO^-z6t~S2}(}*#opjwZqbZkq|d|_%_ zLF~ztUX&)$_vn+0mDs~Q)N96!#K$Aer4*VCw<>jLt+>G^mJ`Ht0W?z=%+o2R>{Qu@ zNI7j6nko|=(}%Gmqm~N|^VG}SJ{9{^QcSriKli7w&!M)8YACAiDWhuLMI~aYQD6Ik zZMC@+)sZQ`F+xx&JQXXpjEce72WSLEa#;&QGcbKW`ny^z(k_Sk9zpYvG`U+b8p(gu z1JIs?`lEy9zNrnMu!TkiqInTr#ADM)pm!7pyn&KtGE|j7@33RbQm=NBgvVZr@q$EK zY6%5(e9-(*bh_g(Q*y?0ZwVZa>dZa!gaOLw2schmvR6iZm`A65EM2GVUVS+ z9E(;lO)bi5D#@^s4jl-Z?@4C~4^b0O zJEGTvA!95NXl_CwTYF2A_kx zP1mHy>F_$j4%ITeKrs~t&F4p%>)_N*%sOi69@3=1D&j+mc$T!y?pGlwYQgoxOuq zDc{qPN49}E@3z}L0nG3TU~Ug)@wo6etxK)TVRyUKQIel2s58>KJ|@5X)0-JK93dAp zQs&XzNT+{OEB@apsxgPEdxA=={7H$AOF8LZg9D;>&sv;n^k+5S{X<8c5HydBN{$p| zzQX%t$(0H?)hHPZ9c5C=4f0#*qnJ>Pt%K^~Y5w$gVp=zG3MQ_621n6N4w@f`u0bl& zGj4UxO41rB{x8z9OGg#gO((03O>&x48D_|IDrW}GBcf9|`0boU15~}x2raLxP>j(; z&s;`lI!G|BL6pO(@XQdbXC^^R#ybrCIc0A?az(sEvCsIJ=DF@J7 z2?*nfB&OKXcGxMDng5B>R*5VR9l0`Sz9aQ>o?Jg3q**meNyA8Vq_?zcCsXXzDc1=o z8>oPf+d5J07Hfv5t?x2=)4^w_tchY;R`ZV28pERBkit5Q@R!s#%T3MbN72;=&DXcx zw)Pt^D$+4b_n%A<7!4z%%f8z&H@&5=q^xTwd70EUp z*yV;?MN^2I=FzA$!x#!?e$c!rx@&3J4`a$J>(PFwu1VTqF=9r<|6$A4L|;q=8XKOc z)ogLhfDvQI&Y#uPikL_z)R4xNAW0RZ%ENERhdF(AZ4bZO87NW3xiZeApcbUu`myDc zdjOF)0^Rg;!b%EEEj|i`DLUceN;Ik~ic6cM-e#0gpy#EWW*L`n+A^l~Jt_-B{mN_6 z8lA6wfTvNUkg{%1Ye|*P32LLSf&#kW5QM_DRD%);Pq+!GPVsCy_C+a6v)E$b`An?s zE{Ogfsi0MAS5-sLp+jGssxuJ1nn`@3t!*t6_G90fL}y?|1I2Vn&^$CM)DHe8p(yEu zPF$*4FQg+ZNma^}`2tSA!;McsCpZ<9lp%BkqqhM&HF`0Gh=D@vk=Q(EsLY3LA6@uN zuxi>DQBX@$ZccD05AU$*;*j>qy_CYd0@p6}-8r!SJ76ZHYD2G}psos<>)R3(8s(ai zeifE$D3+^(=KG>pQqjr(QJ@41Ya+ryefheP&f?lOdJC~htx_whT1kD|oJiOa-2=n1 z&yhNHKjUT!`T908VOtA%a3$8nkXr~fVB^kM^#b2c(cYM{3`*KJ)}eBKdR$&?A)6ql zVvS?H?y$F1ErV85P%GNYxK&x&_fjx7rK+2fr#vOb>o9>HpMFae;H#xNRoz-@d};Anl`!+wN}!`TOK6oaggyQ1$s}=+}IBMptz;Ti@u7{ zfH$~d)zhY7eFfg)@F(R%5*4hkqN(j_PI;F?zc=MIK9uGlt+muP2tS}e?@KF0ejtZn zEDb8LA3j-5yaKDNuTLqO`_mB6?lR3ZYn6Z|Wml@E&QxijfOh(d?Io&ei;S-*rj4np zdZd~JlS&0cBQdOKY<2wrK!IW=V3V&5rhw3rUhW<}w@vMXZe8x^X z!oz7b9%xxFBorSgciSeV2N}On2#=;*w5Uw7Weg;QK<=mN1UYq+lZ?F-$YUvcW?I(K z2T)8gK{ROW@L|fs;*p$y!f9wqtL4t$6zLNw`^aNRn-oX1A{U{vjt>50TJ-;3c(Q+!7Hh52r7s)D-7-4utRl|?0huE^jbS&a$q;ZL=Ri% z6DX!P+NbKci63A6)|93{zjFZv{8rGsxGl`rgSWHQg3?XlZ4H{&MByFAo-WkS>OP8{ z0~G(e>G%B7yg0`?mGmjD4M&nq)eHDP3gEpo4oMR4yGqj}_a=DDd|`*m``)=P#q@sq zi5{`ONYNh>TwyBtHHZ%RVXDi33O7kvk7^mbJ_jagC+M66M~~kVQY)0<6w}9t9#di; z5T_^L!A6=|n7pOlQY`jNFsM6^rkFlW15*;iB+((}ay$HfXY&MhV%B)@-6vILMdt|= z)93Bnje{no%}3jIU`2IFvD!1-c?t#jWgB^hn_1rn>{}4YImaM>^R5M*iz(W#+d0Fg zxzl2F7@y=o|HYuOV@Hj|rYE$1Om&pYDE4putCg;vRbNujyb(UlA0gp#`cq*j+Nvti zSvwr4=KA-TX|=V7?XO6HO5 zWA&S-ov)_k|7?5qREg0W=&<|#qiht_*f-I!|4yjrNs+ZQt>R|NQZ2$;=*`yoHj3=u zHu5wl*y|+wFfKYY{obALQqUMWrm8&BP9pJ#%bbp|`ii@jf?|T^yQ9aUJ+EX+GLrUV zd}D^9^%JTuRW}pwd_SFlp&eJX)PwcIDwX^Y1;zds(pGgAAEN{FDNEa!Gq!bSu^fw7 zs<1ae!Fbxow^!Y~y2KmEQxuPQD0@e!#QAe{06G0wwpJEi-|59QbtTpKe0Gvlyv{Gv z@jD$Rc0nr|6R6em8x(&=s#ioMfTj6hsp^X8#Q*1L-ggkc?Q;0ScD3*N9g3+-%86|U z$;4`f5{rFA8#cz}x+?I|`F#qr+hNj^;s6EiL&NjO7gyBd%Zwqsk*S&B6FL*iAqe5L zswIW$1b;~fxBdU<6QJ>!Tjy`-46@tE>lznA1@Rv>${-T{s9NBkD7@UZYMhn4JWZ=V zqzc-v6k7M7`HZObk)r8Ht|5v8LMw}=^B;7aBU4WEY2_s(nsO8`#vm;;wd*oedV49N2j4AlH?;K z`ZgV2ufwC!MG2LJx71YpA!aB|0;83rCNJl$9WzTXt`cdeL!xDPIVacG6ZQPx-ulx69M~@etbW#_v-F$-1~Gs6eWqPzENE zpz1O;htuKlH7_8+uX}tUY$2a`OYP#PpiXIzWGB^OJB@RkL|&S@Rg2snjWn#!zk9ihgruQ6>ptEG4@Z{Ga3^^ zT8f)W!6eRsF5~Ig1KXvCr)m`@QzU~^?g$ZE1*H#0vkpN?a`jXg&Y~EGrh3|v`#(uu zoG0M5yD>Gv?sECD#IRZHtjy=PJMnL-tfMYrifMStH{VuV3{XRBAD5-tC;YnC z6DTcHYtQ)<)VP$7R4Z6VqsfZuNS#YDOgKzjP$(9(efJZF#-A$EJ_{GnnM?|r*GJV} zf@NDFP3m~hFjx|5DNEog3t%7cUV3?A4k zK(YqtDvU1=g{zx*1XNsk4@Fd-@?(sxkoQV=DeUuCT^TDxhA+cUS-fcFSR{w1-vFTBu!RJ#n_uDoa(fC$fne`jT}w&J0vP#Pf}pB zgXV>8woZi84(t+BZQrvLR&CI{AquNKRdj1o1!}cg6?A!tPNe=nYr(GC-Ph^pbA#q< zqRV*jk}bJ#KR~)nOdZD)U`L0GOQ#oOdo6r6N$vICN}8y zoi8Zd^A3{-C|bBRkb@Di_mi0?-_XfG(0o-}tbKET8GZ7uHu@k?sJK_fEX)HeP&&xt z8yO{7-ipuYVURts6KR(pDb5Sp$O>UL{n9&@Q7^ zyv7m9)nu+J95#yM+Ms!Qx^vaMgGRa(Cb^4t&7;F#pYqKw<~-MwI<2+XqWNfjYRW^? zu!TZGo@Lh_6ylAkdPtMM(V>nnVpWRQ*}Not5lwExk&aFZ7q%;>&2`C*XQ>^Q$biCWsOOQISInwI3{E2tEu7woc z9c?5bT5H^4vt~40$cs#blT=23*8vpbU581c{{ga9>B9`FH6BVQuqOS=8kOrs(Lt(D z)ay2i4tr0k22ApRnAhj`VEoDL^!r^-(i_y(+@p`*wtJmEuPR8_bsWWXZ_vCb&BLdu zkXp-b5$8&+dcle)xcl0EpHvz!NQb>YXda*DnoxmMU5Cx_@C9iUpyUJf97A-ljct@$ zrID?vR%7LK{0GxYYG_#)qD8EE^XXbe$9_2F1CcbpN>zN10WQ=_&BOR%SFzLX^s1iE z?pjMRJsLC*PIL1ISCv&`H}mq^#4?ty^C;R7+biJ64{d9U6+sfc+$+(D@q`5x5i~@Wn zeFG>VNEu0Qr2nH9Iakr~o=f%ErR{L=*~2ck%J13rI*RFqp!vqsj$Ql?s<}xhgN9+e zw5p&O-_ayL zkt$0kPwP=>5=X%6aWucz;J2~Uu_vTT)a`m7#q`$U>&jQ{h|9O5pb< zhSXxcM19`UkV}10f01JP@W1YmsN##)D2R{SEl3k?Rgk9Z+Z5-g?V5F>VyX8iqR-P{ zsr15~*a6F5re>*+D5ft{#dHWv5#(}JpY^VvQ%qmCQF!fOeYCW9U*rkbh!j)HtQ{2a zHz}u^wN7$J!)r}kiz*)bfkOGNo!2hGDN36#CDdxE*X|bz_=g1lM=Ajv@|{+`&hD`H zvbXLT;jX(WxSvw?1-2r~>EQFkR2%*mh4^zD^=2331|@b<B6vO|^k-a;-;yygcD^ZH0HKNa8Q(z*Pdayi(c+gp4n zdrY{ljwIozH})us@!vKQeFtkNpgRl;7%=ZnwUEauAdMY^=KBu^rZ_;XCb4#2J&%*< zJeagHY?F)d*1cd#?~Q6;4hqbWh6U@x2i=NYiMO!kiH%MtKKs@@D}@w!*j*u~%Jb08 zOEIx2m!id-GR>mpWC@#os1`$~(82jb(BDAQlSj`*@@nyJr&9o8TG!MAJ2LP=Hl?0b zF+9Q*>ab3?{uG3qa;ZZrOt(sWH(R~-L+GfTQeOMm#wVUclIVw}QDp0zf2;;Dk^;zR zXPFq!NRG7RO>`l28%sfUN%hF49fx(eOS~S{JFnd)QB2+1=#h@r8Cv~@Cd4R`WXX1$ zN|9Ppex2{ospi2|rI?^lKBxTbaHu5{tdv5wrIFH<RJKkHMbkayb6sk8drKYR;%2HC z-)nX|Fs;Jw@;hA~Y}aAe2q%}6mD$TYSY(ipP1kKM#dKuKiEFLU15vknE0^gU3aLlR zN+N!MwNfDCy~C0uL3RTQ;OLZfqoSpvHhD>XRS565;i`yVc64=hokrGtw?%Z+V}s`E z=y^N1EZ5d~$GlEzRha6LPdg>T=?avnU%~43D#g?{Xr7wpLMa7`5%0RPJ&7NvcjqlSX#bQ=u$JXa zYHAjlS%CQgeFn_FmtrLN-4nTS6({{6MO07!z$Po3cDDO zUuv*h!DmrVO)JGzW)8(vnsE0xlo}^yx=@YM+^{UKtt_v_&L_p_Wl)8^GLNLl!iQd1 zwK6{u`whAss#>PZV<@I+LG#(E?Oc=ey-u-zYg^EHnRbe1 zW?HSl4y=M{#R2(uErIb+FqLiKlJ{ZnMZ3rC#K5T2j+Jrv6tAXB-sN@pR2n$*WD2VK z@RwXFwa}Zwo}GUEh~iSzDxg)3*N=`@+s65Zt$2bhb1;Qc-^Lf2WN(wJmSqIRGB;=* zk*4@jdv;Y?Y78A@etM0zl}ktyzfNL;d?Z}hmKTTdFtfJOx6_oY`?WIRlY&e^OxhUmCv6gkdM)|DPV<*aU zNU=XOdGjQ54#jwJ&>Tv0Gqk)tiV>G2)Rx&mhrXnZTEHK`7H{(Gd@NlC(N+|fHeIHL z6zh^y4|npk3^W7WF6>GXz|weZcn&5Aql+k}rH4tYgXH~v#+24n zS14US)Q9y_I)N*K=Ihery+d?kjC2x-CPu3c^A!~ARfnDfd_KE35WolGd>)VA=k$7A z8l7iITX$a>2KN(!ESc9(OjoCjDc#5!cEa+w)OPBP6w|fo8+`FFX7oLjNWrjbUv8%3 zUEgl^7!{h^DViG(|2$_rl;yMPhGXxX;%VV%>5j^rTTSP(BK<%;x_py9lB8O`d+Crj z1khZa(%nEUm5G+JWyNbPzMwXFk~Bn8wWe zGdE;z%zOZhw}5dK7;gpRZH<`^Wj@@Yd*-8Hyd8{<3neh#v9K&?wvMVPuPm;a->Tv$;txV9E6jDzN* zn|x_ZxB`9Z;gI@UJqn+Aa@nX{|0fe0>?a-J3^*1{YH;`*CmrGTE{M+Mnamf-T%OH* zF7x@!7r=NI7*~UF4H)lk%zP>HWt_{aV7v#6Ysp;hZP&R}KDwwN?4Hu#2{>>LE|1HD z^YOV|fs>A~$L6yw^FuP9_cFI}#)rZ92pEyGHZ^AcocRmR z8@wu14U z_CGq|WrNmD`5!$xPG^k8EN)96DT|$KIR&@mG%#)h<9lQZ+ZUFhgxs+w z?_4mk0oykx?s8wtAhIg`Ed4D5ECa!aGUY=segwvk8!dw^Ly+V!Fn$8YPYKD-+I5#p z-NSNkeTA+HJ}QHA@i^Uya~W@$oOFk$Sk9z7`~?`ljO;KjPk33KzwuOMdAz7VB5K1D zvXm1_VN021nq@i|zXszDFn$BZZyPN$EHf#k@4)zd`}4|*UDMX|y_v2GcEE^DVUDGN zOku8No@Kt}Y%u-^MwGHU!T58ddG^=`z;%QWq{QKYXH{RXnDZ$AkOAt zU|C=}G8?{KXS2l_+}mr-Nc0kSzxr)qPawmgs1(XAVcA2&_b= zBP+7Cas~U_h6$*4>~19G#5QKQPL6;J1uTl%s4RKY@-A7Yw=8d4wp!i+wiB>sU^9U2 z+-TWmc@GJ{4{R4;yAs0P+I5|t-u}zN;bn?6ba@;oU|nvNfc?_)Et!g@O!?08y&_Xs z3$Rw43b8&>z^>SP=VVETYlMwVaE-9tY;0k6S@sZWn#bV}%U((?3)t*9wdJE4{5LLD z0D63JS@*A1pKR}}2CK0{cPj^MF0grIiM!LiTV7Q#wbSGVFWEYW$8RTF=XDXwJ4V-P zvUWkb)=pNlHN)B&*du{G3fLaN_H49vwRS_gIN76tJ%-Rdwq3XGP(26bkAtwd z(;gt|k(jG7X=**vdJLJ(QPv*To>oMCJg_GKdm^wWHCm6g9!J+ZAJ|@z^=`*|-HPAk zKObvQ#E;YIiBG|0^^+;Mtsbk_>I2petOHmlu&zdHz}(f%T9L@wV%RlzK0C zVAZV(tNQ&!8{kW>$Xqe`X&qo4O6D@qI>oDtZoXbdHaXP1w zx%{tP=kh|qOTTd+<9)%QXt#jROD;fYZ6_)O>m=(`GNB^tWa|{`nZTY7Y$33HfbH98 z4O-8_36%ib57_=>LIc`$LYq&W@Y;{JQL_&P9clvyUgo?p*<`J-&c?ZP%;}ihF|T9y zg<}?uT{v#x_(p4uwHD`559}ad2a|aWQKZS9@4h(k?>vPbMvU<2;eQj4UIR7;NVYU! zJs(M42<$LmhZB+`6q1`y9SEOoQ%Jh}E+pwB0yKUUF121kNG`EnW?gE%9N1C7o&oG= zV8=9Cue4r;B(DZ`EU@DU$?@&F+8_2Ql9o-J(%|#BkYk(<2hqQNw;S(j@%h|jy$$E1 z>DjQ}Zf(T*+zISNU?-9J6e&xzd#I<=ke}!hdAwxR2}j0BuC?BeBsJBQb%S*y6?jvC zJrhaB)YZcKdYdPsx*|wjXvRg+XeXU;2P(DBO4KL+@3LFdsS~;yd=8RdEVwx$CSyThS?G z{T0|sV58&(?#?rMvA2+mSXjtpM2bfxQ6O3ma`cZAar=js^B2U>A|OT->g6sh_@m(9o&q zp7jQhe_fa_fb;SDJ-9b9ceBl5^OE^EZ7!SJ<^lE+U@rxB39y$n+I%)Y&Ik4TQeZD9 z^SPp3=MyTN_RNM$&@ji2{Y-nBlEdh zIgdNH54XK@r?Tfxhi}1z29*QbHpVs)DQb#&+ay~NQbd7#EwI-Siq})YansDu{%unl zTy`&!9Cp-(=-u-=6D5bO#5N7-YFbma>9%sDI}_L&fn84Mu2AT{Fd%#3+~xF+>-Q4< z;kBdvAD{hf+Z;ku%i6Tfwar7Cc<^ol_GUu!7KLW1cb%0zn$jeG;zEY#7k8Jqql4VDBfCHz<^MZZDqObsXJCq)luKXQFLkd(!qS*~X`APusTGo&oj& zU>^kbAz&YFv^{5g9%;S^>?6QFN@#9s*KK@w)$&c}ou;g}%R{Du|4n4`w{7p?R5b5} zw(Yj}=@vc?>=W@VT(RrTeJ7PnZNQ7O&yB>eW+A@ZpV_`9%dO>7w(YQeL+L#U>{D@i zrS4@rOUBdX##h~AJh9XE8(HO_ZNJ!d*--Uw0T$Q#Szw=QwEb?|O-Vfu>konbh^+6&?Yh2SZ@R;^VjhYsS3t3W?GEBF#I_d1+KXw3tUg%- z$uesNs9v!XpHWIvvr3VYW^gkrloiHRo(AmK!0sTc{EZ?)ckFp{O2fyfsfl%g5k{0d z4)l%3=UJImL(pj%vRSoRbx3m#u-^eof-LM03e8Ouj_r5kQ*@q6G|Nq#DshtMWC2+^ zEzy&8e%1v@5{=29fZa(*{;ZHJbx*GJ+(0?hj@Jj|dXI|clB_F{rlw_)bye0fq!Oo&U)FjgxdGTef&Gh++^3NIYJuU1u`4La z05PmQ#N~?~|LD#?lJx}A)ZY2cSx+L(r-A(&*nbGke-)ZL_MGyq^?8M+%Sm7=SsC$N zdLir8q%(gl>ve@KxsJf;kYvPW*y23*#3Sk{Nq5X<$a**H12SPPmuA+7SszhKdf*Ik zN-KUp((=d%v?d*PG!igY9J?TAeVO$wQp@@(>+7r?S>FK10>=Tz11B_QeV0Yu+nfj- z2>}pX?Wk?)uNyxglOA}?sElpa?ySGa4EALGk+nDLPvA_zbpp-|Tt;KozO4OpJvsx| zrTu4crC7A4dcU%x_*_zS2H9-3lx#<{Pu-7Ms{GK( z!};_Et_N^E$$XAhj`e2m7sm{K=S&QDq2fYfKBs2^ddkoSBq7h(B{;i(_K>8DJ~Vq6 zk{$sZF5z*6^zjO5v^;vgSWcyoon$E>(FD1kJvMt1lGK!y*+totk>r`coe11Xgk-)# za!dZG(JOD9+@NH&pht+fPy_x%37lP;J)P`nC_9{8mOTx)0^sbxIe>FEW|wErK&lnM zxqx#Ms-AY;(<^na4K!S&ERfIc#;IU9HgSRGWRqyW=E2WCCmW;v+2;Z01I|yT5>OWC zO4G!Vw_Qd>D()nPi|lyAiJ9@)7iC{cR%cQ6#o3FqF9Gfp;7$eZG~oW%n7t(XG9-C9 zaL6R56Ox7Px;j-a@4e`bF!9YOGTrGRH&w5XSg$c-DEo%&n{Ymw0p9GJvv0xq+zMP@ z;QEpI^jDUs^3fX?IP<171nf%S*yr~XhQ}6U$VQHs-pIZydo5DdK1=szuS3e~fg1?i zAVPVtLV2@i?FjBZYIV33hejYkh{jLRL)niJqFSmg`|<22sOTCB+%O~>bzcA8v$}Ld zGh;jZ+3c4HDJ`=g`{nFc5Z`OS;h7vs@QqUNeYNzRmBE`SbD-Hiz5x$>%qGa*n!Ozf zY9GhkU&%(Lx}x;V8;~b4Z|B6J?Ge2LsJHXfU8Cno3XxDayI)U*7x+-4twG zhqBm#eJ63NW0AldQw|9;YntgfU2`zZoMQp57`PHbwN#;6KjZAq8!jht3e1^7NlYRt zZjud!7HkA;PHxUoNLJHu&FPWT6UiO}To|}ALUx)$_65%~Zx8LS>{37pA>(6)31TMa zq#Os5%*oH`l~a&|va%ew8Nkg1uA(u=nd3r|9^fj0BmQ{~{qyZ;A?nG0#g#$ktpl!}tjru`v7g@d<=S%`%3}N6N@||!GLthUrN^N)#y*m!p^xP=We8#b7#(7IjeKl0Cy>HOMtr!xTTFb z_vEZan(Kf=?RW*Dd1bpEgFR17l+M7^Shtr5Nz73qVHi}6s`)&UL+*H*@;qmA4&L!{ zo(66ia95N0T%)Yf7Uy98{oTsCyA?@+51uAUiWhQ}JDw)%Qtt3A?|l|2z|JoSo1cvNWwxf!{cNLSN0o@>dqBHb+D zkj07rp1Vh(yL0<_XGiazsKq8UkcdD`N9G=pdo)3+mDiGcOzyEr@p$0w1#TUoc%MS? z1^;C0bpz;u^Ei|cu7hNz$B7o?5-(q_J=c-z%te#@e&99$w-LAp8go6l=;h1x1NR_s z4-ujdx9f@f_}itOMvsk14~&@*f4gdd`s5P-o@Q2A?x0-s@8u2!?or@2k-0pkEYQbS zZLPoOT!oo22Zu1TH_`mhJtKEKlGGGPxf60HB1trHo&at$A^D_2a?gXAKi)f#1b2wt z>A|b+L@WVFJR-Sg<%S7RO`a>uokqnwN`@^+HF}13yT{a?t4uyIaVxhfx0dW*b?&U( z*|{~qJqz4(z&#J#3yryTx%CKeE^sdb_Ywj4a=Y%|!)I;VKk`gs{wZ0V9z2A2mkyB2 z1jZhtx-S=U7vYRFMN;m?xr=c|s1;rX?lm%_*OevTv3qUr9y=*#V3%sb8Khnz&gEC; zUW+s}!^gSTvY_l@Pm*K0B+H(=`$q9;8{>IGK&C(inV+@x7Axu4}? z)=Tb}z~TRWMaX`wklj7|^zshh&{<>J99rq*2a6bU|Bcb_IyPk~DML(P-{DTS_>$h}}WOS=LbW_TtD*4<9MTy~f)a&L?)K&&$mt zH!{t#lDr;ycq1cA^A~XY5|(CHN&V&%wIi*0C+69ao~Esl=g4#7;<$nP8#v;d=csS~ z!_GY>eQ+$Yxf>53rot;Oe`4Ln6SMP9&g+e2H3Ktwr{@(S*}lMc1fKZkc}8LLO``{N z$X{2~;3vr>eyYOVB-tm%7kPv8Mi8)Cc0u0AyirJXH1G!Cjf5(zP<>%US^bR7RPEt{ zqR3Ds_~lK^I}=IfP0B0Eo18ZVcpi8GcoBH1F>h*KP+3o22Hq4|&vv|y&gwnv#MdS$ za@rLL#O%7f8F}Phrs+}0o0W(6vbhzjrw|1(T16I}?i!$R6S& zG`QL^^^`Y1k6g_(?|XUY<>A#V?|k690^g0$%~a@CzLb&MX*8Zf1W5P+3voyloyrrN zw>XcK)3rpQiU}WaHoGH_+|4xadwKWd;oU56-TyK79q>^V zUEe{lY`Zsh5yL_y$)y#gvKSZIQ%*cHVJ77(m7 zQNW7WQ4|nFAHH*D_TJr1ZUTtk$M^o8_Yrw!{`bt8GpEli=**y_M7<6Z^$4}`VAH|N z$D_q({r{LX=Z!A8sMpk3H|@E!mx!TSH*eFHr@f50uY%4FI!c)9c*1x6X@Ka^MK9^I^V*y&1u-xk+ubNe$WL7HF!)F z=OWk4ACAXlRt`A?cz=dfOYx>iqW1VU?MGaWW}K4tQ`*nCoSmSPKu5WH9p>tBTX$~0 zE3fSK4>e%ey48@p+Lr5!-be?S)o9VN|mJZb*jS+}1n?#C?+Q(KoL zVY=-Cys@2w{}PN>63y++Cn2)AgZXFZ?)cMj;fgYI0=^;l@une{1VGa{Y`I?A2v zj&1I|@rLdPGHzlz7M)zuu}QfB^BLw&xD?HtjJdP93oZo@6WRkxo9lSmeA&Ix13yo| z8v-Pp0SpnbrXEgAIGCMg7lCReWz24~2SI(H>j^r_nCp1Pd{c_Sy(C2VDhX#@y09_x*al^DI2-fdJBnN<{_hYo<6&v$ezA-;8mZc@XH( z3{v7;#}nuEzl<9B{hwTl19%r0JxF$uMD;4nmzXI`)248U8N)Pl1L*pJj#B43o;u(2 z#8g)}jBUnr;Ic6ThrUWMDS=~dG>;`xwVLM44-Nm4b66Pfgb5+G%7d-4npEHMUXkZSfE7F>c zoqmb9x0z=muC_ScX}$|_XMyfg(A5#{V1|pmjnR8{A}%8l+x-a_`*P4OsYcX%zj+>U zR!c#c=bING?!%xP0y^rL*9~L1JNBO5elZpgBz*3O1eGZ`{$>a8069e1o)@|o~d|A*Sy^PI-+X2%H}uBD-iWf(2W9J zBcYCFs54)?RY-jTC6%tL1aq)q6|+$6*vg^!5qQ`90kJ{L^qD_2e}us6K-UDiF$6r8 z0q?md!+CiLGpV#~;ap0;z=&m9PCvpn=?0n|=Z?T$1nEkbTe;WJ@;-+6N2Thv}f3 zLAW!ynDidk6x7hy{&<1&zZT(A`h;J;3POHsh(jh97W?mQYEg#NnK@&1vVc$jaw+Hy^+J2TC4Er**Ax&?8#Jr`W{S#j@}M#LPI z!o6E8t@i6#W>~Oa&w|^5dYdxkI-V(CO7Ww!#6qolmPaj*Ssu461s(q93D7+Wx~CRe zp0r@Ap5+?zk6jy9clDNp$lC!xsvVx&{J?9-ZYC%L-c0tCrU+ zuUp;#-SeP(0d&hi_u@j!O3RzLp0`2w66h$Yt;3}DF$~4WUXlA~?WCBSf_>|lGL>*) z_(YK+LLXW-;9|57^+wAkE{(5(j?(F|16U{D@$G(IhwLh=9)x9^6Ek9X)BPgwYQOhpN@5tfZphFA)7IFA(c9K_y9$TCK6`FKP zs|96jUqPi9rZ&`gld$D4%R$7|G;r1wYdcQjD$uP?AaT!6=iYl_JYN@g!L27*O+=-Z z?y?G2k)y2v-P#1SeUnX{o<~~~uT#Y~!g_|442)*kz}nf02F8j8=6%q8Kx@Y4d5rp& zmAj`18_=bp01c~1WVnL(^dL&EYGABRD;XF~URvE&G%!{)Fdu`CI@xvWW4yd=zu8E< zB)?8(U3C~Lkrj*M%2qNknnguxH!B(#(ux~Fw<*5*ag+1b*YxDlD98N{Yrd7LM4CRW zwU-sEL{^h!d-}q0rOP`v>{f+=$Lq@gDZbil$X03g#ECA7Jkhb=>VgNz} zZUtRJlD$s8^~W9`W2``TzjNbx3v1LmxYf<6w+=xHhk*{^D9NtlN%m!fdQ>l)fm_WU zYQjrr%%(AO2r~gGi@Df#bS~53ebH6I!d$aVrljP+xutqN1H_se2AP^ zbT>)mA{!K8y4p%MM$RVAx_zMAPq2S6 z*qxhiJW%lsKPl9ThpCFBwhZeA>!+=f`k8eLVr~Q7-=O=4F#pF%U2faFWhuUFkWKYw zyxT^C6UIkM6T(32H`ebFS2Jw2{$TwPaeoGV3h3J*uKqZNyUM@nb^p)Uk%--bh?|2i zK%jG$O_4^zY5d*#CjwjluwdOL`jbGfLtxsZV`wzD zezkPVE7Q4)gjacEXUf*WrX$v9C2nncn}M&u0D5C=1(dkmt-n5H9ZE-w_Bz`swlfh; zQ!U%hvUN-`+d6?>0KG_zPGyWO;_`Fx!TG2)3@EKOOYco~}QWQ|Os~|B9a(g*n(TMlUImNMx%m zO12)h0s_@~A;?x}D?-qopzjF!vk9~l16}cH=j-O6mBS#I3=s2T(7-2VMQo)u){>^B zO>9**Y)M0HqVEFww1k!K`|yjhnwDXN?E>2+M4~2Et?g2dWdXf45$ofT9lD@OZvK>l zt-&@5!8GGwTcd3>uD1#FcF-YpC#ZCBv6P{C5r`RUg zt|nY9AJjJ4HU)73^ckS1M7o|Q(#KA?YGO9xhS{Ybv>0?wbF)LpRqRq#r2GaO^`&Vx z(AsXZVPBeUI_TY?_Yg2|Ki}T#YxBncab<-Ue~4OYcZ=r&ZL@515LB~e+jhV00nSoC z=qW)T)6mlm)ms-m!i7Doraf&7ZA%bIdvhMOJ;sqjpqJuEbM{TKR(^y0BM7i}+bv~1Al#L<@fXMg(agQ!JwDTf#eV^vW>d&BlNu})J#yklF%(eTXV z#?j{NDL(PUv$#o<_zG@ZfYuh&* zst4%LOMu$lF)I(1Lbgl_2E9ww39$WW`;|DR)k{=J;^(YsX1pmh_*}Ec9n*N17*WL}++8y*&ps%L2*08ni++3LY3V(D8RVh876$&QC zxb}QIYuwU$0><9UjxM>q59s@YegI((WSBdC|2wBgtj|BdOV~+wQ0#r}0R4razXs@6 zXCH6B0`x;cKMeE@pucRP{Yv{)2s#P$!$CiSKt~=^1}$Fl=D{U8Ce<=_I8i-;jFqYk z?bq9HX;mn1wcm!12H-Ji1pR1Si~5`fy!~WHju~&!u-|3B50Nz0gMGGr4szuI(4!Tl zl)Rp&1;cTo_&j5L462*_YTKMa;)RKOXdyj@M6M+qeDpkM7Cd zjW?R{+6AQ;y5g-E4Cr#0VOFys_Gj#5QZxfP`-^roDfX8^e--E_67nR5yr*}Q&}lBG z786h8>{Bco*A*-5?;xh8(zUO$ujaa9GU%ruX3Tt!8@hbiFMn}Nyd0;>?T2;hH$qmXn2Vinqp~x~gj1zRgbg9?i6<{Tn;a_vo($J*D_#RE-=teo)a~ zykry$6)>|9GoJRJ>@4M@6|LKM*)io~-wpa3Kz}3g?Iy;zy3<#lQ?Q5=hSf6Un@oID zyAAAr*(u$lNueXf!P7nZTR?wnB869;{%{SIO4uV9Xa-dG+HsPDY>GqY&^rtcq~dna z-vN4bhh{8v2oAI<4zwvVL4PNah^^ztFoYShd+@SZzjM||EGErOdYi`4$zj2@XdWtu z)nVfzH4F6j#91@F{pdbJ?q*M$t9sCp6cA^s|X4bJ(_RoAK$d zovvcX9GeuF=}FXrj;@Y#iOso=ZjN&tXu0roKM4AVKtFe(qle=>ghc;h9_Z&2BzBb_ zLpFbN(xT=2GT2h^2VZb0_z-u(!FBX;RM1jN9HovjM>*&p2K^$?F9!W13mttOmAI5@ z&@TZ!CDZkoOh1N8S+UVE>HEbb__-9uha@z66wm}H?+|yH9Tz+3bp%ZZ+%edJuOpD( zvJ~`B#06*0zS;An^;+(vV}#>!MAOW;IGP+|kU`@>{}kw-CI&sj#AoAjPnW5Uc*_It zJfhs>N~DJIULjpHQ`N9zqJv%`)IR#)z*h(zsF|Jv{qqF;0s|g-_odli4?qrd#h2tT zCd$Q726L-evBG2^8j+Z$a@_2=gJ{-rfgRHwGdKrc1pP~K4vf2~;kQW_WD@p!QVi1-BO~$w@OR!4nY7u#iv3y% zKgZ{eFK|U)f&N3#e?%+#n6ZE7=4rpa7JHflZ!FPGlO)LR96uwIG}|*Azc_Xxk;H31J<~BO@PE3zEO`!h_^jnC+t&G9}w+!BN$4Lw-6ku0cbJ#t8 zRotAXI?rlt+0Ks6vk|m2=syQNJ=w3vC;Rb)?AW_eZ`eQ=nW@r+MSV)2pq9WB634YU zGZ0s^W6+uD%tBl@=)VR%y%wY2&TtpGX8be767x3j2~G+QsbL@v8g%BgN^IEK6+yd! zeh28kBhc?Tv3p)L{x=7kOM&f zJLvx)$lb^G$Sqs_MR_f1<(#hc!aV+PA~o({v5WFy@wca(mpX^yk~AF}=P+jjmt@R4 z|A|XdQcUd!_nY$(b7<6(x^uL19O7s`qv0Iyyn^HG2mN1hoELY@J22#X#_fa`{hgDY z)B&cMjdfn@#11gBlz)SsQuNB%+SPN~5*I%Z2@$^YR%ffF7w1eTFTLmwf*~aVYCo*I zo;Ro^Hs&~IJ6YXDOE)>^I}y>?c6+I=s)~kM=oo1Ll+@YK$keF zzOT))C!JW|$AubB0KX2n`KZ$aQIFx9K8ARtY=PQV$nL%^D>U@nO>A;|mBhA?} zN^icME`8Fe35Dagft+tUS=~h|q;RfvqG2JraFwPwTAh5`yK8s|+BN>uO3wAp&9s$T zH};*MIzL0EZ3Tk}hE!tO$&6{UXFYn_pLR6V0oG%NS3FT;VAm<0bJb2ezjl6yxSC}; z=l9MZ5celAoC=212={b`JM+y`E3UniTT}M-1G~2qf5gbS%ej|m)p}CP`KNOqU-g+_ zI4i#D`kCGAU%tvPL-8xm&V%Xn0)VCum419Wz5tMZ5*W?~LnorHGouds>fd-_Gd~L0 z0fH;0v{KwNNf**jZ58vh^wT+YX<#tNsT((g4e0F?mVvoNlGHY6)|? zE!~b3I>BHCgN-P(GYWTZKDU$kO3pugvy)=&L}5;Mrw0g9%Oa)+(?gs2H7;ig7;?eTjh1r`6R2_5Jze`pB^s!()Rjt}GN!K3K;d0eJnHdo zk@V}*Z$@ZM-Ijh!`mLPj=YpX}oab|Pcl@YoBvme`4H}=0iu3)>^m|(+^uF}jNa+1w zI3EmoL}-4D(9kzGbj5QN3^Qkg71Bas>KuqOV1D{y!qs}~GyReDC7jAaFwn#HF(F$v zcSd&MsZ27m@mT^zGM-AOmq;}|*7O(B@g>sqm%z{y484d#-uJ%az}kYl_=6MZTeW-$ zKK=Fdw}^*YaZUQ$>F*%sYB2NxLkVH>ruQu!wpbhHVY4JD4{U&D4m+lG@L$xBF8zaa zDz|95gXtU6vD}jW2^h-3P(iqT88;Wz-ZjxWGq!3f&?mcC>D$s-H-ilu*(j@hyF z9bl*e1104RJSo3=vTfy^e;}#6_Y6Z$6i}>MCGOA9>APCx>hI})AnYD6^algw|a3Min6jP$%ncHI5|M0#K zMJtI)G((?3HyO1dQ#0@;V+ISWyZ@^4Hy6rnRH#~GPe@hv!SrR`fYEcCC5mVCaN=3 zYQqS#Ax7rt!U_323XpTD*#l$2L^{J6-3e36>Bu-YqX%N*sT>Z55rm03d|Y~6N9U%R zb-33U0`NyG@V!1Vo61?rD9)hY&(J(e86_F``xzN1oQ+_hWV+#U2D;fcf($eP85e_L3>d}|QTtV#AM4OOtB}cmo z43th+(Dq$_aQBdw<~U<&#&w9LsoXQJ&$t1Xd=nV11_QOX8>X-&j~n)>fBm|v2xg+B)+8s^NygOqS;@%4e00ZUJ4bvFz$Vb*@U->axF&=MxtB2JR z*cIA%b9Bao84GCfT5e~?!i!;qE-+Brx*|e*F0GuuhaOpDH`v3NWm8uDT?Qp$ zG^4tVUotQeL*ji88179#+tp*sG`e8Wq6;WvPX^t%&=&6l8T`hDVKx}%B%tlT?%?(K zW<$&3d}jO1lMqc)z%zB3`V?~}a_s>yJV@M&z4J2Mep{z~c!wuU>OtztUKP*Aia$!F zT1d$}C6k(!H1$vBS((_Zl-UUk^T0qUc>|{8DGjslvf}+1|4=#^bs6?hqq4x)f0MLO zrZtl~mNdJoms<$>1i-fXS^bsyM}bi z)^V3U;pd_ z=vFZ=&ul`>v0!)+43z0MEN9z}cg;@tdmttqv3nMG9j^o6=7hQ}()WMmgh$(lB4R}%;z#+La5B=GhfJDmWiBS z1BSI=cn=KkFU(w?`7%=XDi}Th!-quON5?eWy6DNf|G9rSp3|=M5M3x6lK^Ns!%075 znz<_TJzS4wZZq@!%nxuqAAw;V7}nEzHZTcWRs81y#^5H_NL4&2&rj} zGq+}LDw7(OHV zwlMnY@*b`>kHZKshaRlX#riQW8`D3DH2#sfkCv_VN@eE$%)b!!Z!l~F!{-G11%usw z+mDZCqoT(4eyTiB<&9N$s)uGW6-Br7kg zm^Ly$t01c|s|XCc!GLkyUNHQ*Fso-)FC?)y81{jITE-37GJXsT$BV~4cUk{;xvIin zPaz{mw~L@uLnW3q`o$r=Z& z%Nj<^)(Wk&8nQ6wlr;j3DPW`?btCUlUwzB88<*|HL&<&uliOlURwRjq8T3&U<&8Bzp{K(-#qb}AxR7-{;%fR%SvO?eh`2X{ z@gy)(}2^>EfACd9^6FrJK%iZ`-n*i#2FUD-1G zp0zZK0u9X^W7e}-7-(d@0LD|nNV#nz&u#A+^h;sROI(!jZ9rT#)>BX^s0Kgll`Oh? zrwzI?3$NZ~y$!}Q!AQw%V@I~?J&m5y2^(Xk6rXXz7cQkF`COaz5n^g9&5yIzA?5}! zb^>E(!tBB@=TErp_w(}^C(*q{$S}Pu7vG%Evc4crYUQc2zRdcHbJ7e(OPrHiPR>q! z`58{89B-J<`abIygwl)Wq-leA%Ygsgx!DUP_y96+1 zfzd_4ZU(%)L2k1$LiV2={uYt|TdNUV`Ls3s{~c2_0>YsMX}ESC#`JzxxiF+{);1K#@8yxZI} zxCy|8lL@?x^nqOWrH!Hmwe8?_lg_+c+Q(&8*rWPqMEPf8<4JI z7uI}T5ioWIBejwnyD|E<&55p%V!wolX;^%Rgl_i4=__|p%~vy-;Hq(9&DS*mjNQR_ zEM_+BSD%gK*Yxh{6GW*{w1>>BLi%|OQU!AM!?n8sK=`RuX+ z?kwYP_9~vdYlMp$fwZ~T$=g!0t~GTjO%t61{kgxV8pHHO-$>MS&6$apO;o5iU8`x2wW@WlcU^0c!uP<4LO75p9K+@PtOetxV60o{`p)$|!u|-x!C*dfQ% z{d(-B51mF2w;S}5BmJgNl3v>F+K+3|ynEyN%XNTn;4m;Y#5Zud*yHZ62XIvvf9=qH zoSUKxO=i1Ka$|HsXLL9iDYYFt?R7bqSQqFybzyXmVu8Cm)qOfbY366#XSmNyF}pj0 zaTFLEiNXB+mc1)}Xc!;+xxpYi0Jwu_I8;ot+d&J}`T-QT)1A)AYXT$niO0x$@u!ED z47YRfiubO&z3vbqX@-z)$t@#wIba+I#_>em6-=r|-uTuDlkkUGu-ynF53G9g#xQy> z5>o=ovb($U2w1D`>dto;AaD^FuLR>&1U!)e?|D$a;+YP z7am7BsXpYs+dUgmHT9``POMMKI6bLPX~%xluEP}>n(w9xt!7WL`w=%*Xx)#2aV8k4 z3*Cra=xE1RY!uIW;xjJh*eQZECDqp4PrF|rnl#07nfpb~xmjRLXa?Uit9H}2Teymd zKThO+-OUQLT5h2GZ8sKZ-K)Wffyrzlj<SYkZh8%t?BR(fxs& z3bdMjt9yeR3$*S}!1w?dDYI?lneAo$-A_IK2PRA~azde@U$(?&+VEN``mOQ9v(4S= zB?k95ZhncuI1h}J*^UX#$dQ*<->`+B$ej3n2=`C!-;hSlG@pBy`*);qHy9U!@nNEI z5w}Hh^y>+p!4RKDqtgK=qm<#TrV_nypef+W5} z={eKW8KFF9c{+N|_F$xhj>c19d>V|;EcA5oq#=zKFg^>$=ZMDVkEyxb+OKP7=PoF1 z+3bjh+1nQA{-9pLw_ajr%;WO-i8oq#Wlz8p#05!UTn5G$X+bYBrmqaWnYY-*4g~qN zD8f{Ci+ewwTn{w^dAfPd@pSi`3&xkh_zDVeFB@Wy_RbnblQ}y8tNHATcy?R<{9Z3#gS0yyc>mz7kabj76iQwj30uLTEUIH6@1UE(4Jo_IrjsoU+`{gQhT}QP7mw-(fTn-&wU>3 z{qfun#`R#_K$sgDq02q?r=qv9CClvRP1x^n#yem<^F3r$G|iId5f54w4_Xz}v6~6= zQ---?|2K0_YSb1LT+bHImx$)s>e=S`+=FCn2O|ayJHYteLeE#8uaR;1H{XNt2V&fh$Mjgw z7!3!eOu}Rx{)RNUWEkgShKG=cKqit_?d-ibd{0zol;@sK$y!p;i&ohCg zb|v*d{q1c>jMmbk-s8OOQ_SAu!T2i}e zVu0j5+iONlZzpeOZx?SG82<$0J}~YF<6jHC7Oxd??O;3r#=i;opJTd%-^(ZOI@?B9 zI_Opwss&sQR$B4T>IQnfUJ70`bB|uhi@}Q*gO`I~O2Orr+A%h5v2?8(+8y0idg2M? zg3{P{Lo-MtjLXeMa9`CbfN@C=&TgQ)|;#?IiJeQW+2*8xvWi-#Ayy}ji`q?VuP zt?>3mBCEi30+^^}+;kEna{Fy7Pgrm(b}GnJPsI0jC>Fwkb&`Ax^paiCUVPMxcEx)s znDk&W5U!Cg{#4JEYY>VA1pBT~-QtUii3}a)9f_FU2JdCw;ocEo62K&aDHTj7FZ7P` zHX`QbU^)d%rxNCA$5cMPNn3K!4fJGlfZ8lV7%5RJYKT3}8?S15Cwl2}oc6&4FJ6wL zy*vX1^UnC&usDvlgyx zj}80yGzGqF+gpOs_O9G>!q7<-ut|>y>qO%A4cg+kyEc7MoJK56b~bm*%{Wd7mOKX}Q|or@hbMYMuj=6HJtK zH}S0doX`Kfc_F_KhOR2bY+aLn2FJVH`#NH3W{ACScvm3in_$WU6D8bDJmHSN)-!uX z0he?vnji}DX`G{q4y&$7eO#lR{R*ds0)8= z|Mm`JrVR+53ZcXRBDhdVk|s zVK8+~#9DK4=xJ5s;k)kYN%_dsEe>YD=%A8$|` zL8`1KJA%FwdxrP6Ici@oUxbA?rZO;W1~z z`-X6ltN~L(GJej!jVZ>umg4Oj>1#qP&4-75V|-(gzVToh04B=Dn|L<2a0FNj@MVwcI}6HNI(_$P2+lIr*5UurkzmUdejy_s8Fi^WE&b zqgABoz8M@T3Z_dEkfepjU;Gh6>WXPiWi#&ekv-AWz`h54XirElpleVUN1C%MxOydj zLnQG6oo|tk)mgNzfBPQyVV%YIB$(>KG=%szl#AW?-k%(}3QMRm@eSLjS?>*kvnp-V)dRm5=HynrS`XcRsAM_&6K$R3_q4xNbIix z)3i7*=Ir|Mq<33h@Ajwp?Fgl*di)N*lS5qxCQ93@{{MrY?#N-=pO`N6d;CF!@_YS0 zzuzAK(~V%d2~0PG>6V55kY7qM`?JAxE0`#;Zo?Yo2-+>CL9*W&H6@x^;@bAc00sJ<0xy z{FjncXmzLf>->ZHR^AJy`{G-<#gbdy{g384!9UzT8j&<#S@U1+Z$b+3@Xi6#{Y2pd zjKYC0UVMVdfu$!(@FQE<5e_ERBm7tSry#6mzQ;e+51hbqy}dHJ$|a*Yno2~ z1AeUD`{#mb5ttSe=pzjDwVdq-rGGemUGW|t7RIufyQHcc{~|vb7Hv`=_oHEd*+88+>B{1|8st76V^0${^fpb6DFvoV0t1BHD_1;hUp8mQe6I({#A&k zSy=V2_P@)~o&wX;akLkIdVa$s%wf_`q|pyzD1?3F-$;aM9+gf0Pmpt;g6UZ>QKi6y zNpQ-shMM|qTgpU_ey5u)P{fe`LW|$EcKAzPtm35Qw#xTz?+i3IFKI52xJDb0o3prCea?TE&qiv z5lt2ACycHgH8{qr!ZDnn>OjAF0Su8o1JjnizI7?tDLL&=3=Ev#{=}5ri#c0LkSoLj z1^vRnMaiGLBruG&wHCLvE-*My9~crC3MN#kxUFA;=_@dO4W@6vw0&`)A#hn>cwj_e zWMEXF5lr8LX$P3T0~4yGAHei8n0{$%TmSEE<%IU_=T6cqF?QZarLFO zO?WmHiGDTkS~B*Ez?+1<5={S_7kCSVlw@o1F4v(4qp6_@-U)S$gX_I`K+$Lv=1Nj= zAUDsOkH6IBmGi<~z4_VsMc%NK7cLCt6$bK)3zd()7r^Y|yukY)w3`?B5CrVZkEW&- z=J&({KBjR*eM#fcqQMPg8=I=?NNZKrjv5+^9q~4Y;z0y91U^eie>ku)uqp6KU~}M8 z5ITTx0thF8pa;SDa9~SdYhYX8^S~D%2q0vDPzS=ZAnr(B(RQ{X;rM9k;Qz=f#Oz{% zO+Qzh)jSE}3_JML-V(v0nLb;P1dcf&T>#22(%~K}ZGRWDrgP z;natN#|7I5I|PppqPU#~!s#G%2B8ZG=_JVB0kqjt%q!u7T+viNzM*m4n54ffKtqc* zI#Xl3FK&N)V@8i?7)$myn(EOQsz6uQqAxO*e=dfsHM}b3HYomFNn>5@*akdz(bRMe z^eF$kL~!&yk_JXb4Qb?`jj=B%22aNG-R`BS*Uk$fzs{ICgqfG%>Fs9D51tV`69oLv z*;A(|GOl#g6Du>1;I8ycy6!< z2zC%0Q-}29%<9K^s!0g`Wy_<+Jd|Kjuo&%g(grl&$_2rm!Coo3hZ+`5JrxZ^^U2B* zdI$R?8HQk4uzyPWBf;`uMX+zMGFTO?4%P(wfshG876>j7+#q;B@Pgn2!T(5bKyYAi zQ1F7_g~5x07YCyt1V9La5CQ?2(jA0zLC6CEAEY_*6XQ-=K4)i4P-6!yE|dQ^&zIIo zkI|7ou3^OBs`{~G(bXOkqcF8=>vXfYrMgxfOWPl<8P$NE@0i*V(NyUOr?UB)`l1a5 zaHw-VJcu1u32s@iF^G3H<^@NCfJdeY1etd8WO6$aPb&AfgX8J+UlE)DLN*Aw@zXys zI2liCa8mGU5OP2W&ks(a^WT-7|BDbT9^If9jQ>s=ptN@AsD`oQ2G<{sf>31o`ru8; zUplTw@Rq4hfY6OJ!}JSdnxP}-wZ;}IK+}UWk{NMV@E$twcL!&IfCv4&`R%R_-Zym& z2v}>2rgr9l%opoZe`NvsG_|8Kx{3v9X8u1bBEY4kumSac?UkiyN~-ckj8MM(P;h>$ zAL8n3F{!UdKqyF5UylVJ4=xQNcZ)$N0-+FORu`AB+0j&c!m!x% zE1Bn>JW7?KrxEIO+Ic) z@XM*MV{G59qPA{$?NC%>lO|PEmKW9(R7XqkA0TmZs_AY25(#UP+XxCDgS`1f|v_fBh9SU-A1 zaqiVbpL&Pf204a9~L!KN=~ks?IAZiBZJK|C0uvok)J9 zthl_QBvMr!Esm5R<$T;>|b8lCt6jMS6R@Dt+h&_+(=XG?UJV;+bi=b z2Wn0bX+mD!=+PtUEhQ!WN-bS|0gKmCfG5P#vwl>46Z1GHO)4nKE9)68uc(fcMh4|o zN6O2hC}OlZY=>gHBy@79Q%d@h&?%u)L#KsK51kP@Gjvv{W9V!U27^!!!VnOKf-nq( z1`sX-VK@jQmV`Qox`fg~=8z?14cS8WkOPE~AfVgU2m;!xD?zvlgoz+b0s&tWkEXi# zsf+m!Nj?QT$(7?q)T@SE%kV3HcoIU)5&!?iVbKZ*Vh&3|?daM|8_>T-y`lIkTFY$9 zztMtM^kH*9^YG&hMGUYTksDpqvI#0O*~wA{0P^!CMFo2@MSmBX40U z2vb0q%)EvF7bygEBfed}=}q%6h5V5rbTAi$Mg^Y@;h_cQNnEoa)D#*MLjF$!;aU)` zn>wU_?9fG16AfOnWF$Bc8o^atGQ~MHkvN6g;(FDg2u%s$gAR*AQ$rBCCNwQ{ZRk1> zZUEs%5N-nDW)N-x;nqc=8$vfShhklD9SFC9@D%qbE{%B<_QNvd|Ihr2LpeB!3~tM# z2;JRo=98(m(5%osp?gF3g=UB5gzgVL5PC55P-t#wUTA)3L1=;_cip=U$Sg`N++5LyPp?I2+MJ{^P^Aj|~eP7v+_;cgISfp8B9 z_kwUA2(v+$1H%0vJOILjAUp)ZToC4gFdu{kAS?voVGtI9uowh%j+cP&C%@Hhxd zLBMnMr0R%-UMBN(X1hEzkPTz8#xky{9&IF6x)OYm&}%fPW0JR`cp(XnLTCjI>yk9A zpuA#Ww4zsDRZ$e(n@ELXX5XTrj-;XNe^ezRbEz9UWF4XO3DY8qT3SXAj+ioY0{w`snAC>?vOsq zdQL*JGm%%)KW|`_;!D(2#k?)m1KC8A0YJqrrZBSaSoeW0@PXhozzS;VJ=kaKtl zIJ2OjMS#vOl^A>R{B zkG5dSFQrm3W-%-U&7jKjdly8}*HlRQiLko0z(TKTbW>v;mKT}FAESfL1;!x^qqM4L zRZ*GBRF-hbb{^E7JaS3Mhj~&olrojg3_YuUgd?+(!A;dB}X8g zMZj%}WfE}0#uOIe&R62*R27vV;pIvWLFz=1ZEj8yWP`IBr zhB`~$sM--%URB?OI+8`{_*aoJW5&g;Kwep1$-qHqoXg6~%4$kVm}4wiFm)thmE&pE zgOZ#C+nNUz^X;V!DKjZFk=zmncn?b+$t(FJzZ3xBIS?>=_yP#%1HSk$zS1d4vV_+P zK)@v8au6`T_a=znQYfbttF{%A|9?|;(;~dBAp4N9uXLV-7xd;y=Y#O_JPBjiSK0`5 zrD6V|7j(fN`B9uv?i@wk><;!QL%)# zgn1^+4X|gm$nkr$?{5}XDfbby*AA#^O2OqvprFj&P%bUnqSl;_5m zRg+022v9X_d5EdRJN_t(D%B-j_a* zK9oL^K9<%=>!l6SMro7uiL_bzRQgQXB5jqnNuNt!NMA}{NncCfNZX}vr5)0D()ZF2 z(vQ+l($CT_(oX4D={ISY^t<$jv|HLE?UnwN_DTDtzoY}w-_k$Q|D=O*irh{GvzGVCA(!0w)XgBzZ{T*GX4q)zTu6pXJErmS8T=YCZ8jBm(P`Z z$mhxD%XxCXTp$<9MRKv+Q|={4Y`5Jkee64()e7$^we4~7me6xIue5-t$e7k&yJYAk4 z&y??!?~?D9XUX@-_saLlv*kJR{qh6ygYrZ2TzQ^6UtS<%+F&CHn?U#kgv}s)3c_a~ zYyn{_2-`sT9E2}G_!5M#K=>MjZ$Q`%!nYvo0O30jz6aq45Pk&VClG!H;TI5gg77N{ zzk#p|gx^8<1BBfm>;Yjf2!DdG4}|?7`~|`R5dH??9}xZr!a)#IKx_x%aUiw_u>*+5 zgLndnCxUnqh&mAUAR0h4f@lI!08s=n6~vQ4JO#v4K|Bq_(?L7~#4|xW3&f5fo(*Cr z5Icj|1;jKE%^+Gpw1Q{@(GH>mL??*pAZCD=31SwAE)d-ydO-Ao=mXIYVgSS-h#?Rq z5M>avLCgU$3}ROhb3yC|;yEC82k~4Gdw_Tzi06Zt2Vy>m1t1oJSOj7*h&@5<1!4rm z-XQh?u>{0Y5X(R;2eAUgz93eDSOsD=h&3Sg1F=7d13(-I;vf(&0P#W)F9Pvm5ThVo z0%9$Qmx5Ra;$RT#K^y|&P!NZK*Z|^XAPxs{1c)O+90g({h@(Ng9K8W5*}crA$6fp|TLH-LB}h&O?FGl;i< zcq@pvfp|NJcYrt@#2FyY1o2J~?*j2|5NCmS4~X}Icpr$fL7W5P{UAO7;)5VQ1mau} z=YcpM#04NO1o2@I7lF7K#796}0^*|}J_h3BAT9;*2@szI@hK3W2JsmXp9S$b5T6I} z1rV2k_#%iefw&yRmqC05#8*Ll4aC<$d;`Q4Ag%=QO%UG#@of;_0dW3LEHf1Mi4iF_z8%cLHrcN&p_M);#Ls1f%rLyUx4@} zh+l#DHHhDUxLx%DiXSRJMuSgpF&Ilx>X0Ye@+o7^ zy!|v;vNbGVNn9+`RqfeTI9(PeS7S#&+V2Dr_DK5eqWKCrW=JSe*BLfuTU!_SN zby87jK1Gn~)GAB_DjC2iFEc7L--s=QHNQ`q6w8ue0;{kHzgLJ6ZdG|rWkHd;kT+>U zQ2ms~Nc(uyz|ik4Sm@l+x7U7Faj9j}e68F@^u4lGSb zuFCJVG7O`b#9<%OFsBAuFBUx^37E&Lz|>|$C14#*$&O7aAI&=7aDRCIk#oGNf26ot zP2NR$p)(Ta-9~~)YYvfL6Txyc3x3P0RYq;5v7MTarBE+9DEQXtq5`~J9rt;}Kntx_eVw&Z? zi%Sxu?nfHbIcZQ{Np(?W8F8p*WqFN~N|%3WYh-zUWU4~=Z#1$~E0~z>RMG_UA2cwt zm4OvSm8Fp?;tVbi%h&3<_R=g%(yW9$J7$+z)mtHEKaEarZFEI>RWx?Xy6f6X|gRe$(ck$XDjb~;T+ zme2l_@oH;M7R|A>>s4Pn7+V>!2td^@rL(58rYwpH)8fLSVrB6j8t-Ujd~7TeLgkEQ z`)Nj}c75ubM%9n7@WREUNl86QtX(EML<2jwG7$6Xl}cePJG(8&s_Jq@(PVd}foZKk zCad`Db7<_D?bPn2Sf@})IdvH+%kM#hobf@tzlqEkYKr_wNdyZN&2aN*in+ya`AvLdcovU}3djQG%!NLe4u{S`*4&@aMj9eNf$OYm4yN0Pc3 zy=jssJ_(HxR`byBDpWs2jzv|J+!EB?s&AED+H4tZHFsrK&|qu)GcDV|qWM^0Rk&K! z47+GR%)>*!Bfq?aD{WNMq9DV~Bat z{*mfltveRkQJQmld=8#pbe|II64{s1kkjHrm~)lb&X!$IBRVIHsP2WEhE7a1mZK8Q zY4$K0nIMnld}LwLpy4zqK^}W59!Y6l6>cAu;3&18&`_5>il!v62StFb9%)d;F5 z7N@dLQBegtCA{yB+>FXHb?jIg8;*~~{ped$jwU}QkXZ4qp|UmiEa<491x7hVwD`HY zfM!KBmL!YSOd6K38eDvWWK|@|)7>;Ssn?Qx(t*e=bhOaUqxwRVlIV72-%C?`iH(s3 zSUy9MiMN2#a>||7!H;3jXwL747!jnzKC~A9E#yv>mEQ#MJ8i19_YF46C zJ!NI|JR04lmC?9A@h~uZAq{mW4vkZdyeGxUgAI%+QRS#nS@vR@Xic1`^0cB)PXbBe z>!U45il2mLR%0m*P2x$Mq{O5D6pc&bdwd*MFSyj04~tPilAt|HbM1-LC(Ny>uE0Vq z#o~FYl)uo%$C5t7_K#&9)Wi2u8z1Al5%W=${dJu)a>?lB6QOia1dU z>Pi}&)WA!!Oy>p1GP_w7?Ny9XT3&UYGUsiYbA~-uSo5Hf*C=Ne8-rCF&(qpDoV*mozk^S^xAQTAs4Mp-IkW zli~-os%4*c_70k2Yc_?u+L|Wi2O56_ecjnV(*$#~4^$Qn(nSB226t~ZxM%>2`za&E znmEugOjt>@swSV;>dPzR3772OwUA;Ngd;=RLr9&Q(S&UfvBQpsS=ruwG_v`gvBtDE z7i1@FKs$}5eH z%V|gBkn0L6SEO<5L@k=rfd*&B2jj8r7oo%iS1P=@zN(_AAn|TO&WSWDt?gL~IeHp< zYCF2!%obTq$xI9Z6}_Beq5+|V0c3ikEI)#Zjofl{+|ZonRbc=?mof4pB{it-ln782FxH!2i`S$qm~F!5I>qy>#skJ+us@=@M)fIkPSu_lDWlJ+&ZT;U)bE`^^QnXV)bcckH&Er!Rk#(r8W6NzAJTVrZfsADPBlTc?BhQfi=(6Q<#1E!u(d z1`ozA;4zk}TD*4HfK4(=J0lh}P_e}Sa^PfVF?huCNnS!qGpCp!TDEYuAci1Rt-!d2PA+Xf zi|Li6MF=hb@CZ`Tm|{|v#!iCDsuIFGnqK6b@*`Lu*IuYBE8{AR9E?h(VK1lh2-e59 zo>>@F3O(`G7wWe1{#3hC*H=S$hasfI{g@zNTBxk3gz~eB0aKVifWTabW%v#W^Bk{exj8G2an6twfON}YvL9*11B&3$T@S0~7dsoY`2vkyD zUZG0aXqtZ*XPmb%(gtf?6I56`hR`(54McmX z6v1oioX}z2oG`Xd@(VUFB7Y#d_tcr5#msiq?1z0jw56WGKgNMNo9(g<@=v2iLpoOuR zCR&A3f!|P$$~}Y^J_LW`FocMXS8_HaXEuQxYI`+jK5#gnkMWk3sxd#Ow5Y2*nM)uo zkGm$1EApz4pUus~0zznMmy!{xdSM#~+PE6ENRhLM=AC;8?s0R(@BW~-#f$Jf%uU#c zB?Q${7b_%a+~H~EUQy2Dgw*_voT_mc3Wp!YbK=F6na(=P|JG) z2|!8r1QK?Z-4kfJwQmt%OLN^CAh+5~)$)r6HJJaP$0B%WxQfvJn|ot9YY3~QyV^Eo zvF&8E^)Jd(`Sw16wLA$&dI>G(Bf{z1LXMl^5Zh5nq9;y09T=Gj}Q)Zp7}mSt=UoiBGz=L0@1uU?OF`uzF0PRtftHlwB2V6QQ*{wr!wQ6cyqn zH1%?2&ZmUXau2i-s#!WH=J%lJRz-^{E6XcYa<&p!oA+^=UEos}SKsH>x!uHOH(S*UlVpoiw$obo1YN2H-+T|tbWO3 zd^F!lRDVmzt?u;^-e=7D{z&Omc~FcEgv`UlK1!bCKn}+{-Ew{+!RTk2=ZTD7gf^Y*awC+f?inkQ*=sLN1)aJh7p`j1sWeA!t#0o zbJQ;w=?h1qwVH84`2t$7>T-(2RAiBmkL1X;f?QVApR)JL)}2DIEi*E$E13jo#ztyh ziK$Ui!{O79a{Vl9LuJV0Z>(vk>pzQ-k3?&$>yMo^?Dwivm)(ibjzkg?mYr~!Rz1%x zj&eASzEFFFvc@erI^41DG<+aH;Rp$X>S(mE@nI`{?xKJ9IYlWcQ6VcjP!}bIA}v4V zpih+^;is5grZsSvf(7K!YJ5-vBk5|TRWY1F(EsYxw|+@H>>|*Xu6yeuu1Zc#S)Vd& zEEscb82<=GBXaWxz z8g=F3m2R~v6wWT-I&2b~J+rm2qi zfrJMg-3Jo96#~Ym3o-GG9#=%=_(ey?8dYF<7K$4}Oi5E379FC3a-~)qzT{t1o)C1Z zTw-A@w|B}BtD_IJX^N6~uTmbXz$))dP{~QX9v2=$plv$J+MxIXW-AX^gc}I5P3x8f znIO|VkJS7Di|_~n9MG0|PXM&wkAPUP)~VF)nVDz6*uTI-9d;L{sux}$NvJG@8|f=G zNBtF5#PP)$>=3}#rxMkfX!>tHf$1b_bSs=Qdm4fCk{rk{Gzj+fqO1aH$$ta^nf(sZfK>FPw(m(*;F zYmDDQ3r{A1?o};bGhq*#jAD6bf%6ypkivqpNZFgi>DY$_vU_>~G?6{B%MtJz8Y`<`?zsV1Zj9`c8sA zif3_Hnmas;P~;;fuxEjqs<@93G&X}j%Y(;DRc>BYsVSn&Uq{QUMk}Wt(EACms3k>* zk5^VzP|WhR3acI>$bzGRL>a^?j!NTv0@OHqhfgCn#LAmYNqLEf3B2dgP>WWb-egLs zdxak%P>n-)_!P6gqj;Z2_%TB4bu?tg!Xms-qh{8_PY~)+?LHYSmPt}}|LLP9G-e75 z%CSVGpgu>a8Yf$2XjOgPxTc1&sL8qhRb?J;b<|YS9XVE7r;ZZ_91rA{5p<*t&K?>X zTP3j50?l{4y)(R=U#T*;M{cCzkriyelKuX?%n{{|dbN#0Fxe9G?cjyC)GUZY5k`4{xNYj^vMMzcCwo z2rK+acyrRTZs9HAukl5y@Ye9Q@aN$#!e55*wUF;X{2s&~K>QKJpFsS1QTYGRcHegF?}7+|!Z|FFp(tBOo1K%iNt&byAbW42w51eE3vJn}H+wGuH(HP>L&SlC2r3HV zVhN%MC_{#Vf(R)3eosSbYwPpy`n>NSpWn}?O|JX8uj@J|Cm~I~cbs&ba-4RYaqx>D zWrd$)h2Nx7UMdx&Qsw{i2fY1suc~UGUpy{x3fhy||m_ zeT-efFJZd%!Ses*8(SQ|IIi$faE{Be!o?iNud>3W|L{?8j_Z6Boa2V$cUj?QS>e~) z9|h<5Q;XmO+8lprLRR=iR=B)WE5S#>Rp7IW_=2YYmtWAL-J{)Gc%>2^tvnwESK(^z zr=I_i`>E%*22HD|)e`mBXqB|eS{1FTcE46ldqAtM)zE6n3fE+X>$1WPS%J;`AuHUJ z75S%SfdRl$0fmBMcj#5%8rKEDVRPK>VS*eth%Dqy# zPcK^ifdxKuzTiNFzoV{gdP-Ur-|fO%-v8ltp_J6Dq>S8~+?t>wv&FY^B&4SE7Z3VZ zbYyZ;q%0AuY)&~OQHPfZ>@%#n8T1fL~u}rS!l}hOx%_o&IdJ`IiYkTXy`SrV9 zxaNGz=ZK`t16q)`nhe%LqECq(Vt$F7?yWoIecDV{{xc~pIjev67kP^}*9w1&sMbnr zT{xR|TKmF3=%jTn{DW><_rgEur9B@0L3r=+d{m#-S1L+@&neWBw0>GLpHrw*l1fFX zsJWk0_%FW;u$Zs2<3kFMZk=@~t4iKRQ?+!yJxKpJeVO}V`b$llH9noNg;Xl*<^89# z$?f0dSNzu5FfZ^hZIh_KL5b0ZYa_Ig+9++bHbxt(jnl?!*_zZQXcM(b+GK5tHdULZ zP1j~2c%L%Dz&8YkW?O)N*$@xlL|+6 zBdIi&ia{z-QZY%zA{Cod98z&gC0Z(OsW4JbE!~~R#Isrm3C6;AeBy1 z=^~YGQt2U;UQ+2Tm3XNnNF_-s$x=y?%0Q_Ml1jQ%hDaq-Dnq3*Tq+}_GFmEQr7~VB zQYsUrGFd89r7~SAGo>)IRIo7!gWE$wY>i?&sJN86^ot8LeIXzyw7YaeJk zwGXwAv|ZY6?PG0^_KEhX_L=s%_Jy`r+o$c<4rm9pFSSG3VeKpJi1xMijdoP~Ry(F0 z*G_2PY2RxnwNu(@?Tq$=c2+y5o!2gCKWaZ|7o{>+DvPD^tW=grWtmi7mdYzqc}*&B zNaanbY?aCmseB-n-BS5XDto1JP%1~H@~u=(NaeIt&P(NzRIW(nhE)ENYDuYC_Z7>6t8cf5_n1%w~ht zYTfow+3mS(5T5Eo~)7_3^*Z*cm20fRHB zIf9lz$YcsR3kLc#9H?fIKyjMG8E`oK29qyHpkTmk@R|89Q^@9b1nf?e)m|`=vt&5X zgGB;49B~1H*$kS~Y&2M%A*aFT@C6MaV@L}*j26vq_Z7&dbkBc*e z?0!Db(HiG7IQ=I6%jUE=Lq@;VVloyCbYD2oBSiu^15TUGrkM>UGrO`HLq>zs67U(! zl+G6CcZSRkd%-{z!-47+31keKf;N-WY>3mGCbkx5H)vMBW-!`}S}??K_@eWIf$k3n zs$V3KMe|$zcC&*z8*OaJXEbO=N65ez(3-3PHm3zl1q0O#2WnU(kSQ)`3uu~Vu-QTZ zgVjNyG@oWPI6|gi$l*7cELx#J4~GLiS|pI>H`)A}<}^5hc0a|YP?~0S7#xnE%@Jp_ z`W^m4HL4#D)TBrtlhYnB+MRxb#TKI2!JysXw`+cb*%D_9Ip{rGEV#AC;Xsi^0@*ZU zD9%p5jUjW$V6~eZ1}$i0DjgvspX}+hSOflof%uwdz9C-WQsi&Pn#p2wYCf~Y5C~Y< zTF^rKO-7?3r0Itl-(haktOWzv!hyKX_*)>&6c^|7+f4?a)fzNdRV0ul&cV^-GaKv{Bc~CkEyyN=4uj8T4%r>HIG@Q>sMxK; zfntgVGTH1ln~Bp1z1C~QIgP{4;PCr2vt1*owNOSK!ht+R0y#9!UjduNU^iQQoFV;m z&2RIOppA=EQ^?`46*?bv4F`%X637nB9cN6a`U{{W|O2NPxw1?-_hHR>A<6jvmWjq`{@;{uH&FUyrqvUX)P?10!?wUEkgOD#^<5&w=%?794Y@_0Sr`hKZI5`#z zZY?z&s9BLf#(2aKE2y);M~ip zmhGB3Z2cCex!@tPH5{makwE^S#&sFzR$fftSo4`V)_g&qAx=c zB7sbtMl{M{a5#BzvzncpD=i_-z%@MAapYt21qufGFdT^QME={>IAQxkp^(*J@lzQx z;)2~s*+X#_yPelMjB&w2NB^F1ptK@^>=r9;VDS444w}S~8?rIkTthl+4yWJf^!d7 z2xR0Dz^SoQqsecxnC(F`7a0X>bR--ot4JUl=VZ;!uHuaPrJu&-vERsX!m}f;xtTEb zS1{1AaG+sD135zUnJaXkLw{Veb1|wp?BvFElizQ#*|}6H80cg;(1;>|>_KbLY3Bsw zVjW)t{sC{I5{gZiWL*C=!U*0E2!G1A~zw(KVj^$4I`b|tWr+rH1 z#ytG{;G~5U&5OE+N7%QJafA~qL0hUw(}Nl@9LP}HC}9dF1ug2GhOxI zyzcmXal!v|MdbyrsTW_|qkprBtFJ3j55~)rFXXs*-u~j9>>@>9Xn<65ido26(#6Y~ z;VwTYm8JQ+*ivX0LtVr5g@;Myr5x7?sVu*fg~#X%^BU=jVitBybWO@zc%@WU6*)5Q z=!Gn1aw;>q#WhWz$qEGqEG|APUMj2g`E$*rag_iTytIXg|l<(4*LJftNRnKMf&y^N@Z=1ix*ed-O2W!(YMdbup#Z{Pyfey{(GeIc5(SX z?An)?|D#gbayx(50hd*O`TzF8b;$LVzVuNvh=t5(#NFoZgET3ckvQ< zxO7dVvi~Z1Peu%kjh8JEo6+oCvO)PB7+4W%jW6=KMf_bAod70X!a^QBR(Xq@} z)%+QY4nzm_BE?DN%be(tR1V!qcOKK1ZXuP!#Vs8d-9B$=O)6jIFWr%)?<=r$m*{T! zJL@i$uk&`+OAr6JRK6)LeDi1?8NxkqN#$sM_+-LYE)ae|bgCXc_ktptf8#oq7k;oF zp2vmb#f5L3Kg2QlLmWLSdW>Es{#ER|oM=w^-``1@q`uGushlisp$^g0^Y%fjP8Hfm zfnCgwo~tiBM=ED>qUTBFhdWt#p}z1Ushllt;jYoozZkt#UpPl9 z7jmNchqfQ@WZ{+i!mFh6Q*jITir$d7uwN<{^B109@Z=M{Df)H2Z@E_i(QioQ=e)kX zr3ZgoD!&vLyl?c5yx`38<$nskGx{TaSDeRx&57P6m8*BMt55WWKb6XF#Vj1XKl(u4 z!q=p7y~rc|9leg4clG(V9()!3wVo}f{@-(=zmduxcarUKJ=+sfxmirM&eZ51^0I9v zl|OHnDf%2oPwfJ8_ha-$ed+MPUy`cG>%wI{{1vH26c>I-G^hFS!TFd}OXP>YN%#ly z!^eo25`>S5km_AIG5jNJsXN$NOlg*m;h$^oE^g^zF%|NbZYkB$x0fz_!5UL3rV0y( z&&@GarFze;eZ@SW2d^&Gvc&|CseSw0td`^4T%^f&^g?j>DmSKXOnp6r+!K0C1F7;R zo!pCtm`C*t8cVf&F&Q|s^H1n)^%MH7VAdGBUPqf$l{>%4iHVMJ>nY@3Sj2dwS}`vL zpPqtWs+EdMVMhMNLr4AM;Z_PQVp{3Jaxai#T1%DpyoLAEP7lVvjaMx$*xdY!pw9Y5 z(5+xSVtVPpaxWHQ9+zshykPpfP-6N@^?~AoEsROY>plMpUp>F~g&vn<2F0Z7!NZjw zEY+HM!87&XSyHW4T=1v!A8>e(I+~a1*qHHpu-u22m~5#&loxEG9&D0SYZn)6NzBZ= zOnXZ8VLj8_OZ=GGoC8&^mBSbGG4our1C@41a3!C&-V-tT7wIvJrCK*{y{Gl{o{?(3 z;?`Rh!wEcmKvK>6`Rgsy*W*&;)_N;kv;8G_kJ;_zR>!=YzrD3mZIrj%27S3#r21%a z%dLvxJs9ET`bf3$?M6iN;9TJ7-xBjqewVjN)sPo_haUVrsYVtTd|m!!Utj&Q@7Bod ziTRXZ;ge*{XHqrh1>36!+b301alu}V`6@5dM5&r@XBzW0XDYo9dEvi}IZpVPV^X!| z#GH_-?M}{Ar}U*yOVwW7(r?CaNgXZ{ZxwOmFMW}v)dEZZ5~IHdGUl>Wo&OgH_w|?? zg`UC0+>H54FA#%pA;&GG8gr{a?viBbzDug^;BamPsYu{&tBJ62D{ zC)F0krE<*8qinc{!=&2s_C5}ni8*g_XcVE54x#vW8f>b-?mAIcCK3S?Aiwl1?|C~4~|D5O^ z?9Rv!##^5{=LH+42OBQcF2x1=DgU8uO#VZgJKH@$FN&0Ew;cCGsdm4UqD<8nnkLmA z#VvH%&6~%<-54j;p0|tgw{AS)*3Y!z!_Li__VK)3=x16tXWHJy1;6ILeWvBnpwB-A zU+!M1?`nlq`{uY;Nj2e4cBQ`@;$A1!#Nrmd>3%J5R}-X~bbDd<8?>5hqT8*0%e_V4 zSMD{Jd#hBF^Y-t|s% zXW`W1f|qvRJ_}Eg>Y&@f#o9vWfurtk3m>T`+~2cW_?+)PDb>MwtDVtT`$4Khid#)} zU(DOe45?<^UQItJ3e@Sc``7%fT$O58UhwOB@EcMcT3qm|`L9aN(qEMdpLIMXJ*D(a zb5FjWyQMn(Rxr;!1oM=Y>WJck)yO~j&e2c4w}MsjR1p<(Je8$7D#ydQbu{l{=jCMm zP1hZ=GWbyYj{0|O=iR!7r>>~D%2U%*%k!Y;Ax~}3!=6Vxb)-60s*L}5sWPV=T?`FJ z*Q8aRdZMm)$kWi%$n&VDv3N+Tle3lVd!vBJZuty_JgLANw*A`ln6s>@{w8!s z;;^j5+^h_|fX;i3!_nKO#wR58Pfz8$-3+(y;ToL((@yajgY4F}J#PI$YSyqe9r6kr z<7px4ulKk;9*@@(>+yN~o`5IL6ZC{46i+ixa}UpbW=eIoROd={zEq!(>LRH=Db=T? z`m9u+&rX!;3sTLI>az8omY!Cg)}A(=ww`vL_MQ%&j-F1Q&YmuwuAXjET`tu%Qsv0! zf@`BxUzO@6slG1NH>CQeR5wd?i&Qz1-zoGKQ_thM{~n|A4?EBGyzq$ArwNTYvqL8KsX8oy~hkc9bO}vG?HkjI5Epx$B^$;prIz zGozZOrzfW-8CoZ0h5vV}^ua^>Mzu3SI!8pys)>@^M71fAD)717x>?WD?L2CaAq_`_u|FJgQX^r@_Qq6QTbiO8?4_$kf3Z=>wu##iyi2@)6z{Bm1VuXCxMEiwr07 zOmF4!f1ilH{ixtDhK+yNOc;1;CJO$baAT%~7k!RDHT-vr<{kq2);LxFa{Pf2-Rw;YD8h|8>};hfnO0`Nzwxze9>@&EMqe*A0z>BeIgx^zUEESYNI1 z-QMwR|L42qZEL4z*MGm!r=BnV`;88I4*%^&U+G!Cb}P%L?k%*ZZ#^gecIyIr%1iFF z=j?yK(NCVA|ND)udamm?`pt7qs&7g4?WLXq%N6S0t+l=-g3d4$ zd65)vr6M=F-M+jVRrA&q^_O`c@K*Qo2RH9Zm1iJ3mU(M=AM|qV`JPn2k?K+X*^!Y; zTwg*~$}s+JzJ1c*^o%V1Q(CyjV_Z879W*FDV`O-WJ0vCNKK}C8fJN?d>w{dv@2@Bv zt)u?Ho%@odw_e18<=*<<2Hu9=M&3uI`o2^@mg=Wc-7D1txBsrxYw()%)FZu7UZYe$ zkm^pUez?qQ_FBBWs`imocS&`(o_hTvc@)aDZAMDE{%HVwYvO;tp`Vj{ha|4V;(1Kz ztu*@2w+^q7`yQ1#v1P)?N`k0euY{=0^>~4^X&3!^ZZH?Zc|CD^c^^rUjg{@_4S3^Z zN&Ylyk5oU&?$@q%r`$*Byt_5`@)B^7yLnrC+j!eb^)sn{F4Zrx`_+k6!pXFE)h_r< z#oO7-pCA^w|6N~g&4BE;n{w+y7G zre_?e_25HgYd`!*9e&<{uVrG@e!1WB!T;8u&((>oAFh>ppORfVy9|RHnfozL9Y$tm zB@OD-To2~w`OCig4H`D;9!@olJ9Z8KXHw>^1(!B_l-S{qTiM^lP&P8kNDf`}XI<

6XcDVbfCaAr$hl1t5x^1p3%ZWx!s5tyFFv9{Ev|4`$tN)-!KCNW&Ir7r%C zsFeRP^w>Y{7dzaR1dH4noaw>Y2I|xJ`+>> zku2X=s$WUG^-TWyci# z6$Ea%&pSet^N#e6(kD8LpYoTuSI-_gytlSgkL13o$UDY6meJB!R#LZzcWT zU6d7hLRa)+J~63Z{Ls{_rW{c`Yweek962B}J*{9Cx31d0|JPT$caVktc?^-cXSjkZ zzYxA&Jk?v7_do1E_c=tdPw+Xp3%aaI<+aB z@{hfhC;r!p{jUS%OXMRFco}YF9IqcHbCM}s+|A)kFZNa@=4GlsDyAgF>jRP*mHROZ zk<2ZxJ~5iS7{dz)!;(_d2lE`6*Zi{jrDqIcaP`*+J9ciM^il6YVfJhTb%03JbO*x&*W zGj;2r5$y1wIohHVx}X~#M;|003A3;aui#zq@KyJH?8HadjXn4j-{4yu$9Fi1)A#}B za6yQArBM~+R4)ZHu@c;--dQ2)^ZWW-wAHVKD!3oD!CvZH5d$9{LksZiuYNj)AQM9| z93wFrV=*2Q6EPXjVF_NuQoMu}SdF#VfQ@(!Z{jWN!zo-AqCp){!v-zE8V#o5S**bu z*bM5^;4qGWx-_6J4X8^4>e7I^G@vdG*mDEw(%>fk5~5)QN}?2~L&JMe4)>t~RMZ4L zZ5TjzB!L_nvY&>pf^{2yiSt4 zF_?~7m;?4`V2_4Hcn2SYdKx~*J{-W8_!`WF;UdV>Kzzfmpymc@&Q?VvHIJm`kw!E_ zAEY1^X&4Ol8p+y`tQ|QI3$PH2@f4oHYS7!rb=ZK7*o4=?%tx}{$PYn&kv|F%6@gly z)=^E-0jwRx+ED{B2mjjU;8O(Sa>XJanrgW4Nk#8SM36=1JM_G)CWM)qp_ z4AkFv6vuEv2vb>*o9Q7m2KkwyV1f&=2%~%Y&dE7V2RMpe;I~ z3%cQP^g&-xBTE{nk0l%Q$HGinmVusFIPNUuYN19JYGffl%NN)S=FoBg^uF3?x&=U_k9{a`;<=FNHz7x6PLgL8_NzFVoi z^-mtcOQSN_hmC#M*oV!4D41Y@4GuWb60Ok|?a>kJ&(;;)!5(eFkS^uhKbsI~17 zeiOo86Vaf5cJ^(b4)$$dh{fO>VrTDm`et7a_HJJddS|D1_N`!F_Ul48%7Gp@$kkzn z73Aq4PX}u_T7jG#)ZWn(%!^|nMuD{*<3Ns%37CW_n1(0uG@ixtcmX*eCkHt>$jR{; zE(xJkg$rFU8gnog>_a1lMhuPF)7}9+({_M*Xgl!{c4H6dpY}QEpT_)YKY-q87w{AG zwSK`BTm^m9Zr~3goORF~L&5sa%{T-4>v|B>#MK`|z@A;~*+t!5?9KHOxV?*jzL71$NgvoBdB$B2-G^d4cegtsCzUukA56|z&u1V>(OID zt)r<;G_{O=72B~3ALA2z26Bns2l9y~hv@4<#ISzMUAP-%pyC0r$C$>j!;d(SQw;gU zq+l>6f*FgUA2IBiHw229*YGCZ!WOV*4C}=l$0<;EcUiEG+W=y_TY$Z~J0l+bKwaI; zl$)7yQ(yN~P+#{9Jc(!U9GD|_4wm6{ya(pM%^bMdyPLhc*}I#yJ?xM7M+i?7M1op- zm~js??qQ!E4KBpMgIJKCryF{p7kYynJqbucGN^}VAO;~Ff+e|=DO$T@Zs$TRj+e2%@?4|0t?gs*TKKY-fAUI2N={*24`RS2JgN5TC0$j3)6 zzByoBAM5#;JKqXWCm(D3Sl7q8zBjNLUkKs93z2Yu{`%>!pZ@wgpf9Mue+r%e`|`6d zKeOe30j%L)4(jix{(j>6iR<42`rzLI`s-&OfqT&q#11526zFMS24-U(SR=3q&*P{N zad)E@>Z1V~!Hxhzpf7P8A91bG4jn;H;u68^#SK6z=ug}bWP=$=56#+xe4F(}68d8x24NJ)vl)3dn}}(krp?H? z8FSKX8P?)0Yy!ws1NVQ7`#(mX z9%~Jbx5ql73%a8hdV@Ya_5@zUo1oq;=zk0HYe8Nu$fZR$^aQtWLG4;lyB7UH?OLRP zW3@#VhGQ1yVgWd=T0Dhku>?7I2`jM%)U(Ag{4PXGYSEJTExBLIEnvT`s-Zo`U^?hU zE9R#a^V8~SJck!R+*a%H3SPzQID#`ci}UzNh}M-r-CDC&>xO6yBP_6kxoJ%gT2s5$ zZO|3*NJKIQfLUpsjuDWUgsGT;*L>p$IO%%*v2HI$d277A5p4zadHq^6CCv-&* zFb8e=fjza!z)+09XpF@au%|W)un14#Sv-#w*nn5@IyPeq-U0h-L*3g@_clM{3Vs8} zLz|mIv=u0cyTS3$mbq?Q0TodN)!;-7yx_QK8w4}mwgp;)nQqHWx9tLEx@|A?K?2xQ zTV}K^`)SKuwjBxPsO@P`)3(2YeA<#vJNDadGU#JF=Bynv+@4yuCztk>Q56q>y|-VE zl~@htw?j=d0<+Y?03$Ynb3}*j;2hCmrw|=EjyjTW#}4R(uJ{ILL7h6%?~e4l6M1%; zf@zq6*+O)t*PUzOAv}z_SPE+2nYB7^0<+b{g{Ei@YSe`qb@?2u)8$(n$M-^XC1%%W z@EkZUyE3cY$iJJ0Xn5cQId=9$IU?$oS%4Ul8^ z03JgM#)GKv}AK@^L;3&wuJ9+nr#{kgJ9-}Z8*_epQ zU>`l!U>#n;t9T#8?6C`b@R<-jsZr0i=!HH=0Q1t5JbSXwo>MUoPhc^~v*&8OjP;=A zJzo=|mjyAP-o09)4cdeL_F^V`QKw#Gz}|aJ05$8yzI#0h_TB4wyeP!u3Yf3Q>!T5x zAPQz!;YKV12%V$XW@8EH zZNf6Fz-l294X}egCWb)W6RCS*8?*ztCX#C+=YT|FCXNGnCX#33LOcoPCaDJM!UQWE zaDh2V@}e19pf%cpyps~p4=G3$qF+_eyM9d&1^U;I{`I4O{fO0%9Q%=DKXU9xtbRT5 zIEd9R5y|)&H-t#0Z^<=5ykzDqxgHvVc}tE2c_x!*GI=HwJDL3^w?`*X*W?}`b~3S( ziJeUB@S65D5WXbTMG40c^+%97VEK5hyf+R3=XJ>%D5loFOdYSql9tJ&4tq*cb?GM&TU5F>~44%UW_!I|l z2uE-f$M6Hr;ykWEUy~Z8QlmlSH>eC)f6#s4d@_i74N5{PsPiCl9Yn5!*yo_}n1IQc zidEQ+3-}e(f6(u^DMT7IN@E{s>?4hRq}4$a7!U>alhzSEK%dj7Pa1tr8wl!@HUwE< z_S4vF+S~XVC&7NwSTpS+eikB~J)~3Pbk;~`jdUks;DsMSG(!utLT7YAH}nPjP4ABZ z;QX9E0;9ovr88gY>^XfV=xzEFSd6DY{nM9$I;6i0<|O?UaNbDYh8>_c=^tVj4&oco zm-O#&3hX2OC+Pd2Kk3ZQ;QOJ0+6<;9gOfpSgHu5-22+#4}?X`lu}=74pF zu-*{X8L|cJbqISM!d{2$!Ke5F`@x=ve2Wt}iPNBOLoVSLAu{M&2K&u;5D$a=G8!Nf zCRpJBJ`_8Gi!icGRY;AdCFv-GO1T4Gn>h}nVrxRy}|5cvR)>A$xH{wN+$iu91i9t zb26w~CUwi4jhC<$JHeWndvO4Va0K7r6sS?=4`3!Tui!Ua7b2?!N`snZ-G>UOhFYi% z^3AFTGpJWq47{LjSjQiQj_Is@pnq8h@gr^sF_bljDyW32 zcmOr=5FP<*4{eBOc;G`Enu2}}ZH2aA?Vb5W}g#@cTgy!|B;@dNy4D`Eyu;7eTK_P@fUxHKG9;fqsryiVfI^O?X3y zkw&;dy+)GvNb(-ZZANazHf+cHLX2W>qnd#lj_Qam=#F0C7#hVsMzN1k>0lm4QR7kb zK<`I=jPG#)7x4?|@2J0o7=14)pdzY(yhm4u9pp5+C3@g-#3K>ZZS(*P!eETY1WX3? z8qM5|o`d;#0`z(`bs4=G$G}XDDTRk%274RBtc(eQJ&s{s#?Z4d>~TyV48#bG#3+mb zYmFiPm}l@jh(CtyBgHajZLzb;s#z{w2it5-0`MA72*C z^7y)FfJb3~5f(55;{ynRJjXK&}C91JOyeu zp8Use#A{$5xJWdc2za17`0s}K_fsQJXIcopU~%m=eP@d;4h ziA(S@n1P9J;%&Tx?br=+nMf`Z$z>wBO#Bk;aUwG?kr|kH0T=Nz{uE*o`AlL4CaGWs zCf$$fs0C(V5;HJ~UQA*JCfVTx$ND5U=+7kPc~T~Zft)9e!FWu-WH1+#W?~N5%cO;P z63^gy(6>oTu@211r1wGolfJ@9kn5xyLQF0N1vO9~Cd8mAI-(OggBnhbM;eA>6viSO zG+^>9(AUY-bTajv{1nJ#@*7|-CSMX_$~~xvDyW9)s0TA_&_E5QFfUVL(Gw{cfzcQT z3Hmo>DrSILo$?%B0Q;S?9IHStr>qBaHsw{kgM;`(h^bZa2-xS;Ht34p=!+!u#{jU; zsjNMf`cGy3sjNGdd6~+3Q(12+>rH(L8}T++V=D79m0C~z5T9coSa<4Se2t^HF2uC^ zK<}ruL?@8Lv>xaM)}J;MtUZl&r?Ky8?0Xt@p2oV<*5WN}#k+V9JFyGQ=`_}w##+<9 z#R;6m88D~Q&f}^O(@UTx0$|PQgTX$hv!ChA%yjBDeG^^>dz$_!n4jtFY5HCq0Wqi3 z*Xfs`$7EmA{}5tEDU?Q8u+JF^==+RF_`&{Wu%8)o!0gUoc4siVGnkhd)ON;Zu%8(R za0sk5gSyQ)j_<)SH-o**xQ5^Hrw}tEa2Hs2CUZ8k99Vm1H5kzaLogPsHFFKv-%M&a zlYY&lUo&^(6R^*jUxKw}QoEU_z+Pvv*O{z0s|<)ei+#2d%=2XVR;Fz7$2u;9x zb69VV15R`Rb3A7_Mu7g#8I5U}4%VGB3r}MeUdDQC#A|pH%=MgiKwak0`#H?>9Oik> zVSJ5m!QSSuw>duwF_+%YC6~D&v_~q&gEi;Q#R4qCQ=s2-+1p&!p3DB`vi@Awol8&V zve&u0um_)l8JJ5y=F-;V+Q6i1M}|2BZxy+Q0sZ4K=0>eV*}hX^L_*C&buMR{L-k32T&6afwkw? zLqjx%3vR?B0BSS8Ia;DM+M)+CK`!&D!Tjww0%|&+HRs z?-$YgMf83Vby-C37t#AgC%{ZEqCShxfqWNnoGiKw@?LZuf8Z}67MDONko)3tC=V5t z!5$V@M=jJw9n?o7G(i;1ut7sK*xOY*VT zBN8T9;eZQn#3FzYnxiG!pglUFD|+B@#3K>O7=S?-j7$u}NQ}XFOu%GJ!%WPfj<14A(aqcIK=lQ0!CFdOsm1Qz3IJck#s6w9#+FJnD6;x)X9xA6|P<9&RH z-S`BbV;>IUFuumOIDwNmgLC*1mv9+ZaUFl)FCm^Tfl?@gawrcKm2p3+qZVqT4(g*3 znji{h*q|XA9{3Q4rg#jk&=wug8Qstmz0ns*=#PO&!w_U)I7VSCvM~`;Fdefn7Yncm zPvKcCK@MKRO02;;yn;=518-p~-o<;^iCx%(&#)Ht;39s(uegTa z@uv{aMBpxzMp@hk1(i@051=LjPm|%qiF1Qhk077VvmS}_a=!CB5fyWV# zL?mMX24OHVF$^Oy2IDaSlQ9i5F$eRp5Kjv6{CWI@pK%4h;RbFBL8--(lDHf9;9gWf zMN~mG)WCyy7WEU?3g7$o(vg9o7=h6k z2Z>3TiW!)Vd3XYg@id;p3s{QfScR9d9vkr*-o)E@2ix&JKE!T(g3qxJ2XPo*<6E4- zNu0qs{D@1qjH|egKk%0jFO)zjltDR^hl#n; zCcJ^Suods(J?z9T?7?T)ivu`>BRGoV_#UTm78h_4zu;F~!|(W0h!-Pp7fPcn?t_9# zsEP+r6A$4L)I&owMkGwI!T}fDh(!P)G)GIcL3?yUSM4+!TVAikykK{eFCgLoKq(EyLa03$50!-*Jp;YSe7 z&;qT|4js`2-O&qukbr(jK`PRbfuR_I(HIAbNtlWmn2mXO0*mo9p2G`Rise{^m$4oj z@fzO5+js}t@jgDpZhV5zu@47v7+>RCoWMz(!8!bhOSp`yxQ;*Ymk>)!pcKlW9LhsQ zW!#VIsD;|7gZgNMCWwL=HfV^32R_81DIP;Bv_%JWMmO|CZ}deH`ePu{Fa%i`j!_tk zY)r%yOvfzD#R4qCQ+O6jkb{@75^JyyuV542z+2dgckv!}Vi)${Gwj6y9KsPC#c_O( zQ}_i}KrNS2%VqR_*v9`F6a(&T|v)Rkl%`9_)dtG#9B#xSN1?J^uYy?=gQx4 zQwT;)tRl`TYQHKG{g8sQU{9;Af!tPc+tp(+1G6v(^Fd8kp8>aDeI7pvv4;Dv83t;x zW)!&J8tSy>FCkuzKuO#U`tb5Qp!P5C0Q+0Z46UUOYnhw10kGG#A7VcafO<7*TSP{f=HN<4UW0>%*T52UB6I>4OQ?6>Y_dxfi*U;#s=2dz#1D^;}s`Z z|CMH-f3GmRuP_&{9ESe;Z$Pa#($9^LgZgfyjvL8q<92)sj?0aE!3@0`4Pw1YtXEs2 z4K`yZ=ILF%BHkwAZ6e+#;%y?{YaJ1f1hD37{qdU+uiu5c zQ3mBeFJ7OB$(V{6pjWS7!wqo!zfN!7=nm@n2K9VnAku_*votEBDypFd7T^WsU>Q~j zvH2lT_s!IOvk?|-!7h9Z_O+SZ-)e!bV6C?}?%(Py#M==l4+RxLf8Jh&*YPIa0>|i< zIw1BIW@L*M4m=Ik-@@&;tid`Vw$=yhZ?%EjY<1yz@cXT6u^thDnCwcGODa40Cv|>pMZNQ%Q9mO%60QK5Wt@e|{ermO!TJ5J+ z`+vrtLL3k%0s41f7$#ye*!Ka}JXjX@;{h;p2kG6xFY!H2fnFRuC&ZTnFdX#$%h4Ez zU%*}tMSvMOM7U!uR{s8$LCZEIH_V8G+{^41m?uVI=uT-%9SGB>+ ze^n36&sWdjIV?er5Jwt<-W+kji5P4Jy*aW6%*GMw^fmSPx)*vQ9*H=O%lH+);f4_3 zi~%$74Y_{9df&_w;wXDLN?(pv!TqR?WMqLl9;K&8sl(A7_zbLfbRP~1@oi@$APL;( zTWWW#4va8^xj#nk$Jqa|Cqd7TJqz}Cyd%iU-wq`*xs@Cz;8U+~=eM_I~mS zyojZE2`hy-MO{wuyHnKV6m>a8T~0j%{dcSJGS&-m+Jad45r?LrR;ND$wK~n7PE)Hh z?Dq`&J;Q#_u-`N6_smcD9XCNd*kMRkp(fMlNw&#g;z7d*W4(QE!VxA}Fd1`U~iVznfP!gq3 z2IO~v-(O(O3*>i!{C@0(0Z2s}hTuK0){pe#$NiumKe66VO} zB6EJRIa;6<+F}Ek{fpbM9q$WqiMn6n_m|p$-d>`{m-gT=j^G;{6XNF}+JoQ!+!@`3 z_=SD_QXb5}FBL(be;J3F;Mn+OF4*hknxICP8^Zu2sO9BD_zGX+TOqEn#ue7M!Wvgt z;|gnB*$39YasuDuv=F~ivtM7qt9T7>3UQTMT;={(n}U5`Z3*skmHD`O3BTZ1A$}vb z-<|?<_}g=!57)@=8u?u#ziZ@ojr^{0n`_Il605ORi0jPq_1bs@bwMw#^ZV=MaQzKz z2D5VG9#lm&F!MKRf#dbY%UB2caD&``C*R-OgZ=)_et&1bzf<$y&)_W1gZ%&KhfE9w z$L}Abgt*xRPSF3GZqUb@)am90aG#s(`Q~LI{-h6ove!S^>!0NCCo}gK`TrFK6D+WU zoc`L1ZD2P3dS66{0NSEGI-&~>;UrGu2b>oX5&S$N5&h5~1MwX$;%8jORS{8QBIaQM z7UD?}QL;HYqbs_j7k;amWVwmL`|dGmqB6*} zJoPQV9q(bMh^W8}RG=>vdLth6T2W974}sb#buk}H@FLi|vRp){^*~)z_N=mJmAa@; zLI2%Ktj1aqQLzd5UBzg)5esst_$jDI#l1KnA}UdnO5{+9Jy%Ks^H%A)h^S0oDwjc7 zu&2tzuKXI_z-DX_5mmTPmBGkB7KV$6s_eTe{jXXZkAQrut^ob2`a0eOb9cW99+21l zegv@)IamhvbN?z4QLP-Rf!V6YY*nK^)%Jj%R6Bxia7;uz5Qlc?fKKR&12}>2aSHUh zx&^FRoi(d-pXyDq2IN)!EwHcZ^s7dFaN8O-IN$>Ht3my0P`?_~uLkjJwnh)o&zkhJ zW?!&g%}Zd`YhJ-`BBIty*ogl_(Ot%QRjz9QUTf%~yBWH>yE_dOP(f@^kZxNf1O=r* zy1To(ySux)VPE^h_2n@0fA9Bxo;CaY&RH{C59#%gUWXaXCqs2=Qk!}#U@hy}$d(|C zk~OLU?ib~LQFb&+M^W?e^-+tEKVudOQkbI1ol%yIvSgGcqbwO^$<&3O^r8>_f-tk0 zWzIuB3g9y`>nrnPd{1UOk@-~+X8DTtbfh!gf-q|ioR`%;W-W=G%PM!)gB<24W|hsJ zW&57C_>63w*v=8mGu!{a=5!Ed_ssblS7COaoxKM?1!0cZBqt@Qu?so=!@lR3$!zqI z)4t~PH937v&a}8!PWQ_>oRRpRoMXAqn;^^;5J3#wEtg!m+%1>8<#K+mmqD1@*|~jg z?sz0*H9OeF9_&IM&sd(?)WuHZF{?a_*}x|Bk;h&0`kK6rXu`)d!~W*I#U0El@53O> z*8=_I(?dQv^8LU$ZgUs;^Ofx4(3{P0s1WV9bM^;PKx~;gvAR} zfl5@NI{Gf|uEpK8xcd~hTP1W^!rqtYg$yO^MG1YDc)=^|L&*@IUs9JPd()TxL0HOu zluAM}QjnS+_?%LI^8c?H5QL@UlZJF;AQSSJwlk&IvL3VWcS^!CpJ4uFn)3y^E^`x^ z{FeoWWgY}!S((eqTvq0?&MGT&xdfy|XXT>Ej60UQ#1*dLbIN~!S(X2iuW1>C70jZ7 zj1>}+8270#nDIM;l85NyT(HRw;QPCNdCNZCdm`|l;L0GviO|UbS z?M!9&tNc0$t3(nH`Kvg)%0G-@920PMRrjju?5cL5YF-L3msPA`9UFtNnmJc{hxf3z z)fypRHTkN^S53ZZ@>Ty2KO?HoWHx3|LkBfBn$JB1fIEL0C5z#VJW?%CU>%oa8j;g0S9S_`Z6+ubyt}nQQ&%xToJu59=q# zUF!S%`ueUv75&to6@(4UyFq2DQk`1-|9|eT4cxWCYV`k3YO*8uJ95915BGX!3e(Wh zJGy??S?}7-ceCQ_-*ty~o%QY#mazh#`Cc4!{GMFzr6D~(&=+02=T7ep9nNZ)2iY1H#J@F^vEe92qx%ms;@>~e`3Hq4%05nGb|0AC z2lna1@90W*KnmE5n zc`9NCO{$^GCX2CeP0XOlsv!KxT|O#D1u9`yA8lek2RV#vAJ^hT8uJlNxzC#*{6wBl zh}?S%#T4m80p}AZ+#tEzwW2*0^&seKdQ-6Q1!h z2tOT;UHjCoeL9_4LD<}`HFwYE@;9%Jyv>iH)8;;-`E_*q*^ij*XZ`t`e}eGyc-X*4b)Y-48-e(^rd`J6BLh8IEjWh~+l7rXT37s&o)dpgo32*0uuUzNp7 zzA}@q{Ji-}zh9l>0+*2U>#qDkZ~7wVH@^OxdNkl&8giT~T;m3}gRq6pTG*);KkyU3 z;@?_4Q!&lgW4tAhJ zXS#6{89Th-75eVzGdj9|NB8eI5NG=xwy={-es3-8l$9K;U@P0%$(|tWY!^CzhMnu| zyv{AK7o8s>Lua$^?5r;St&1#OWa%PH7g@TTMvq-?A$ynmLD<#Tbd{&;ul!E0AnX7gVWuYL2DM|^J;+&thunisl zT$&oxq7L=(bwB&MpM9O*H46Q%QTR(W-b3HNd`J^+VwS(?_ZPGLVCh@Wj+g8 z5`@1MrwTsfH)s7;n|x^s(vA*vqAQPsuxA9(F^8UZx~IAHv?o3P zWiZ2n(C>?cf9U-Wz5kJktW0JBi;&}w{M@G-}@r>c)%l`24SC?e1Na-qmw>9zt4U=+kJdap9@?L!oJmcA79(o*Y^F0 z6I|swy6k%=2>)!u5B$V0xa*%gu@8RdApG+bI_uY%ulNS{>DP)^LD*kE{o`Us{4PM) zzY9I-MIZVF;a^!ONMVXnf~9O?3ugY8GrgZ5{{08N>5DxdkPY8A!1oO>*8!!_>j2;9 z-S}|84t59OzVw_9S{FCzkjon zlRS9N{vE~$JWv1H^+9=Y{-DZKqb5FYkR2Y>2%kBqDL!-1=a}c9pP7Q47^KTVm(ly6 zXF)hP1_?+^GUOPX27L{dWpFNJ8Jr*e4R+Sx_L$S)Y0PI0dKi3|GuZ9HmoVqSH+aYs zp7WA7K{zBJE$%Vo9rQlrSNicUWAXXkj}M2;!8t?ZAEM78>)6OX-JY*m0 zGl$AKRL4WVpe^0_0bLCJjX(ICe;CYA^z41|(EH-yP|w8Bn?dOP?a+JM;V?Z6D?&wT zQI`h1hy5Pb4DXOD392s8FhZ#cq!k0?e-oaJ5K(7U{$ zcX`7R?mEIKF%6xmq#|GIiK?t-(Y?tr?V7y9_g+lojvkx5RQsXToRC&WH@V7MzWBNoaDxt zquggycP20svmdpQUF^mEM%n98$2gC@9(9%L+~Q6U{uhUWl;>;O(1Sntmr;yCXa7xN zD)TVI|D5;VGFGx0=ltg$qx~!!Z62e2=4ic)ejh!IZi&pJoiVySo%o4g=}9m8Vn;?# z!Ox}9Cxg&CyP`GCflgLiJjF>UEVAO68} zGiDeg8IAMCOlKBzn2$S-*~8-?94o`vJlN~8<*1Hxyn`Bg2Q~B#YUmx*&^xH%*miWJ z3*Gnu_Z-_B=X$p^^iFB$ozifuF1=G4dZ#q>E@|jp(s1l%w&G{Q*j*gv3^#d%XJYIN zUI*d005cq?pK%GXH{+6FU&lou^SI(vp)L)05B+%GGxVNk=snNSd!FGq^BLEQ?=gpQ zvUwLX9CwAgybQwe(Q(H3G^EFFj?Y4N%w>FGicx~ncs|BA!u`khXA!G$zww7S%{eaO zoblIrfX>H1!#&5lr}qp)?-7O*%zA=5c&{&<@C$NI7{oZHGmE(_U@`JfSkET5u$`Sa zYoa}!m=@hk)Xl^u=whPzP3+E(IAh}P^kM-2GK67_6qQQjY3) z4!p}3dY3QsE?+oFCzC#+DfVZQ{h8$KNv-M3&-BMxlZG>jG1#3+c4v~^nPfhb+;ft9 zPLg5LI^1v40nTy_J2UAH_j$zYAoTx+3@1k;2C<1G8?sH7Y4UIM!?}|upwG$6ScyI- z>(e`h;pE-y;~esZS6>Vrw4}RiTey10G_=nLz z6Q1*mH$gZ(BpMlT|LO1Jp3{5s7sHr@&!0Yzg*a#WayGD;ZRmdbZua7=8M>b#`-~dM zIio4x(2*bc1s%@N;f%igi#g61&PYZxmKC_ijJrWN)9%bP$C>#sx0zL`htHoW_skFR z9MAlmuV}%yw8ji)+LxKX;=Gwd@%b|+Fqvu0#QbJ1#kn)>l=nhmrQX+(3rz~0T0d)9ZzJ*zXi zn)N5c7{^4WFr8T}W*IA4%{n%4ItXV+N1oZGsX_yEG}}3|buzm>ozTr}bDwSQvwP#b z+5H)S{hmD%=gzhRv(0|CZf5IdwsU6Nf!TZ5&moR*oReJQ0eYGfNm5dvmpS%(P82!G zgZ-XUkRs@6PI+pgr#a2AKXblCFLT<`fgkvp-;jNd>~qX_&Lnms$J`KS&vo`(XL@%x zoLd-Y%q>Y7oHe%wwXsuk^)&ZA%x7*>TG0*X%>9Et^y6>LWv+haj%F+qaQ@ucEM*hB z*~dZjGxz_wE^wJ^+~78MgK(a#^9oS`cb@02^Sa}1^9C}Ap^V@^?C-p3%*2`V=CJ^0 z&U+Mu^CQSj5z0~xcboq)dY%6nU*fF!&YItW&e)0hc4GdIcuwa#d%io)w|n#L-h8_^ ze=g3Nzmhe0R_1SH3)`?0^G|SAougT5A;-NM&F zxF|sOMKOtk?2BYyB>SQ?q$i4;6vjR;s)^^u``+QAMtnq5zNRIukbRNti)3H)1AWo+ zB0pmmO=lM7yJ!K6G2cb&vCoUlchPoqzvvJ;Uv!0snCqgqLAW>q^IdGdixZQKl%ysd z8PNISf|RBP@9;kQUaarM`d<7MEztMk*0iNPc5`u0{$eD4J}sVt?2BbzEc@bR*v-YV zFZOeBvE5v(^TqaMv3*%=KNjES9(lK9x0B|2Y{4LvW>^OEvZ zqAKz)k$;K&OXOeDn2*u>lCF$pHfFr!3}(ES^g`v|$)#wbXr=+Nq^$u~SRkZ|OG7W~tdMb7;{)=XO`XPMG!7` z*X8zNxjk5J50*P;xpS5mqzGlHKxOp1T))d}@gB}w?yTiLd--qtK_5JK%l~E=BN@$D zCNK&8FW1#_T`k{*Su8)sNz7!qZkFq2`EBgP@&`OdS1ZhDMLf)9g??7#AwPvEN(m}b zh3eF#4)tik0OVO=J}d0sildyvSu37k?^e9xZ4j=EAT9~e(Mq#fX*MhE!^%wPYNd`= zI&Y;;R@S8fx>@-F&G?Kj`I?q|M=x}+(kxb92*ku@k+l+Rq_Qqn}lB zuDZ-M%x2X?UI*doNRpzL)pDL#yVb)P&m5Mrg4L{JBlc%E3t7T)R-yAX zI$xvnH9B9jkAob>bFk(HkAiS*NF?z|gud76d##?=+K;vNW3Ak4w0rtzk$t|?|RSD`a>M$IHxe*^>VMj$BQ7` z5DT;2V741{y&*N}Fxw5Dr462?4f!aD>>E7$8>*q_4W6S7U-Av#;yK#TmTvrjxo(hs zgX|l6F&?=#=yAiFAl&HejmgM>b2jQ_qh20G~OkPvg-oSgy`rWl@~&E~neDm9REv#vMmdb6%K ze}tXf{2lgivsrF7%gwsp{3p8JJd_dWdh-~}c=JSNv4nN(#>_V#=M-nTz)kLOpNBl* zSrBfqi(Bl#7Ts;JPg}a7t1UX(;;bz?*)o}F=w{0t^s{9Jt1;Ux8`y-iw>W!?Gq>ny zi&<{@{~0fN6NFnsB8g8T%yw&XQj(Fp=xS?a>QEouY_*458_^scZT*@S=xS?gI`a#E zF%-GC>S*g&6r8bb6}s7`n{Auf#(oZQl;fDsHqXJftK3IV+hy6Fm}KZ>dm7SXpSI^h zN89sLh$585K5c)8rs!sSOIp!}_VnN6iOgd;a&MP=yWHF5-oA(Z z$i3Zcx68f#B$v3$t03GF1G#s^B?0p9kb8&RJIr-QX0nk3J@1ftM|Ixk6Lh`fbH1Vl z?a=j(E_CAuQy++3DHa z>Fk~MX{Y(_^t|oV%}&qTPG{^i&z*K@r(N1Pg8vwU=WVC1cFw|iJGZbGpTF}k$I#7A z-R!)|4Q`>Qo%e%qS3JyYS0&77m)Y!cw_WD3YY@)aHI@lXW*YNZ#8RBO%N=*EVJqgb z>mttB<&0eq@XYP<%Ow+fMDyLOx1U1+&;)i@G$RF(1yhr9e zGVhUjkIZ{y-Xrs#T)cxkdvvvD0M6bsg}E$XG0Sk)9%t>*&z>FZVlVqS!Bxy+&zm6J z>zuuj#3vC+NkL`8qk=J(c!+&_>$JNr30PlitPK$ci%wFcHeaLyHDnQ%aD1W%=`4aPv(6x@6+!- znfD#yG}n2++aTN@o4AGt1*nhu=8y=`bJ5yGIB>N%R53OYz2RMcP4n5#8&v?n3AUqt4NaB-_B*=d_Jvk{%1!_`<`n*d+n$jF| zJuLfS*$;n<-9P*zdOkde{}_vz9-hoJ^n7>`OIgk;JOhWfa)>kBhB z$azG!M?61AHnD}h9On}Ek@bkIM`iUp-r>=h$a++_M?E`7?Z(kGq$3-JC`WDV#8EqO z)J`1zgiq1yQ9E%|&ZBnXsD6)j;wSns1l=B;$sBZhRJTWUdvqNek@M&_cCwozoaGje zu@gt_#8EqOEW}P6vlGV>A@?!4kI8*3Bl#&!4c*O<($Bw#SDt5j`FE`Nz%h_*QnHm*e{|%j2ie&++q^>+vhx70MA`5b#vKOcHe9B&&YDgnKqAAVste|UD_Hmq3oaF*`>C7GO^N1%r=VcI{jZPx$(^=2J*+LYh z1f}t_?W|ooTZ_8X=Uv`M{dIq?T6fFzusj5|WN=$b2pj`H}sc%;#i2SCPt8L-*(2LEdxU z&=GSzXRhbW^;|FdVy@?gFdUiBjYj5k(^$Y-wy~2v?B@`tImbohJ}37%xz9c1br7D9 zjpz2fy+5CctmGg!MJP@w?8-&5|?8f9N?7Y`u!#mhY4C30U1(f6fT$bCufODRZ2 zTJ(QO@0W^Dj@rn3N#0BHUXu6Hr^tIr-b;GFB=4mTbmAv;eQ78YnZ;Zdu$X16N7hTS zUXt~aJ-M`p>p^%~rppGBz_a*r25ctt-~WVwIwcoc+J&Gl*|_VKFRSLMDc_thxu<5jt@<{&qDDTe3%YCS%r37=rL zS3l=FbbeLmS7pB{`_-=e%3qAce6Q;H>U7NcsySaZ->WNG!#Xyi_p7pBJ;`P4ui43KcJi96*VbbvuWeyF`o3mIuAN55*BToNGrb=j{c zCj)sXNhPXMgWBl(`iJQIy1uVB<1@ZM{_EZ7#Q^NZ^&t#r6qA_B4BX+mJ6xa73S_@- zudkm#=IiIU$QACQ^Xrd!$_w1#Mhp^?4*PM#Uf+=WMt%xWnsQX6GSzU88}Fd^8{g6a zJ>QV~#t-QGMj!g|Hv`fA4fnV)2KjHud_(6qwz7lW?BgJ(ILietah2=He?#V*GT%&s z-MFdeo0-T;4hm9);^_Wn8RWlNo%i^Jmb9l6UFpG3^yW|O_D$X2{FlLuW(o^g#acG7 z8PDZSo!_)4H~lQRc^vs~o`0UeCuPH zVMlI#$v3p61A4#Z4!7Ll){oemTLT$^%(v{vtpzM*87tYuR`h;r7khDsTepJnwp_RM zdAkvB*>{|M=QLNa zM|a%w&Rzb0)=SLiZb&p@5}U-N#a!+d!gFx99G-)_Rj5t_baeLv{4Bcr5uc#1yE5O^ z)m_iPT|0Dl7$ecmT|a~FPRH|m*L?2EepmLpOR!saw{w8A=;y9}?%v@(k9Zw~_X6a- z7Xx|k*{yp?Nl$KyP@Gbfr2;jnLw(-iePqAaj23tv?#X=5eD9g>J@dWyCxbBGdv@xc z`Q96YJKS@JdothKh&{SzkM7BR?=Z(W&n2#+_j`K3=N|W-2H|~u-lY< z@>HTKHPF!m9X7(ocCZjqtMd>Jw2F+J$*2TrL1E&M>x(Y z%=*Cv%=&?j9+>rmhdkj~5I#(b9eikp58Kj>-{^<)9*$=cQ<=eR%;w>8R|$Q>VD;x;dW@Ua|^&Ej!dGLVU^{CAHOU(C~%+GrA7rJ?-n`h?vYy=aS%rwmP*=*(__p=S?>Dh5E za2dTkll__O&;Fm;4nBL$+aP=%L3Ct(o`$TL=kwB(qaszPP6N#J`3K1UT=wUm@D*(^ z+vmN|^Yec(%jf!juJ7lQFw^JqKA(lW&*!s(E$rhoSGdkC?(%?_ya~b=A-aAM6J5VZ zjLa{xP!K)8C`$z@qwg1be(^4zofjX{gpaX{FGe$ygPh_9PlE8JGhW6cAxTJ1Dl(y; zm!8v?xyVa?N>Cnied(N+x_POamrb#QFYVyVRd}$wF>glDP zUh3(keSEou<*Z~4>)4LWFVCT?m%4hXtCzZZ`50Zj($%Zz=;~D*;*yLEte&H`kAwT=B8 z;wUFD?^ovi${k+0!z=sw>OK#76@;(lew~6SGLwy*$p2dY*YdxX|FxOFu0SPf^FDfi z-4eaO*8A)BbVB~uztWRl^ratz(f{k2c#dALXA@i5!ETOl9J})R4ClFs?639y`fU)t ziAQqm%bT?5`%NZtlMj3PrZB~j|Bc??)I!g1KIL;fPjB@7<~uslh3@>o&-{x1-{}2~ z9ey)|g)G6YyjjIsbpB>1d+>a{ImlsTe{+*(LHIVrp1h4kBz_LO)%n}hq(lC<^1t{o7xhcS+cOkp~+Sd8A^+LgDf zS;q$Ce|wC}*pIjNTkHHLMB%>L}bY?LZ*<;8aL-rW$j|g=2*Vl07{)UL&t%MbEMPH9S%;ou zo{DmEfH3&Nr%Z|jdBe5nimFdVGOUJQxa1A}{*(w|FDs=s8Y# zDq+9lRL7pgkv&c`zQbO`=|XpYCVC0XZ^Ek8c493~OX11}DJ?K1+J&ALg zvs~a3vd4K5L`23y-pHh+zdWjg&W1-bi^P<&Bg#Qr<{;Bjt^hH&WiX(Me1ibR0J$ zS#(Flf6+0bw zH%BqImzVs=9_|L!h^Oax!x+yTma>A?tYag)*vkP9aTNLE={nv`o&^!{Lv$VAe#DO? zKK3JiYV1e+49Fip3v$QTdHnL!p&@d||A?l@A75^N?>QoV8`{wk-N*kKoyWJ+@y9Tg z8O&xLp2hg9Sjz@@Rr}L?nB4Ip|W3~y?qN9X5 zN|>8`c!m-dMpp?-P?5Sc;d8#C1>f;KUFeSaCe%~H-{{F-j9?-1n5`!U~yM>x(&WKMV=9VLoQVsw%yC22^HXDLxG@{*rI6rnzy_??+7Vm0QG$T^90 zljs~5xq`VQGM7Y;F_%Oyc#WN#cPFVkl#oz+~)HVmp<17K_=0ZW5nB=EO25mN~J^iSHtFVwn>^Mdrk> zf`}ygNn*c~$dtsnNq(d^&P+0zDNM&1N#?SEm8@Ys8`*-L@^`}{k~llbdG6qxB+g0l zI`F?T6ODKzBnioIe$v!rMqf!wP?dVT!~59jr20v!pQLsvsa;C?Ev;!oSN>oC|Dl_t z(=f}VbC}NxRNLPilYKyAKITilLEdDokT+R7x})c0gVA%caZE(t$@HB}&&kZx-&2oBCVMj3lX(u3 z={eZ}WKL!elRdypli9;$c0bvhAR>7zB8g8zk|2Nb^vIlC=H%s&Ie9J2KDq44-$Ul) z&G?Kj_!@mDmpQr2$$w`cBlr*dn0x{{Pp<3ax=y}`B`n9TB)6Bz_i&O+T;&G0xyN(# zo?P!KLZTBB*;D8`MJ@_cjFOb0JT<6|T}dH-3i(qsq$&DN(TU&a$KU*ezEccC-zoH+ zLfG>>g(!`l zQ&yx3`c5flN;y-0KqEfl6Taemy3muq&~r-9eafMXK+h@loKoJDGmtmsTL$pXTGy#tAbV<^r|v>`e&lCQa%{n%+g}of$FtVpU!6`0tho?bAn&`wQ5lKlwYBH0Joa81S@~0_DRqEq;O!Fn* z@GY%rOE-Q%-ZZ*T)018dU<6Z`%K~(r#*U=%ET-ASR(7xp`P1k=jofK;p5_+MgNU>d z#2_|t(RtdGq(SFtqmVysR^(1wiYmNA6F%WnK1bha^_;dH9qCLrdSFM=>NKrANbBcs z+E+nDx|k#+9r{V9pLBW1Pa#TEj*3*K8Z~fsI%lW*nl`kj6ZSKmv(o7(o&8K_KhyPR z0RJ$ONz7*j@}x84bY`4x3pz??#_7yBof)S)!6`0tmuEpl`WWaaePWWKtMqBeLU#NN zOYaWp-64HJ^p#%r^bKf?Ij8q?H~nXPi8-e?=ky(LhxG1{zB|7&kWoxS=JbnLitOoS zPcL)&?U--+J?!ToGN-@7JzfV98DbKLcqAkVX~{q)WX~Xb2K{F!gw8YQIYUF5(wr~& znwIE0gRB{3%^+)rAF-bq`Y@QW%wjRiSjigJV;?i@W*@rFpz92}&Tx*~JPRVCbR4DQ zC>=-XI7-J+I*yVvO3o-9N9j09$592*aa2t{K(|r4jnZw@H+Yt!bQ@(CqwHc-4}Rho z`Z9zu%tXIY`i;_W)N1TT)OPe6WjCVq8+C{yoaGje`G4k@ya^&QhC~vdMCdwWa@-+f zMr6-emOzxjv33}Yk{nZk5*pJ@*BSk5N)aFR=0Mea;;XOcV9b6z2LW_@RlPE71} z=5*wsDCMY#+?nOhEO+Mj_yD;xe}uj>e~O+nx94a2&=0vY%bi*7%%d5H+?gjcjTvml z9%OzIL}bxbmOPZinOWYU37_DMET5yBEZ@_Pj@ZjA_A*Nkdh!=K$})-B=p@TRmY|y~ z8*pZpZR}t-b~B59vL+^q8aO+v`(^!xw%DJn&dTbntoA7DAGl{$_sr_dtOK!ES?yC+ zXJ?(y3hYnTb!=n{&dPd#!yM%Vr?|`=UIr1_%p+SYB8g8TQe!sRZx; zLk@Syq5mA~kuS%SAi{s|PDIWe6s0`Q%BiEAI?8FbIh&%ZoMxM|6>VroM>?acoP8OJ zb8=2#GSisJB9^j(RjkGNId^c7Q(Wf(kNAJ4XP9*^J>`l?9O9Aydz#CPb7iL><)}_A z>e7Js_?TwsD3>|s`kEH}jv3~%ce&2t>|Bp|8${%`Pq~wminQn^cP4UUHo2XbyD;`D zcL^%u%-kR1jNJOkt)JZO=|oq4#cXn$P42$*=Wm8#9=YeEo7}5e$40iWmjfK;C@0WW z?#n#jRS=O!FL{z6a~_%V$ec&!Jm#54<~%azDL`S0qN_YLcn5RMqmw)>kvosvdD>%_ z^8AF{d32Sh7k!wBndLbaMC1){cHR_t*77zd3BX{HoD5YiT&s#?`h02??tX~7yaaY%u`;Vt9&Vu zEni*iZoVHek9_~)jC}SrpMA|Y2Q$fMZ}Y9jO!94HGuzNXK6{(bx%r%z?;-#HeEX9> zf*8amG0AXle*2m~9cGi?Z1NYPG-i=sKl$~O-wx$(!Y6!13%;W@Z84kte<4@?4cx%l z`JG+BED9vS9u%+#1?)iqXBBW(0ees&A7)gbFhwaxP0XY~Gd|->zTsOs&>8b7U>^$n z#4qTpz)8|D=3D4f%(syF7Wy9ZE!2Td z=(_xnZWKPpMXqq2TRi3&FOj`SKm>6~MkaD&w~G{{2*s&DWvWpF{THc= z{6%Cg(h8Z2bfpLSF7hjX;toXy@-IUe#yIRuk)_z}BD>kgL5^^o3tUF;MQ(5#KZlCQ zU(}u#jZYe~keyuQ#WPv7BxNX1Mf6@&_M-J^gy*toYwSqT4s@m)zwrlskiF<%3}84D z(0kEk=((ueMYpnp-N;>3?xLqS!+G>yRPROgUi57cQ7j(GNkv*Rpz~t6$%oF16{Z*^ zkiD2)DfS_HF7`QJVMmJD^I{$8LU(@PXY^jot`ysd-7WS!h$tR`85ehMapx7!irp;k z+~UqHUYt^tMQ6n;p|j$3adz=9Xoa5z#oN<~t~jfBPkPgr{`}1_#$!i|FJ%R*S%+N3 zcd?fP9O5XtEB+{mD4~}UW?AB0JQpRtp&dWroD$|z!dyz&sS<;5*AinfpAwUp%5)a6 zmhG5LiL+e5PL;4zC2sQn^Pcj8SG)}(N`^#|l1${M1f}sDmo$@-x+$rfk_~u|hBTrH zW>nH_O6sSieoE@6q}(OtE-81(p^QN8l4H4C0G$zDqKQZkp) zbtzq!8p3c!B6F$PEMqf!uy3Wzv(!;ea1lR$N?k|arS2eqspmmN>1ZS(HR*^VGuhC2 z=|U96Zk8^E{H3d6FG@GVUX=cp*0jZQT>1xo<~L+7t@qM>`4@eco{qgJy%@PmuVf8d z*}-mfU-|&2sE~k+IHy8h3gGS)>{|sht56wx zR>90Fd`fdZrwwLQ!Lw1pepRqv75?Ne1~P~VOlBH0nZtY*u@s$DFslkzcoalbjD>kr z%z+tHEJP99zoIM^DK>rn; zU-3G(&{;+MQt>g*co{@giX=XXNQ%9wBxj{`n0+OCQ>hGQRLP7g>86riDh#!c>EZj~Me5tU=$d8(|F$~viRK9$X<-c|A=R~4D6cwVaf$8?-iWj6Ehc~#>O7d=+>c~v`OURD2~H-B;*v#6?r zsyDbDL{uw9b>5{RjrfSBH0L|MryU*XLU(>(7Uo+$CbCu6N%gKcvw9z#S^X~t@-Jpo zeH;^+g1)LRW(BKQ!$!8Sot@a1>d%6R8hWY`NjwsuuNuioMH;eD5;Ln|W;M*LMlIZ> z#+Q7{^lQsG6Egf7{g@TvBr9ia29i`>Ap3yQx<2`tW6#2;k=qUtEsb^ zI;*L(nmVi59nVqCUoe-NgBiw1JV!P4Qd2KAr!XDo)|`XgspZbKO7Jx@*4iIL)Q&}Q z-odPD>%R7brO-D%w!`c`6z(>tW%V7G(pxnI87%LL4~?lfjFA05;^ z&2!!a5%qLjFOv8q#2NK+kr#Ka=g#$tVejgdp(b{ro;|47fOly~BXm-)8#2^e#f2cE zzU=k&SKl1#m!d4?sX;C5QGGM4-wz$t|A)a0!`{>%jlHRFZ|a+4{prlY-qc^pa`thY zlmBb!KBKd&vcCb}-uAB85xe4GS1bc|QS4GeFB$spt7$n%`D_iyi$S!=$8zk{%b%xbu|hKy=F!*evEIbSgbJ61zZ zH8x=vYV5&GYLsFgHE-uWnKd&%M?r}znTtoaMSago3HHwbHm+`--4 z%l$mWqdd+N$f=fEYstLU7no_St&{{|?YpSX*O+PTRPn zPyu~J*qaEMM7S^FRs0T#FoTFsFv|$DjQAWmMYN<9?dgOWMYua+2xb$pnM6`Z$4*D& zQGj|P)DxlZI+t?`w^9zd*3owzwbXe8_0%z=I?wYWFY`Wjtj?#@Bm#4;vx_uxFz-5g zuj8INCos1g!S}OuPTk`&j{35FN17kQtu$e9N`%DrJjAM_rLF-!@kt}4K>&Mi+_W#zI~~G zCFWWGdTv67_1#zBF4Qm2eLRR9>sO=_Pw^~vsJ?ew{}tZi9h&eJQRuh6KI@MqhV^XZ zFtV)g4K#3H1NStjKy~EUpcd`vPERH?2RSuJLf;Ltg0P{Q8>+S8o%miu`8SkN!*NV! zCi(n`Od8$8!#qkPt!Tq?;!t0s3;d0I8tbR=$Jmj^Q<%$qPV+0j2Vs*p`2f4rWCHGN zVx~>x-$d?Beh$K>RjI~%sI95mn)+PRqd2SSnIQc9MPA2wpASW5pN}V%T=en9V?4ui z^kX<9IY0r0K^UpO$R~N4XgbjqSw_k-auD8Eq+TMIq4&rYtYiy&*+&ZLcyp27LZrQl zlwqX4BJEY=Pv|dFe_y)u%TMWpGrzQpU&^N06Qx!A?clsVF-Y^%Lj#h2OZyU;G<{(IHo2hS6piE$3)CN6R@{&e3v? zmUFb6qaUFnm3WG0c>$S6|Cd*hcl6u5%lmx9r_>~ZdNia7_Aj~_UlC0k+S7@ybf*`6 z8NgtMVTYo>VH^{g!gOXamv8x=MJ#0nv8-Vo8`;8kc45Dx6WLEH2go9q0*W}yQBF|G zY0mO9zw!r{_$LTkUB(q$&2`+!E!@VP+(QK(;9(x)KRn4ZJkN`~LRDVpEviwS5BY=| z)TS;CXw2t)NeiNAO*=Z$g|F#JANn(hp^RWOW0}BYrZJN_%x3`$S;BHwvYI$Hu$gV_ zWH)=+M+)g=l0!a)9O6fgQ$iVMIL`%s=TH6)!qx$oa~0Qe12$kmr+oz$|_61xF!VV!DY=kIH#j?Iy$H05>}&!j?U@moQ}@vbUn9oCokiSPHz)UC-l>4 zEYq05dUmjj)BMWsLD=~L{(~LsT$6@0W(e-@JPvnv-pY1P;Qr3`yUWeojTv^S!aKZ6 zGxX7=9ct*JhAwL8BG)ck@V>e@r;BsCIH&7vm}6IS?D_`p^C5cZ+J}Cuzznd=W^sH2y;^z!{)GVN8&NlJsT zcX=McyYBrlzSp}R>hJA)z183QTfDd4%aK8E-|u}m2>V=zo#=B1RjI~%$gt1X$gs~$ z^xjABee89gB=++&m-ssf`^vBHvpi1|z9Nd@jAtTS*u!4*-dFE^{|v%@4Bo*8uYxa0c&qfSC`}??C+y)bBvw9q79QeRrT)4>aq6 zQ`w5U2PWe8z`$Se{XzHhIPM?x5fM0JkbVaZL%)NTu@?6a%I8Op1>xW;xrtj)_u$ug zlkv=87Db$(BnXGdbVy}hqATtgqPHRIG0P#lf^euahdOhpGl!~Us9uKZW#}~K^Bu?0 z|Il+mILukY-ry|;q32;^*hD;LJnT{s4)-R8UxoUHe}GSnQruB02|TM$la)6lp02q2gIZkeipHJ?NxlG=Q zxlFm5TkzQ_pA&^%r^sQ79Hz)&>gC*kJEwkunx?vQs(PlXXR3Op-HBOEdkEj3CaY<( zn&#)z_L3BY(;uV~_G`MVrw_)SOg}&Yg+VyOXJ+`!44;|dGc$Zu;cX*fKjAtUb9OlO$oTsmO`kJS&d2*T8m;T6So_yvd z1>yWVxF4UL-yXA?-xIT(Z$IYC=-cOT-?y(a68C&NnN$42uR-|Tn|y%Uzw`6&W-*7O z_+9zknIK&7A+@Q)a`e4meGq;xyYKI&JRNcW_q|!k1~zeyi(CrAAF5J~_n5|f%;N_= z|4@d#U-%-gqppQR7{fSQ>Y zeX$%D?+C&r<+zvoh(ZrbI<+@EdR(f?+JBHl75MP*bcdW7QO!jXYxQ%c=l1uDXhAxdC;pQr9YVty0%2b*(a&Rh99- zQLFyTtGvP6yo>r)eZ;58f0g`K^=2-~$bPk+SL<)J@2`%aJtLXOEPQsg_r6-ztCz6> zd9B{W7WA>&S*z2?Kp(5U`8AiJ-Zjo%Q<4Ah5^7!Z3RS3%8rOV)I@i>v0rFham}pud z%QdoG(-V2F(Zia)jAAt3Fcxob%}nMnk66~Q4mGb)@7imyXKQ7*_7T*!wi5PXtr@TN zPS>h&t-RK{cWrlm=2!mU68{8Yoc`jj;A*bpMsDFY?&KaS@Bk0<7&3@^5_iS9E6!bU zvWTn7>%2uZp zZUU2$S)9z`WESU5#hG*5!XRAt5RdXWPw+I)Q5pHHtHNu%iTAM1dsz1YAM+Wt@HW=f zrx8twq&Y1y*L7yPt^=LvMi0z(T|WjggyD>04C9%^RAw+6b6)oyKd_i(#ITCBtY;Hj z*+D!BB$150U6(;Nxg6sJC6oo>`Ulbb`e!k-^=7tS?dxT{UcKvk(U;Y1XD9Ah?~e6$ zWy3Ytj}13)Gw&js4H1~>1~c8Dw+&{nK|dSj@+~i`9?F}sP~QA(C@}w?BUNK+;lb9ay>Uujhfi6O=h*ptTugvY&XrqPHdWwY&QLf zY&OYeQ)v)xzMm(N&1Ttbet{^uBAd;!*=&}ZW0C!4*=&~0=G|Nj!Yx-Jn=P{0awB$V zOATbRMK)XNF`DVfW{YgLnDv${jv$*Yve{xzTkqouWV2N^Tg_=}OS&MNt+Lr_PFq*9 z1=(zs%~o^T`bQ9MyAs)Klg&1B+V(b|A)9To*=A1LMllW9Y?IA4bJ~{4VPvySHrvc; zdj%>Xo9(jMZcf|3qBFADE}QMeWwXrFmEHoIiA%ba$NU<$I?C7WI5wCe!H$Yz&pc9~Orc^*eL z@v@0Gr}*Y{L^koVi8rVCj-jrwG~Xmd$Q++H(&Tk@lZ3&FFw^_Q+ zk8JkJX0JKzUCMf7vsX5I&1vs1L6{hzmPENE$|dmzZbDy)m3f&eyo#A6$~>_h&Q5f8 zqFxjAn%EOHC#o~iJ|wPWHSS8>OEP)LF!9eIOp-xTd7eZ6NpDe&>U@aVBt_w#BzGja zBdG^t7|%o|F_jrCWC`ogW0Jg+__RDI&toF-le{CX>*M4)~- z`ZPjT$)D2;^(D(DS&hk~na(Wc@-5#Zvt+YOR&TO;lhvARmdPcQVV22fIFFiB>{p6e zrkG`l`cm%X9xCtv4`J_8WSAnulu!7K=CmZ5R+wdqS*Dm}idm+7!#E}~1+}KkWC3bS zS;8`Q5YHYGNFteh3OU4&97oQnYE8X?n<>ZbRN^V*nQHc_W}o^J@9-Yxo>~|Cm)aP4 zrrLv4wWbb5rm6Bw9m9A0z#_DnoQ`MNN##A|{dNZm224Pyr z6Q7UD+E+x;n(o+(w7v{rFhj8m zX-io_EUVeeK4hDgf;~>N7iou4dz#wQe&$#H;1d4?VS2!|$UI%<>9?ZZbeX3=iQLoG zp04)vH+dU-oUZP4dz@~M)9rD3Q@$XYHngWB>P;WPC`L1u3Cu+w>2^BZEYkIpzMMo3 zAp3OLr^`P54CncU3;fQXL3rS5uH!Dsa~}`#2#=xG10V4zHIefH*&b+3TiT(<16`2s z0r?&n&m^WYgW0I{z$W6c!w2l}f&Iw#z)@s--~^?d<|2OuVTN4&9n3I8t{LUH1GQ$T zF++_Ra?bb<-e1OByhAmr^C5nxW;CQFtx&VSiy3BgrYmaAP-BJ~Gv*=B48O}VHe#PM zwy>RD$Sl*YX38w{cGQt6vrL&~sweYd-r#*?mMOE$8ni%WneNY&Tc*r1WtQ2O{tRLq z6PdzvW-*6_EMYk@$S*SiGtNvRnKTM1<_Kn;Y1WzMocX_+%{fz+S>9FFLp;jkR6@O3 zW}GFntanj+mfExG(+G8EsX0r{S!&KQ=d5n@V<1BqhI+H+GM{hxo<*oDtCZ86|gkei$R#9_MB_Eftx9Z-N?~P zj$U%~k>efZRONNlpYs;#uX^=M8@TA`;LJ>_&lFFAb~fIZ2X#!O~22Yuz}D<_sU ztV2)!c4nBf4ZY;-BZV}MagtI_p|2c$<@_ClxdE4>m)u*pjR$#zid5n$p5Ybrl&hy) zJ>}NH&g3?v5lxB2-sE_uP0AuI7#X5(*d zhItLpN1i_NI-{4ouj$D!MqtPC^pU5JJntaS8_4qp@)lyR^8BvLOU3Wyyi7_cL;iW* zYo7O-=e_1%hT8Mho_`y6at{@_AARJjH~&Mt)BKP56q)DCJYVMd&1p$Ab|}9q-BEA; zSSB!u$=Ij-nJi){>)FIsb`Xy@k+1fAwdZG%gWB`eoqrCs=l`$v--EE=3a;jM?xH;R zq3(i*d6H*%p2~cH9t!kO@ENtJ!-&K0+^raxQFveJNCbq52Ekq5eYk7mi{KMpeBh2Qc$ zi&%o33wILFZq#3>{=!@eILRq~!p;=_g7;da)*>|)sj zUL^A(yHOF;%fEOOV;GP8i}h11|6=(U%fEOL@-LQuvHXkWUo8LPz3fB&#quvM;xP8c-xm%2 zebKP^JQvW{q3gMcTQHMD_UDlOIrJ!x^8|W2q^CnKU?zv&!#oZ(NBxId(UuN$rW-xz z%{Po=0{S?lk3;5i$V~j5(eRLd4lQ5>8#q7-e+S{=a=8C+HEJ;$=Nz8MT$W*Wht2MA zD!zL-3%wuK`(eEw*85?-A3lL|50@c>!{>wWh`x{9ik^??`AB*0!yJ!1LPaX^6wmSk zFY#Zh@;)E(F`w}he_}_D{1b#fUWT9lSdRuYqA3Yvl8st_EC|A*ay|MfHK;`$*02M6 zdem%>n(Z<79BYhoj(vgmaLgIUj-kh6dOW7b<96(LLwxqQ&mNCtHTM3veK{VFI!@^A zgdII$M^C(tI!+8l#wR9YUMI}!#3_E~BA3wLNuN9Eb0?=UlR0=}Cx2iuONnJY`Z$?H z4hJdb2x>lA%4y8yq&rFiZloM{a5wkyAm&o?7|-wu?;_(8{g%kCL~bSZk#$KETG5_P zbfqVK=+8jLVlPYdS)$JpdsXr+-?I=iD6y9%o7slkO5|3emlAtdl7&7>^iiUZ5`C1| zt&$7;uNQfhUd8p?gncW$lY6MZ{XB{HRa%*sF^keSc$;d}q%L--v@wx1rzO$oue2M3 z7|tlhU^b;wn2!0BE@C+=S@0r;;pxk{ifg$6eV#VQ)8=@(0_J#HeW#xZ!ZVYY$^z{F8U3Bn-x>X#asL@vpV>tk z8Dx{kLEL@j2uFkPtZdIdO??Jpu4nalb{2Ed*V!1Hf7W?t_n@D%&O7V8vjynq>~a3& z?;t$qZJpE0IlY|I%Q?NAyP0y_!QGVSaVlXq&*|yhd{!XCbE{Yzgy((kyw9EYx${1E z-sjHmB$agZc3v$%SD`u|@DZP~0rmd8mn78vOB;I7i$3%Z!V7A;@Gy^35qDis!v*(T cNWlM@_`iRy2>