From c7b9b76bfa6af79e34aa8a93105efbaa5d6c4a51 Mon Sep 17 00:00:00 2001 From: Matthew Matter Date: Tue, 3 Jan 2017 15:10:44 -0500 Subject: [PATCH] Initial Release Download GIFs natively in Antenna Reddit browser --- AntennaGIFDL.plist | 1 + MBProgressHUD.h | 436 +++++ MBProgressHUD.m | 1484 +++++++++++++++++ Makefile | 10 + README.mdown | 43 + Tweak.xm | 80 + control | 9 + ...stbyte.antennagifdl_1.0.0_iphoneos-arm.deb | Bin 0 -> 52260 bytes 8 files changed, 2063 insertions(+) create mode 100644 AntennaGIFDL.plist create mode 100644 MBProgressHUD.h create mode 100644 MBProgressHUD.m create mode 100644 Makefile create mode 100644 README.mdown create mode 100644 Tweak.xm create mode 100644 control create mode 100644 gg.gh0stbyte.antennagifdl_1.0.0_iphoneos-arm.deb diff --git a/AntennaGIFDL.plist b/AntennaGIFDL.plist new file mode 100644 index 0000000..7e747dc --- /dev/null +++ b/AntennaGIFDL.plist @@ -0,0 +1 @@ +{ Filter = { Bundles = ( "com.amleszk.amrc" ); }; } diff --git a/MBProgressHUD.h b/MBProgressHUD.h new file mode 100644 index 0000000..94cf19a --- /dev/null +++ b/MBProgressHUD.h @@ -0,0 +1,436 @@ +// +// MBProgressHUD.h +// Version 1.0.0 +// Created by Matej Bukovinski on 2.4.09. +// + +// This code is distributed under the terms and conditions of the MIT license. + +// Copyright © 2009-2016 Matej Bukovinski +// +// 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +@class MBBackgroundView; +@protocol MBProgressHUDDelegate; + + +extern CGFloat const MBProgressMaxOffset; + +typedef NS_ENUM(NSInteger, MBProgressHUDMode) { + /// UIActivityIndicatorView. + MBProgressHUDModeIndeterminate, + /// A round, pie-chart like, progress view. + MBProgressHUDModeDeterminate, + /// Horizontal progress bar. + MBProgressHUDModeDeterminateHorizontalBar, + /// Ring-shaped progress view. + MBProgressHUDModeAnnularDeterminate, + /// Shows a custom view. + MBProgressHUDModeCustomView, + /// Shows only labels. + MBProgressHUDModeText +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) { + /// Opacity animation + MBProgressHUDAnimationFade, + /// Opacity + scale animation (zoom in when appearing zoom out when disappearing) + MBProgressHUDAnimationZoom, + /// Opacity + scale animation (zoom out style) + MBProgressHUDAnimationZoomOut, + /// Opacity + scale animation (zoom in style) + MBProgressHUDAnimationZoomIn +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDBackgroundStyle) { + /// Solid color background + MBProgressHUDBackgroundStyleSolidColor, + /// UIVisualEffectView or UIToolbar.layer background view + MBProgressHUDBackgroundStyleBlur +}; + +typedef void (^MBProgressHUDCompletionBlock)(); + + + +/** + * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. + * + * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class. + * The MBProgressHUD window spans over the entire space given to it by the initWithFrame: constructor and catches all + * user input on this region, thereby preventing the user operations on components below the view. + * + * @note To still allow touches to pass through the HUD, you can set hud.userInteractionEnabled = NO. + * @attention MBProgressHUD is a UI class and should therefore only be accessed on the main thread. + */ +@interface MBProgressHUD : UIView + +/** + * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. + * + * @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden. + * + * @param view The view that the HUD will be added to + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * @return A reference to the created HUD. + * + * @see hideHUDForView:animated: + * @see animationType + */ ++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; + +/// @name Showing and hiding + +/** + * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:. + * + * @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden. + * + * @param view The view that is going to be searched for a HUD subview. + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @return YES if a HUD was found and removed, NO otherwise. + * + * @see showHUDAddedTo:animated: + * @see animationType + */ ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; + +/** + * Finds the top-most HUD subview and returns it. + * + * @param view The view that is going to be searched. + * @return A reference to the last HUD subview discovered. + */ ++ (nullable MBProgressHUD *)HUDForView:(UIView *)view; + +/** + * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with + * view.bounds as the parameter. + * + * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as + * the HUD's superview (i.e., the view that the HUD will be added to). + */ +- (instancetype)initWithView:(UIView *)view; + +/** + * Displays the HUD. + * + * @note You need to make sure that the main thread completes its run loop soon after this method call so that + * the user interface can be updated. Call this method when your task is already set up to be executed in a new thread + * (e.g., when using something like NSOperation or making an asynchronous call like NSURLRequest). + * + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * + * @see animationType + */ +- (void)showAnimated:(BOOL)animated; + +/** + * Hides the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * + * @see animationType + */ +- (void)hideAnimated:(BOOL)animated; + +/** + * Hides the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @param delay Delay in seconds until the HUD is hidden. + * + * @see animationType + */ +- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay; + +/** + * The HUD delegate object. Receives HUD state notifications. + */ + +@property (weak, nonatomic, nullable) id delegate; + +/** + * Called after the HUD is hiden. + */ +@property (copy, nullable) MBProgressHUDCompletionBlock completionBlock; + +/* + * Grace period is the time (in seconds) that the invoked method may be run without + * showing the HUD. If the task finishes before the grace time runs out, the HUD will + * not be shown at all. + * This may be used to prevent HUD display for very short tasks. + * Defaults to 0 (no grace time). + */ +@property (assign, nonatomic) NSTimeInterval graceTime; + +/** + * The minimum time (in seconds) that the HUD is shown. + * This avoids the problem of the HUD being shown and than instantly hidden. + * Defaults to 0 (no minimum show time). + */ +@property (assign, nonatomic) NSTimeInterval minShowTime; + +/** + * Removes the HUD from its parent view when hidden. + * Defaults to NO. + */ +@property (assign, nonatomic) BOOL removeFromSuperViewOnHide; + +/// @name Appearance + +/** + * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate. + */ +@property (assign, nonatomic) MBProgressHUDMode mode; + +/** + * A color that gets forwarded to all labels and supported indicators. Also sets the tintColor + * for custom views on iOS 7+. Set to nil to manage color individually. + * Defaults to semi-translucent black on iOS 7 and later and white on earlier iOS versions. + */ +@property (strong, nonatomic, nullable) UIColor *contentColor UI_APPEARANCE_SELECTOR; + +/** + * The animation type that should be used when the HUD is shown and hidden. + */ +@property (assign, nonatomic) MBProgressHUDAnimation animationType UI_APPEARANCE_SELECTOR; + +/** + * The bezel offset relative to the center of the view. You can use MBProgressMaxOffset + * and -MBProgressMaxOffset to move the HUD all the way to the screen edge in each direction. + * E.g., CGPointMake(0.f, MBProgressMaxOffset) would position the HUD centered on the bottom edge. + */ +@property (assign, nonatomic) CGPoint offset UI_APPEARANCE_SELECTOR; + +/** + * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views). + * This also represents the minimum bezel distance to the edge of the HUD view. + * Defaults to 20.f + */ +@property (assign, nonatomic) CGFloat margin UI_APPEARANCE_SELECTOR; + +/** + * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size). + */ +@property (assign, nonatomic) CGSize minSize UI_APPEARANCE_SELECTOR; + +/** + * Force the HUD dimensions to be equal if possible. + */ +@property (assign, nonatomic, getter = isSquare) BOOL square UI_APPEARANCE_SELECTOR; + +/** + * When enabled, the bezel center gets slightly affected by the device accelerometer data. + * Has no effect on iOS < 7.0. Defaults to YES. + */ +@property (assign, nonatomic, getter=areDefaultMotionEffectsEnabled) BOOL defaultMotionEffectsEnabled UI_APPEARANCE_SELECTOR; + +/// @name Progress + +/** + * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. + */ +@property (assign, nonatomic) float progress; + +/// @name ProgressObject + +/** + * The NSProgress object feeding the progress information to the progress indicator. + */ +@property (strong, nonatomic, nullable) NSProgress *progressObject; + +/// @name Views + +/** + * The view containing the labels and indicator (or customView). + */ +@property (strong, nonatomic, readonly) MBBackgroundView *bezelView; + +/** + * View covering the entire HUD area, placed behind bezelView. + */ +@property (strong, nonatomic, readonly) MBBackgroundView *backgroundView; + +/** + * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView. + * The view should implement intrinsicContentSize for proper sizing. For best results use approximately 37 by 37 pixels. + */ +@property (strong, nonatomic, nullable) UIView *customView; + +/** + * A label that holds an optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit + * the entire text. + */ +@property (strong, nonatomic, readonly) UILabel *label; + +/** + * A label that holds an optional details message displayed below the labelText message. The details text can span multiple lines. + */ +@property (strong, nonatomic, readonly) UILabel *detailsLabel; + +/** + * A button that is placed below the labels. Visible only if a target / action is added. + */ +@property (strong, nonatomic, readonly) UIButton *button; + +@end + + +@protocol MBProgressHUDDelegate + +@optional + +/** + * Called after the HUD was fully hidden from the screen. + */ +- (void)hudWasHidden:(MBProgressHUD *)hud; + +@end + + +/** + * A progress view for showing definite progress by filling up a circle (pie chart). + */ +@interface MBRoundProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Indicator progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *progressTintColor; + +/** + * Indicator background (non-progress) color. + * Only applicable on iOS versions older than iOS 7. + * Defaults to translucent white (alpha 0.1). + */ +@property (nonatomic, strong) UIColor *backgroundTintColor; + +/* + * Display mode - NO = round or YES = annular. Defaults to round. + */ +@property (nonatomic, assign, getter = isAnnular) BOOL annular; + +@end + + +/** + * A flat bar progress view. + */ +@interface MBBarProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Bar border line color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *lineColor; + +/** + * Bar background color. + * Defaults to clear [UIColor clearColor]; + */ +@property (nonatomic, strong) UIColor *progressRemainingColor; + +/** + * Bar progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *progressColor; + +@end + + +@interface MBBackgroundView : UIView + +/** + * The background style. + * Defaults to MBProgressHUDBackgroundStyleBlur on iOS 7 or later and MBProgressHUDBackgroundStyleSolidColor otherwise. + * @note Due to iOS 7 not supporting UIVisualEffectView, the blur effect differs slightly between iOS 7 and later versions. + */ +@property (nonatomic) MBProgressHUDBackgroundStyle style; + +/** + * The background color or the blur tint color. + * @note Due to iOS 7 not supporting UIVisualEffectView, the blur effect differs slightly between iOS 7 and later versions. + */ +@property (nonatomic, strong) UIColor *color; + +@end + +@interface MBProgressHUD (Deprecated) + ++ (NSArray *)allHUDsForView:(UIView *)view __attribute__((deprecated("Store references when using more than one HUD per view."))); ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated __attribute__((deprecated("Store references when using more than one HUD per view."))); + +- (id)initWithWindow:(UIWindow *)window __attribute__((deprecated("Use initWithView: instead."))); + +- (void)show:(BOOL)animated __attribute__((deprecated("Use showAnimated: instead."))); +- (void)hide:(BOOL)animated __attribute__((deprecated("Use hideAnimated: instead."))); +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay __attribute__((deprecated("Use hideAnimated:afterDelay: instead."))); + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(nullable MBProgressHUDCompletionBlock)completion __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue + completionBlock:(nullable MBProgressHUDCompletionBlock)completion __attribute__((deprecated("Use GCD directly."))); +@property (assign) BOOL taskInProgress __attribute__((deprecated("No longer needed."))); + +@property (nonatomic, copy) NSString *labelText __attribute__((deprecated("Use label.text instead."))); +@property (nonatomic, strong) UIFont *labelFont __attribute__((deprecated("Use label.font instead."))); +@property (nonatomic, strong) UIColor *labelColor __attribute__((deprecated("Use label.textColor instead."))); +@property (nonatomic, copy) NSString *detailsLabelText __attribute__((deprecated("Use detailsLabel.text instead."))); +@property (nonatomic, strong) UIFont *detailsLabelFont __attribute__((deprecated("Use detailsLabel.font instead."))); +@property (nonatomic, strong) UIColor *detailsLabelColor __attribute__((deprecated("Use detailsLabel.textColor instead."))); +@property (assign, nonatomic) CGFloat opacity __attribute__((deprecated("Customize bezelView properties instead."))); +@property (strong, nonatomic) UIColor *color __attribute__((deprecated("Customize the bezelView color instead."))); +@property (assign, nonatomic) CGFloat xOffset __attribute__((deprecated("Set offset.x instead."))); +@property (assign, nonatomic) CGFloat yOffset __attribute__((deprecated("Set offset.y instead."))); +@property (assign, nonatomic) CGFloat cornerRadius __attribute__((deprecated("Set bezelView.layer.cornerRadius instead."))); +@property (assign, nonatomic) BOOL dimBackground __attribute__((deprecated("Customize HUD background properties instead."))); +@property (strong, nonatomic) UIColor *activityIndicatorColor __attribute__((deprecated("Use UIAppearance to customize UIActivityIndicatorView. E.g.: [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil].color = [UIColor redColor];"))); +@property (atomic, assign, readonly) CGSize size __attribute__((deprecated("Get the bezelView.frame.size instead."))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/MBProgressHUD.m b/MBProgressHUD.m new file mode 100644 index 0000000..a94f921 --- /dev/null +++ b/MBProgressHUD.m @@ -0,0 +1,1484 @@ +// +// MBProgressHUD.m +// Version 1.0.0 +// Created by Matej Bukovinski on 2.4.09. +// + +#import "MBProgressHUD.h" +#import + + +#ifndef kCFCoreFoundationVersionNumber_iOS_7_0 + #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20 +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 + #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15 +#endif + +#define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread."); + +CGFloat const MBProgressMaxOffset = 1000000.f; + +static const CGFloat MBDefaultPadding = 4.f; +static const CGFloat MBDefaultLabelFontSize = 16.f; +static const CGFloat MBDefaultDetailsLabelFontSize = 12.f; + + +@interface MBProgressHUD () { + // Deprecated + UIColor *_activityIndicatorColor; + CGFloat _opacity; +} + +@property (nonatomic, assign) BOOL useAnimation; +@property (nonatomic, assign, getter=hasFinished) BOOL finished; +@property (nonatomic, strong) UIView *indicator; +@property (nonatomic, strong) NSDate *showStarted; +@property (nonatomic, strong) NSArray *paddingConstraints; +@property (nonatomic, strong) NSArray *bezelConstraints; +@property (nonatomic, strong) UIView *topSpacer; +@property (nonatomic, strong) UIView *bottomSpacer; +@property (nonatomic, weak) NSTimer *graceTimer; +@property (nonatomic, weak) NSTimer *minShowTimer; +@property (nonatomic, weak) NSTimer *hideDelayTimer; +@property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink; + +// Deprecated +@property (assign) BOOL taskInProgress; + +@end + + +@interface MBProgressHUDRoundedButton : UIButton +@end + + +@implementation MBProgressHUD + +#pragma mark - Class methods + ++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [[self alloc] initWithView:view]; + hud.removeFromSuperViewOnHide = YES; + [view addSubview:hud]; + [hud showAnimated:animated]; + return hud; +} + ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [self HUDForView:view]; + if (hud != nil) { + hud.removeFromSuperViewOnHide = YES; + [hud hideAnimated:animated]; + return YES; + } + return NO; +} + ++ (MBProgressHUD *)HUDForView:(UIView *)view { + NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator]; + for (UIView *subview in subviewsEnum) { + if ([subview isKindOfClass:self]) { + return (MBProgressHUD *)subview; + } + } + return nil; +} + +#pragma mark - Lifecycle + +- (void)commonInit { + // Set default values for properties + _animationType = MBProgressHUDAnimationFade; + _mode = MBProgressHUDModeIndeterminate; + _margin = 20.0f; + _opacity = 1.f; + _defaultMotionEffectsEnabled = YES; + + // Default color, depending on the current iOS version + BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f]; + // Transparent background + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + // Make it invisible for now + self.alpha = 0.0f; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.layer.allowsGroupOpacity = NO; + + [self setupViews]; + [self updateIndicators]; + [self registerForNotifications]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + [self commonInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if ((self = [super initWithCoder:aDecoder])) { + [self commonInit]; + } + return self; +} + +- (id)initWithView:(UIView *)view { + NSAssert(view, @"View must not be nil."); + return [self initWithFrame:view.bounds]; +} + +- (void)dealloc { + [self unregisterFromNotifications]; +} + +#pragma mark - Show & hide + +- (void)showAnimated:(BOOL)animated { + MBMainThreadAssert(); + [self.minShowTimer invalidate]; + self.useAnimation = animated; + self.finished = NO; + // If the grace time is set, postpone the HUD display + if (self.graceTime > 0.0) { + NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.graceTimer = timer; + } + // ... otherwise show the HUD immediately + else { + [self showUsingAnimation:self.useAnimation]; + } +} + +- (void)hideAnimated:(BOOL)animated { + MBMainThreadAssert(); + [self.graceTimer invalidate]; + self.useAnimation = animated; + self.finished = YES; + // If the minShow time is set, calculate how long the HUD was shown, + // and postpone the hiding operation if necessary + if (self.minShowTime > 0.0 && self.showStarted) { + NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted]; + if (interv < self.minShowTime) { + NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.minShowTimer = timer; + return; + } + } + // ... otherwise hide the HUD immediately + [self hideUsingAnimation:self.useAnimation]; +} + +- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay { + NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.hideDelayTimer = timer; +} + +#pragma mark - Timer callbacks + +- (void)handleGraceTimer:(NSTimer *)theTimer { + // Show the HUD only if the task is still running + if (!self.hasFinished) { + [self showUsingAnimation:self.useAnimation]; + } +} + +- (void)handleMinShowTimer:(NSTimer *)theTimer { + [self hideUsingAnimation:self.useAnimation]; +} + +- (void)handleHideTimer:(NSTimer *)timer { + [self hideAnimated:[timer.userInfo boolValue]]; +} + +#pragma mark - View Hierrarchy + +- (void)didMoveToSuperview { + [self updateForCurrentOrientationAnimated:NO]; +} + +#pragma mark - Internal show & hide operations + +- (void)showUsingAnimation:(BOOL)animated { + // Cancel any previous animations + [self.bezelView.layer removeAllAnimations]; + [self.backgroundView.layer removeAllAnimations]; + + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + + self.showStarted = [NSDate date]; + self.alpha = 1.f; + + // Needed in case we hide and re-show with the same NSProgress object attached. + [self setNSProgressDisplayLinkEnabled:YES]; + + if (animated) { + [self animateIn:YES withType:self.animationType completion:NULL]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + self.bezelView.alpha = self.opacity; +#pragma clang diagnostic pop + self.backgroundView.alpha = 1.f; + } +} + +- (void)hideUsingAnimation:(BOOL)animated { + if (animated && self.showStarted) { + self.showStarted = nil; + [self animateIn:NO withType:self.animationType completion:^(BOOL finished) { + [self done]; + }]; + } else { + self.showStarted = nil; + self.bezelView.alpha = 0.f; + self.backgroundView.alpha = 1.f; + [self done]; + } +} + +- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion { + // Automatically determine the correct zoom animation type + if (type == MBProgressHUDAnimationZoom) { + type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut; + } + + CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f); + CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f); + + // Set starting state + UIView *bezelView = self.bezelView; + if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) { + bezelView.transform = small; + } else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) { + bezelView.transform = large; + } + + // Perform animations + dispatch_block_t animations = ^{ + if (animatingIn) { + bezelView.transform = CGAffineTransformIdentity; + } else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) { + bezelView.transform = large; + } else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) { + bezelView.transform = small; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + bezelView.alpha = animatingIn ? self.opacity : 0.f; +#pragma clang diagnostic pop + self.backgroundView.alpha = animatingIn ? 1.f : 0.f; + }; + + // Spring animations are nicer, but only available on iOS 7+ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) { + [UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion]; + return; + } +#endif + [UIView animateWithDuration:0.3 delay:0. options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion]; +} + +- (void)done { + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + [self setNSProgressDisplayLinkEnabled:NO]; + + if (self.hasFinished) { + self.alpha = 0.0f; + if (self.removeFromSuperViewOnHide) { + [self removeFromSuperview]; + } + } + MBProgressHUDCompletionBlock completionBlock = self.completionBlock; + if (completionBlock) { + completionBlock(); + } + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(hudWasHidden:)]) { + [delegate performSelector:@selector(hudWasHidden:) withObject:self]; + } +} + +#pragma mark - UI + +- (void)setupViews { + UIColor *defaultColor = self.contentColor; + + MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds]; + backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; + backgroundView.backgroundColor = [UIColor clearColor]; + backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + backgroundView.alpha = 0.f; + [self addSubview:backgroundView]; + _backgroundView = backgroundView; + + MBBackgroundView *bezelView = [MBBackgroundView new]; + bezelView.translatesAutoresizingMaskIntoConstraints = NO; + bezelView.layer.cornerRadius = 5.f; + bezelView.alpha = 0.f; + [self addSubview:bezelView]; + _bezelView = bezelView; + [self updateBezelMotionEffects]; + + UILabel *label = [UILabel new]; + label.adjustsFontSizeToFitWidth = NO; + label.textAlignment = NSTextAlignmentCenter; + label.textColor = defaultColor; + label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize]; + label.opaque = NO; + label.backgroundColor = [UIColor clearColor]; + _label = label; + + UILabel *detailsLabel = [UILabel new]; + detailsLabel.adjustsFontSizeToFitWidth = NO; + detailsLabel.textAlignment = NSTextAlignmentCenter; + detailsLabel.textColor = defaultColor; + detailsLabel.numberOfLines = 0; + detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize]; + detailsLabel.opaque = NO; + detailsLabel.backgroundColor = [UIColor clearColor]; + _detailsLabel = detailsLabel; + + UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize]; + [button setTitleColor:defaultColor forState:UIControlStateNormal]; + _button = button; + + for (UIView *view in @[label, detailsLabel, button]) { + view.translatesAutoresizingMaskIntoConstraints = NO; + [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal]; + [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical]; + [bezelView addSubview:view]; + } + + UIView *topSpacer = [UIView new]; + topSpacer.translatesAutoresizingMaskIntoConstraints = NO; + topSpacer.hidden = YES; + [bezelView addSubview:topSpacer]; + _topSpacer = topSpacer; + + UIView *bottomSpacer = [UIView new]; + bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO; + bottomSpacer.hidden = YES; + [bezelView addSubview:bottomSpacer]; + _bottomSpacer = bottomSpacer; +} + +- (void)updateIndicators { + UIView *indicator = self.indicator; + BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; + BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]]; + + MBProgressHUDMode mode = self.mode; + if (mode == MBProgressHUDModeIndeterminate) { + if (!isActivityIndicator) { + // Update to indeterminate indicator + [indicator removeFromSuperview]; + indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + [(UIActivityIndicatorView *)indicator startAnimating]; + [self.bezelView addSubview:indicator]; + } + } + else if (mode == MBProgressHUDModeDeterminateHorizontalBar) { + // Update to bar determinate indicator + [indicator removeFromSuperview]; + indicator = [[MBBarProgressView alloc] init]; + [self.bezelView addSubview:indicator]; + } + else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) { + if (!isRoundIndicator) { + // Update to determinante indicator + [indicator removeFromSuperview]; + indicator = [[MBRoundProgressView alloc] init]; + [self.bezelView addSubview:indicator]; + } + if (mode == MBProgressHUDModeAnnularDeterminate) { + [(MBRoundProgressView *)indicator setAnnular:YES]; + } + } + else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) { + // Update custom view indicator + [indicator removeFromSuperview]; + indicator = self.customView; + [self.bezelView addSubview:indicator]; + } + else if (mode == MBProgressHUDModeText) { + [indicator removeFromSuperview]; + indicator = nil; + } + indicator.translatesAutoresizingMaskIntoConstraints = NO; + self.indicator = indicator; + + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(self.progress) forKey:@"progress"]; + } + + [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal]; + [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical]; + + [self updateViewsForColor:self.contentColor]; + [self setNeedsUpdateConstraints]; +} + +- (void)updateViewsForColor:(UIColor *)color { + if (!color) return; + + self.label.textColor = color; + self.detailsLabel.textColor = color; + [self.button setTitleColor:color forState:UIControlStateNormal]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (self.activityIndicatorColor) { + color = self.activityIndicatorColor; + } +#pragma clang diagnostic pop + + // UIAppearance settings are prioritized. If they are preset the set color is ignored. + + UIView *indicator = self.indicator; + if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) { + UIActivityIndicatorView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + // For iOS 9+ + appearance = [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + + if (appearance.color == nil) { + ((UIActivityIndicatorView *)indicator).color = color; + } + } else if ([indicator isKindOfClass:[MBRoundProgressView class]]) { + MBRoundProgressView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [MBRoundProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + appearance = [MBRoundProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + if (appearance.progressTintColor == nil) { + ((MBRoundProgressView *)indicator).progressTintColor = color; + } + if (appearance.backgroundTintColor == nil) { + ((MBRoundProgressView *)indicator).backgroundTintColor = [color colorWithAlphaComponent:0.1]; + } + } else if ([indicator isKindOfClass:[MBBarProgressView class]]) { + MBBarProgressView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [MBBarProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + appearance = [MBBarProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + if (appearance.progressColor == nil) { + ((MBBarProgressView *)indicator).progressColor = color; + } + if (appearance.lineColor == nil) { + ((MBBarProgressView *)indicator).lineColor = color; + } + } else { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + if ([indicator respondsToSelector:@selector(setTintColor:)]) { + [indicator setTintColor:color]; + } +#endif + } +} + +- (void)updateBezelMotionEffects { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + MBBackgroundView *bezelView = self.bezelView; + if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return; + + if (self.defaultMotionEffectsEnabled) { + CGFloat effectOffset = 10.f; + UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + effectX.maximumRelativeValue = @(effectOffset); + effectX.minimumRelativeValue = @(-effectOffset); + + UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + effectY.maximumRelativeValue = @(effectOffset); + effectY.minimumRelativeValue = @(-effectOffset); + + UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init]; + group.motionEffects = @[effectX, effectY]; + + [bezelView addMotionEffect:group]; + } else { + NSArray *effects = [bezelView motionEffects]; + for (UIMotionEffect *effect in effects) { + [bezelView removeMotionEffect:effect]; + } + } +#endif +} + +#pragma mark - Layout + +- (void)updateConstraints { + UIView *bezel = self.bezelView; + UIView *topSpacer = self.topSpacer; + UIView *bottomSpacer = self.bottomSpacer; + CGFloat margin = self.margin; + NSMutableArray *bezelConstraints = [NSMutableArray array]; + NSDictionary *metrics = @{@"margin": @(margin)}; + + NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil]; + if (self.indicator) [subviews insertObject:self.indicator atIndex:1]; + + // Remove existing constraints + [self removeConstraints:self.constraints]; + [topSpacer removeConstraints:topSpacer.constraints]; + [bottomSpacer removeConstraints:bottomSpacer.constraints]; + if (self.bezelConstraints) { + [bezel removeConstraints:self.bezelConstraints]; + self.bezelConstraints = nil; + } + + // Center bezel in container (self), applying the offset if set + CGPoint offset = self.offset; + NSMutableArray *centeringConstraints = [NSMutableArray array]; + [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]]; + [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]]; + [self applyPriority:998.f toConstraints:centeringConstraints]; + [self addConstraints:centeringConstraints]; + + // Ensure minimum side margin is kept + NSMutableArray *sideConstraints = [NSMutableArray array]; + [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]]; + [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]]; + [self applyPriority:999.f toConstraints:sideConstraints]; + [self addConstraints:sideConstraints]; + + // Minimum bezel size, if set + CGSize minimumSize = self.minSize; + if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) { + NSMutableArray *minSizeConstraints = [NSMutableArray array]; + [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]]; + [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]]; + [self applyPriority:997.f toConstraints:minSizeConstraints]; + [bezelConstraints addObjectsFromArray:minSizeConstraints]; + } + + // Square aspect ratio, if set + if (self.square) { + NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0]; + square.priority = 997.f; + [bezelConstraints addObject:square]; + } + + // Top and bottom spacing + [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]]; + [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]]; + // Top and bottom spaces should be equal + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]]; + + // Layout subviews in bezel + NSMutableArray *paddingConstraints = [NSMutableArray new]; + [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + // Center in bezel + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]]; + // Ensure the minimum edge margin is kept + [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]]; + // Element spacing + if (idx == 0) { + // First, ensure spacing to bezel edge + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]]; + } else if (idx == subviews.count - 1) { + // Last, ensure spacing to bezel edge + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]]; + } + if (idx > 0) { + // Has previous + NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]; + [bezelConstraints addObject:padding]; + [paddingConstraints addObject:padding]; + } + }]; + + [bezel addConstraints:bezelConstraints]; + self.bezelConstraints = bezelConstraints; + + self.paddingConstraints = [paddingConstraints copy]; + [self updatePaddingConstraints]; + + [super updateConstraints]; +} + +- (void)layoutSubviews { + // There is no need to update constraints if they are going to + // be recreated in [super layoutSubviews] due to needsUpdateConstraints being set. + // This also avoids an issue on iOS 8, where updatePaddingConstraints + // would trigger a zombie object access. + if (!self.needsUpdateConstraints) { + [self updatePaddingConstraints]; + } + [super layoutSubviews]; +} + +- (void)updatePaddingConstraints { + // Set padding dynamically, depending on whether the view is visible or not + __block BOOL hasVisibleAncestors = NO; + [self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) { + UIView *firstView = (UIView *)padding.firstItem; + UIView *secondView = (UIView *)padding.secondItem; + BOOL firstVisible = !firstView.hidden && !CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero); + BOOL secondVisible = !secondView.hidden && !CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero); + // Set if both views are visible or if there's a visible view on top that doesn't have padding + // added relative to the current view yet + padding.constant = (firstVisible && (secondVisible || hasVisibleAncestors)) ? MBDefaultPadding : 0.f; + hasVisibleAncestors |= secondVisible; + }]; +} + +- (void)applyPriority:(UILayoutPriority)priority toConstraints:(NSArray *)constraints { + for (NSLayoutConstraint *constraint in constraints) { + constraint.priority = priority; + } +} + +#pragma mark - Properties + +- (void)setMode:(MBProgressHUDMode)mode { + if (mode != _mode) { + _mode = mode; + [self updateIndicators]; + } +} + +- (void)setCustomView:(UIView *)customView { + if (customView != _customView) { + _customView = customView; + if (self.mode == MBProgressHUDModeCustomView) { + [self updateIndicators]; + } + } +} + +- (void)setOffset:(CGPoint)offset { + if (!CGPointEqualToPoint(offset, _offset)) { + _offset = offset; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setMargin:(CGFloat)margin { + if (margin != _margin) { + _margin = margin; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setMinSize:(CGSize)minSize { + if (!CGSizeEqualToSize(minSize, _minSize)) { + _minSize = minSize; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setSquare:(BOOL)square { + if (square != _square) { + _square = square; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink { + if (progressObjectDisplayLink != _progressObjectDisplayLink) { + [_progressObjectDisplayLink invalidate]; + + _progressObjectDisplayLink = progressObjectDisplayLink; + + [_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + } +} + +- (void)setProgressObject:(NSProgress *)progressObject { + if (progressObject != _progressObject) { + _progressObject = progressObject; + [self setNSProgressDisplayLinkEnabled:YES]; + } +} + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + UIView *indicator = self.indicator; + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(self.progress) forKey:@"progress"]; + } + } +} + +- (void)setContentColor:(UIColor *)contentColor { + if (contentColor != _contentColor && ![contentColor isEqual:_contentColor]) { + _contentColor = contentColor; + [self updateViewsForColor:contentColor]; + } +} + +- (void)setDefaultMotionEffectsEnabled:(BOOL)defaultMotionEffectsEnabled { + if (defaultMotionEffectsEnabled != _defaultMotionEffectsEnabled) { + _defaultMotionEffectsEnabled = defaultMotionEffectsEnabled; + [self updateBezelMotionEffects]; + } +} + +#pragma mark - NSProgress + +- (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled { + // We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread, + // so we're refreshing the progress only every frame draw + if (enabled && self.progressObject) { + // Only create if not already active. + if (!self.progressObjectDisplayLink) { + self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)]; + } + } else { + self.progressObjectDisplayLink = nil; + } +} + +- (void)updateProgressFromProgressObject { + self.progress = self.progressObject.fractionCompleted; +} + +#pragma mark - Notifications + +- (void)registerForNotifications { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + [nc addObserver:self selector:@selector(statusBarOrientationDidChange:) + name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif +} + +- (void)unregisterFromNotifications { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif +} + +#if !TARGET_OS_TV +- (void)statusBarOrientationDidChange:(NSNotification *)notification { + UIView *superview = self.superview; + if (!superview) { + return; + } else { + [self updateForCurrentOrientationAnimated:YES]; + } +} +#endif + +- (void)updateForCurrentOrientationAnimated:(BOOL)animated { + // Stay in sync with the superview in any case + if (self.superview) { + self.bounds = self.superview.bounds; + } + + // Not needed on iOS 8+, compile out when the deployment target allows, + // to avoid sharedApplication problems on extension targets +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000 + // Only needed pre iOS 8 when added to a window + BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0; + if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return; + + // Make extension friendly. Will not get called on extensions (iOS 8+) due to the above check. + // This just ensures we don't get a warning about extension-unsafe API. + Class UIApplicationClass = NSClassFromString(@"UIApplication"); + if (!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) return; + + UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; + UIInterfaceOrientation orientation = application.statusBarOrientation; + CGFloat radians = 0; + + if (UIInterfaceOrientationIsLandscape(orientation)) { + radians = orientation == UIInterfaceOrientationLandscapeLeft ? -(CGFloat)M_PI_2 : (CGFloat)M_PI_2; + // Window coordinates differ! + self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width); + } else { + radians = orientation == UIInterfaceOrientationPortraitUpsideDown ? (CGFloat)M_PI : 0.f; + } + + if (animated) { + [UIView animateWithDuration:0.3 animations:^{ + self.transform = CGAffineTransformMakeRotation(radians); + }]; + } else { + self.transform = CGAffineTransformMakeRotation(radians); + } +#endif +} + +@end + + +@implementation MBRoundProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + _progress = 0.f; + _annular = NO; + _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f]; + _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f]; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + return CGSizeMake(37.f, 37.f); +} + +#pragma mark - Properties + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + [self setNeedsDisplay]; + } +} + +- (void)setProgressTintColor:(UIColor *)progressTintColor { + NSAssert(progressTintColor, @"The color should not be nil."); + if (progressTintColor != _progressTintColor && ![progressTintColor isEqual:_progressTintColor]) { + _progressTintColor = progressTintColor; + [self setNeedsDisplay]; + } +} + +- (void)setBackgroundTintColor:(UIColor *)backgroundTintColor { + NSAssert(backgroundTintColor, @"The color should not be nil."); + if (backgroundTintColor != _backgroundTintColor && ![backgroundTintColor isEqual:_backgroundTintColor]) { + _backgroundTintColor = backgroundTintColor; + [self setNeedsDisplay]; + } +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + + if (_annular) { + // Draw background + CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f; + UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath]; + processBackgroundPath.lineWidth = lineWidth; + processBackgroundPath.lineCapStyle = kCGLineCapButt; + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + CGFloat radius = (self.bounds.size.width - lineWidth)/2; + CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees + CGFloat endAngle = (2 * (float)M_PI) + startAngle; + [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_backgroundTintColor set]; + [processBackgroundPath stroke]; + // Draw progress + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare; + processPath.lineWidth = lineWidth; + endAngle = (self.progress * 2 * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_progressTintColor set]; + [processPath stroke]; + } else { + // Draw background + CGFloat lineWidth = 2.f; + CGRect allRect = self.bounds; + CGRect circleRect = CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f); + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + [_progressTintColor setStroke]; + [_backgroundTintColor setFill]; + CGContextSetLineWidth(context, lineWidth); + if (isPreiOS7) { + CGContextFillEllipseInRect(context, circleRect); + } + CGContextStrokeEllipseInRect(context, circleRect); + // 90 degrees + CGFloat startAngle = - ((float)M_PI / 2.f); + // Draw progress + if (isPreiOS7) { + CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - lineWidth; + CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle; + [_progressTintColor setFill]; + CGContextMoveToPoint(context, center.x, center.y); + CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0); + CGContextClosePath(context); + CGContextFillPath(context); + } else { + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = kCGLineCapButt; + processPath.lineWidth = lineWidth * 2.f; + CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - (processPath.lineWidth / 2.f); + CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + // Ensure that we don't get color overlaping when _progressTintColor alpha < 1.f. + CGContextSetBlendMode(context, kCGBlendModeCopy); + [_progressTintColor set]; + [processPath stroke]; + } + } +} + +@end + + +@implementation MBBarProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _progress = 0.f; + _lineColor = [UIColor whiteColor]; + _progressColor = [UIColor whiteColor]; + _progressRemainingColor = [UIColor clearColor]; + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + return CGSizeMake(120.f, isPreiOS7 ? 20.f : 10.f); +} + +#pragma mark - Properties + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + [self setNeedsDisplay]; + } +} + +- (void)setProgressColor:(UIColor *)progressColor { + NSAssert(progressColor, @"The color should not be nil."); + if (progressColor != _progressColor && ![progressColor isEqual:_progressColor]) { + _progressColor = progressColor; + [self setNeedsDisplay]; + } +} + +- (void)setProgressRemainingColor:(UIColor *)progressRemainingColor { + NSAssert(progressRemainingColor, @"The color should not be nil."); + if (progressRemainingColor != _progressRemainingColor && ![progressRemainingColor isEqual:_progressRemainingColor]) { + _progressRemainingColor = progressRemainingColor; + [self setNeedsDisplay]; + } +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetLineWidth(context, 2); + CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]); + CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]); + + // Draw background + CGFloat radius = (rect.size.height / 2) - 2; + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextFillPath(context); + + // Draw border + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextStrokePath(context); + + CGContextSetFillColorWithColor(context, [_progressColor CGColor]); + radius = radius - 2; + CGFloat amount = self.progress * rect.size.width; + + // Progress in the middle area + if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, amount, 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, amount, rect.size.height - 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextFillPath(context); + } + + // Progress in the right arc + else if (amount > radius + 4) { + CGFloat x = amount - (rect.size.width - radius - 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4); + CGFloat angle = -acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4); + angle = acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextFillPath(context); + } + + // Progress is in the left arc + else if (amount < radius + 4 && amount > 0) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextFillPath(context); + } +} + +@end + + +@interface MBBackgroundView () + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV +@property UIVisualEffectView *effectView; +#endif +#if !TARGET_OS_TV +@property UIToolbar *toolbar; +#endif + +@end + + +@implementation MBBackgroundView + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) { + _style = MBProgressHUDBackgroundStyleBlur; + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + _color = [UIColor colorWithWhite:0.8f alpha:0.6f]; + } else { + _color = [UIColor colorWithWhite:0.95f alpha:0.6f]; + } + } else { + _style = MBProgressHUDBackgroundStyleSolidColor; + _color = [[UIColor blackColor] colorWithAlphaComponent:0.8]; + } + + self.clipsToBounds = YES; + + [self updateForBackgroundStyle]; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + // Smallest size possible. Content pushes against this. + return CGSizeZero; +} + +#pragma mark - Appearance + +- (void)setStyle:(MBProgressHUDBackgroundStyle)style { + if (style == MBProgressHUDBackgroundStyleBlur && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0) { + style = MBProgressHUDBackgroundStyleSolidColor; + } + if (_style != style) { + _style = style; + [self updateForBackgroundStyle]; + } +} + +- (void)setColor:(UIColor *)color { + NSAssert(color, @"The color should not be nil."); + if (color != _color && ![color isEqual:_color]) { + _color = color; + [self updateViewsForColor:color]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Views + +- (void)updateForBackgroundStyle { + MBProgressHUDBackgroundStyle style = self.style; + if (style == MBProgressHUDBackgroundStyleBlur) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect]; + [self addSubview:effectView]; + effectView.frame = self.bounds; + effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + self.backgroundColor = self.color; + self.layer.allowsGroupOpacity = NO; + self.effectView = effectView; + } else { +#endif +#if !TARGET_OS_TV + UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectInset(self.bounds, -100.f, -100.f)]; + toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + toolbar.barTintColor = self.color; + toolbar.translucent = YES; + [self addSubview:toolbar]; + self.toolbar = toolbar; +#endif +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + } +#endif + } else { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + [self.effectView removeFromSuperview]; + self.effectView = nil; + } else { +#endif +#if !TARGET_OS_TV + [self.toolbar removeFromSuperview]; + self.toolbar = nil; +#endif +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + } +#endif + self.backgroundColor = self.color; + } +} + +- (void)updateViewsForColor:(UIColor *)color { + if (self.style == MBProgressHUDBackgroundStyleBlur) { + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + self.backgroundColor = self.color; + } else { +#if !TARGET_OS_TV + self.toolbar.barTintColor = color; +#endif + } + } else { + self.backgroundColor = self.color; + } +} + +@end + + +@implementation MBProgressHUD (Deprecated) + +#pragma mark - Class + ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated { + NSArray *huds = [MBProgressHUD allHUDsForView:view]; + for (MBProgressHUD *hud in huds) { + hud.removeFromSuperViewOnHide = YES; + [hud hideAnimated:animated]; + } + return [huds count]; +} + ++ (NSArray *)allHUDsForView:(UIView *)view { + NSMutableArray *huds = [NSMutableArray array]; + NSArray *subviews = view.subviews; + for (UIView *aView in subviews) { + if ([aView isKindOfClass:self]) { + [huds addObject:aView]; + } + } + return [NSArray arrayWithArray:huds]; +} + +#pragma mark - Lifecycle + +- (id)initWithWindow:(UIWindow *)window { + return [self initWithView:window]; +} + +#pragma mark - Show & hide + +- (void)show:(BOOL)animated { + [self showAnimated:animated]; +} + +- (void)hide:(BOOL)animated { + [self hideAnimated:animated]; +} + +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay { + [self hideAnimated:animated afterDelay:delay]; +} + +#pragma mark - Threading + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { + [self showAnimated:animated whileExecutingBlock:^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + // Start executing the requested task + [target performSelector:method withObject:object]; +#pragma clang diagnostic pop + }]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue { + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue completionBlock:(nullable MBProgressHUDCompletionBlock)completion { + self.taskInProgress = YES; + self.completionBlock = completion; + dispatch_async(queue, ^(void) { + block(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self cleanUp]; + }); + }); + [self showAnimated:animated]; +} + +- (void)cleanUp { + self.taskInProgress = NO; + [self hideAnimated:self.useAnimation]; +} + +#pragma mark - Labels + +- (NSString *)labelText { + return self.label.text; +} + +- (void)setLabelText:(NSString *)labelText { + MBMainThreadAssert(); + self.label.text = labelText; +} + +- (UIFont *)labelFont { + return self.label.font; +} + +- (void)setLabelFont:(UIFont *)labelFont { + MBMainThreadAssert(); + self.label.font = labelFont; +} + +- (UIColor *)labelColor { + return self.label.textColor; +} + +- (void)setLabelColor:(UIColor *)labelColor { + MBMainThreadAssert(); + self.label.textColor = labelColor; +} + +- (NSString *)detailsLabelText { + return self.detailsLabel.text; +} + +- (void)setDetailsLabelText:(NSString *)detailsLabelText { + MBMainThreadAssert(); + self.detailsLabel.text = detailsLabelText; +} + +- (UIFont *)detailsLabelFont { + return self.detailsLabel.font; +} + +- (void)setDetailsLabelFont:(UIFont *)detailsLabelFont { + MBMainThreadAssert(); + self.detailsLabel.font = detailsLabelFont; +} + +- (UIColor *)detailsLabelColor { + return self.detailsLabel.textColor; +} + +- (void)setDetailsLabelColor:(UIColor *)detailsLabelColor { + MBMainThreadAssert(); + self.detailsLabel.textColor = detailsLabelColor; +} + +- (CGFloat)opacity { + return _opacity; +} + +- (void)setOpacity:(CGFloat)opacity { + MBMainThreadAssert(); + _opacity = opacity; +} + +- (UIColor *)color { + return self.bezelView.color; +} + +- (void)setColor:(UIColor *)color { + MBMainThreadAssert(); + self.bezelView.color = color; +} + +- (CGFloat)yOffset { + return self.offset.y; +} + +- (void)setYOffset:(CGFloat)yOffset { + MBMainThreadAssert(); + self.offset = CGPointMake(self.offset.x, yOffset); +} + +- (CGFloat)xOffset { + return self.offset.x; +} + +- (void)setXOffset:(CGFloat)xOffset { + MBMainThreadAssert(); + self.offset = CGPointMake(xOffset, self.offset.y); +} + +- (CGFloat)cornerRadius { + return self.bezelView.layer.cornerRadius; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + MBMainThreadAssert(); + self.bezelView.layer.cornerRadius = cornerRadius; +} + +- (BOOL)dimBackground { + MBBackgroundView *backgroundView = self.backgroundView; + UIColor *dimmedColor = [UIColor colorWithWhite:0.f alpha:.2f]; + return backgroundView.style == MBProgressHUDBackgroundStyleSolidColor && [backgroundView.color isEqual:dimmedColor]; +} + +- (void)setDimBackground:(BOOL)dimBackground { + MBMainThreadAssert(); + self.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; + self.backgroundView.color = dimBackground ? [UIColor colorWithWhite:0.f alpha:.2f] : [UIColor clearColor]; +} + +- (CGSize)size { + return self.bezelView.frame.size; +} + +- (UIColor *)activityIndicatorColor { + return _activityIndicatorColor; +} + +- (void)setActivityIndicatorColor:(UIColor *)activityIndicatorColor { + if (activityIndicatorColor != _activityIndicatorColor) { + _activityIndicatorColor = activityIndicatorColor; + UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)self.indicator; + if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) { + [indicator setColor:activityIndicatorColor]; + } + } +} + +@end + +@implementation MBProgressHUDRoundedButton + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + CALayer *layer = self.layer; + layer.borderWidth = 1.f; + } + return self; +} + +#pragma mark - Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + // Fully rounded corners + CGFloat height = CGRectGetHeight(self.bounds); + self.layer.cornerRadius = ceil(height / 2.f); +} + +- (CGSize)intrinsicContentSize { + // Only show if we have associated control events + if (self.allControlEvents == 0) return CGSizeZero; + CGSize size = [super intrinsicContentSize]; + // Add some side padding + size.width += 20.f; + return size; +} + +#pragma mark - Color + +- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { + [super setTitleColor:color forState:state]; + // Update related colors + [self setHighlighted:self.highlighted]; + self.layer.borderColor = color.CGColor; +} + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + UIColor *baseColor = [self titleColorForState:UIControlStateSelected]; + self.backgroundColor = highlighted ? [baseColor colorWithAlphaComponent:0.1f] : [UIColor clearColor]; +} + +@end diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7ba4526 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +include $(THEOS)/makefiles/common.mk + +TWEAK_NAME = AntennaGIFDL +AntennaGIFDL_FILES = Tweak.xm MBProgressHUD.m +AntennaGIFDL_FRAMEWORKS = Photos AssetsLibrary UIKit CoreGraphics QuartzCore +AntennaGIFDL_CFLAGS = -fobjc-arc +include $(THEOS_MAKE_PATH)/tweak.mk + +after-install:: + install.exec "killall -9 amrc" diff --git a/README.mdown b/README.mdown new file mode 100644 index 0000000..619c95c --- /dev/null +++ b/README.mdown @@ -0,0 +1,43 @@ +# AntennaGIFDL + +Download GIFs from Antenna natively + +## Installation + +Go to Releases and download latest deb & install with iFile/Filza + +OR + +Clone repo and compile with theos + + +## Usage + +NOTE: You MUST enable photo access to be able to save media + +### Save GIF +* Click GIF to get into full-screen GIF mode +* Long press & select "Save GIF" + +## Contributing + +1. Fork it! +2. Create your feature branch: `git checkout -b my-new-feature` +3. Commit your changes: `git commit -am 'Add some feature'` +4. Push to the branch: `git push origin my-new-feature` +5. Submit a pull request :D + +## History/Changelog + +#### 1.0.0 (1/3/2017) +* Initial Release + +## Credits + +Made by [Gh0stByte](http://twitter.com/Gh0stByte) + +Suggestion by /u/GiraffeFactor + +## License + +TODO: Write license \ No newline at end of file diff --git a/Tweak.xm b/Tweak.xm new file mode 100644 index 0000000..6ab647f --- /dev/null +++ b/Tweak.xm @@ -0,0 +1,80 @@ +#import +#import +#import "MBProgressHUD.h" + +@interface RCGIFViewController : UIViewController +@property(assign, nonatomic) NSURL *mp4URL; +@end + +#define SETUP_LONGPRESS_ONCONTROLLER UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; longPress.minimumPressDuration = 1.5; [self.view addGestureRecognizer:longPress]; + + +%hook RCGIFViewController + +-(void)viewDidLoad +{ + SETUP_LONGPRESS_ONCONTROLLER + %orig; +} + +%new +-(void)handleLongPress:(UILongPressGestureRecognizer *)sender +{ + UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + [actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { + [sender setEnabled:YES]; + // Cancel button tappped. + [self dismissViewControllerAnimated:YES completion:^{ + }]; + }]]; + + [actionSheet addAction:[UIAlertAction actionWithTitle:@"Save Video" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; + hud.label.text = @"Downloading"; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSLog(@"Downloading Started"); + NSURL *url = self.mp4URL; + NSData *urlData = [NSData dataWithContentsOfURL:url]; + if ( urlData ) + { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"tempVideo.mp4"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + hud.label.text = @"Saving"; + [urlData writeToFile:filePath atomically:YES]; + NSLog(@"File Saved !"); + hud.label.text = @"Importing"; + [sender setEnabled:YES]; + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + [[[ALAssetsLibrary alloc] init] writeVideoAtPathToSavedPhotosAlbum:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", documentsDirectory,@"tempVideo.mp4"]] completionBlock:^(NSURL *assetURL, NSError *error) { + + if(assetURL) { + hud.label.text = @"DONE!"; + [hud hideAnimated:YES]; + } else { + hud.label.text = @"ERROR. Try Again."; + } + }]; + + + }); + } + + }); + + [self dismissViewControllerAnimated:YES completion:^{ + + }]; + }]]; + + // Present action sheet. + [self presentViewController:actionSheet animated:YES completion:nil]; +} + + +%end \ No newline at end of file diff --git a/control b/control new file mode 100644 index 0000000..f64fb8b --- /dev/null +++ b/control @@ -0,0 +1,9 @@ +Package: gg.gh0stbyte.antennagifdl +Name: AntennaGIFDL +Depends: mobilesubstrate +Version: 1.0.0 +Architecture: iphoneos-arm +Description: Download GIFs from Antenna Reddit Client +Maintainer: Gh0stByte +Author: Gh0stByte +Section: Tweaks diff --git a/gg.gh0stbyte.antennagifdl_1.0.0_iphoneos-arm.deb b/gg.gh0stbyte.antennagifdl_1.0.0_iphoneos-arm.deb new file mode 100644 index 0000000000000000000000000000000000000000..ce6955b4cc5ee36288d0037253c8c575277f46cc GIT binary patch literal 52260 zcmafZQ*1AQlXY#|w(a)b+O}=CzuLXEZQHhO+qP|d|K02+-`k!{CYgtsWX?$@^Kgjy z44sTE1Yk@|jVulA=#4Dx44piPh=`ckxLDaZ*;rV(h=`c}Q~z&fW@6%CVuKH*VUB8Y8~}^-nnC=B7W5yUYb0=lW{Zfj_L7Z`?JpX znc$~X6zCZ6#dxEr#=EXCo=IV+wf}W;MxC;exYD4w*%3)#Lb#)!tv$JXeZZhKmjk$# z^8KN;TjR1XE*w6K`S3+!ds_>*tQelSKRgB;&4@CZY->=~hlw^V5TDc( zdN!!l?we`SkU;!5N&jEETkQsp=+w*xq5=XkF$5U?H~BW6wub*v=Ks=;@)NPQo`?wDQE!`ch!Zn$;bJwRsC7{o8I3+5fG@e zb$!|Iu}wJ_(5l+10LiFsqG=Km89%q0;D6)D-_o1FJhYLRj%WcJYy&->*TIKU;^x-$ zBFCC6j!-DDI8^tJMT&_o-F>Uud^ZJy-C_+nP}XwleickRX+onN7sGy)vX?deKxspo zxTB&Crsr1aD}Yar`S``UVk6&L`Yn6*0;LS4NqWwxPGLo1Zb$r*LBG*{n(p~-cH3Cq za2h#YDk5lb4eGB_J`{h8u-1?1j2JjB3T-m{V<3B@ zdB-5(IoAxnTYm(zJ~SFz$Nd(a)O8<#NKYFy24|67YL+^QwPsxLsORUWj=tvOHHUW2 z<^p9xv($XVKNvNP2@b$%{>>3U2YmuI`K$t=K->!X5k-SQiu9-M1Y+ z!tGSNuxkzr$D^q%F&`}~U?T~BOx4e8T_}`H; zdaujS&;tgj?qtQz(WQEULnD7~;t~yv{@hX?_IWZSZtcuQ3S~*NXg<^4G&=E0hEdXYrZM7 zcCrrCr4zb)LVbsvxziS!j$2_@O-63HiTT6NyZiyI)->of?tKhyZ5UrfGW%;upNW-@ zxn4qDJR#+&HlcrsAU@bxg}L_`jx5Uj%dhOuW~_Me*LZ^(2q1dXCFvJ*%pz~6$e^V! z{(c9~-BeneljX>aio$S;kxlM2rSbxuTz_Nv4;kG9Js5W_i!>>8kU5@IrufGBnf+i`t}HdC=qwb zTYZAHh?k6VTuX2oZ4{3wjR1S_BaLZqXvR)m`6i|gO#*MmM&N>12R_+<3l*-B3W6o% z5(>`~c*TTX4~a=Mkr?Hz9<3YP`_@t)X#};E!bUov6{(4UZF>G=-HE5jy8ql(!%&%J z+GF1vNk8vOBq&%D$a@7&Qs7~?33IQlxYq~RrKsWj#kd|AlW9Y!$R*U#@CPU*t&NHG zOY)66(|znHKse!{;}*(pg3wRV%PP{3dA)!UMG<@qOt~s~Sh*%qX`lmDPMn$>-%(!e zkypOU4X;m|bpf-fnUfG?L&rp!x8@{kfVE=V{8eZe!-ql`4O9n}L&1K#szYnEHsn^r zMWcAYoXI3IGsq$h-dvh9n#P0oqg9fw<-fcpgY1kuZs}Z?;r%nyvYA&P|AkACfH)3wUX%+$@D@N2Ur!OTKPAZOTXTN_h*H+cz>T38ULNbm$j1U>b`$EGfpn3RKKQgF;b*WNg30g^U+J|Cfc^8AksYEQ`K)V%?|XG;#f@bdmiJcUPZM-;kR z;+7I&YKc=;GaR4IjJWs7Pp?V%)`PB*Hj~X~AaI?=&xkjE905^(rfX69E+OI>UWx67 zx1t-rP(9Apl+7H_>qph)-z3-SlyxE!oWw{%xV^UXu0?-1xperJopVqwvuk^A(2}WD z`;@vkx-k3ntvJI9zWTPCmZqu|%{Hx?#=!#Eg=2v)?a31GDUK7D^Ehy5iYSsu$Xg07 ze7Fo5=+1H4(E~ScvST`mdTyQ!-q-HHTM2V$(Z#v}BUa`SxDc>CK(i!x{UzJaQq+PG z$vlF;^|QXI4RH>Wd-yYAi3cp4;n2?VYx_{{d`QK@5fh#eR&;Jt(g1_i1FfYYdvd(5 zH{Mv(R95mQ4)0%X&BM0SNb0ywf_aZRFE;P4^lWw=xlnb-*Iiz>!CQE_BZs1Xbyb^E zN=P{itN3r2$k`xd^6Qq8u|1<>3gzRlBSSH?eNin#r!;3+@BG8-IX)v%mzukyMkYaK zZBXEvwvBzArS(0@6Te#0=2)~NB)cM^?uLwhBC=$c*`P99XHJIEuo zQ57n!khuHcANJ9K4nAQqCf$+syG$lVRa+F)L?8()Osz!k2=Lks7xEsqVpL0Tg3sdn zpF6m)eR&|a==>iDQ0Fvj2hCTB&h429D4#ztD+<@;w{9>u=e19R9_XrE2|) zBR^Yu7gf>MXhbrPsm#5_I}bw6=a6Og?EO8=E_ZW*c?~vEI-W+X_gZRv* z;o3c`)2D0XdIxN>E-`otsk~;TQ^ce{8`>F^`zcZZ^iCvrRKYW z8M$rh38)HcXi#R1aTejt@ctK?J!3}UVrd_=6fOx~d^ERJO?p%I z=$!$~!j!opO0=Z6w9sYp{El_LpXt=%S8_YF1ue=NgwmHG-%7Syc|Q!1FgEjcRP2fK zybIz$CqZ^|W1}lCwCb=Ifq4=ig;GB+WWhw_fd}&6`Q@HLSg>&2T6qUksh#xdc1esx zWh9cvyiJxnYfC_dv&N+2@T(0OX#>m#V$&=GtVUrPrK#z(^RfBnJk2kim`q8gQCJQKKx|A+_1<-bXxlsM%T98WF^IQxH`&8-Fi=B^Uq2=7x)(Vm(VT zh7EPrSP?e0ozHt|{3gErySvi-x`_O{2M*prvEM=(SJhy>4J3!OA?Ub@uFNH%2*m@G ziUJfsD0ZqKCTmKb5o=}t7wnBHb}YiLZC7}!)-@07p|5QjqidE>Q7caS{29+7qxAjJ zc<1l49lgJDO6oi$yXe8Z!O;o-V(u1b3`$IDcEbP+uu1rlLRX0KODBs7XK% z!QtpkDUSxgqUbIMF5LGP8rv^1EF1<_0ndZ+TY|yWeZRjCojcdS`x7|IIQHMo9X_y_ z<{kW2vW#~d$H9=NdrTsnK8Z%1xeqM?WMmnT{;ytniC)!LMi9{!q4HaqYQF5j`XxvHt^xMg-UVr9*5`dX<}sR*v~G z_ZZ7s7I0Ht^>(Q|=NW0+(}q!Ar>?Hs21nIc01qc6{1K zJWvXY_|H{l7|2v#vXko5x_V| zZtWTHQ4TVqud!j(?mov<5)gdPt#@ebl_7FN>?186KE-^97(dW&8hZhUDP4$8A%grb zdd~Y&_P^w?(EohO6--5B%DV_&a^VERz<1Wmn60m&sH0s#^Aj5o7};q&Zmv2*g?-lF zfy%C#fq(}dcVsq-K7e#sigf!w<{<4gh|`VN-fEg!l-?FjyI88%AAQ&-7#C@#1e2}N zvxaakShv#kh(iNgwsR7&{+kRYKGnoGY#3M9Z<3gSWvzCa#4L`DRi-%z3- z2vY5cHkjVx$%!+3E$8muSpXr<_5me2AqBgkCPe@GCHuM4omrajJ0aW}?$zWA*K9tK0BS-Bk%f{u=TUI#Yn2f&PaQk-7@8w(wpp#tpqs@*!GRiF|s z36$RhrR!eXSSuw{F&z~eER)v@kPfYH2puf%N4~QhKD@{rwEq=$OB&ZdA5;&feQ2=zgQ* znwL1`gH)-QYEu!~M~*l~ZAa#6qgGElJ1s+Izj=Jdrh zXtwyA&a$?(7#9P#CuKf)VW$eev#Ye!&xDy7nb-SoRP#eJX}#;I81~6|$E4g~{-?{- zxF8T~+fzW^I3HtFW)3Sap#?3BW4!OTbvW961`ZhLIrqu72wWMt1L$~MSaQ9O*<0-x zHrm!S-Fi{G;28j2-mmisB*|JkvQ_Y0!=i(x_0v-CU%@s-WQra0A~c)G8qMHq-A9hw z6(oe=O_<&^1!_PO3a1b6mAwf&`aIo<*F#vY(*Jt*2ChiEn*F!`-)^NJLC%e=+IU30 zJJmc&N1}Hpf%kYd0~@Brb`3BLMvch>J96=AUBIx{7JCBud|5ePNXU)tj2tOFkZCangu> z4Y1-ef-zmisams9K_z#E7<_L45-ef@3?L033oNTqf#XsHBm<+L3Pvn(-J75~L0k5Q z+f=2?1*Y5IxPJ@BRw|237CEZm!cWxf7%k%D&mx6w<`ijY*aCfKB41Zmc?EXS zb*wypm`#N)t|vWiARbH%aQX*cYZFB~CfZ(e|8j8VV!*)%H8-4qkE~rGx$fiZqU7WY z$oOv*`n@4qiS#iL&?$&16P=4xwwAR}2Lt4UKx}zO!`I%$YvMTB*uN^+(oaEsaw)Pg z!(F8OE)Uo^SP^cE5rYMcs=AuovK2Ef_3!G5{x&}mPS6ri#)+2QnM-?trAZaIn&*Qc{GABEus;$ z3YnJ^Qaf~8LPJliEmCktwM9dZE4a_a4{8~e-pqG4KPcG)TPxj^9R$kamh+pQyjsc-ywq9 znUQ?!e?CO!_=Jl%?-cyuzwz$M7DsSP8oi6p)~Z0_&N>zVyO(Z76O)&iJl(l_+3-g9MQfya=g zw@(2%Kidw}lUdD;gB&u_AMA7=kU^w_BT;|M_D8;o`XK$L*I%P>)2x$&?eZlvA^^chg{yvh zE7rAn6us2DqvSJK)KK$o*InDWjp?eijJ$S&Dnm{H@6cTw$}BuKZ39769m@*$l+N*{ zjkw1=@9?Z0+W{Ez_8xt+dPCk(G;Ui9d`^!QmfR>B7LRGT?Ks(?)TZxo3b;_o<}mx+ zFKZ!UcyZ?fCMM+j3}$wFqzy`^Lub@o zvo9xA&&r3=T)oLkqI<{C)rh=S?n@a(g@SHvW;wzfzBFl0qrqkVWz8utPUj`fg2Xbz z?MOak`;KfTbw%R@vCe&ct$w=qCo24LazWfsOAn=2CIuvL32sOi8Xtc{l~BhU%n#(aZJ)zY>g-7?(V) z1)3a|BoDOYda}bmdze-VkYQz(txy@?K3m$`{VW^;*AaM`}Y2bq}6LF5Qke*6L{mZJ?7ozz#b@^%O-k~(c5=gzU z$VkIu6SEzbIX-VcirWk)QB3asbLwH=+eBwMI=o%MheH$yw%pPKU34$+0Z-HXr><2q z5T9fODUGXB3E3_~Q-a2^n#G`nEtO?4+b&Q&p>o3$#%0_8n@;`;lY%juY-lXSoHV_w z=03oY9Y}j@1a9MYW1<9H{bdRNDqWNr*S)`fduSb2LZMJK#V=iMv>z&qS|dH4PngY0 z*x|(P;Ey}1l{3Yw24Zi?P+q-78qiMhIb}3DZE`%YJQVO)zQwV%*_1i77)}XZ2wPVu zIx0O+s&o2B+*Mxy7P=w53rZc&R```~9#LFGB=9;z=nU+!C~vCDxrvVqPSVc}3u~7h z`LEfUIu}D{uRG=4v7N7jt2QcHLV|c;zADXJK+M-|Uos%gH@tp26YfkpL0D1$)3$ zn3uqn;4h2~o<^9*D=~6|M{U2}KiPi&o|z?{z($6uh64kQ3eSQK-)!DMI;*m~OlvE_ zps~P!)_aKoiy3T`mW4gzC@&1JHfUd7SSW#JPhGBl0?;OeQMt-hVT)v4wR5$|T;es2 z^HM!i6&K6riW8V0cad{7E=^mWgAggOUuQsbzzFe2Josgrmi&o5@34s}1zO5;vK#n0xiwsmW$m&T!8!4$;H+j<1q zG}#F#O8_P-F}2X4I2_k|ZHv4N$vNF4uT7+?S>>~*xa zNdK4Rn)|&kXMO-&3+@ctoJKmlOl%21WMr`42>Y(_X;?@d?hw79SgnG;fz_E(t|ge_ z$GZ3n%TWl!28>9Cb3n;(EgUaKI<}fj>oSWULFy*2;%759M-GIGGxicZ@(8ey;X99J zCS_#}sE2fscVae>Oi6t4TQIKsSxTlQnQY+oimc2$Ces6$+P~Ki{EC=L)swL*<=TBX z^NYu`m@=Xa2DY9>PviEVO1af5wDC&c{PRv|6W^p7Ef$#jTzx4-4gMokw9rMBqoA_S ziXY&8a+~y!LwJvq5jtQX*o`_vd;o(|^TT^=u}J{ExBl9xq0kgmeD_Ti_|Ktmg@@z} z^oc)AHHDdpo;yc+9eI?QqPviu)(q~-gh(Wm8<@mKIjgi>T4vZznl%==b(rJBogywk zh7v-+>aFWlitA$CXQ)=Mbrd$hjgXm#Rg{w{O*$8yKDKmGrQt;78{~N*P{?`SvcWu?bBR^Sy(USz3&X z!tms9x6U}laPto`sGG5zsMv8aqwxpy3SMJhH8g2^01!|k4h*r?mE_gkUg;)$Q>XC9 z6QS^=6!N97AV*~5R&e-pXomSbdkE`&J&n!?p`Ab#TLpBCzU_%|l9e;Q6JEO1pX$}V zFjO)71D+^^4eizBZifv_h1c(6cm5}s^!(4O0CC4fT@%OEsdcPAWGkcngT>{K?R9$b z=GJE`2f{`QRqnJUs!$T=NHrwFvLOs!#ko$0`lW61WK)t7Jec9nR@UV9!BM`xny4U8 zt&F1aa&Ai${b?1uMwc69Lnjj(W%(E%-7m{Nsd#BCM_tZ-B+!}_D%&RqVIF&_txgP- zh3x3BRX|H%UfEIOU&6PC+`t6Jj$gtoMDMufT-ZIL`pqE?JJ4icZokvq5X$1q?Kf$= zG6Gh%osAi81DZ+L9pfq=DQ(`9dGO&Z^cx$*`n^yBkPP&+3%kRY2Z2plyGv+S+?Pre z%2Xw+pbvfrkP9kQy&b{YtfYfH(!yE6e|By8+SG>3-#-UbbfM=XriVd$)AFIQz6@H+ z3q}0F$g1#PgMWM={5gfzCv7wZW*HXRQFW5~AcY?ik8jB?NsM}gu_4T$ZD@6enphEC z{53>4pIw_-XqK8J{`pI{n#!3up+u?RzFy^L$)j#30}>GQf8wTJQyc ze&~xV{4H5X?_TP~#h8;*>OV*c2k#*>+R(+snu+?&Z=F zb?{+eED+@wB{SIGRav}x>Eynp&;pkK0$So%(7nHUkOnln1hFEttV2>viGgD!cwb_c zC?i@Z8ySkg*R(&vx~3peB2LDeC?taf;&6jhMf#tf9BR}-+`rK;61P?y`fEl&`J+vN zBPqRoMD*1h<_OleGrf{-RR;khr42Oy`?J2^pAU5ZnQaRu^5#~wdk&DS{RyDI|G-Zk zsdO9ji3RUUG(S3BnE@>h)zXnazZ~L{&P-b|d?4+L|N}1sa z?tc!iR7o>Hnnk36i*Xu@P6K>_U`8Zn7%m|Uri z#i1*0)i$V@%ww4;Fq_=aFCC>>IvTC90sX3lRa8P@lTY2HMAbzBG!Z259b;jRv}@pV z6AaXo7u<3JY#_5r<+>y|zqs`|Ek}bX&Z*Qtzj}~1k)3~+fu>BHdiD&8GZH9K_qBno z_CQmHVL9G4w&CFivVVbg_qb1MUhv9U#6z|GjYmp4z^MUN@#A5A_));_Mjof8Nz-U* z0&(qhAQxV%i%Ng})hx1cCijS_oH)kU1)(mN%p+{sroCSY`y<0Cs4@oT1};lkYZt(4WIj~zPFn`^;okeB z1Uo1kT@KvU{0LHdoyYrz78wMVw)^*ua}Rowc@g256268nvDn0|8R;i31|zw9LIDTV z=YxmGg-%T>nsb%n2`t0rB;ss})8E41WDdcNBS}-`BVQrZrP<9I`;kUXb+*jR2q6kf zBjQ1}c0wP-m*!GM-FuKmg0S*c$*J{>I@}v`Ru&2hM!}2d431sZMiL*zHy_eP}u5afm1_EBf6HUC+b3M>tYa?|R60ng1>KSrNvvVW0 zZ7Gx7W;8rm1kTav^G#JO*K!1d)O?b(>zG6f(@ZJ{JTC@9YJ@?yh0H`#XD`P(6D#E5 z|ENWs8N%kcf0q8xVM$rnd~n=K|ZsC{tWbbvirvd3&x+hDSpAS4xQYLP0P;)_PRuP5%LAR{oLI%7uQ!2*%$4e4UdF zTBlq2$zWIgR3aRt%v<*Jm52hzGv5O9GoR? z-GY9-nC&4Tx|SnB(^=06Nq3kjBH(7>sO2E%gR41z0MJ`ykW09hL5cNdiGcH_ujisGDMoubHmgngVT^o>3PB9bx6e^X-Ie!W%Y~kl5UnQD ziY|C(s&3mj}xrGF(RhF)trEw&><&n&D10v6zrEC9d*a5QIm{Qh0g>e`8Q zk=|mC{hB@7;#IQI?M3|A7il(PF3<4U#QU)UK;95WwMczFC@*{yqwcHAue8nXhf?Ko zbuw0bzvq0ANqj}zHikqb!d?>o36>rj(F`yq*amL^m`IeX62?HK9+(!O$Q=I*C|J7t zR2FfuxOK%z-aPc9DygJvsk{eK^;1?KC68mI?vW#v9uH2f(1PAs{TJfa8CL|p0s=Q7 zTwPE4{AdvjH{9ljA^PCH4KSHAb-K@(;h^1%Gy*)V4V0uSp<)HXXav8mEcM zDH%^2KQlltiGr5J)ZRt*dPN1(pk!uKx3WtTk;(xQ z>x~*%?&R&OI`<&N76i+MUQoS>QVK-7LNu9G21cVe-(0kh*P4f$Xc}C-Y|jMKzM2-W@gdcsZT~ zz7gmpsIfch{;lKk<#4a_3?b(f53uR15#^KldLcf=-BF&&*cE+ReN2ewq_i<>IAa0A zE+3CYw@+5u*jmp}D= zs}RiZhXYNvLdj|In#fX086rZ}O#Tk$ir-qY++q8~M+taK?)qdHcbDL6Jc_ZhA!tmL z6ZP@S@Q(_N`HloS+%p+BN3+n8bhxo%(H-8;+~O_~EPf)Mb<6{fCm6^YX;4!@E2qC4 zbtb9SVg}0Lz0grH6=WfTr$g0G_M!NW8gD%ImbP-S^H23tCav6Wo4l|sHn7ter!>NL zTHkvzFzyl0AeA-Z20PzQaeKF*U~8aYcUj|X|9rTcCIsO^P?PanQ4MjwFQYDYDdixa-%wfq(#u{Zj56%kOe^&XVhpXnz#Zz^1;9G4TLHi)-id^M z56}~>;lNt22sf3KaDfs$Gq}IgJdvS z*DDB?oG%0B6?Qm~)1Cn~Sw%=SB7<7sqH(3Lw z5_kM?b8PNelxWjtL?^OK@?Ez5S@kgVKk{aEz1*@|XUn;ihtOPu%`jyCV*Hog((($n`BS!vjghZFHl-5l^0Rikn_{wBhl zsgdjf=X;>ltgZ-2P_UHJThHt=?QEf$7NgT+FYuUsTxx^W#rM(PFlAC?P?#C)d;8;^ ze6&ozZ}3vks3<;1b}oOA97R8gGS(^pzR927ubip$w2=^{357Sn}4SvPf#k=rSj#nDwq#y^i`-}#i zkP*Lg1W~2fo?!^)zBt5$;p6(P$#?nM2H%pDm>2lT~=IdeX&u`MT>1!Cck`?lpW z*AQdTw%mTVPe;zlQ;-b%Pgkzj8hoNxz++H_;+Y+w^mGBOA2(zR0;RLzVh#VRxDD>J zP{N$N$Q!4=MM79M4JV`Jt|f&gl&4>QvQ9b^P+ch6P{^rGG=!K)zOn--K zMDbPyekj;gKT^C~FGT=%uGHFq!hkr}G!d+%yN&A)Ht$i$D}Y(LSe9~ zONN}8ONk!dGCg~GzI2;2zFG9!QY2$pO8go$&i;r3X)wk1Xes<-DXB%{W z1PHIG-`W5^_tEq-yTlrwfvz}A>d*2LITHS6tzqD&@uS{5*EJC=28SIpDp|S9Sm(I4 zjaE%xRyNI$b-TV`-m8-pPDs+xG_iW^U>0U70p$axDcVVax`U?V4(}3~B1Ny-Ujzqv z;kjUsKf=Ywg>)PwhaKJGnn^dU9dMPveu&czME*;6;BU3XL{THkrJzN-=BU36; z9(+g>Ec0eF@PGHfVDg@kM*QrKVQ1}K^9lS>g$p`w4M8m1a??XWksiOG^;fhYzR6U_ z+zos)HYL;iwW}@GSoT`7qSe)gTdJ;`>$1$n?ywJwecyDn`?6gnsS$Zbx$b51f5lFQ zUl|T#Cv7x>v|#3wF|NXH!#YovF^t|Q`OZv8#sA*>%*@KBu287&lq*Z#Bo%^V*~Hdp zPH|OaDS+f5R}m0pWoSRccf@ZRbnGvVKW{!MkOg(Pli9(Q`jVjQJ>fM{;(eT! zh~gFo_z>U1w;q&Ucmug@d~SU{-A;R0@OPoxhf2@ZySbHD*942wjTfSZ-GqZ=;ehQA zI*+{UuR|o_8RDm8Xry0(EN~T}C_F~4Fkc>Po3d*g{zJzsCB^Z+T9|S~svuw1dLMmhH-l; zpz4d%EsMm`t*<;AQ}By9A(p?YgxbgHyfXBRh3-b9#jbF*U0^f>tyIDR@R?Nxp z*9~O5`gPH2Fn(U8R&^hi1BCLmR0+t0)!1q*QF08h{qkWxk=Bkc9J0={)O`|v1B$lu z7@AZOXO&`JvW!xO+3sl_VB!0;c_iRou7Y7Il8js@80%A~9!xZN*xN1jsxG8MPY`3i z_;#ME5b+I?0qn>(Y4gQ9IFmVp|l9!wos| zMy0M(;ZX;dHVsh~UlWp7;$!@Dk@&HAC@oEkj;AqT>cR+L$y5Uc%uaB#5|hkqKzwk% zPV4S+Zk+25P#WW|qo4q_^udJ`_hI9u@7A5od4kx3ls^7T;-`Km#giX0ZCUSp*Ao`; zANPR~qPN2koUJkzI*TbYku+|G@tHBUo^rGRh`I0=n=CZ+zI67=MR1fFF<<9pgHk&D zyTGlv+95ROWgD(ayYI|q5p%spNCd5I0|MfD`&GGfEN%`8?p#`zgw;n9tO*pfEfWxp zdRDJO4Nbqt8bCti!P4n`xP!L2oiukP!d%#)3!DCe(|yqb*uQ;3%se_D%>IQAxJ)iq zy>FEXX}DI)EojbHc(*}O3=K8HhRD@|H20F@QwBX~udu?2578GWB+0~5V)QZ)E{SDb z7A|)C@Q(ykn(6%uta*4sVn#$Xyh^!lygloZ!Xc6Aj}ckIrjjh=61^4^I2G(>6KXdfOS^YJ!|4e}f46+E8AKxo)CE%|KQe1Pu zf7#B&jv;dCzk5VxOCzo7w53YZPv6noROs(3yCLmR4kQ_xJ*y+;DfoS{+ZEkDK7=a7 zjnV`dUz#JRx1Ebzt=#j1!pzty9YSnnvkd7Tu3}c|;Q>Dw7?wa*#%u$C%GBsY+ji^O zPpdKD`D=^s_J*@X?F{;FTFGDg4|#~N(f_&U*Dy&|m%=kYU`E51AbLleRvnJZ$&c~4 z2+x$d+h(Cl$2_kNGk?OYqV|$z0o#_Z59i(3$szsvbFEQA4ZO^>_Dne%EjG3+^XNU> zONm4rJF|*;)Pg{rsFD#Vm_jX^RK11J@$t zA$$FeET#kAvon|!=!wg^8bnoHR+9aOASPe{$?t_&RmreDSEXt=GVg`Jh>}*tHCXS+ z>A%XiPV1w&DA6B4F{^YlI{xa}N`AE|tLx83*o*;_0W>HYP5kSA4@ZC`N?`5vaL4kA zGbY&^tv;O}Q!H^e2TqntY+jt*6l}Hmv|M$mQ(m|Mj59xZ<-Yo5Ap(cP!DP|$mL9fU zZFPB*EuuGXYhzVHa#o7cc|xV2fHRpdCcn~0kAHTuJZtShrBaqAbCz2L5L<7jG$L=7 zyNJy?<-sl>S9w6MF_9-?{z#^vA-3jN{_8p{BAUw#amVXFr&X}-syHp8X_}KvrxHmI z_P6x=adhIPpKtB6#-{+C74}7z^~ZvRkU=^)MiASOL1=M-`Rp!1*nTc}ONQ6Po&jo$ zSt0O<)WjN|;SzpHmnSd$yViBQWfW7NtKp@2xo))qJuaq~@Uml{4g)V;Qk{B%SlG>O zYUH-nIh7mf$|bmJ{5YBZSh(y<3&$Q~n}8cL$I@V*78=e+)4RB+uqT#h%SX3Ka1IzR z>+z>GkynT-%qmIPX-DR!s~2^`U}Qe_P{Uj#UNk=0uMov-p`<7u#*WhsXN0aH>S-Tc z$int9W*C7<{Lgu-M*<)x^}aQhlV<~}FDng&JZiH=;5gC(tD8alEd);Aj?EsD@ytg~q|?Z+`+jr`kEaS;Of0%tmU_GO z!Vjd#gGCTZQ>${71S{+%{#l!sEie3$_RqR3npPUn3@jB`W&84DuLX`~Ac(@K^em%j zW|hh2JreV+{mSSUB?Nl3><#Nm#=m7BqQ_jn%%qK3D2vf&40apmvb3*OzU=NcXPG;B zw}NxSIq0RP)Xl^u-uIDVMw!`VV$%I0^m_=aFAcOdDf?t^umET89$%ofWndV#G>h!P zZO4Xjrwq7fT_p9MwAK84QV2;XOkAk=RjZeGgUqbF5B=!}B&~hWXWw#Gm2Z33jSXmx z)hqinE1A!`H^MmEfF}nDp5BvInxt6Lm`k#DcxLGb;KxX=qMIJ-o5-<(6}}rC6DIf>P3qSbM!1L!B$B#O5k zRRJYTt0WmBg^V%V08%q}So_7jP)3NJ9#oq9fsz6}P#8jAK~y`Dy?sG@YVe)iJpkVMEJAQyzRah-(67@=RPp|0KfY1DT~hQ3cTaMKd;UC_%4{;r2(GGhMZ= z%D>j}Imh@EIKRlLMAVI_=)S#3$mh1&&;tOR$)o0-*eG{P`a+3au@X$Wx(%H0F> z6OOfWYkV=rhF_ywye{r%S>d}*=&FTJ`Y${Yg#rvAbi|F^=DtLh7f#DTBqXdMpOAtW z+h=O%5qN}9;#wohk5WVfU8+Aw4C1HQCh097!{njFSJaxtv<+?7T{BiyM}~ESCN~23 z68+%iiUL++ngJ@XRO%Qy$6Mk{@KHx$%imqhWZcu+LPCVdmSAa~nFpMjbjRc4m(7nq z572WRS@F3nE+bs4I(9qUF3~gvz z@PLuoVJqyGJgDJIAz}#(15h|G@(XHgt>6F~PwBfd0rY|@2uc2PoSp|qL}NpfJF}L^ zGU3)|*1EHW?!G&#*GGxK(Q{0cV1;HWn`l$5hmI#X8BG1VXHM5sh-V+4%a$cz^((7Q z2NQQL{n~!&F5@Rclt{+`bthOeuT?s|Lp{k*+KPRLlUf6~bV#|prB}-t3Hqn!`k)n>Bz4r;;kseW6{xlOvOV2rv zMROha6g?mqUqC+NnG|5C2|WaKJi2jikepR%JG?G{msq*7;&yrh$SFe=ksFc(i8aSyl_AR7`sb=3cM8GV{jqM1+$7b;r{^}K;*yZWW(o!B9cbQ zrY3C!P3H^W*rcMVo)w^oBBI<&c_o7e09~ z8+zX+?gxNU&vKK}s?FIXhwr?&(xW~?G(Ou?1O&slMjQ#qsV*h2OvyVu zIce4Y4e@z2t}35gO$b{rSCnj$PzWOVa=M<{$YtH1Z(p*GX2UwO6lRsZGMmn93N~S}M zSmnT6Q$gLXDY=xy%ghAmh|oIpBID3~vZgXY9TV=&VBM#bR7F`jhjI51;hKabuik%q z()zi$(fo|DRni%wXNYT{cuKVL6SH-I zb0DBN*k2v$wCBiizT_w5_?Qw+K3Q=LVg|{zKISb8L#T_pS93#(qLp>Pdx2Gv`y9X+u_!Z?W6z;ACN~2dUHiy)KHwdHy@Ppj{Zvp|hO=`kP-&CUb8*hLN zk(XG?%n-XPvH71G&qa0MS$Fxtj}Rjd^X-+B@5>X&Z%4}tvmu8(xXJ@pBltVFD@1-D zAVMgc%&D``rJDfB7w;(*Z>&{=2h{%7o#}8fcky^xyvPe-*cDgD(W<%w9qKU1oR#JO zun8^wBtBnDD!s6;ytR>FrJVMu7LxE<1EfGsZ{J~D5D*!9tyhYKxgT$|nUQb^o7`nD zhtKO|PrehS;q0gQ#V%&*rAHZjhYKz3%*bF*SiqJa!zdw81_6s}dyg@NZVy6FwP5Z? zTDl}l^iEbl+!v881$vc9762V+?D7KN9|Xpgr5<7CmDE;&&TQ%C;xE>6aP`u0gW3lSR)`N>~oJwC)$#R8$rM+U5OpTYP796WiN=S}M&IOvSq z`KjCqg&WN)JW*iS{CN`n)jI#fn^P&Z;UKDTXR<*k^De*Sa2}~edc;3FQR-68G`_IIGpuB^Bd41sY^(oUt+I4p?xb{gce=UH@B3|Z5G^DxZ zyqha$z+8DmsRlf+U*I`|6HdCtTd z%-d_}C>yM3s4TSYBpX)$oEd#qu!yaG_TR!1MQ$~ngav*S|9_O5k^3b<&lz#ySK~S_ z07S5L4k-*o*sO-rh~&c}eY8b$pCq}1U6qKkx>v%4Cd=jeEPvgnZ1vd9GWL3AlO_m6 zz7=&E5JUF>i=*DrI=JTHKeEkwk)ch$U20P(-0nBK+PTd)yy&gwH60(yH|G`<}u4jDaXyvZEqQWg(QuOJ<`6Rqm22 znUWVCY>~8jPi-r8e)T-_t-@F2F8xJBnlQ6K;OAMP3D|z6s8fPuG(FQjUKqQ@;%or; z)dN0D^IeTHo@=aUT8T|ndjg=-Aic2S$-4AfRiCw5g#G&_h3$ziWgD;L9&$Rv z_t(k+yZ~75PyvB7m$wz(-aBCj6!vWjXO5Qf>lqKCI$u+Hrg2!d?ND)|%vT*vKuNbW zKK)pfbu)QYgaYcL%$%e%ZCWr|hi2bJEmzJZVo z>-h}h`pxw{o+(AJEF4x>^}ObCyBad{@qAH1k~uCD^~K=L$V|L>Q@LRW1?q4v5&{Q8 z{^%zOqKr-hKVif*9{eSGNG7>5=#7vqbb=ox&6AuGJ+x7#w9x}<(iIwd^!F2`L!FObIHogvcZSIi;9VpTivHL^Mt!dy-#p4)ln_~Ja4s&NZT|K)jN z`JKyIdhCnr>YHEZx)%N%AXB?epo<}ff7wdwK6}QO-^V1X~`X6bny$|F4S+J*Y0M&4fOp@eX0?q6rl-E#@+ z;H1}>QezsD5JJw0&pZV(W>EtOEv{s*EDuCLd$K!sR4YqLutM!^8X0Z^SVrT0vR2AN zQ49w$(|pvFnJ$of*LOFcP*!ok=NwAxtZ^#eSW10MT1?3-IH{OUDDY<%Z8KBZS*Ro+ z8Hx?39F?qA_7t&>VgoLw%uH|=FnF^AL6NX)q(jK^52;ecz???p?x&9kzCCypE(P_4 zrlr)1@fD1meDW7E=g5YH(mi z(}Fw6aat*zy=drhK?$E2R&KB<<8!rxhO=Avt3gAp4nCn0;jRlrR)di7n z)7)lMdF7&SqWe&tu_m2?xT35YP)cs}(d0Zmxtn3{ldD4HOM&n~6xo4Y8#T{Uhz**f zxp5#8_R_|KH%$~tDtMbZz|;bv$FG1}xEN4lp=xy6I>Ci;DT+?OX;ArG5*S=E_lXA^ z2fOX=r~=b2uRov}0x?gwmgdD&{cA0Erlg3HleQET-4EO356qO*j zOeTUurPChh%ukbOKIj{BV2C#PX+dq?4j~#7=7--e!WM;ci1#%E5~fm^1US3A{VmrI zN)F~3T({Ca6o2YubJwc#CG$3d99MDvs0lXEQ#u!x$t zS)dK2M+v3xN`A3Y52ya|c4ykzdobbl&?)Dx3WMl5cfowGNaAeAI9M{nml!6xWKjhb zEi1_JDmt`sp4`%EuBhmsivM5TElN9Gj@7yP9`&!q`bWomFKuQpNQ+V6V7~ z|MwE<9u4iYWXK%}$EWs{46>cuK@h=8w$?7C-RjE~K9A1XpnwyP^Wca>2i3Qc1BX8G zn02rNrmtw}M(t}ta*5bpvAm{ISV7MF`CKow_kB2)QgwZQ0XKn-ZKVH$Zf*9^AKKd{AXD*vBAB%>}au4X`|-t(=8OUfKXt)q8d}hdthiqjLL? zJ|HTplv2?u3xi>sylv&>Z(8J|EbWF=H>gV~$fet5zxZyf1;hamMfDknI~uuecC zrl>HRs#X9i{g|K3{*kzm(;|yWY_MPknA^}(bUL)9r0*wc7OEGz&6haH6hRne#8QrV z!}&w*HpdRr}3% zA)KcXQG7>EN^6+qf-W^G6G`Y4_LF8aP^dweEa7imid86lJ)vE#b4eMYhaLya1_uHk zSGF9aKu9AL{tUEMA&;JTJbCepERZf}t&4!Xg~@SyBADBvz4Liij~gjspH39}eHXjY z4sVKCX;WXCwp+-DTOCDOvt6Z+v?GV^lBREP=1zmlqfj2v8LwX}(qdWxY|!*9@{q{< zg@RS$m|Q{Z_Bf3Lsl)!?PAK;su1Be6;1)xYmOp{5lfU_Q+kt@=^===qnSqBtYph=p z&o6+!i1BRhp#r97M|*+Yd+PsfVKqn?rIqQT`iBe5EjG>RK0 zH`O^{PqA$=qwN)+pf+sLp2$~vR}-0GU`h4Zb)tBVj03@G-5BX&x1Xn<=jhB}uo*r} zfWES&SyQE8=fM^e4=_ma4;Al;vtHj8`tQB{gPn8vHgGeD1+6ZOfC=$TUT8s2lCo1| zU%q5s^1oz7#_PJEln$-8MPL3$bvpO@(Vy-W)fBqRB=c^Mslwn3cj?->4>0od03Sg% z#N;y$;dYP|E0SEdg_$$-gcY`tv??aGu5}wBtWBbj~ zsoaQ}4b)H17Jbf~=@gp7FMFq|Yr#<|W)+{COywv$H-&&-2d^8iAf4gdI|R}@f(8U* zBXMUn!N0D$(W>@(Uk;a0GB;`O7My=ai0+R zL3I-WrTLi%w6XM8m0On7!zy`LC8MOB5G1{Ko@;X&Ne(_as*ibR5YZh|LZgDTYJ8V z^g4E|Q261&bj~Pn&N2Zu1}htvDyg>+$l&}NMa~ss=^1wJh<}!7{d$S=$KCie)7MJ< zS$$w2tO1hwofNjP>_Y~mbB*1PVHK?jx7<>YXbXlsTs-Gl)|Zih0>cQs5H7bW^I$&1 zL^U-4X{F<7mXSS8B0K-(gQt!>o4guLLf9L%lv6~02TAA5J(@!bd9o9+h3~m0`jQ}- zike4+b$)eWOD5M4g!Ki|5VqZbdH8&eEv5dx4)}-1HPA0C1o6oSCwQ_liZH(< z*~7aJrwR_e{*8z8!Zw~uYQlSgLxfS|@+j;vB$QsTZQ}W)-uq zKOSp?BjUiP$}r^!PPZ?x@{ z@zJ~IdCpC+9PK+t6|sAOP(R^6mfTL z->5icOmmZ(vX2JzvS{`SS&zy)jN26$-m>eovi?|~Lt9Lr5pZP#D$W~_GFKuJG^q=D z3)9CCJ*BxL*7>dlk1&!B62q;i`#5UW=>D}vxepBZ-EMi-gW!CE>s}jJIw7!aIF&(> zg-sQ{zTZ?toxuP>-Vyell5;;Remoj1pc$GUd=?hv`BK$;PQ%XDV1{URb)wrf8;cmu zB`JK`At_Q$;1Lf{-zM*!=xH`3u=!JD$@<>KQ7BC8l?uD0HGX9=o8d68ni|C0Mek?e z%T!w(w%Y;bXid2g3xn4|gBV@$r(D12lXawPyE3vnVh~$_rpoQ8StEO&w)LzF^;RDL%ifjWRX$ixF5gN{P{0VhmAQ$0z1E?n$>{GaP>igt6%fx$Hed;4o+W4J3O*P(5r>}osu*$+)+c^#9L zgDuluKg11+a=25}la{QbH!^wE~mD zYEiXDt+S=v6lp%eiKGT-o_!dB>D2}qDv!KvQ5Cg6T;RIRXiR=*=jXI|gcqB4jW45S z3VvL1`Ck5RZ2q&|7qQ+q+boq(#7L}qXVP1@tZmI+l}p)VQ-sQQ?$Jc7uA6vMl~%nC zXDzMlOqgLD&HM)!^^AelVbNFC;a>oBzIafFw^i0>)5D8qzyATFzjq?S4?^6#7<&|A zZSa-2fg+fQ$kCHEz=PAxo9fb>%WT1z&J5@x8iL z!9XOl>^imvdmY{Cw~WE{pWAsMLF^0o2j6?41r?CMVa{DPZs^eG;dy8vl!qWcVwxJMHk%sQX z-d6o{(-qfTxMJY#Re0&>owLbI*OR9dyMxQTmUT}BzX?6ds617C9Sg{mjRq9Ct<&-= zHUwr49S-QwPa%C1ikB1LlCU9$1wxvpD=X(Py@5#BILQCO_BR$}6=ibi%=79YmGXwj zi5{zQ+=Lez-AWDCl(VC%Q0E$QN%D?u43tOhGc{~mNAmW}ox^;Ovtu&4k6#ELQsc7z z!F(=7O4@RK{~Ak#+i=~-x_;Y^h>DncQ{^88JguY(b8gD&ZUWv#8+WBW{$LT6YU39s zU(?a@s^ssE)a%EV*|p+lY!-*4t?R?JpfLo)S|x&L%GsLMU`pQd;UwjZul32E*Hc@#`#bS1XP07UULG z@+N%*zb3^X)myTM6oK9FGb-8lUC<(7DTL0m1ZmMLIf($d$5+9@+d~0UJ_M`*P1D~W z!OLwriEx3J1L4+8Nv>-4tOQ%bgj=;Dwo5_eR)`g=* zY}FSXxMkoX>;}o{!rP!U0rKE#AHJ|Q_VV8E1paJEtZXtHrOzjJDvt3rE7u&Bf8wIy?|;bco1DE; z%olOV!CMGKWMAiT-Kwv_(QEA0hGQ~>XgXDL;97pc5SQPJfl+}LLbku>BNZ>!ncRC2 zdl_VMSAEz+9{VOH_fb==|M9t%ur-+b@e4-e_C;ujmiu&w*I|vYEv-mkbW}d`#9e}iNVpgSRSc5~Lptoz zS%I-Xl7u%_kA>aaH=!e8BUux-`@hrUGa%3>fSj~1dy+-vX9RQYT?7GJ3|t!cVl_XZ zwwr)(tN`T|xBiRgxoD|UkMkrQ`EP|@`^|x+zx6ryVOQr=;dY4;m>SG7VT^V5Ulb>goCe9{2>`&h(96A0(Y2Sp5_CLjszJ zzlaB$H;s7{Wm)_?fZuOesmdzh$I2aYNY}Dfdh8|T!2%P8W-g)@_cHaZ$qtMZBA|Qw z@~#_T(R?|HoSz)}8&N@@VGw9TCpAA3RCI=kDs?4dqanFuZuhS`3__I0MzAiJa1J3( z)}kSX%QUm~7j}{GU*HbXYAnm}$;YfX zD+}-zRLP~563+kr7fW8Rl3T4gjMZmv!0+O+D9OAnnS^^P`7Z@Z|InHW@(e6kd7~XQ z6En`&rt|`xPXYh0|H>H_9eEE?^|`6`hfq1XQo|8fQ6V}aju6Z%#~^I{IRMB*IRnAN zhnCUuAit$}un|^u7OhMdpAM^Bs-8LVOT|RP0a9g8yj+p0-E$+Ox-5H0rN^PU2|0`Q zoujNXEN&x!8DWI~*;T>gvos`&O)uo*m6LnyF79hw$Un8}7k?iQjs(P{`8{Xo`lyew z@@=6fot4aYUQ^m6*=%=zxxn^PAU;2Tzd(sZNveqHf#IW?jPSQ5H58BLTG$I?`5E?T z;sySL6W~%`19#n8nCKOx-M6W`C{`L>(+6n(JD+o|uwO@b)rk>gF;v#Y_ZwLPpKB{~ z<(w}|?ai4CQDHb))4{hn>W!*X9y`|g9?5k@gY*aZ3b+gfJ57U(XjymiBdY?UaI2w? z-UV_j=tSa#na%5^279AX=yov{-1utKMu>`T2Z+nNp)o-47-g7fzC&S<0YS)EKGBkG0&K%R?D?;=OUvDzK;wKL&&~NPH9qkjHWr25r?I zB{~sTMxT*t3C|8P_PJmg(EfB5??>_Ect(F8r;;PsuuNrL_eaG1K)1@m!!ExOk6su?4rtHd1XZD<{1)|e z_27$)yTCr@kS!we5$AkMZSAVp0WH)i_;8AhsKrHsRUUhsDTW#}(Doq0otMS+)~{Wh z(|w(A0iFX=Y^iXIz*y?cP!0CyQ@1jXP0Er-q@*Mw+TF?Vbqrn*{x)}%A?KGB1t;#G zYF)<9UpCWo$_v7QINM!LX$FCgk>_#J{K|WS?DsI#Ii*#>M7((OK`zoTX<5jdzcpZX8B z{}pI7XfH>T5EN1Aq4mBQNtKsikE^F_R&R)4y)1C!v@&z&4trVbUM3z!sLx7w90ppV z#{*UBvg-6zAdSh(%*&NgwQ%j93iT8{8G->z3hRq$-7iU_v}4-7)mT3f zj;Xn0Lns0ZiC*O~40rGePcJu62;@{o>uz2kb3UAs{de*+=EAk@rhl5Rkw`>HUm`EV zyDlYE4X~r5;BhHvy75A+S{NWEFdm=f-G+7SV66)#1dt2m>iip=jKrblXAQ|5E54tD zTh}A|jhGx;(sMc14S`fE`F2cUP;)lBfJeiG9oUrRr@Ke~Y&5jrP2Lx!S&Q93YhaD@ zc&MMV^HJ(ijtGN9mK4kzx=Z#0hV9G`zWQ^pAJ4JZw4cRzhE4P@r zurwOFK%Xj9HdSR8nY5f+i~BE))$MmgP_;$Aq}z@M!{jOPTdNg z@HDnSfPw914y%quV0u=Qph0#{{vS=Y-SZd%G(@=AG%`{@n=-vjN$8T0}85)8_3SmV;iKFVmTp5ZfTjSM2c zA@v{+OQyeX9C$(tlt9Btk}v=6TJnEoey&ROEdekcMX155f4|7W27ggtVc${?xiyrS z01z7TlOLxz|1~C5=-_ zKeqVr6)XYFAk8J~kT5lYZ#JjA zEUWlc3pJS_)+!f~G+*U|gc!D)J;;%lYBSv&c>OqL9?FT`tw6+XHyP?fO*=ANh)#&^ znonJ8m18Te_C?#3-#qPJ*z1A8{rjsSa_1zJy8`Y;uNxtk{qIpkn2-n>5U~MJv(!Tl zyY=_o6N@I);L`=9v60)PnQ;y!VH_gfjd&Nu@a`Juh@nUnA`8V?YK9JU61$!KqBipj z=nKH}-7#(XbqD0;5rDJEbs$KfxiR&-q8PQX`={XZoco5zFq5;jEU8yEh%vRo?nPtq zHDTdz*{F{r3gorAq8Y_i`2D3dqvt&aLfnu4aI=)Q#iH}MX3rOj!E^$21k_fE06%1i zt`9yxJU+ij-^sI})rmmf1_rU?K}Up4p)GR5e(A5!Ef!3isA1D~>r(16Rs|z>Idc~P zbxR`ZJA#U}A6}(PqhJ~nO!^4yVS*Gb<9R$~L=Fo?xBr^YV-qX1iKpOp0tZfEX;+?r zQ#SY&AKCalQrXuH@uadedW<)tYB!45rGY%5KElr@<^*nPuqPDo+w5M{yrlPIVDc6m zJ^A9HKy`7vb26X@9eG`jWRKMDAcD*lt|6unsiP`7oqHQN_`)= zIhp#twNQB5Bk=&R@5g)Mu)gTYL_O)ZK1Wdn7^R8T4AenG^`*nm0oVwMQDKfje^#g!$(3YBU|fZa;pSE6}z+bjE_wJLXgRA5%@FoUr57B>@)Tz zt*ROn#!8Mx)go4XG5cBVRNv;fh}+P{^N(EKcdb7YxtFiE_qJS(r&;gu zX&XQHFwmzI+4sW4u56^pTTzitOWpMFM9LK`$QJG@=VI?`U{Hp~vcHq0{v8&UXhV~vi)&p+n)>#!>nmx>n|G3H5s!YZDPsqL%?2gE(oL}+W zsqo}w%*RI)0kp*XS6ppZ6oQGKNN722Q=Xkm7v5^2%_g^;N>QK8RfM*gvl0XDva|b7c zR@Ts3$$nPJ&iRxG0ljBgyyUB=THm@rA0FglMqR%#(WZhTRJB;oN9q*2 zQQR@Y-d!ODu*o$2#se3}4u!segGs#5C&kNtd&d0b6!=@IfY6Z3QxG<-W(K%e=^0cE zM?)`agyA!eU-+Db*12XbXg7eS6mn!*`Xss^=1}fr4cPC;+r}M3uoM{%;2yQ50 zi}5^6Zd5z^A6RTJl?G!qE{lKM4Su2;stJX;OOY)9N|igDsPpVT2Y=`54XPqFO47)5 z*L>jS^lGNf)P0KAR8Sw!5g#Ubxwg%^V!=77;c+0egBvog3Il04AzOIB#;zfy1*FEo z>f4!kgJ_2jYY^?VD)Ag2Tr`y`F*9#*aW+b4av!`3H&$+{F*t;1rNW_iWoepLbDOw;G_()4oKzL`_+L+^~hcmbJWEhvGk z${k*gBige)Gm5vrPkv!jZ9B@CSU>C95|`8tR$d_?#MygD7t|E&+7yOrerIPqF2@ddzJ* z-q+)#^V+g*0Byn*4-!`VF6q`i*Eu(t#(%4f1TJPQxea|OgyNfzO$kei76=0ek@o*; z8GxLFJUQ>z136xcFgR?_o`Gr%P}N0CDL&Iq7f0cL-eNzR1q?a zc+qip=)Q)}3i_r9oRi{T#qY||vXUi7b3+2K(Z-TH1kGi=)K#&P%Ey~9i*_qe{B_kwKMg40t7vec#|D(Q6<^5pVay;& zw`WASH+Xbg_&%IG$mPu%!gOURvvjRA3tf^hD-~`Z`Dai_GA=@LT`vlf`_XR-(h#Tv82t2cR_G+Je1Y?;p$Jk1$wQfeM_8%dq)8glt!!} z8!P*AwQh*e7Nb+LBieN=Vl7{EhB(*@(7y!SaTVA+FrlQRDrg2O78PYGWG*(KzguW} zPf?auwfHxA^uk%F52ZmQ_I|N96S|Mni4p9-DlcgX`~z##2oXs2reRvMr5Wl6@a}N6 z-M`QTVj+aaw>(SFw*o;{7Yz@{1CGnNjI$~og*|3?GRY6vb!A7g){UskzsJ5;s+%;v zgwt<&zAqkoRgoVU0ffsYm$SDF+Y(t?E15YeP1J@+RQ=~QEig!I z;>6x>RD^LAEvb{W!HIRla-=VnWc9ME(jMZTBphHd2M0d4YN+A33SY7s=wmw~mnm6Y z>+9}L>kaq=%wNs6s*S$M%0Svp9~7iK4#@$Ygzp2p_MiYEr_f6Z0mjq%C!?fsA;_xm%1(vG~7XiLqQlcsA_21a2aXi z=zr|WZY&D{#LGszQgQ0GqDQUZ-sTKeUF=1&Q`E&dg+_B05*vdXu#P*0( z;Ij>?Qhh<%6f&diU=mObrVt@ZzDr1BEli~vi3){`T9PXKG#R-Zr9<{pka@ePrq2oZ z6eC-iSX1hR`PZ#h*0yo~0!F%SJmS>b_uh8LMGd6s=GW0fVbo1BQsmZ>!`%j!jKPt| zD05>tVz^{##VM>wm8PvY2{rcWb9e>b$)}3*^dxt3@IjX%14S=xel?wSS4a^ETllqg zUWY$bhvIt&YV;@_fc&)4u3pN)#~JA&PRe-S=}EGwU#eECg~ zFa~O;O3;s?@7o{lhCertdo$FQoXmygj$4!^;z6Wy&7s_yp92BwM0?tsN{|$p|MwmM z!d>y_+3-c$rnq9Q%GU)lg&evplU7xPNu?0s764iU~(# zy;PJ_E*GaE4BF3;r=;k~2wKfOL};%-C2sRzhqH6y@;>9*=2QUlj;({ivzWlNJf95FD2(LLp}aMc4t4 z<*6ifN!8}#rk@~TFw;yvJ~unvNjC?}9azW)&W*z#RRyfJoRB9Imxqsq6@MYmAJ*<` z>FTHXX*c_t8q1oVPJ0m3am1=QyIOQc=_=Sw%gAy+Y25#>!@1#XNxSuq^|`zCtqP%= zpI7(`UpFb$jTB36-_yWIJ>-*F?>vB;#lsc~(h86A8`aJ|sr$7pCGyg}s+x%OyWEXT z_y<1Qcq=9z7nj4^x+2`$`ZzMj7E!bK!XKvDGDt(?b$lGBueWw(DgH`P8IJ@D&NM}B z1O&|10Rx*FXzEZYp<^Kwgm;fVPG{ni^`|bYB0C^(V(o=7={MBLSxALVva}%mwLhW0 zCa;LYS1LS@bJb$5kmji_R!_~@d3uo5LCiP6H+#R}klCNbn%y10whgF}J``ydbNOC& zyy#Nt(5GV-D2q;RecbD48J|;KxkK~_DN}>2u%YiczYz6MIZB78j6~9WsH3Y7UWwfu zuHRprOX@Xd${})4{8_WZC&v>qPj^IK26jIyn))SYmO48+Ry%UHwuJ;Pek{#M{=Tyi zXgQg_WW3;e>h99@^R(mq@(yr+TAj&FA(M=6Jtd?~8+=w+tA7WZWgHP<x0tcA1T}f5FN}zKc?;(mq0N7GHa(g+fo_&hsgMi*B?D4u-4S)0aLSAXmApF zo1@?}w3UFNbp61w;RYk@uOP4vWiuq{PEjIQNA-dx;80pfk<%t=(Mr>Om0JB1@Dv6k ziz?aSX@TCGVtJwEZ%ErC9hMhPZnF|Mj<)(zCX1Bn;%<%gE=!9aZzFRkzi%YPv(m$~ z|84ti15oAjr{JXvES#IC#-%A)RL7DO4=>Bwsdr}A0q^aNHcU2CSx{C+cg?AF4LVxO zF7ZMA0@gYZYJ=Zm0oLK>w$`rRygDM6HDy%3qj^UyWdGjU)HBydjh|%diYPw?etW-; zg91Zp+#@-A%zzh0bpFuQ(*ZX`C`l_ebSZ55=dX?Wk}FQvp;;N(%#92Zz=0)KZz5UkE;+RG`(#@+=_`kZeiHP|xbGn&L zr_T%RdH{Kll-WC6Qn}H+2;jA(UYeWu%#eCaH!v>V+_w)FM!Hbii?HeI8>kY$lAu_U zLD8lo(A)_Y=XgBWlcxpw=at@`f!Hll?r3g;Y}v1en-@DuWEkQ#EEFE zSi@ar(_o=m{Y6599}zOfwk^(Ql7Ww1fOTfbiml8iR70z@Qw)D6#W|IyU)A)VWcu33 z*i$R#JQ4u{A;fXUGd`znFD`AZu!Lei0v0qqieL zs;%s7q=2Zg$5`(2m|aOKUJnMpgbo8Jq1ZXwUDGpXsFY(zOqUoP>t8u%%u{##9hliJ zt&25N*)d7aK3z#(um6Er1D=u>yfn2Bk5LzQOI%)Bg zw6WHNFbJZFA#ZBeB^G_M7N_#WrTelbNG|vV^Hdf zz4C%Ph{~mE&(d&m2-j^RjId2SGY>YyN)jH&&MdudCZ~%=3i~ubYn5>TaRH2_;jxq1 z(VPY*6+?1=aHARtZ`UVPD9TDJ8IM-Hn_PDr9c&(4dGeU|)Lpk1+rn z^VB5b<6*;70TA;h`T4?{<_^SO5;a58M%GcD!EHbd6%`P4eaR_WzgCew| zB9}V0*+wtEbn_{P(X^+(r3^KQVL;>bm>@;{48YnuWDs|RGl>1Hf_#yU?xtXKjV3!( z$eS8+hz=GYSMbv;X@biBbAUOhr+xU+mZu;IOT6p4rIp?0*iAu3ASx3v(WDLSQMy{ zYZ7-+q!&KSuJD7QD~43DO?v8@aU6)8Kybs%8yy#0m4$4BtQQ=za`!YkU5WcKs}o!V z^xb_zI#Rn1HFc~{&gx%;PG{x)2I6l^y>YBTr#xqTV$f{gEexZs(?sERQ6z7vYwaLj z_Ht5AAe}7HCN16a8O$veK9+NEy4eO%$H4OYP4a79Wxvd=2X`gZSZA?CMAGdZT(BG? z%M+j2lBbI0k-N#5LLQ(3nw-98z&KBm%jt3?(viHc|N4#?P`2s5hMdc?+VhvH)kwxX z@)NJSD*tTj?At{Ip#A8mssd#$aq*3uIsKoXGOT@&F-p8oNbZBw)4@r#OXkAQtm9da z0S9-g>H(7SqX#?ng17LKOzg{5`QisDbDVJ1+ludDnkR{p3#4Wt&N~}EYUGZqEPe<} zpwQ09*8o2tXy&LU3HG54WsfCRHBP16O7y4YKziO(BIg>76(-w@5hc>Y3I^%|9Cy=< zr6a{hFLRD0Qbu~o@G3vSe=qIye$oTgq&gwM zd*PSsvM39_b3L6wZ1Tx1KvJ_7*wDQazcJ&~F4MXCcZKGo9Jj~^cyJN4B$Rq3vJR_XJs9{}FtMI@tsWxTMXF5`j%RARWvay4L6j14FhYR%?Xc=!fI z2aT{in^s8HoX8CRy8v=dyC!=06&YYE8i%Y!#+V@EW%kHb8P|eceEL&!WzBD_ z;GF{)>7jG>1%(<9-TSjJ#95j_`#XYAC$SeJRr2BrN9aC-vth}j*{TwhQ;OjK)GS&N z4_ns=JCL-3K~H3*VCz=Df5U(4)?@^bkEl30Wx)++AS@BQ!_dn1=jUv9TU|CV=a44u>cr~TtV?8 zzepQ|lb-YM6j7czM<&^y*VLP{dANU%r&BUwy`8RIBoLRJnr{GQo1*{lxk(?I8Q~&U zMqed;)jbNZ1>W_nIFCATfcP1X;MRc#gDbIFBwnp4j3NO}3;;+%9n*m>yHJ=j{itP+ zkq^rsmcx@Ob|jIyg%>Pl0WaPqHwBnPw`Ik%ELqU6<=6pC{mz<4T748wtNbFl&*JIq zTtSSFg{;dNTG7h3fROGo#lzZW^*)lPPjf6xKp6#v!{o@skpjQEDl2MvgrOxIKGXep z&C$x*zG4S?5evBF;13LMF+_>8ms?pVM+BXf(=MT ziyx%s{=vnh-TUlU9rsi>@H&P_C-JIEA-fpMH+R;IKNGbxZvAK`Rrw**^2>#e9_@UT zli&2tT2^1Q+UIIPvY=nAL<4==7PA>?rf1s0s`-y{C3y`iLuf8u>8cadj}hTf(T6=S z&Y${~i>tj(B^ep{DY{x0KAtDySkw7Q_G$erDc0Ms*!Q+d--}-tARi-@7U+-bJ^JTf zWd}TFOFtmZ!L3Cf5c~Tqispp>8^x6wWSgZ86P(|2iOMa|Iy3SjIpj}`o`b&QBd7EF zJt94O-ZlmWs}^edVgUJYl>fs6Y6f>zZab3>c#UIXTiVjrrGA#*=A3e zgv$x_6@JG-1X6DS_S}EnrVmT%NnP^(&WRb7<94d%8&e}L$Z`VbCjqf|gEt$;U9~#= zu0OjbC3flP=u6WI&I`i$-#6qW)uO083LPFfBNOaXcJlTuV6IF@MamT|*B%uSxZPC& z>JhKOsZ0QTB95Z19mezzR2#<6=}+d2<%k0I#~Va;Vi`){FFew#ZnMhjkwvQ~K8X;= zQOo9?2+fRA4}|rVYpswhw6ZtYUVNuUn2llH@{G~IK$ALJbJ z_M6QJTT%t1DbqWl zsZ8YH{ucg-szu2!6L0z));j$G_l(qlmy)Sj4Br8e7iBo)3umoPvn5O@oQcy zl~(Vxp{&2)&bl;*3|@Ixbv57(v-^?Bs7QzgwMG3=U{tGPOVQPH1F<_Dg71&=7c$0- z1k3_AYFn>Z;Je0#j zv+OxVOt|$w?j~HIN36Bj!$@U$o~;#;R)2KFV~G)YL!C53vf%R+#R19#w5@r zHeU8I-J^HsVnlp!^Jg8>ya`Pzh5AP&{nk8A#}CRjoqZT}0Yc#m&1(tyDkrsUc+(h~ zGn;yLdi-OF9^{VSEpcY^H;3nmG;4OM3k-CX>c0BH5iz}1-9S9sioDjFx{$e0Ys)(t zzv2Tc5I|hJMg5M=o3e`iaF`6Fsm)F4r$9lNiRzcL)P-y5ZU@x%#5G-fHD$F{h#*Xi zIuk0B9L3A7@-{!5gzav$VH5KX<#>zq0)1S$6J0M+>X$Ds$R7a>QL>d@ zKD=$cdo2dI)%r1MQ279{a}?b}tNsC=a~)X*Ni>CsE}cYYQORI&)eo*vg)Pg* z)m#K%$Kha2*bE@!Q=Vw+D23j6aMB12xJLx3U{X54z>~O0KhoD}w&fL&D^q@10&>RU zLTIR#7jD9Z_2Vu-FM{BBQk1))A>6RL?|klFFDKBjI3n=RQLTY=3EEmnV%dbNk5L=!vi)81fmyN2Ar{?jE4;??9eW=1E{2IGxmS?MBlrV^QO{SyXAMBMl#e z^OyS9-*M5AYPc)hptYU436G+@k54C`~kkseQMz9cbU7#W8UHSy~DZJjF) zAUb>*k-4waeaEeAklc!RivF*d>9`%jV^d9T++(PFf%P`FNyvxKm>8o1$JXg9c?(9L{knjppM+i1(EQEyS=9x`|YG8~<^q+p&C#QLr3RliS3tsV^@ zZD>HS=n~GMW{|HXz1*8<54*596i!RwQCYl?WlUK@hHQR)v4I7LOF9QleTMCruTX|e z8iLf+(8^98UV?G<&8TFRNmh{~*K-%zYT&Pk0}1O}cxr77a^hpQQ2SGkWfh4~4m-8! zQr@3+onY{h;d=VWun2?QF&O$LE0NH{Uai8qUwhCaM)Sdooq3 z-GjQ}L{-7i6ZIT_Y}>%|XA-iBBZtHmt8UeLDty_Ypei+1p?Anj=i&(IfgjHqtP zEl(kG>bY9;19^UfYD2pEU%{j%6@Ba>3>(8RlSLfBp1A_Q6#zZj*Xl}qN-$|(j8u_7 zE?;M7fDB$kDAbP`0zXQsFw^Nj75m-ZFc2-OwrKTPv=6q&9hLK~SJHp}> zHF!t^2Y%_6(Dfh*ojpMQ1ZPYE+wb^nPYm+`iMBC|s4nLjc)iB6+?z=XSO4#O%W?ut z58pyqn6Eo>K53j_dE;zJ*D7uJqrRy1jfqpe{CS~)$s}Ou{oeqpRNef955civ5(QcQ zz^)tJ>e%4vgS32G9d@8~U}v91c;7amwV*npt7+!D+_mJ2_G+WTW*u(8#gz-1#p14q z8AJu5oaC?F*M#XUI6{I(oue$cnK;_R;^Moh?5Y=c-hS_@0J&JGxr8xVTd?c|+<|J9 z6Xgwck*)kgpGNp|`8Xa=pifJ!BCIOPzk0J_?NJd4C$IH1S|2Vh0k}@#u|f5j_!6+c zC%||X6_-1B-?y8;Ump^v>bC7SljK8pZ;X!}V$dJ#6d@jhh zFsI&@ZO~Bcx$N!3p=I$>{rdQ97RE=K2knPYoHw=VjQUZu;Uu$>RXg4%sUyXgxl8t> z=Q!qOWB^T&!_S&U7y~l29=!(wT*yFukj5=DWm%T}_{I;OmOVAks&PvT9gqUtU15Yh z3dKowA6rQRk}eQrjR|b}rNNy}+V(y9cBBFc{|RCUuMrTa$l3m30KE0${CJ=q?;lTT zOPP6@&JEQeiv>8+;Vk^R5r;Jd3-pM|2Hy|m{5HYP2c&+Qc>1d-JqJ=blC|S$>_=gI4|Xo+;kE` zw?MmBFxLeIT!VT>Gx@KktW7P$HoS}G$ z`Ua!XcS2N&9h(aj3QNH& z7?|a=(UFnoE)wj>2~l3XlkTzFCPt*TPkQ&S`{hzx4Ki-hBcqy7u`6(ft)Uo_-r+Icw!4RgA<4}x=<-IlCGLCrBaOaJ{ z-ck+;z(KyhvWaLnC>)U1J43#}Zwf66i}dQI9~X zQ=qsSM zdbzQY_^vpPvbby1%r1SoA!pWyXyJtS1EgEp0Z(FZntAwGGNSn};PH5kgjH4?*9m%* z!i>bPv#EIDr%sQ=Fh;jxL!m;03UCk&_65&7|4~njSAqhLR?=9Ac2~M*U7q55jyE_{ z=%X;#JFz?eNSbfntcv5IFDLW6Ix-1~u?8JYok^xPbm~(!!=ftxpiEAZ@d#OjM#v7~15{4vK7E;mw408R~WdJWxBZXgs0d_hK7i|Vc1 z-1?vL@wHXGpnJB@>A5@R;RMoKbFe#;9KU*LkFDNX!7y5X6B1IzHD4{dH$$1PTQp*b z)n^}&pHK69J$4=)A8k)c)z5Ex@wn@DnP{g#h|lOgSv5$nqpEHDMwdOtSj6~1TTe~f zDt0t@d_|@5gBwbIBXrWFdXFL8zL=%2_@M-M)t~f|e>VUw|6k_CM8r{r-dNgy{nNxM z70yntK&=vtxhIv)Ajt{q(*dpdFWS^L>mFZ=V#5qDg4!b}j7l_rAkcHXTQ52rFc{$s2pL)6|;=H#|k?3U;1Zm_l;_tN2GF&Ssu~y7A49cM%!&@RL1cGY= zH-za)nEx(BURa<9($5c8`t{)>OVkhSkO(5r`Z$>LT0^k@A-cy)(;@wWm*-Hwq`D6| z+#}jvWW}f(Gpqi-8Pj6+Alml?6$Q)DrP{z)Mbx7o3F(l#1%a_hmOXJr8>a{OT5yJurUK^lciA0+tF{q?pC!ws{BK|bA4~3n%PN$YfF}w67B6kb;|u5yhxGI z)kJ4o5TLxz=&dyqhH`T8@I_7L<7LT>`!Pcss}Q zL7dw(@`!?W@Jpdwy}z0n5$J| zgYE``X4MXd=R;(tfSc=4Q){m$PY~_K)VUFYC)YJiO(?X-Y>v6>^KGx!mth#jYR5{N zxP3Lk`6%Bf86Q+?1c9-`E}A( z4mxR#h_~R?P7tPO^I+u(eIkzXRwEdel$v1fBw5(?cOFFM0LkH3cUh??^(>)_Cb}hj z-83c@g9yhDyWg3$f)eOsPm177SVcV831(p$!#0gDtKvb_8-(Oa!WYU+s^O@CfOnGDvQa!RtZCmPm&t)U#>u=MsR4)a+<%(!j?vqXu7(9g*YaJ!iVcAtP zDo}X^JLz61{DV-{7B=w*06wiR;P%Hi0ULmmO3NaWhI2T)U~_g5XE>xVS3l6N%qhtr zJ}$Q#N;t=I5$;{J{-2&?Suj6>pnN_3@!%Q51mb`;P5ygjU0M+vM(OU%6kR zxYG~Vgub``T|K>z$+a@%tB6Xsp?gzmv=shxap;{kg_r5C$#5+HjVQ-v>(vmyp`8_u z4#_CZfC$dX40^~&RpsdA=Rq3+?4x8i_YQx9-v=@I0XyP+UnpW4!BLl^0`o7BzL zo3&=qtcVz_hBC+~b2>1n8)WkqlM54o>!NlIYh77shr1|7R0gjTiuP~l_LXkJ;!>vU=2|OnKM7qDmMnYA zr|%T8i20-HlEY|f)i_S9Ob0yItIphf_kYl1=I1CDTin2JxHs$iu)L8lW3ZQ22W|=# zHZ)-dJ|guIN2V}IhHg1^9XX9$`N8)5!TKE|lmL6or|$d6N=1|=5w|+2P49PR`q5{| zqzDx@M%#A`BHIS1CR9jQ&&CufE$2}iJOkfP<6o=tU>!>PVP*=!OrOh`RFIe9ku`@4vcWR8k_FnVfn z3M?vtRpwfFlX-6M^@A_c(g{fpUq3q>7904THo;TY|6cA|tZBeI!MuYC=s~DEf|XFF zbCqaE z$fo~%wBrD!X6Oyhv^N5yhXN&5x;Xg;lQzG=e8cKjV9?q-n65eb2%mb=eN+Mjf|~>ohpc%76IqUE zH?4!aS$k7dVK+ySh}tm5wfirV(2cK6&OhL0?Iat$-z zD{=8PbmC%KCvdEohM z>0tsA3>vUf*gCMV$384OC0{<2Y+dg0uCV{_8qi^`-p~63O>|`at#eslonou#v^*P$ z;QekFbE8BmPX`pe7D?bUPO!rF7a%K>FzBUXD-hcnhPDN8l@Bs8qDkac)lxF4Oc+H8R~jwlSgfstFQO)1S$ z*xR(^#OQtjXes6Z6UeR2PiLWV45A*qQG5Y=wqHad`CBq@RMRmaV>n<-*272!0xBdq za)Mq>U@>5vL!tzn(pwBVHmA``VDgHq!-F*xLqUOJJ>{e>4?ix&6jN6(VC2V+65CZf z?J$D^eXbhHer8@LUO!l!Gx*Vo{5b)_R-MKaJ1qqYV<{?$jM=!8qFZR5PCE~manL#c zTzFKi^q4Bjz=!s7tdufPbIPmFrV1H-{o5gQUCHt;&U{?EoG7ajhppBynbaNMB}KXB zUD;n6j?eh~*^<#km`RfbFbRyk$!DE*A4BF?Qs2=#o!r*55W^YX#aX-xcVY}x{-@{g zrW!N;UP#o!TplUEpg=v^7;}K&C#A)T{~!AAa!!1a2t-0acffT%>4*?B0a-rHhNFCJZS%}Y{9 zEfm_RyDt6~{>gEiEnPYe9Ukg`^c)P6>R}yEn$m}k8?TWs)pv`M&+h%0Qe=vlxX}M? zT))bl$a}IGW6e-9j0^T2HoAJS@+SYcC~JK#Ka5=sMbXE>M@G)>2ghd1VWn2x8(0ZN zJTGY!Ojy}gI!;t%Y~daP_GtcbZYYf1ZszQS`f(=<4Q1Viqh=#`yXU3JE|Jr zflAaB98(Q7W8%u%Gdbg`i_ma!XTmh&pG23b-@ef>n4P8LDj}Kxk^N%;y=?Qqw$@`- zS`IRau~*1yr@HvCA1x)>XW++^3Z+Ust-dLU3Ge6g|M5#6I$X5Qo$eH(=eLoTic(Wf zYbYX^{VC*e1lC4C*gmq2r(*@3Q!!M>(KdC}F% zOGTNlla+ke7c=GV0@rqVgm-A<@u9?sdXbY;00_0`=nVLaVXDaJNDvpW&Tue zy4*5~L`4%`g6CTF|5T@!q)a%7A8#aB&8%x1)DN&`P$c-H1c<>=kpjst92_+(J6Vgd zXSJxWh6X7V4^YJaE})jkYTT0$#Z`@cO}LHinD#r0gUCmf>F!Tb&JT}I+6_XTo)!=8 zJebyRvpWREBcV?ijsau`X|>iKDJvO)lK|o&aB0^&Oz3tdzfb z+8;la(U0GnCOmrr!7vV}pDWbBYelPZe**0;s!wkIU9FUcnY^yR#$^+hT<~|k4#D-; z*9jZbXE(khBUHTLmCG$4>_5#Tep?XbxM)QH=Q1_>+v=2QOA$h3PNZ~5_uEz$H>+%~ zvr1zd;Rke!W^L`iH>&9L<%cKp;+>R5#~b_`*@4kmurbdDyAxu83a7+t9`E%rQJsxf zDKH*g?Y}^B#eB!IsI5a&+!vkGLSc_Cg_|-d2nA8k{CsZ4MSn?{uk5GM1`kPr7CgjE zQEZpNpIgM$379a*ci9+;s}sNjG&wVL&+LOX`qKmr;y7OIqh;%W@qL60C0s_STWg>; z$|6Z_M*a1VA_QHaPkvn6aInETc3spqeh9Y@)J%GD!swUDg!dQmhXe?-!E*m)rOPk= z0PGv7w}UZpt)QP`a`c&@!>p!-2b9}Oh6kS+JS_F>UZiFddmilotaDNcrd2Z1o$~>IxIRX7EKokH68vi!S?fb zPc_PcnCvfKn(be{-T0ET`&sYR@;Y*O(OXV*>7VqXq$geq~$a!bt9 z(DUe`4q4CCNq+pIz}gpfKvD?#w(4o=Nb!fR#&iP+>%Nxoy$Q1qG>j|@6@vGC zJ*?uct_6FWi=8XluWMZ!&&Zw|o39D||7m0gxT7|r`bf;G0ecK@dcI(+-t{HzFakRh z>}(7FPJx^}9AC5Z@874E=T4SDq8@;}m$z1mgp+~rzDc&@OZ?@;}k{a$d6~j(Z));`iIdWJzGwV`D_vZp8p+aA{}K z!+Dr#E|Dz4$ov&l3%!;R_T_N+t<{?!v~9+Q_BDch^D>qD$y(+{s)mY3CE1>Ik=Z;b zJ*PwMyeeGZnr4hCkCgksCheb@cGU8>A^a^&M#Dgk1Z5mwg)KLSrGKyn+mOMrveb}> zpi`*DqPhTNc8)saHKJqr%rD14|sZNJvMqBme z(=C>TfUpF9A_JEJg#>WGoA<#DnWGf7UP-SsQahpz#2U~QNmAg}$M8q_w^L=#$69ey z8XDAu`!PG~PvTCUOjix3S3YI(Epd850S?zqln`3i zhJP0lg9}YVYW*J( zk7?$8?>+Pm zQjEQk#!ESVrupw9$=qkT23>JuOd2CuA6FszMgUmd0~zfb)+6pMF%=gfgr<6q8!Bgd zTs2C6)7dAR?o@A(eeWttAcAE>0xX`H+);c|P6*>T2}bG%sn@DE!YF)A4wK+d!y`Qb z+CdB&&5`)W=%s90++AP=a90{qbzV$HbQcX7WA`?G7{NRCH_l5Xh4;iG7Yxo?$P!1n zcF{Ks86&rTx{2bgGQDUnLcZe4N{$>FfCS8hn`CxmJgvQ-`|Fe;>0oAG-;=Zf)8WLR z_|Y%xG!G=({D}ktlGw1~)4=?AroK@>W{<@+!OZ^pZGHD`HmnY{F2Z`eYM%2!t;zl} ztvPw|9MWPLHBgv^`XD8qMq~%@>-bL9wfb6l#J*?5vtvnW1+wgL66jLhHL?w$#vVigQAtto-yN~+RUMX5N*jO%i=F@P}VEfux(#-#qNVyx7-_5F*H(@q@ z2b{xw$Zt0p!`-ZuR4$H4d%Q_(qAm+XZnbtyfh)G$6K@(++l!)U>NzTbjDX%G7t_l} z_}C-uaWy{-UqS{A06WIVUyblb$zR$>cQMBu72za6tQZuf*vZ$0!X=-rtLoWOi{}$pio;t%<+~>i!SC4$umpkMP#WcMJiwV~E^Ex-t6_op;-u*O+uU7& zyLg>B$!jPa$jurzSs7RO4p`n^(3HPI+E0KrU@v77K*0e7D;9IHg8|S+^R#n6z{@Nr zVo~-~WkXYhz>E(GIaOrQscQh+pv~~+`547K=}X)i`kdA$rILZ#WptJKDn4*-u~_C{ z7-+Gl<2+v$^WwlBvR{KL3#phug!a7irf%oPzgala(~R~w1bJ)4RDj%sd#{{}?qxcw_h>0U3=KCdOQdEy&%x^_DG;h}Ya6lk&TDRuFL0UO z-R=Mg(fm&GrY17+@}BMjc?Xl^u!u}YL@>R)$zypeg=*V*VTKzZ)pBFAcm^2iwW^`a zjNUym86ayx^jDBTw|5l##~q`wYz|7#HW7IM5IQLU7gNonyVa0@0O}kalHVoKzwbM~kTE{ouqvk{4n>dL672#pdwTk0rmOB6AU}dci zuaB$>;P+m+#3{ethzX>H$#b@4+yKTgSnq#RakYsrU;YC=Bk6@nzOajJ8pF?}Po!t$ zMZ=Q&RU=R&EbaA5dGKIZd%jjWobt^wtz@Im5v$hia0}jHl(cHDGj}w9k0`pB3Ti0u z+7G}zXa&DBRf2Gm?tK$1v2M-_QJ2wJDYn+WApQnju1knzP17WXXmoYav{)aZo6|z+ z0d+CdU9k2L*BsI^+`&-2h#tEBMaxr=7+iYdLahS8S=F}29iU~okf0dFI^;-uGK1v3 zGC%St!iw|ES1f=|I~*OSz{&72oo+Zq(a$;}C7~;!t1Yl5*v3d>yFxdcUR9`3W{K+J zrPY|8M;U#D#t9veeRRnvxl(ux?C{DejJ_!RIci@ZsXna1nXSsnt%V&mF zDngf0y?7%o$gqUUvW&a#>(M0G!d}<9A%hkE=;!CuIsXEv1 zMqS?<))WQRkvbh{*Q8_8O;dD5t$S#IUu~O*s{jOJ3W~ z&r!ScY?^^VC5-sSePscCI=1gsTl;*zEXEgu|A1oCi5Q#mgc1H>Ad|dQL_>hF(U1TP zz`fVK@cy9Yp%&3NdV-xX<;7}v|5uIz*XB6*JM{M2 z>!W)`!>U1cwd&tRjqBNIhU3R93^Xu)0j|UkeK6$ep0B7yw^+`#eZ4{VG@+RHsdggo z59NjFw?|CcC%@$Q zM{}f}`msdaPOi=gFKvEJv@*^``|vLR6CUbCNiRMIR2;>71&h2j*-C9SgpTuWqEZek zsoi!&?K(pfk!S3qVhMW`0AqK$0Ue4$kloWyV=iW-mt-^b6iwJyDjs1Jz81> z?ONMu+36$EBpCQ7wE_ww3qMTAq*bqfh8*`yTLY(2kZILZYR7SRW1{3O5nLe{!uOxu z9{JVAHlV-OnU-jq8`+th%s>9?;;e`F{Eln&dIs96XN!o}gC>IuCok&Q7Y=e)ZoR1Q z=dV3D8aIk5Ue^i(5Bn4^2E}Nnok>OcyJu`q&ffLGuv<9ij!C3fXK{7CZtz?#jtTOLL)YnTG9sOnMd( z?zBtR)xJa-~pasS?G_e#3d@mCYU-NV=quY{*H=`<5dAPT)&r zw<~a2L7r<&7sPzTLi^)SUFJSP-Pqo#&FQ3b6zCRXz)j=m$M;0|dZf>ZtZU~xmEYzk zd`cn9H-%e+rH7puQZeAYvx6%a)q1UABAZ4v<3RcL3Pt*AD6$JCqYddT9mRoJOXhR8 zzoRI|YQS#A2EpM*d#e`ixN9i`Ho>rXdEHquLotzqVvhrIy@9hH`VG>MK_iTyCz$&} zb+d#wS7;kIKTVZ{3}o7JQa^s%sM3+oaM|X`C+6>L8(>j&Ct)xpbD3VmP}N!DF&eKm zXMSK@^ECE2673HejFFEj2esB11UM=YEQNgE_Lijyj8PvMo%{++LS%_(XbwZS`|4;1Tviwp1qw#+W6=C zHD|o)ex9E2tG@P1YV~aFK(N$^V%lIBiug1hkKZjo?k!CgDI0rR=WS#o7e6^uWd~jx z6%F^Q50-nPife1R6)Ox*mK2Qa)qBMz%Ttdj*T*S~dABOMz8~34q5vK@`hZJ-l;nT^ z&>82k*H!&1=*;_r(Dt-GHMURU%;|3q1VDp@SgI#?)_Nhc?jl=q2&T_s0v)BCiZd&w z#0t~3#%6!fF;8vMM-xfrXNC67geoT$w;v~s?B_-LA)K_)mq>n_@tNMWFTW;ZVZ#9B zh{(+0GgJusq0Zbe&=M{(6N@9!V2AKZZLrpy<9_l|n(09@m0p2VQ+dS6)n6tQOBFKm zhgrs0^LFTIG~Jex<7q;`RU)sXP?aQCvvNX}rRO1ALYZ&{|1f9W9cEQmg%wmd4%t+p zZ_ItNQe$)ri5$)mu{zoVc{6^DKhglGS&4rJre^5vC;BSnCZ-lp`hy||@_Yn6o1|Fq zjwlb;&Y(i#t3tb1i~AL=sY_~?RtkBaV+?q>914Bsa0;ow6~2^z$F8%8$6lYaP%Pi~ zdD{xbVEyV3uR-nxdOe8g!dHs%flR210_Yqm7e1Z{m}YW2sF~8a7|}F^V=08?4mC3Y z%*gP7sLG4g%14=z++awQ-x0wavs4ML4akq!ULv{XC8^1AR(A+NGS}hL4yOnM!AKQ+ z_6Dwaa#F6^k1>YA$%s2y@CZmPLeL_Rv$_#EO1l{*dha=rZ_rA>0ii$%97m$!dUCHd zAaT4FYrGupkB_JCQS_j$L_cKe-e<=}g&*ch>Msfxm-QnP9I$vMtroZMd;xM|J1Q>D z{A4R=^Uihis{*APu$so%o!u7awY}Eg-3113KYvm+)C>$iEJej#SE^HVadN2wo+P`T zh>m9;$b-G_+;y%+LAG<$EiT!9P8^}iyMN~Jf>>m!1M#RHnY?Z$6Hj;H>icR(M0-0g zOKz=$dyUERPO*ksFl=pV)6paOFFVaZM##4q8)UyjQ7vvXAd^@`=&&wJ>Hhoaj3Euy zD=sq=r+whF{^=Fv5-Pk<1$(qLUWIqZ$dF)k+_W}tiGUcw&@csE0}s&X=mXpHw@zTZrE%~@kRD|Oo^hzqC(z7EO3!S)yrR< z2RsG3%`(ArdfV^)uFcSHq<@HjQtGe;oLD0nA=yLbUx37Wio_q0KQc29YkMtvs4Zl_lTmH%RMN7+HE|IuxuKKO)>98d2sv zeRQctL#lOsrX6J$Gk!IfK+WqSK4eZ)MlxC*e#^^d>~3C>WlM!gW}lRA8sm5H!xoAY z0oU%|b6BQQHDl}ss?D5PMOus;ryIokW4w<{gig%nX-Q`L7i6$V`gxRTJRXW~>oB#R zNPD09$DrPkbJ*fX1@9`ixD%>z&E!eKA9Al&aNB|%F4zw)$uu@$;9n(MjnWjb#zLg@ zgQy!o8(ONIY{Lu+O$gFw5tI9^f(xUv6wWJ^<)0YK8ar8`6C@qEGvFb7Zsn{gwpZ2Y z=scEJNr9kX)z}2#;HQQx_g(O^T#;vchn`>co#FPal!?l+Y1dp_+f4>eugzY@h$Ky!p*jtbs! z?_oKj@9rM%0=ij57G)502hLFuXe)JVVy1T%2gG1$%g7imjI`AMLXfuXfRUPAf6ti2 z)O?R^jSn|@ssT>Rg?d+4A#_wojM+8}9ocnC!xxX|ze}0)4YYye&uO|^*-jo!7CP+x zzFbnq;jxD>40wEto;&^G^F8?ja^oX`o>$Cu3gq}lHYE#S=rTP`3&Weh0qSUQ5Kf5$ zw;e&(Ow!63e`mw+w8OuYFae9_7Dl8lCz*U0aS6C~Y3IqC)F!c8#XSl7Z(wz|SeG9O z{RFC){KQ%59xZz!%~|RjPeqofXp^l<0?n=90NtyxIxj+c6g#ts3-Fd#Uymvporka+ zSX$}AZ1kaMaQ9M0H{ITYapl>mFT^ZRJ9MIb6fJ5rwhnH=IJJnQTg+Q%oh*(71p14C zyda7jdUbSh`_IkJn|w=^;qqQk^_g9JA1jAok@6;|F!=MmS$O?g$x}C)tzvd6AFng1 z=kOmDJXV_gW{qNMS&-ysL&TdbeR@*F8FZuI?21tnb;;+vPcSV#{WotzPNPWi$6If*RNH8Q>(^b9RcEK~fPTgsa)51q zq|^$}hUb*rs=|D);Jt&2Y&4hp+UByu|4jy#O`Fp6kJY9qqu`nVl^xu-6q6>zQOKH^ zUzrc@g>k7{|B>eNmOra~{4;=n@iKwJ`@(#y|b zA#6Ob5*HnyXbz*bVAY$hPd0%u#?WAk4Q6I&?e6e z;xY|67Vm1~HDG&e?G=(Pt4;1m$1TVr-uyxtzN(ZLSi7rv%XTzAuTiC99=^ERlL#d= z8H1eU+Db^QeQQ*Q15-V|@KuJ+BW?_F#(w;Y~7@@Se?KCR<3pL;D zbsYXTI?D!f7#BTc{(87Y*{%zK`H}Ddato00fu@rE3<|AXXI4@RU*%&~I1ouW-awTR zYDVhBOpjU_e<*D4Q70foEm*TW#99s+(LN|$r6g%{Ch#DVlQx!US49d zAulQ(b2IRh2jX~tFB)rcx}-pemd0m;^V{5E;o|ltV@}R%!}Vov@b2XYY>CQkY=_wN zD?;G8`SaRjJ|i3g5GB8$?7y{HU9ktPkBz7KdS5#6TiOh0?g04ENXBkTLw+Zu`Zf0I z$RWd=h**quEV*DmL&O^=8z!+E%paK(1lm&sz5mjf9{)=2>hx<&hekH~F6}$dNAJE( zA!YadEFuxdZrl=4Kry(a=}`lrE>gWPFtO=sF;QC+v}6xeVll99;=Mti!kXbNlwhmN zy$)1%0Wy*;06r;jTZ~rjMPMH3UZ$n~yFi{$!#jy5G?`O2cE(1|Hkj^?azU;2Gm~|m zyJ84fi#)sUrQ}I%4*&O+14;fNQLJypN3n=pMSNXFUlV3c9Ddi^0^I{Np{D+ z3l?Sli+gp@KZh=#rCUX-(!n2i76A}BAy{Jr?y&vd;Pdx(pIX#+33=BnQ(Gp(@|yYj zbHvZp(~z#@m3B&$7#APw;#2AmFNq_#N%_gkL<)F*uq03+xv++qH+H;6c&hv$X`B_ovj6enX0nbJk86{EtS9N(m`?B=>BItf zy-~-SP`i3SQwGxl>e`+^bsEG8Y_(<8DYYbKbEs}FJE=%Ky)MRAH(!|C zQZp(d+w#+uR?_g&YLWto(afJOjfex{xtI0KZ!&NRSLmq?HlT%2ME%QQR-O|# z3=kbw!oa6<1p8>Yj^d^u#k}g1jWQiV#WNF}WAx{jH5;SgCB$aEStj+CsB9Veb$WuP z_1-x*l~X6^^II*M>SrmQMI|=OyRHBLkiO(%oi%MeJ3i3gEaGoNqjjl{$}Qn)vqB#I zPaM1GKcWI|`RB{tZTB&=Uz9pGjxe#%&7}{UTsjqWsI4s&L8i1Lo3QIA6ovr2CmKCq z53hI&TN@rw42qzJHGfTm-|D@O6oTgn&)SE7BSvVA-bsn9!bRp{<3|WMWr0S~dQ@C-PJ7>K=CUsl%;}9jmcPBgj1fh{FMX_|21j=rD zu;(3=jo09D#B=`N5e8;Sj3Ze61;{h;tI@_Y*0tG@f_=}JI*8tuod1Y;ih~=JIi3y-w?_zCc_D4#SXLnYKgINoy zFUbHLivl6MM4Z4;q<}m;W>4|1)D^D^?Tb_-=j~*CGAsfOk5Vd%k937J!AZE^864}| zcpG`#DDBrdD}VAu^49_4bjzIb+40MQMf3CWNJOWRif%P$vKQ9Z((XA4 z?}G0M?vQT!FBlOSnGm@qjkqDQ@eclCGBuDMQKg-~Dd4*&{U$m@p z`YLMLrcu{}X97dWmCI~01agjAGH^YcuVyybPo275en$VEp(m%oa+wvD)3m&Hb8hbQ z2wBo4QdgO4!`@I&s>MBsnaQ{4*@Hd*rs+i<06H7?EtmBGo^UDYLA`1equ69R5Dqle zh(zAQ#)`!b@cu^J+DmG4?9Vqu8awFPj(OHntIfm^D+8ooHp1wEUbFwMmNeeIz`J0@ zP7)qdp->)X7w20iirj*3j>Ml}LnvaQi#B&0bT1!-15{-O+u_@2ZNbo0BY-fHQ;>7$ zNZYKO-oR+$_XW8@LYm@7!Uk-yx^6OC3r{8)#j4)gZxMRYbJ!Xs^XP(m6;rXEz%++G ztH@Fzgr=~f8)qhEB=hZSp}L;2uc{f4JEIKP`$d-!E^ux5b5pIUJXx^5|j?FR-@(?;X8 z0+{Yb#83*#&JiW*_`0g}r|pXF>KvDUtT&^m8mV$bJ_-^La#-((=PJrvEa%QY*^hWBzTJgUlIe zpr;LYwwA}sccYW*T}4sz(&(;6UDC>1)n{rwGK`SVx(*XBk8oZgnpugK?5T*^ zP`qd=0yjr_#X>WmK{DP^_M4~M7yJ#DaPoRSoq!&_mS<_4|BoDaemk{x995;)I6v+S ziJSj{ZE$yNfjSp?%Cq%m+;%l`D*`|hhvfbF=)>ooX^O!-(sprA2F8xMa$)DoW#5Zf zU3w#-#ok@~&bf!7xnyY;9@H>a6noPDO4KP!*>n!f0v7EIXVIk zaha({(6UJa*xJlKBf|}tD}o9Sba(lPG5xSvhE9?lkm$TQ?TfiP>6QONK?Z<9^xYk8 zz*^ceBZ33>2irG4Q|B<(Ou5F-@>4Kd$1)YZAHZw5x94u30+IQ% z4N2L|hhJ6SEcO@NpaxZV8syu|Fl6+B3scsEvXRe5p+0R8iIBP-91uUoA%l3^K#5Jv zkt->SmTRD{aC3Zd5SzSm$#^LNzijOp$_ej5Cjw6#`lb5J!GSt&Xu$kEUf!W_Jw`^p zGy|!M-4y{zcTdSU$0b~(%m68(8%yTE*e;+j$6EXU&f5^=EG$IUdUY)%a9D5HWZRI| zmwV>E-=AlkJ zU+Z&r3~Xwg)C^bh%;W%+mmPTZ%m%Bkq2o+!PFmF~QQOR3n{Tr(gr^t4FhG|EjI$Nol)_o(UCQMJZi3iLTvYBHcneIP3kV&7zml+U zUWXBK^H-MWU(1>^q!d!!8Fgi915Bu9-A2uUEQW%V|-JEV9JV_QVQMXJVt_W*(!naIeg=v^=g^uX_!nW}6kxVuL`NVHTYg|0!}~Y7)?-OJ zD`7MK5w9OWTpSli{55ghkz2)$z-;ThJj+w|C~7eF#lW$QPahmLhnNqM3gez2Ur}k8Yt4_mUt0Y38XJ zYgE8@J0@Gax5Qg2(g?|%E;0`?K8Odu`6<-6boQE^$Y`@cx}yrYoADieqK>;_FNY#X zMF4qL0z(g0ku4lx+Uk2ae%hZOnaKt?6}nDao>7-%k_!x>r#}DSWdw_O7H%|axf_d0~ic z-@HM;D7LJq13k8Xc7io4#03N6xjn^u1+awZGbHc&DvKf*qH4G~nSt^1F8rymFTolQgR?|8YxRd4f_qVnHOnw@WBdO&M)UD z*UO(3%62RF4izL7Z5m0)gNz+3&FI@>*)J|1zV2T;=V0!nu1EcYFe zo%ik-_*6e>B@=VG(p)&+I7#a9Kw^%K&M%PG_W$Br$Vc8W($JaCowU=PuVH0-AuV^1 zmqgtMv-CZ*N!o&1cEEROh?L?X-dTLSmfwr2Y*AltDf3})fQ7v+jijcjXO+Sht5f46 z>ny+{fs&r0T9UIA?^!9{mtQ~Ih%8Fd`i};6@#2+&EbO%Lcn+?PyG#Oato;X&L1^5# XmSlB2xcR(oyKHZMv^f7>W_kb$!va$C literal 0 HcmV?d00001