From 30bc2ef27537d2dfc3145cdb76633d44778c8755 Mon Sep 17 00:00:00 2001 From: systemed Date: Wed, 5 Jun 2024 19:21:22 +0100 Subject: [PATCH] OAuth2 support --- build_date.as | 2 +- com/adobe/protocols/oauth2/OAuth2.as | 551 ++++++++++++++++++ com/adobe/protocols/oauth2/OAuth2Const.as | 17 + .../oauth2/event/GetAccessTokenEvent.as | 173 ++++++ .../protocols/oauth2/event/IOAuth2Event.as | 16 + .../oauth2/event/RefreshAccessTokenEvent.as | 173 ++++++ .../oauth2/grant/AuthorizationCodeGrant.as | 140 +++++ .../protocols/oauth2/grant/IGrantType.as | 17 + .../protocols/oauth2/grant/ImplicitGrant.as | 129 ++++ .../grant/ResourceOwnerCredentialsGrant.as | 80 +++ lib/as3commons-logging-2.7.swc | Bin 0 -> 83350 bytes lib/iotashan-oath.swc | Bin 6299 -> 0 bytes net/systemeD/halcyon/WayUI.as | 2 +- net/systemeD/halcyon/connection/Connection.as | 18 +- .../halcyon/connection/XMLBaseConnection.as | 1 - .../halcyon/connection/XMLConnection.as | 74 +-- .../potlatch2/dialogs/OptionsDialog.mxml | 24 +- net/systemeD/potlatch2/save/OAuthPanel.mxml | 226 +++---- net/systemeD/potlatch2/save/SaveManager.as | 7 +- potlatch2-app-cpu.xml | 4 +- potlatch2-app-linux.xml | 2 +- potlatch2-app.xml | 4 +- potlatch2.mxml | 13 +- 23 files changed, 1434 insertions(+), 239 deletions(-) create mode 100644 com/adobe/protocols/oauth2/OAuth2.as create mode 100644 com/adobe/protocols/oauth2/OAuth2Const.as create mode 100644 com/adobe/protocols/oauth2/event/GetAccessTokenEvent.as create mode 100644 com/adobe/protocols/oauth2/event/IOAuth2Event.as create mode 100644 com/adobe/protocols/oauth2/event/RefreshAccessTokenEvent.as create mode 100644 com/adobe/protocols/oauth2/grant/AuthorizationCodeGrant.as create mode 100644 com/adobe/protocols/oauth2/grant/IGrantType.as create mode 100644 com/adobe/protocols/oauth2/grant/ImplicitGrant.as create mode 100644 com/adobe/protocols/oauth2/grant/ResourceOwnerCredentialsGrant.as create mode 100644 lib/as3commons-logging-2.7.swc delete mode 100644 lib/iotashan-oath.swc diff --git a/build_date.as b/build_date.as index ab73fe4..86caa49 100644 --- a/build_date.as +++ b/build_date.as @@ -1 +1 @@ -public var build_date:String='2020_12_29'; +public var build_date:String='2024_06_05'; diff --git a/com/adobe/protocols/oauth2/OAuth2.as b/com/adobe/protocols/oauth2/OAuth2.as new file mode 100644 index 0000000..92f9942 --- /dev/null +++ b/com/adobe/protocols/oauth2/OAuth2.as @@ -0,0 +1,551 @@ +package com.adobe.protocols.oauth2 +{ + import com.adobe.protocols.oauth2.event.GetAccessTokenEvent; + import com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent; + import com.adobe.protocols.oauth2.grant.AuthorizationCodeGrant; + import com.adobe.protocols.oauth2.grant.IGrantType; + import com.adobe.protocols.oauth2.grant.ImplicitGrant; + import com.adobe.protocols.oauth2.grant.ResourceOwnerCredentialsGrant; + import com.adobe.serialization.json.JSONParseError; + + import flash.events.ErrorEvent; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IOErrorEvent; + import flash.events.LocationChangeEvent; + import flash.events.SecurityErrorEvent; + import flash.net.URLLoader; + import flash.net.URLRequest; + import flash.net.URLRequestMethod; + import flash.net.URLVariables; + + import org.as3commons.logging.api.ILogger; + import org.as3commons.logging.api.LOGGER_FACTORY; + import org.as3commons.logging.api.getLogger; + import org.as3commons.logging.setup.LevelTargetSetup; + import org.as3commons.logging.setup.LogSetupLevel; + import org.as3commons.logging.setup.target.TraceTarget; + + /** + * Event that is broadcast when results from a getAccessToken request are received. + * + * @eventType com.adobe.protocols.oauth2.event.GetAccessTokenEvent.TYPE + * + * @see #getAccessToken() + * @see com.adobe.protocols.oauth2.event.GetAccessTokenEvent + */ + [Event(name="getAccessToken", type="com.adobe.protocols.oauth2.event.GetAccessTokenEvent")] + + /** + * Event that is broadcast when results from a refreshAccessToken request are received. + * + * @eventType com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent.TYPE + * + * @see #refreshAccessToken() + * @see com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent + */ + [Event(name="refreshAccessToken", type="com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent")] + + /** + * Utility class the encapsulates APIs for interaction with an OAuth 2.0 server. + * Implemented against the OAuth 2.0 v2.15 specification. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15 + * + * @author Charles Bihis (www.whoischarles.com) + */ + public class OAuth2 extends EventDispatcher + { + private static const log:ILogger = getLogger(OAuth2); + + private var grantType:IGrantType; + private var authEndpoint:String; + private var tokenEndpoint:String; + private var traceTarget:TraceTarget = new TraceTarget(); + + + /** + * Constructor to create a valid OAuth2 client object. + * + * @param authEndpoint The authorization endpoint used by the OAuth 2.0 server + * @param tokenEndpoint The token endpoint used by the OAuth 2.0 server + * @param logLevel (Optional) The new log level for the logger to use + */ + public function OAuth2(authEndpoint:String, tokenEndpoint:String, logLevel:LogSetupLevel = null) + { + // save endpoint properties + this.authEndpoint = authEndpoint; + this.tokenEndpoint = tokenEndpoint; + + // set up logging + traceTarget = new TraceTarget(); + traceTarget.format = "{date} {time} [{logLevel}] {name} {message}"; + LOGGER_FACTORY.setup = new LevelTargetSetup(traceTarget, (logLevel == null) ? LogSetupLevel.NONE : logLevel); + } // OAuth2 + + /** + * Initiates the access token request workflow with the proper context as + * described by the passed-in grant-type object. Upon completion, will + * dispatch a GetAccessTokenEvent event. + * + * @param grantType An IGrantType object which represents the desired workflow to use when requesting an access token + * + * @see com.adobe.protocols.oauth2.grant.IGrantType + * @see com.adobe.protocols.oauth2.event.GetAccessTokenEvent#TYPE + */ + public function getAccessToken(grantType:IGrantType):void + { + if (grantType is AuthorizationCodeGrant) + { + log.info("Initiating getAccessToken() with authorization code grant type workflow"); + getAccessTokenWithAuthorizationCodeGrant(grantType as AuthorizationCodeGrant); + } // if statement + else if (grantType is ImplicitGrant) + { + log.info("Initiating getAccessToken() with implicit grant type workflow"); + getAccessTokenWithImplicitGrant(grantType as ImplicitGrant); + } // else-if statement + else if (grantType is ResourceOwnerCredentialsGrant) + { + log.info("Initiating getAccessToken() with resource owner credentials grant type workflow"); + getAccessTokenWithResourceOwnerCredentialsGrant(grantType as ResourceOwnerCredentialsGrant); + } // else-if statement + } // getAccessToken + + /** + * Initiates request to refresh a given access token. Upon completion, will dispatch + * a RefreshAccessTokenEvent event. On success, a new refresh token may + * be issues, at which point the client should discard the old refresh token with the + * new one. + * + * @param refreshToken A valid refresh token received during last request for an access token + * @param clientId The client identifier + * @param clientSecret The client secret + * + * @see com.adobe.protocols.oauth2.event.RefreshAccessTokenEvent#TYPE + */ + public function refreshAccessToken(refreshToken:String, clientId:String, clientSecret:String, scope:String = null):void + { + // create result event + var refreshAccessTokenEvent:RefreshAccessTokenEvent = new RefreshAccessTokenEvent(); + + // set up URL request + var urlRequest:URLRequest = new URLRequest(tokenEndpoint); + var urlLoader:URLLoader = new URLLoader(); + urlRequest.method = URLRequestMethod.POST; + + // define POST parameters + var urlVariables : URLVariables = new URLVariables(); + urlVariables.grant_type = OAuth2Const.GRANT_TYPE_REFRESH_TOKEN; + urlVariables.client_id = clientId; + urlVariables.client_secret = clientSecret; + urlVariables.refresh_token = refreshToken; + + // define optional scope parameter only when scope not null + if(scope !== null) + { + urlVariables.scope = scope; + } + + urlRequest.data = urlVariables; + + // attach event listeners + urlLoader.addEventListener(Event.COMPLETE, onRefreshAccessTokenResult); + urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onRefreshAccessTokenError); + urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onRefreshAccessTokenError); + + // make the call + try + { + urlLoader.load(urlRequest); + } // try statement + catch (error:Error) + { + log.error("Error loading token endpoint \"" + tokenEndpoint + "\""); + } // catch statement + + function onRefreshAccessTokenResult(event:Event):void + { + try + { + var response:Object = com.adobe.serialization.json.JSON.decode(event.target.data); + log.debug("Access token: " + response.access_token); + refreshAccessTokenEvent.parseAccessTokenResponse(response); + } // try statement + catch (error:JSONParseError) + { + refreshAccessTokenEvent.errorCode = "com.adobe.serialization.json.JSONParseError"; + refreshAccessTokenEvent.errorMessage = "Error parsing output from refresh access token response"; + } // catch statement + + dispatchEvent(refreshAccessTokenEvent); + } // onRefreshAccessTokenResult + + function onRefreshAccessTokenError(event:Event):void + { + log.error("Error encountered during refresh access token request: " + event); + + try + { + var error:Object = com.adobe.serialization.json.JSON.decode(event.target.data); + refreshAccessTokenEvent.errorCode = error.error; + refreshAccessTokenEvent.errorMessage = error.error_description; + } // try statement + catch (error:JSONParseError) + { + refreshAccessTokenEvent.errorCode = "Unknown"; + refreshAccessTokenEvent.errorMessage = "Error encountered during refresh access token request. Unable to parse error message."; + } // catch statement + + dispatchEvent(refreshAccessTokenEvent); + } // onRefreshAccessTokenError + } // refreshAccessToken + + /** + * Modifies the log level of the logger at runtime. + * + *

By default, logging is turned off. Passing in any value will modify the logging level + * of the application. This method can accept any of the following values...

+ * + * + * + * @param logLevel The new log level for the logger to use + * + * @see org.as3commons.logging.setup.LogSetupLevel.NONE + * @see org.as3commons.logging.setup.LogSetupLevel.FATAL + * @see org.as3commons.logging.setup.LogSetupLevel.FATAL_ONLY + * @see org.as3commons.logging.setup.LogSetupLevel.ERROR + * @see org.as3commons.logging.setup.LogSetupLevel.ERROR_ONLY + * @see org.as3commons.logging.setup.LogSetupLevel.WARN + * @see org.as3commons.logging.setup.LogSetupLevel.WARN_ONLY + * @see org.as3commons.logging.setup.LogSetupLevel.INFO + * @see org.as3commons.logging.setup.LogSetupLevel.INFO_ONLY + * @see org.as3commons.logging.setup.LogSetupLevel.DEBUG + * @see org.as3commons.logging.setup.LogSetupLevel.DEBUG_ONLY + * @see org.as3commons.logging.setup.LogSetupLevel.ALL + */ + public function setLogLevel(logLevel:LogSetupLevel):void + { + LOGGER_FACTORY.setup = new LevelTargetSetup(traceTarget, logLevel); + } // setLogLevel + + /** + * @private + * + * Helper function that completes get-access-token request using the authorization code grant type. + */ + private function getAccessTokenWithAuthorizationCodeGrant(authorizationCodeGrant:AuthorizationCodeGrant):void + { + // create result event + var getAccessTokenEvent:GetAccessTokenEvent = new GetAccessTokenEvent(); + + // add event listeners + authorizationCodeGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onLocationChanging); + authorizationCodeGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onLocationChanging); + authorizationCodeGrant.stageWebView.addEventListener(Event.COMPLETE, onStageWebViewComplete); + authorizationCodeGrant.stageWebView.addEventListener(ErrorEvent.ERROR, onStageWebViewError); + + // start the auth process + var startTime:Number = new Date().time; + log.info("Loading auth URL: " + authorizationCodeGrant.getFullAuthUrl(authEndpoint)); + authorizationCodeGrant.stageWebView.loadURL(authorizationCodeGrant.getFullAuthUrl(authEndpoint)); + + function onLocationChanging(locationChangeEvent:LocationChangeEvent):void + { + log.info("Loading URL: " + locationChangeEvent.location); + if (locationChangeEvent.location.indexOf(authorizationCodeGrant.redirectUri) == 0 && locationChangeEvent.location.indexOf(OAuth2Const.RESPONSE_PROPERTY_AUTHORIZATION_CODE) > 0) + { + log.info("Redirect URI encountered (" + authorizationCodeGrant.redirectUri + "). Extracting values from path."); + + // stop event from propogating + locationChangeEvent.preventDefault(); + + // determine if authorization was successful + var queryParams:Object = extractQueryParams(locationChangeEvent.location); + var code:String = queryParams.code; // authorization code + if (code != null) + { + log.debug("Authorization code: " + code); + getAccessTokenWithAuthCode(code); + } // if statement + else + { + log.error("Error encountered during authorization request"); + getAccessTokenEvent.errorCode = queryParams.error; + getAccessTokenEvent.errorMessage = queryParams.error_description; + dispatchEvent(getAccessTokenEvent); + } // else statement + } // if statement + } // onLocationChange + + function getAccessTokenWithAuthCode(code:String):void + { + // set up URL request + var urlRequest:URLRequest = new URLRequest(tokenEndpoint); + var urlLoader:URLLoader = new URLLoader(); + urlRequest.method = URLRequestMethod.POST; + + // define POST parameters + var urlVariables : URLVariables = new URLVariables(); + urlVariables.grant_type = OAuth2Const.GRANT_TYPE_AUTHORIZATION_CODE; + urlVariables.code = code; + urlVariables.redirect_uri = authorizationCodeGrant.redirectUri; + urlVariables.client_id = authorizationCodeGrant.clientId; + urlVariables.client_secret = authorizationCodeGrant.clientSecret; + urlRequest.data = urlVariables; + + // attach event listeners + urlLoader.addEventListener(Event.COMPLETE, onGetAccessTokenResult); + urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGetAccessTokenError); + urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onGetAccessTokenError); + + // make the call + try + { + urlLoader.load(urlRequest); + } // try statement + catch (error:Error) + { + log.error("Error loading token endpoint \"" + tokenEndpoint + "\""); + } // catch statement + + function onGetAccessTokenResult(event:Event):void + { + try + { + var response:Object = com.adobe.serialization.json.JSON.decode(event.target.data); + log.debug("Access token: " + response.access_token); + getAccessTokenEvent.parseAccessTokenResponse(response); + } // try statement + catch (error:JSONParseError) + { + getAccessTokenEvent.errorCode = "com.adobe.serialization.json.JSONParseError"; + getAccessTokenEvent.errorMessage = "Error parsing output from access token response"; + } // catch statement + + dispatchEvent(getAccessTokenEvent); + } // onGetAccessTokenResult + + function onGetAccessTokenError(event:Event):void + { + log.error("Error encountered during access token request: " + event); + + try + { + var error:Object = com.adobe.serialization.json.JSON.decode(event.target.data); + getAccessTokenEvent.errorCode = error.error; + getAccessTokenEvent.errorMessage = error.error_description; + } // try statement + catch (error:JSONParseError) + { + getAccessTokenEvent.errorCode = "Unknown"; + getAccessTokenEvent.errorMessage = "Error encountered during access token request. Unable to parse error message."; + } // catch statement + + dispatchEvent(getAccessTokenEvent); + } // onGetAccessTokenError + } // getAccessTokenWithAuthCode + + function onStageWebViewComplete(event:Event):void + { + // Note: Special provision made particularly for Google OAuth 2 implementation for installed + // applications. Particularly, when we see a certain redirect URI, we must look for the authorization + // code in the page title as opposed to in the URL. See https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi + // for more information. + if (authorizationCodeGrant.redirectUri == OAuth2Const.GOOGLE_INSTALLED_APPLICATION_REDIRECT_URI && event.currentTarget.title.indexOf(OAuth2Const.RESPONSE_TYPE_AUTHORIZATION_CODE) > 0) + { + var codeString:String = event.currentTarget.title.substring(event.currentTarget.title.indexOf(OAuth2Const.RESPONSE_TYPE_AUTHORIZATION_CODE)); + var code:String = codeString.split("=")[1]; + log.debug("Authorization code extracted from page title: " + code); + getAccessTokenWithAuthCode(code); + } + else + { + log.info("Auth URL loading complete after " + (new Date().time - startTime) + "ms"); + } + } // onStageWebViewComplete + + function onStageWebViewError(errorEvent:ErrorEvent):void + { + log.error("Error occurred with StageWebView: " + errorEvent); + getAccessTokenEvent.errorCode = "STAGE_WEB_VIEW_ERROR"; + getAccessTokenEvent.errorMessage = "Error occurred with StageWebView"; + dispatchEvent(getAccessTokenEvent); + } // onStageWebViewError + } // getAccessTokenWithAuthorizationCodeGrant + + /** + * @private + * + * Helper function that completes get-access-token request using the implicit grant type. + */ + private function getAccessTokenWithImplicitGrant(implicitGrant:ImplicitGrant):void + { + // create result event + var getAccessTokenEvent:GetAccessTokenEvent = new GetAccessTokenEvent(); + + // add event listeners + implicitGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onLocationChange); + implicitGrant.stageWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onLocationChange); + implicitGrant.stageWebView.addEventListener(ErrorEvent.ERROR, onStageWebViewError); + + // start the auth process + log.info("Loading auth URL: " + implicitGrant.getFullAuthUrl(authEndpoint)); + implicitGrant.stageWebView.loadURL(implicitGrant.getFullAuthUrl(authEndpoint)); + + function onLocationChange(locationChangeEvent:LocationChangeEvent):void + { + log.info("Loading URL: " + locationChangeEvent.location); + if (locationChangeEvent.location.indexOf(implicitGrant.redirectUri) == 0 && locationChangeEvent.location.indexOf(OAuth2Const.RESPONSE_PROPERTY_ACCESS_TOKEN) > 0) + { + log.info("Redirect URI encountered (" + implicitGrant.redirectUri + "). Extracting values from path."); + + // stop event from propogating + locationChangeEvent.preventDefault(); + + // determine if authorization was successful + var queryParams:Object = extractQueryParams(locationChangeEvent.location); + var accessToken:String = queryParams.access_token; + if (accessToken != null) + { + log.debug("Access token: " + accessToken); + getAccessTokenEvent.parseAccessTokenResponse(queryParams); + dispatchEvent(getAccessTokenEvent); + } // if statement + else + { + log.error("Error encountered during access token request"); + getAccessTokenEvent.errorCode = queryParams.error; + getAccessTokenEvent.errorMessage = queryParams.error_description; + dispatchEvent(getAccessTokenEvent); + } // else statement + } // if statement + } // onLocationChange + + function onStageWebViewError(errorEvent:ErrorEvent):void + { + log.error("Error occurred with StageWebView: " + errorEvent); + getAccessTokenEvent.errorCode = "STAGE_WEB_VIEW_ERROR"; + getAccessTokenEvent.errorMessage = "Error occurred with StageWebView"; + dispatchEvent(getAccessTokenEvent); + } // onStageWebViewError + } // getAccessTokenWithImplicitGrant + + /** + * @private + * + * Helper function that completes get-access-token request using the resource owner password credentials grant type. + */ + private function getAccessTokenWithResourceOwnerCredentialsGrant(resourceOwnerCredentialsGrant:ResourceOwnerCredentialsGrant):void + { + // create result event + var getAccessTokenEvent:GetAccessTokenEvent = new GetAccessTokenEvent(); + + // set up URL request + var urlRequest:URLRequest = new URLRequest(tokenEndpoint); + var urlLoader:URLLoader = new URLLoader(); + urlRequest.method = URLRequestMethod.POST; + + // define POST parameters + var urlVariables : URLVariables = new URLVariables(); + urlVariables.grant_type = OAuth2Const.GRANT_TYPE_RESOURCE_OWNER_CREDENTIALS; + urlVariables.client_id = resourceOwnerCredentialsGrant.clientId; + urlVariables.client_secret = resourceOwnerCredentialsGrant.clientSecret; + urlVariables.username = resourceOwnerCredentialsGrant.username; + urlVariables.password = resourceOwnerCredentialsGrant.password; + + // define optional scope parameter only when scope not null + if(resourceOwnerCredentialsGrant.scope !== null) + { + urlVariables.scope = resourceOwnerCredentialsGrant.scope; + } + + urlRequest.data = urlVariables; + + // attach event listeners + urlLoader.addEventListener(Event.COMPLETE, onGetAccessTokenResult); + urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGetAccessTokenError); + urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onGetAccessTokenError); + + // make the call + try + { + urlLoader.load(urlRequest); + } // try statement + catch (error:Error) + { + log.error("Error loading token endpoint \"" + tokenEndpoint + "\""); + } // catch statement + + function onGetAccessTokenResult(event:Event):void + { + try + { + var response:Object = com.adobe.serialization.json.JSON.decode(event.target.data); + log.debug("Access token: " + response.access_token); + getAccessTokenEvent.parseAccessTokenResponse(response); + } // try statement + catch (error:JSONParseError) + { + getAccessTokenEvent.errorCode = "com.adobe.serialization.json.JSONParseError"; + getAccessTokenEvent.errorMessage = "Error parsing output from access token response"; + } // catch statement + + dispatchEvent(getAccessTokenEvent); + } // onGetAccessTokenResult + + function onGetAccessTokenError(event:Event):void + { + log.error("Error encountered during access token request: " + event); + + try + { + var error:Object = com.adobe.serialization.json.JSON.decode(event.target.data); + getAccessTokenEvent.errorCode = error.error; + getAccessTokenEvent.errorMessage = error.error_description; + } // try statement + catch (error:JSONParseError) + { + getAccessTokenEvent.errorCode = "Unknown"; + getAccessTokenEvent.errorMessage = "Error encountered during access token request. Unable to parse error message."; + } // catch statement + + dispatchEvent(getAccessTokenEvent); + } // onGetAccessTokenError + } // getAccessTokenWithResourceOwnerCredentialsGrant + + /** + * @private + * + * Helper function to extract query from URL and URL fragment. + */ + private function extractQueryParams(url:String):Object + { + var delimiter:String = (url.indexOf("?") > 0) ? "?" : "#"; + var queryParamsString:String = url.split(delimiter)[1]; + var queryParamsArray:Array = queryParamsString.split("&"); + var queryParams:Object = new Object(); + + for each (var queryParam:String in queryParamsArray) + { + var keyValue:Array = queryParam.split("="); + queryParams[keyValue[0]] = keyValue[1]; + } // for loop + + return queryParams; + } // extractQueryParams + } // class declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/OAuth2Const.as b/com/adobe/protocols/oauth2/OAuth2Const.as new file mode 100644 index 0000000..616c2b8 --- /dev/null +++ b/com/adobe/protocols/oauth2/OAuth2Const.as @@ -0,0 +1,17 @@ +package com.adobe.protocols.oauth2 +{ + public class OAuth2Const + { + public static const GRANT_TYPE_AUTHORIZATION_CODE:String = "authorization_code"; + public static const GRANT_TYPE_RESOURCE_OWNER_CREDENTIALS:String = "password"; + public static const GRANT_TYPE_REFRESH_TOKEN:String = "refresh_token"; + + public static const RESPONSE_TYPE_AUTHORIZATION_CODE:String = "code"; + public static const RESPONSE_TYPE_IMPLICIT:String = "token"; + + public static const RESPONSE_PROPERTY_AUTHORIZATION_CODE:String = "code"; + public static const RESPONSE_PROPERTY_ACCESS_TOKEN:String = "access_token"; + + public static const GOOGLE_INSTALLED_APPLICATION_REDIRECT_URI:String = "urn:ietf:wg:oauth:2.0:oob"; + } +} \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/event/GetAccessTokenEvent.as b/com/adobe/protocols/oauth2/event/GetAccessTokenEvent.as new file mode 100644 index 0000000..4f912a9 --- /dev/null +++ b/com/adobe/protocols/oauth2/event/GetAccessTokenEvent.as @@ -0,0 +1,173 @@ +package com.adobe.protocols.oauth2.event +{ + import flash.events.Event; + + /** + * Event that is broadcast when results from a getAccessToken + * request are received. + * + * @author Charles Bihis (www.whoischarles.com) + */ + public class GetAccessTokenEvent extends Event implements IOAuth2Event + { + /** + * Event type for this event which encapsulates the response from + * a getAccessToken request. + * + * @eventType getAccessToken + */ + public static const TYPE:String = "getAccessToken"; + + private var _errorCode:String; + private var _errorMessage:String; + private var _accessToken:String; + private var _tokenType:String; + private var _expiresIn:int; + private var _refreshToken:String; + private var _scope:String; + private var _state:String; + private var _response:Object; + + /** + * Constructor. + * + * @param bubbles (Optional) Parameter indicating whether or not the event bubbles + * @param cancelable (Optional Parameter indicating whether or not the event is cancelable + */ + public function GetAccessTokenEvent(bubbles:Boolean = false, cancelable:Boolean = false) + { + super(TYPE, bubbles, cancelable); + } // GetAccessTokenEvent + + /** + * Convenience function that will take a getAccessToken response + * and parse its values. + * + * @param response An object representing the response from a getAccessToken request + */ + public function parseAccessTokenResponse(response:Object):void + { + // required + _accessToken = response.access_token; + _tokenType = response.token_type; + + // optional + _expiresIn = int(response.expires_in); + _refreshToken = response.refresh_token; + _scope = response.scope; + _state = response.state; + + // extra + _response = response; + } + + /** + * Override of the clone function. + * + * @return A new GetAccessTokenEvent object. + */ + public override function clone():Event + { + return new GetAccessTokenEvent(); + } // clone + + /** + * Error code for error after a failed getAccessToken request. + */ + public function get errorCode():String + { + return _errorCode; + } // errorCode + + /** + * @private + */ + public function set errorCode(errorCode:String):void + { + _errorCode = errorCode; + } // errorCode + + /** + * Error message for error after a failed getAccessToken request. + */ + public function get errorMessage():String + { + return _errorMessage; + } // errorMessage + + /** + * @private + */ + public function set errorMessage(errorMessage:String):void + { + _errorMessage = errorMessage; + } // errorMessage + + /** + * The access token issues by the authorization server. + */ + public function get accessToken():String + { + return _accessToken; + } // accessToken + + /** + * The type of the token issued as described in the OAuth 2.0 + * v2.15 specification, section 7.1, "Access Token Types". + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-7.1 + */ + public function get tokenType():String + { + return _tokenType; + } // tokenType + + /** + * The duration in seconds of the access token lifetime. For example, + * the value "3600" denotes that the access token will expire one hour + * from the time the response was generated. + */ + public function get expiresIn():int + { + return _expiresIn; + } // expiresIn + + /** + * The refresh token which can be used ot obtain new access tokens using + * the same authorization grant as described in the OAuth 2.0 + * v2.15 specification, section 6, "Refreshing an Access Token". + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6 + */ + public function get refreshToken():String + { + return _refreshToken; + } // refreshToken + + /** + * The scope of the access request expressed as a list of space-delimited, + * case-sensitive strings. + */ + public function get scope():String + { + return _scope; + } // scope + + /** + * An opaque value used by the client to maintain state between the request + * and callback. + */ + public function get state():String + { + return _state; + } // state + + /** + * Response object to contain all returned response data after a successfull access token request. + */ + public function get response():Object + { + return _response; + } // response + } // class declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/event/IOAuth2Event.as b/com/adobe/protocols/oauth2/event/IOAuth2Event.as new file mode 100644 index 0000000..3002690 --- /dev/null +++ b/com/adobe/protocols/oauth2/event/IOAuth2Event.as @@ -0,0 +1,16 @@ +package com.adobe.protocols.oauth2.event +{ + /** + * Interface describing generic OAuth 2.0 events. + * + * @author Charles Bihis (www.whoischarles.com) + */ + public interface IOAuth2Event + { + function get errorCode():String; + function set errorCode(errorCode:String):void; + + function get errorMessage():String; + function set errorMessage(errorMessage:String):void; + } // interface declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/event/RefreshAccessTokenEvent.as b/com/adobe/protocols/oauth2/event/RefreshAccessTokenEvent.as new file mode 100644 index 0000000..868251b --- /dev/null +++ b/com/adobe/protocols/oauth2/event/RefreshAccessTokenEvent.as @@ -0,0 +1,173 @@ +package com.adobe.protocols.oauth2.event +{ + import flash.events.Event; + + /** + * Event that is broadcast when results from a refreshAccessToken + * request are received. + * + * @author Charles Bihis (www.whoischarles.com) + */ + public class RefreshAccessTokenEvent extends Event implements IOAuth2Event + { + /** + * Event type for this event which encapsulates the response from + * a refreshAccessToken request. + * + * @eventType refreshAccessToken + */ + public static const TYPE:String = "refreshAccessToken"; + + private var _errorCode:String; + private var _errorMessage:String; + private var _accessToken:String; + private var _tokenType:String; + private var _expiresIn:int; + private var _refreshToken:String; + private var _scope:String; + private var _state:String; + private var _response:Object; + + /** + * Constructor. + * + * @param bubbles (Optional) Parameter indicating whether or not the event bubbles + * @param cancelable (Optional Parameter indicating whether or not the event is cancelable + */ + public function RefreshAccessTokenEvent(bubbles:Boolean = false, cancelable:Boolean = false) + { + super(TYPE, bubbles, cancelable); + } // RefreshAccessTokenEvent + + /** + * Convenience function that will take a refreshAccessToken response + * and parse its values. + * + * @param response An object representing the response from a refreshAccessToken request + */ + public function parseAccessTokenResponse(response:Object):void + { + // required + _accessToken = response.access_token; + _tokenType = response.token_type; + + // optional + _expiresIn = int(response.expires_in); + _refreshToken = response.refresh_token; + _scope = response.scope; + _state = response.state; + + // extra + _response = response; + } + + /** + * Override of the clone function. + * + * @return A new RefreshAccessTokenEvent object. + */ + public override function clone():Event + { + return new GetAccessTokenEvent(); + } // clone + + /** + * Error code for error after a failed refreshAccessToken request. + */ + public function get errorCode():String + { + return _errorCode; + } // errorCode + + /** + * @private + */ + public function set errorCode(errorCode:String):void + { + _errorCode = errorCode; + } // errorCode + + /** + * Error message for error after a failed refreshAccessToken request. + */ + public function get errorMessage():String + { + return _errorMessage; + } // errorMessage + + /** + * @private + */ + public function set errorMessage(errorMessage:String):void + { + _errorMessage = errorMessage; + } // errorMessage + + /** + * The access token issues by the authorization server. + */ + public function get accessToken():String + { + return _accessToken; + } // accessToken + + /** + * The type of the token issued as described in the OAuth 2.0 + * v2.15 specification, section 7.1, "Access Token Types". + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-7.1 + */ + public function get tokenType():String + { + return _tokenType; + } // tokenType + + /** + * The duration in seconds of the access token lifetime. For example, + * the value "3600" denotes that the access token will expire one hour + * from the time the response was generated. + */ + public function get expiresIn():int + { + return _expiresIn; + } // expiresIn + + /** + * The refresh token which can be used ot obtain new access tokens using + * the same authorization grant as described in the OAuth 2.0 + * v2.15 specification, section 6, "Refreshing an Access Token". + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-6 + */ + public function get refreshToken():String + { + return _refreshToken; + } // refreshToken + + /** + * The scope of the access request expressed as a list of space-delimited, + * case-sensitive strings. + */ + public function get scope():String + { + return _scope; + } // scope + + /** + * An opaque value used by the client to maintain state between the request + * and callback. + */ + public function get state():String + { + return _state; + } // state + + /** + * Response object to contain all returned response data after a successfull access token request. + */ + public function get response():Object + { + return _response; + } // response + } // class declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/grant/AuthorizationCodeGrant.as b/com/adobe/protocols/oauth2/grant/AuthorizationCodeGrant.as new file mode 100644 index 0000000..3115196 --- /dev/null +++ b/com/adobe/protocols/oauth2/grant/AuthorizationCodeGrant.as @@ -0,0 +1,140 @@ +package com.adobe.protocols.oauth2.grant +{ + import com.adobe.protocols.oauth2.OAuth2Const; + + import flash.media.StageWebView; + + /** + * Class to encapsulate all of the relevant properties used during + * a get-access-token request using the authorization code grant type. + * + * @author Charles Bihis (www.whoischarles.com) + */ + public class AuthorizationCodeGrant implements IGrantType + { + private var _stageWebView:StageWebView; + private var _clientId:String; + private var _clientSecret:String; + private var _redirectUri:String; + private var _scope:String; + private var _state:Object; + private var _queryParams:Object; + + /** + * Constructor. + * + * @param stageWebView The StageWebView object for which to display the user-consent page + * @param clientId The client identifier + * @param clientSecret The client secret + * @param redirectUri The redirect URI to return to after the authorization process has completed + * @param scope (Optional) The scope of the access request expressed as a list of space-delimited, case-sensitive strings + * @param state (Optional) An opaque value used by the client to maintain state between the request and callback + * @param queryParams (Optional) Additional query parameters that can be passed to the authorization URL + */ + public function AuthorizationCodeGrant(stageWebView:StageWebView, clientId:String, clientSecret:String, redirectUri:String, scope:String = null, state:Object = null, queryParams:Object = null) + { + _stageWebView = stageWebView; + _clientId = clientId; + _clientSecret = clientSecret; + _redirectUri = redirectUri; + _scope = scope; + _state = state; + _queryParams = queryParams; + } // AuthorizationCodeGrant + + /** + * The StageWebView object for which to display the user-consent page. + */ + public function get stageWebView():StageWebView + { + return _stageWebView; + } // stageWebView + + /** + * The client identifier as described in the OAuth spec v2.15, + * section 3, Client Authentication. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-3 + */ + public function get clientId():String + { + return _clientId; + } // clientId + + /** + * The client secret. + */ + public function get clientSecret():String + { + return _clientSecret; + } // clientSecret + + /** + * The redirect endpoint for the client as described in the OAuth + * spec v2.15, section 3.1.2, Redirection Endpoint. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2 + */ + public function get redirectUri():String + { + return _redirectUri; + } // redirectUri + + /** + * The scope of the access request expressed as a list of space-delimited, + * case-sensitive strings. + */ + public function get scope():String + { + return _scope; + } // scope + + /** + * An opaque value used by the client to maintain state between the request + * and callback. + */ + public function get state():Object + { + return _state; + } // state + + /** + * Additional query parameters that can be passed to the authorization URL. + */ + public function get queryParams():Object + { + return _queryParams; + } // queryParams + + /** + * Convenience method for getting the full authorization URL. + */ + public function getFullAuthUrl(authEndpoint:String):String + { + var url:String = authEndpoint + "?response_type=" + OAuth2Const.RESPONSE_TYPE_AUTHORIZATION_CODE + "&client_id=" + clientId + "&redirect_uri=" + redirectUri; + + // scope is optional + if (scope != null && scope.length > 0) + { + url += "&scope=" + scope; + } // if statement + + // state is optional + if (state != null) + { + url += "&state=" + state; + } // if statement + + // add additional optional query params, if any + if (queryParams != null) + { + for (var queryParam:String in queryParams) + { + url += "&" + queryParam + "=" + queryParams[queryParam]; + } // for loop + } // if statement + + return url; + } // getFullAuthUrl + } // class declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/grant/IGrantType.as b/com/adobe/protocols/oauth2/grant/IGrantType.as new file mode 100644 index 0000000..0576ad8 --- /dev/null +++ b/com/adobe/protocols/oauth2/grant/IGrantType.as @@ -0,0 +1,17 @@ +package com.adobe.protocols.oauth2.grant +{ + /** + * Interface used as a marker to signify whether a class + * encapsulates the properties of any of the supported + * OAuth 2.0 grant types, as described by the v2.15 + * specification. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4 + * + * @author Charles Bihis (www.whoischarles.com) + */ + public interface IGrantType + { + function get clientId():String; + } // interface declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/grant/ImplicitGrant.as b/com/adobe/protocols/oauth2/grant/ImplicitGrant.as new file mode 100644 index 0000000..e427f34 --- /dev/null +++ b/com/adobe/protocols/oauth2/grant/ImplicitGrant.as @@ -0,0 +1,129 @@ +package com.adobe.protocols.oauth2.grant +{ + import com.adobe.protocols.oauth2.OAuth2Const; + + import flash.media.StageWebView; + + /** + * Class to encapsulate all of the relevant properties used during + * a get-access-token request using the implicit grant type. + * + * @author Charles Bihis (www.whoischarles.com) + */ + public class ImplicitGrant implements IGrantType + { + private var _stageWebView:StageWebView; + private var _clientId:String; + private var _redirectUri:String; + private var _scope:String; + private var _state:Object; + private var _queryParams:Object; + + /** + * Constructor. + * + * @param stageWebView The StageWebView object for which to display the user-consent page + * @param clientId The client identifier + * @param redirectUri The redirect URI to return to after the authorization process has completed + * @param scope (Optional) The scope of the access request expressed as a list of space-delimited, case-sensitive strings + * @param state (Optional) An opaque value used by the client to maintain state between the request and callback + * @param queryParams (Optional) Additional query parameters that can be passed to the authorization URL + */ + public function ImplicitGrant(stageWebView:StageWebView, clientId:String, redirectUri:String, scope:String = null, state:Object = null, queryParams:Object = null) + { + _stageWebView = stageWebView; + _clientId = clientId; + _redirectUri = redirectUri; + _scope = scope; + _state = state; + _queryParams = queryParams; + } // ImplicitGrant + + /** + * The StageWebView object for which to display the user-consent page. + */ + public function get stageWebView():StageWebView + { + return _stageWebView; + } // stageWebView + + /** + * The client identifier as described in the OAuth spec v2.15, + * section 3, Client Authentication. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-3 + */ + public function get clientId():String + { + return _clientId; + } // clientId + + /** + * The redirect endpoint for the client as described in the OAuth + * spec v2.15, section 3.1.2, Redirection Endpoint. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2 + */ + public function get redirectUri():String + { + return _redirectUri; + } // redirectUri + + /** + * The scope of the access request expressed as a list of space-delimited, + * case-sensitive strings. + */ + public function get scope():String + { + return _scope; + } // scope + + /** + * An opaque value used by the client to maintain state between the request + * and callback. + */ + public function get state():Object + { + return _state; + } // state + + /** + * Additional query parameters that can be passed to the authorization URL. + */ + public function get queryParams():Object + { + return _queryParams; + } // queryParams + + /** + * Convenience method for getting the full authorization URL. + */ + public function getFullAuthUrl(endpoint:String):String + { + var url:String = endpoint + "?response_type=" + OAuth2Const.RESPONSE_TYPE_IMPLICIT + "&client_id=" + clientId + "&redirect_uri=" + redirectUri; + + // scope is optional + if (scope != null && scope.length > 0) + { + url += "&scope=" + scope; + } // if statement + + // state is optional + if (state != null) + { + url += "&state=" + state; + } // if statement + + // add additional optional query params, if any + if (queryParams != null) + { + for (var queryParam:String in queryParams) + { + url += "&" + queryParam + "=" + queryParams[queryParam]; + } // for loop + } // if statement + + return url; + } // getFullAuthUrl + } // class declaration +} // package \ No newline at end of file diff --git a/com/adobe/protocols/oauth2/grant/ResourceOwnerCredentialsGrant.as b/com/adobe/protocols/oauth2/grant/ResourceOwnerCredentialsGrant.as new file mode 100644 index 0000000..cabc1b5 --- /dev/null +++ b/com/adobe/protocols/oauth2/grant/ResourceOwnerCredentialsGrant.as @@ -0,0 +1,80 @@ +package com.adobe.protocols.oauth2.grant +{ + /** + * Class to encapsulate all of the relevant properties used during + * a get-access-token request using the resource owner password + * credentials grant type. + * + * @author Charles Bihis (www.hoischarles.com) + */ + public class ResourceOwnerCredentialsGrant implements IGrantType + { + private var _clientId:String; + private var _clientSecret:String; + private var _username:String; + private var _password:String; + private var _scope:String; + + /** + * Constructor. + * + * @param clientId The client identifier + * @param clientSecret The client secret + * @param username The resource owner's username for the authorization server + * @param password The resource owner's password for the authorization server + * @param scope (Optional) The scope of the access request expressed as a list of space-delimited, case-sensitive strings + */ + public function ResourceOwnerCredentialsGrant(clientId:String, clientSecret:String, username:String, password:String, scope:String = null) + { + _clientId = clientId; + _clientSecret = clientSecret; + _username = username; + _password = password; + _scope = scope; + } // ResourceOwnserCredentialsGrant + + /** + * The client identifier as described in the OAuth spec v2.15, + * section 3, Client Authentication. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-3 + */ + public function get clientId():String + { + return _clientId; + } // clientId + + /** + * The client secret. + */ + public function get clientSecret():String + { + return _clientSecret; + } // clientSecret + + /** + * The resource owners username. + */ + public function get username():String + { + return _username; + } // username + + /** + * The resource owner password. + */ + public function get password():String + { + return _password; + } // password + + /** + * The scope of the access request expressed as a list of space-delimited, + * case-sensitive strings. + */ + public function get scope():String + { + return _scope; + } // scope + } // class declaration +} // package \ No newline at end of file diff --git a/lib/as3commons-logging-2.7.swc b/lib/as3commons-logging-2.7.swc new file mode 100644 index 0000000000000000000000000000000000000000..0266508d69c22d38e3354e6f63bc17867e63dc75 GIT binary patch literal 83350 zcmV)4K+3;RO9KQH00;mG0QX!@KL7v#000000000001E&B0BmVua$$0LE^~Kg03$%$ zze8733YO~v0C)l1y$M`g*?A{=&)rT{Q9!X32!vD;NC>G!E$nD%5Kt1dR)nNhH`Ta+ zDnWO#xT?@%Sq^Sn-5u|1$B8$*FYP3@JGQeu+ligAGjgLqFXZ`M3zVn^$e9Kiy{aZ;Y_@pFNFsY|#izG?ERPu+4 zBppnRB{~LrBEGAW$*FV)-VW7`XEHM#fxwk3R~oJ~HcY231w!rZ?SWu85DxE0j{WIt zQ<>P+{Zr|hy2GM^o_KmRH8GQ!n4a=cg|U(8+03E3I;+;P(d9~KW>ZN~<=AK-o{Ue% zr!wh4s3C-k#zs35)2Yc==5TCgCOI)0qv`@z_ov6FM<2fuyBy!2NXF9R2Lme^sZ3@f zlZ+qkI@{PiJvli&mG&KXOK3CQ`Ps6%Y>}pBPESQrCPlsp-oTW60}^`I2TS zU)SkAAHeW+jZ*Vx#b@~%8-k*`>`JK8nc0!#M0z})I@~oimWp5T9gkm`NKcPXiQFrx ztMhh`rzX;w>6!7k?^ryQiciLJ=kJeA#?!u|Dg4ys{7;UfU}v@?3H-Vd?#Y5 z%)}I$pBRf@x)je{shN4&cY0!UbYd!&na)}Dz;qIT`X0U#yA)d|<7g_58rJVHJB_-_ ze%`3F?DXus>vYt*+17N@dU@9Epp7%G?VR;;-qSJJnZ{z7_~B4fAQTLQLcUO_qp@9d zo`^pfphcCn$PSw^Sht6z?Vi~!wV~Rs6SZeX6Gs}ljFyZ9I(sZWnn@3# zj{R08+0Xl}3Ww6E(V@xM#MDqstUeQ|96;-o+b(El&T9R@n${a)X)S&kxP?z)?O;9V zR4AU()A6aXcuJBxI#>fw#b=VS(fAGv-^IqJN8$}=B>-d+2nT~LfsxsXB-TmUz17!- zn|A^(G-SioV8PXp1y|(;Sz|od6xw9_wrSD|ql=XO_Yv81ny?0>X)PVII=^E@h9^T+HvwEUqO&6+{ zrzgf7fUcK@Ngp1KjgH3)*JLUm9==mJ3VoWaY+J{Bzjj#%OlQgt^NB$`@{0qUmc=z9B3fq5|sd9s{e=))Q z-0z6U^34~FhOs2Ol~Cr?y_btBB&nWWquMRMC!)!FN7a}zqB_(${CCImup~K+(Cx*9 z9^tIs2z!1?5zQjPfru8*k0_!|L^u)A?)fu{I3OZih&br^Dn%R?5qXGs!1L>f@O)a5 zjKE^TJ*pbPXnrC;Q81!b0w3*?Jg-JlqlhcC;+Q9jxF!+jvEpP;AL3d?T#*&0c$yK{ zA>xXyIMuTQafd`)i4~`L@+GORn3~f)=-pkqL#>)zTGA_)mg>d-9ZO3OE-ei$Eg{zE zItp|sqm%O&g6QC$xO7e$_agFZrT>D`IWT29o|&Bq(1IO|rLf{{^jIY&UoreB+B~q* zCg!e6=+Fs*DkX4^i6fku3Br-F$r%nhBOSXGcg@67$e4+z()tVn&_NrlaM6Z9MCNw| zHkq#@Iekf>03FB~1egL;p`V^Z6lO&PlGGpSGUQw6||ibv)uoXY=l_^ z^g}Bk+vp@5YHr(_3w+l~88y_{(yDryqsUt!GsVeS9%pXO@;NKutdPsaT-oB>!c>}& zoXJ2#k|w+6fQ@wSa7vhZlp>eNrQWh~5mquTX_CzJE?^K!RT%HV53mQG6^0qE(6skp z+RMiFsKfV=G|mlk=6@qMjUYZbkP)*!&^?aLJsrEUdJ%+j4t9fCwqteKf}Y+;*SX_^ z!;w=1C%Oiu{Oi`-9Ugmi=0>5NdbDe>_d?gjLc8!$kXct^*J^V0wbkJL?=Kq>F6JyS zKkI-Enb{?n5W0B?;+ITN9mCc`*Ct?I(@AUk+gfXzL;|`%0yY9*ovvXMFkl5*K`k!> z9DC8_hhq zyGd5GhTB0~v^Hj~_nde^1)18chC{)i8g2=;sp0mvV8QCTEiz@RO-qg9^mPwG6>bVO zsiBr&GuN0`@pA7rZ(RkpI<1JyGM6>3=pbVV)yTjzUM`n&c?*{-xm?BNtz4~k?q*!^ zb9q0ofg;J0uDG9;-T3Epai^}k@+7xjF;`INDJm{0Wlnwj959h0IehYtnw_;G+$F+2 zBCHqTUJ>qVpgaNb9At%_Je?IVm3K<{*o?JK)WQ@;7toigc1sE)L>6JX3QQ(PWF#`C zvcev`>73FOb*o7j?S7Q`)p2&0X{`QHIjZz4(zr}JyaH6&_)~|`M){ml+g;{jZFuXG zjP`}aanQ^qyp0E)_PhpLUBXeSjdkUH5QX+s4s_1Yf*$dqf@4M)VOLQF+9n z)*F3jNl|50+l`h^nn0d3PAx?97jFV8P&4&WmS8vg^BJ)8Y2;Rcy_G6pZ--G6m!h78 zCrH6VQuTNt;!H&TgWMzxt-C-feyR8BOe8fuY3|!L9pioVD@Mjpy9d^`OL#IZCsgyECtkY$4&Dea5y(SpVX-NbP)l1YFj9Mqu4xYLb$DInF2y|CFb0I6ax5!_ z4me64SMr^z%+*5R4c$$r6^A5y_i{0+^mH;FoBBUAiA*SlR)f4UiCS6}Y%7F@RHw{9m10l9 zP^c2qW$tviXiCTel@n=-iBTZNaCel|#bgUV?aqy#z!3?o=B3zU=b3uNkP{j`u)0xv zvmmgngHyrrQ%8^X4h%=Sx(81UT+G1`TEhsE>Ngl~;{2>`(q<)DXeQ?jxc{!$2#o-) zq1ABN!ygW}g%z*n<&th83}6n~#WD6coTv_yP{39#af#*0?t=V6+O@T=Fii#t2M~EH zP(-VVB3cPWv>I&!Mce`>gO)x1$y`hsb1(Zb&h@#Lx;T!Tq*&mg6UWcuh9RD@4hLe| z$ls=>rLTiU`opftrVYko6}@>+J=b2bX}9VGv9?{Bf#cKZj5>p3zMD9nec;QcF2|Dj zld-EuW)q2c>g>d8;`&SqY_+S}Addg3JaM-oSc7SOm^cC{K0G4wYQt2UHcZv=VY5)d z@T#i7t4zkOxkOEZq31TM6Fk_oHrRKfcfduEn5}zaV`C!QaomH%r{byJm(#!Z44i$w znFTX}juTw5bU4qJbcuIBY#z_doq@KyeoY~d{p}isJ_g!4-omT#L~X0 z>5MOyOio{kkNL)C<08qb*(aL!b+Qg6+TqjE;}eODN;5`J#-}c2##Op7+sv_DnM_~G zHz)1|d={qH(U&=r6PIxU?^v0&=8eL|`>HM1NNJ-qs;7Qo^_!Cf=jnBCDRQE&z)Z^g z`sHcD1#q=NO^nl#`>Iq_74Gt-?Xi#31jTKvKo^)B8QhFolMIvHoI9Tra_oBhc=Tq)uz zn4qPcm2vPglnO9Jxl+lstz6m0mF-;F!C5V5xLDcE^*XNBb7e0sS%`tb_H(s?D*?`e zTn%w0%vmE>nz+);l^V`kxKhQn4z3*F%0aFi;=G3Q2RQHKtcNT3{}@+JaOEUd&T-{o z?il9Iajr}_#~DufxSNsk3knPK%b!+2!|32x5KESowvgpZ2wRU(Xu za72WoA{-N8T!aY`UV4mLd0advS!Ll)F}1W) zYIeDpR%ZU{VAg=7~kerO?hVebp5VJCYi&U`l~F-6^ShiiOPsu zt~VaFW2zPsTSd$;#Z)J%6WbON+eOT)D8`rYC3Y+%c8Zu0#4IFKu%jY1sASX=lB9*; zt>DAb-aC7P`zDz&7TxV{jj9QC-rqJ)hwXWP`+QtZvP9i{WH+m?b|{{A1Gb4cTS(~m zCgU;ss`RAdIYF0!$vBI%q>O%w?2+aveG>1=*VA#fZ&ER)=mjjWLLcgw1~im8QjZRu zS+4NaR)xmns3oztRx1A_F6uH=vQ{d6=X=QklGH^xFTo0p$r;*{p-sjQMQR!)Bya`?9jRaB8o0$hw ze<8R?J^q5|@oI-`yoD|JFv@Rb3w40$ZOAUZyTE~OeLGqp5Z}p;vZAV;p12rsD4FcKQ8?WGk%pt!;6tHy8YJ}`k~Bt zzuEK$2-1~_)`&Us!R071(9`)XVb6$?4o)Ciw2nh%CV28<+cj*;7nTh5Oh~}`xYT{ud4!+1M z(McsIg>e7CZbTsn0ETQ}%^Qfu_Qp-HY9heh3he^Kt0@!*@9jXdKHyk$RQ|m%=cou~ zGDHpo_``{Xj^j4B=HcmAY;Xjp6D=NCX%&OyUU{nxN(u;-jRs6mR9ROu9C_&oYZ6cv zXE-0{KJh`vLCz)>RGw!t8V0Y;#M4gPe`aD+qj;jb`1A~@PtOVf0H?M&ynO@Gli+Va z<`+N9YRGjm?MzOO#*!pmWKmn1_yQ-zh?dpVY%;E!;1!I=(xm>LD zD=SaHHz=-f=~fT|J4FZ0Li@o&@Zs;!PUlWj*z?d%1;oQXpP6ahi6C z(2NiQQ7#dR2&L=zaT5&`ofVu~^Zr15p<8^c*|F)(9Bw^gPMHNn4OX35F^;L}u{aH| z)9g1)Q57<(8LJyb3XS?nWJ_!Gl;&p6G>+Psv1wOgdUk5;d@MOJX4C&pkwRtnzR1+< zSQ*b8yiO;HGl&n!iTUmy6KsU}Y)mk;0;Pv6IXlGJVa^`ltdq+}xTBlP zyDA$g0t&eLJT=L9R7&?IhPuadw((XSj1v zR(hDrH9#khJU7TB+>@S@&p&kGBJonoyE+K9OS)Uu{sEkWztDM>ZFT)*kqph=B6KQGT;0updWp*&`k5hS>0q2o^MVT z3&JDOUAESMW~Lsyo8&;Hhv9}=OqH&c)wgLjcWMAHzk>s zEC1>fqt2+3Q2fdfHp)G9pqxgWBaWI;*NAKHXx`Rnfxj|Rz%CbqC>!A;juBU^u-cL5 zaD|oOBo;Bw%q0NIDXQ&lGP1 zjrXDDm?*j`iT+QDm`ITlD+Ih@T`T$=n$R7Z@sVukohI}bf1%KSXbtoqT?PHemSMG_ z|M+sudeDE;DvbjYvG<=shv>$qs0ij)QlmKkHas~}^ZxDg^gW3ZJnoo3!Xl+0BR?&& zM#?109q~$!71pBW{00?O{pF?a!pm>sMNA;K@%ii_uqMzeI|N@qLK|2sv-EDb$TSRnMt{4vrB6AuxiHd5=`!U zzE%wD@2ncu-(LqdfBj6NauX|l?`FX}xN6pE##hZQr$N#ZRf)pAqnuDFA&ASxprehi z3HXuB1<1cIK>l58CVqnwnT2nl*kgPX5z(#DYKl&HBGoLil@Z1@zGdbN2skO{x3hC@ zTh3Wve8fDgs|O_3c8=+a~SDH7@~PZk+}X0J03 zt+VIkWgG|8?s*Tx;r}+W3fyn}Ii7Y?q3317TAqJEZsU#}`90Z_p@{F>5kJ5KzO7Ke zvolh|1W11=drnZczqYgeFgshVo$W`m2lvQM<8SS3f0vzYm!0kJWzP};`44usf6UId z+s^hA+4DWh_Mh!+|0O$Hot^EUWX~5U+kdsQ-ObLn$IfQRo;Ojp|7K_V@7dYv?QH)d zdqyeSzuMXUXLh!|cDDap_8g>a|J%;?Z`s-Q+1dVG_LL!;$HOFJ0XdSzquk$VEXpI? z62tzsB>B6HTXqEVd<79<;{`iH_Iwf%EyiEi5sK%%h-fqZ%8pPyZ$w19@i%sa=1C)> z!}zfsp?gLUalrUbb_DmFLBv7hpX~^TrxOu}jGx*OPEP<4hmHSXN4PxL9{mp(|I3cZ zTaoT|3+ZmRgIAR=ExDim*%JQ$)X$cF`)5l({MnNJ^QHYiUmE=R()FJ&ee~x`fBf?$ zbv$u6l;Qe19#lcZKl%@Ra0 zTEMZAS|u(^ZIYu}Y6mMw>X2NjbU?~u(m~0sN{6I;O*$+UXwm~xp)Pew9#!g+ijGL# zQgJnrjcnBw{C9TdD&t|SA&4ZuK3HUpvK*sC-1u{1jyde;V z1PBM#fPlk*b)1*fM~BjTH?J$CB6n@&z17vWv~)mVZPL1=c8C)dPNwGBGcPlJZU*Pm z?pQjWll!omyI>wH#Sv8;OwHq{CQhH?WJ{;dZR-OASq*WTRB^6PtC`rPblJKYtJaMr z0sf}PcCDWlpBkMewaNig!OA@}wC9nbq0CfjBwEid6|PC%pn=&bXl!cWHKeX-kX@LK zJ46WW05-w==26|cxuSkGuZ*qRVofj@G*N|4hx*aD!~R}7+R*dUxs=EX^dvi zKVrgQw7}^|$8ijvjTao7h(lp?bo^Q`fsT~g0$9L8yume` z8BfKbQ<5C(+<-YC);j6OH9fYrV*<;CsX~)KRb+QJbbynh4_HIdC6W`th5T|Ca&%$x z+Te7@%V-ozelt)Z%gKDWf4s91^FefniFbz3nQNw=V8b>ywFK4X=5V9h+!Ab8n_EN8 zYI9q--L*0RYD+K}axQltUfY`?njt<+ZE0+3*+_LTYcvYiG;gs9x37t|7Q}Mb*S|=5 zU^%DYAKFToMG4a1Hf4-Dz-PRAt;o)J)EwRERO^dXE$V<25=xU8oXxJx%X$uHDX_07 zbqa;fY`xNes0yMEs7fCe$mJ!qj()UT&*BL?F)dBB;uUQRsaMqdc#FF~9~ zolI{N;1do{A*Owk)dcRTxDZ{Dyk1~Y*pKWO)Uso z8`U<5lBn(BwvgHm@sJHR+LbNPIV`#jR|NX7*!lOZ|2-=u0xOSgFPe9QkA7(dv%Xdf z1>0J}6t=f%p-`}~9bq`wz5!XZW@B$&AKo>&0&Ct6ZLZV?pK8)jAUoB;fpfjiVT-ni zzl>T}^#kc7;@g{oEeNr1Aq1AHT_09^dpD`6#?533tS+;6Q)R?lDzKOQ@#*QuXJ=mW zl?$XdGs}(pH`T6Kk%QA(sIe)GUcpw}Vqc`-tvT34VH0|)vALDP)v<+@etv0F1yDdhELlVbw; zj>LUgb&L^mGx44!@@Y!@)dBU^CDvAA;qSFoB%pBQ8wK@XS2YqJjm@SZK}wQ>A|CRk zQ*{t8y&RiJ(thEKWqc6mB}wLn(Y4KF!-{GIjD&!vrhO}lwmxxPM;2;?g$o}<6wRh+ zFWli19Vd!n8wuS|Mw97Hk4w_p*_UYn6=HZHCx!zP3>#UFt235iPBsOBs1(a*Lu`aO zG*Me_{l4b*5a>zV?c2HnYHO%92qGekfF%0buwd2J_GVz~mZnw}4?*Cy_NGR{Zecu! znwmohA@1jh%uWd@WGxhK4mM-zwlpHCv9Uc|<5i&~RG?LsS80vj^A(NS#fpoS7b~Nc z4>@AqY3Bu}!qc1$a5l*K1B4)kk^3tGG78 zl`*c3^MWKVoZ(uUYZ=b2RVkdkhO_HjeJwA!!R0x|<=5ly3C2s_fZsO)yeApgp2G9f zc)r5bt6bg5)qPxh58~g;s#KC>RPmzXMdRv6@sz33dG+Ve55IxGFEU>EdyH@YeHfkK z>LsrJ0dhRzyx?#)JDpE5r}I&#v(@QrcRCL`oqnftyVKd=bnbCF!%pXGoqXpvnRAPi ztAED$&fBQ&hpZMFlg>FPY$)Y(d6KgAjd;h6pIlj~gywdD4Fs_|@R3-Ja%gTR!p=Fj zv@3!*ihr1dcZTO4k!vJrbncOzl7B*^K)`TzE``{lH?b$KTt%igGc(s)@GB~1Qspyj zZU>Tn#Y%c#R?>&eqz_w3A6rRMK7(ggseD!>?ehO7o9lWOwWt6^`5i<`;XfqRcU1}> zm5=rJJt)HCB0M3&lOjAN!ZRW~E5dUkd`yIoi*QneQzCpT6F+Y=f8K8Xyu?D9nkipAJk)fQG`I;RS(iZik$lyICGL$1jC96P& zDz>H4@2rxl`Ib(pNMe;0;#;cDbV}#*^4Qh^(R{U8dfSOEX&c*K*ClOdzCgFbC+%R3 zUD8grLz7&Pku8-f&;$7Dn!+Sl!kS}W55!(~)*OsTlM<`l+JoNNwY$4VlI6NRfgVYh z>-TQ!fpvy`eqRp+7xy>p)TJq@jDq|Kbx#1S+9|1=0@yj3k}7*(4I&t-6&4-p?t&lu zeg#GyKAF(up<9*64^oC64Fv{=3;?5J)4R27$s>~wvQ410UNQQDYW80{2|5Zlv|CG(X zt@dv_B1a1og{IOPG~XEe2}6b-%f6eV`dzj|`Z0QVC!?@N{s-!9yDqOB9Gay76D+ z5j{}~nb<0P*FVWfik6M&kurwc##jN}HP(CFNcyjoRLde>T&Mphc|`kzy{Nc$=_8km zK872SpW;)>N7P8UC{`hg;l64S?bk}bNlLo6=ZZYg%#9RFQjM7L-wqT9Lx z+4m#(^!Gu|)OeCDL@WJjel|kTyjwAqHEGTo5lsF1Pd%W_%R&l`rB` z#%qm_Ela;PBGYF@rY`AOW<1NHyZqe?iQ4{M^4zZHjn6?)wQ6x8Q61SWMe0!fuQU2I z7X;xoQz|`=w4c4G<&WvpzQq<`BWORp2yophgoq9y^*f@CN2KpSx_gLyml@w>i?GS` zC#Zk_E&59|Jb`;52kwQUfm{6n_Qc*hu?DOmw3aU`nqG%W7OYAZP{~f9SYwebEJlJX z8b}15x)loQ#`V)>AL~K-wTLS(%&=VN1|(5C<@~EKL+IEt;RnPz(u;Q=lun(te+tJg}rw$ zWS8Zi;3mvB=O>wXQ~U^$@j^i8F1sTA1mAKMtI7B$bYO_2gewUF{WEgl-VAZAiFPXM zq3Ms0r(E=I7jJskm!8~GW|Q{RQ)&Ev*Pv<%am>((O#{D7+k(d#?SFVEtD(K9_>za zKSj+@o=6WG|2Hu>&su}?db;J6BfV0+X9gwTKsWUmbkQ&_U9%g0qilV67#VMh7ymB{ zu-Ex=`V#BI&&%SYpOQT;(E`nlPgu>rNj}P&(y!?_ppqU5matqT9g`r0-v?s=Qn9^0 z-U5-cyX+C^Ex><6?5(o#R+-iZB+(KrSRXC7679?DqkFNxTda?EbA6Q9>*JkPCGSKf zJJ~yB<6W}3KB$XmeY~3tew41v(=vOWenXVpA$>zOzAn=e?l%yHdHPM%F#e$NM^w!{#%LT1T1ce>j|Mfcq?*(0jbfFK5l|s4s`&1 z532gERn>O|$lsMAYeC5D!9^6gB@o)5QqvVHFmJJ8e!+tI1sTE>1oKH;(i6;g2aLZV_JaVByO=3c&R zRdtf8DzmF1AdIglBiiCEu@8M&)bcl0Ex$wk;lq(kH#emnGS0pg_>lKmL-jrxqK^xU zeGI~m$huS0c)=G4<9+}cOm&psWfI9E;gsE6h0G|hyhuw!dn`|%&B>rVNtjI-QzyK=_*%WDG$K>eL zY{!9jvQM+$WWcC6OoydMAh`>)suW2>gi&kqFb%$nQN`ds!J^TqKj9BjKx#Dm5cx+e z`luP%oM;Y45j@1ItH-w@>91LIcukUwBtm)5$kA72B>|xZ>5CrHtC1mq3}2O_v8)VK zt%4*X)fVOG$eJV-Nj@a~SdNZ%s^dHH_^)zwtWy~;!{ZZ5G~Ox0R*`HxsYDZu`ao9__erKyzAeqtmD4t&ZL}<_;93)R&DV*UdBb-d{62B(al^OYcYWEI zS z$IW|7l0HKB++!wK)UeL5XU`o=Sm>pi$ICL+ms>i*guf#T{wypy09*|SGi}dPIqP;1s;QQ3ZP%xC2Rc~$1=;#@ z!$|QqF8jDFc;oV3F7M;AV2{fIE(f_>;B19)rvqF+q=69vgjrPiG?Yv#SZUGrxe8ud zR8&#B(R}Rp&X`Hl~zxO2yzx zdAgy3s6cZZ3%H%twa*G+iToMRkH`OIZr782)Q{R0f?alMz4C=OA6? zgcj4qJ5O-&?y%mW{w>~J)_YzePrSRWcd&rOdqGUcegVU)soJZYr)-+?D>qJ#R0O*<1ajzm~R3mPc|XTNSq zYTi`Ayw!Qx@-U!){lUt=OA$6V!(VIK>xIF~~invT6@*wyOdVYY}Z` z6}H5=b1a>#c|>Zlmc*VZpM9_HZch8htNI7P3<}+80x63WSS`w(zbaevOZn83(^&LW zxrKFHu(4&fn6}sKrWW{1=wDOxRv;q_<7~e0*hCsG6mkpS-v3W2#g!9?95+!~pzpv#bvU&$)2<>6E<^r7+cHQ zM}+kv+$Tc62pfWwCnTQ3Fze&gVb55XJK=_cNuDkcT+BtJms~_1xFp6;)G)!rtG)Mu_Fe-AHG)(z~B<*x!-ZA9FrHh^$r z3)%K>Z+40e!0jma%8ni7&I-_(z@QLB1HHIqp8SgcE7GNs;){2pQo!8^2J*SL1JU;N=jiV!u1StMd7;(eXJy|1^e7Wd( z2siZOQC?fsg8)p?us=NnxWxXnjrOPQv_F-}XN`fyTeY}9qCG0&#%;}6G=0Z|D+s4? z?$+1SFBs=3s%O68KR9)ShH948Dg z!JGA?Kfa4>n1cR@zBZAFWD+Ci6>(-5dWp;uC$3PUFq3TQXWanCUuJM z@CpBFo6tT{Pd(_{<3QEju%DCBd>L5bWocG#dSxq*boCuSH_&@_$BrGoK_sO~Jh{Us z`+e>NWR_=BaZ%3YiO>;eSC!0c26-qYZwa!l+BB~(!Y zQ)CuXqya!c^Cu6+fNT$*c|_a7oHI#-L+JUQyd8Z#E14ZWx*>qU;|E?Yc$h3y-kn`5 zw|c~5ef+Xh81HI$;xn*G4MkID{OV|2xF6*cXy-`HiORh288Ws`O@=-yC;b>28lbTv!UAU<|)Z|$U zMP62$xReRE7J+6{y^adUG7jqZZ z&iRM zsrRs-IzdySgiGpx)GRvy+Hx_WMl=R7tb=e{S$qgr4TpryuEJaM5su4-!{V)YG2xi^ zm(Js-mOV=ntbWH6B*E%0dt%=26gc;Z6)6V~)?fUS>U^GSV1KM7VZCJOvDCJ=(Wq+Ai?I!(EDOQ($iq5&5ZMUdSURzA*} zrAkn4-+ZK$)q6-f`P@qEezWZhf|x{YRj6&qY;(wnT5U$W0P9g?(MSgDIhU_HnEaR%#IwRuz%H<{e8)XD z9BtZq|3+6XCL{mea_Lu$5;ocS_q0evuzAdNkOrIJMz8SaO+GgtHghlS6*bXV^P4oB$9v1Q%`4bZ&)HfVLJeLE0Pdy&~BhTcizoh@rNS;EU&h zB~tF)N-Pmr*oHMtVPcb*<8C+?BL}A>p&(=vAeaCSiDla#9Fl71Ht>u0U?X+zk#W-p z*J6^x=~RoJR>%#Z+YOY53JNRdV2la;knOT>=Z+c?){1bK2zQIHZZD5-ryi;lQ_!v5b9E;9%6_f&}9T% z>9J%3AQHeJ7(lF%V+*%osJ;(Y=!JV`As8rx6XsM{r-$`!nMA=!g0&t7z##-qg4Pd4 zx!=6cCHMXqjgb!#?_=DAmL`fj;Yy zhvOW04TIvw%to#y(v4RZTiTrK^?)F}X^M2cHWxOsnV{=cvC$R3SZcWnc9;z>8{4tY zNhe-5AnQSwg(Guf__mkZYEDFU|0A0lk(twLXuS`&QCd^5t!*KRt#Gx-Swt8Kw5=t3z(HjIk7UUn6yw$l28L0#8@VVM7!Y{G z6h2T@B4udBSuU7&Ms*TC&@9TO92IVMnVo=M|LVncI49*|&(4){Yvod%w(I2^_rE9Q zHq~Yh<$)DYR+eTSMV^RF#h^X!CHd7DICyFBcp4^=&@c+l$+FRz6((lQ`Z+uLvTEaW zPla0BH#7TJKntuq-oF&*OKp1%)_B>sRf6?dJb9IDcU=NvwhisT-y7tFO~$5RYMHDx zA)vbv3Q3Z2aZ?7_6x0>syQZzW+!oc$VeeS!P`co{=Ta*tlDYqxxhW*G0-}vt;9n`U zF51`(z5#{R60W{?j*|8Abb}a!JTX$s`f z#`W#EuOj{ic}S||d>7|1du~aMI$`qM*~H~m?r7(7hb1Y>^GSX|2f0@~r*jwQWnQVA z*UiDe11vj0i3XCQ`(%H^{(uO>B5W36iwN5eQ0hVPe27(T&2zB|I2`RHjtJ8_r7gI> z(uKe%91fAdC<666}tKQ4H?x70@XmVBnpNp*0T&fpKit;>ie*bv7(wUuhdggWjOQk3!M@wLOzS5W z#fZbQ5ql2NCrF6YieZq;m5kFw5n3@axeqeVMoSVUR*d2yFG=S~Fv5yaR}8jl!eFcB za8$i@X-R8bTIyX|y0Ek~v9xr3X$eur!|1l>>-vInk4x7f?%uQmW zD+OB;6?khyR4DqYL-dt6Ja0nU|NcjALqsp28L~&tlJIYA*CERVx7fgwB@&LPijX*k zXKaXbi9@SeNDk?=Vh%|!Im~ zcE@PD4UIu3PI2vo*6?qY)!v(6_-&Sy?3zeLaDSFXP0DbWyHqGipJbuUP)n;SDDCNu zbe%gsI2`Fa-aCA{YjCi4;H0Ed3!a299GSpl`WigOPF4|1#kyQOo0(35>gy3RayoUb z=sM-Q(QthXgx?LK08~J$zg+TgSvH-#96ue)jJu{_%*lLId%6aDZKjZ!n*bz0+rM2> zsde3~LI*#5@Yq?Uzw4wD={=&H=o(PEP7mPsqSAlvB>s*oUFVJ}XM0a8r@9A~lc&xr zJ-ywRUpeF<3>I5d@T$%OD#@eI#wP+nOubZvp=Q!4qucIP+GW>?O_;4#w$rBchsR!> zxv?`#&B#5R)%r#^oU2Z|tH2Medb8+d^2C(os)&GjaYZEZPZXQPeV<@jsx$}G9t4^I zP>)}QE1Wc^=yQYJdBa2~$5WKSW#52Lr{pQ*FojfI5p*g91LCnsIO-L@1*3S_Mp z$n#Us^O}sGN{&%X$*I}Q%q-ajy@W}Z?ga}ov&t88;i+?jr_T)zpB^}M6v6;!3$2Fi z8~S@(BRi1$J?MJL5nhHW!@Ddd~$1YhzvyyX^|Q#~u4 zy5On3$oSjZ&U%n}vLeq`?#;>*zjp;`H&}!Cp__nvt$vH4WCLZp!uUYG82N zFWfb+Sx#yKt4E%i=2P)2v|8zx7L-ozeg~&FkzXWHWpWhugw%G~C{{ zS#~9E`a>wu(vJIyRv0#fg-y674hBu-%BCiqm0Oxy+til!wnkV_4#5awu%$776&QtU z=51~ne<%#VK{9MDlDGgohfWDa>vk{Dp% z+0&_76^Ci4VuE@U`T)4^fSE$rBV;={tKsr4GE6Afk)9rP;c}GA!(4t9mq)l9=W>F} z<6NHL@?%_noWoOvJjLZ{uDzPeDK4kEoZ;FmFP!G`Yq<-y31!$ORBz(%Nyc4IGv|9q z{NeqKJ3fNjei$H>Pde!I&ZPWWqIL#b-hKZ44J_O~LrFZsE(pn%X{9;-o)O*-pb^wZ)a~~?^u3%=kn9LmY?3e z{PfK7)2}Q)y>I#HSC^lDZTaZ~%TFI%e)`bz(}$Oz=Gn(ASU$cS_X)<19)tb2UgmOl zO8FAgiaJ5rGrhc1@?m%*$LuKKw3g&35^6OT&(|5 zJqp!UzuTXGL>|{6XDgq0)FGk@MU(>qx5}AEo&F+!vA@J$>M!$q{pJ1&{}z9xzskS$ zynEb9M+dr(Z~AF9Jnw=dvQ2s%dKggVboqDmyV!UhqQM_YF05vGjJu6Vl$D)%n*JjN zg`s}on=2S~`sNBpU3b|YDRr0CGX#5O1p5>Oeigxf4MBsBAi%F1*Q1_(k2L22AL6wO z@T^Sgr1njOE49%ymCi5ah3fQKr`6)Z%({a$Ij zoc#Dwc6ii<8(#>Rj5y$Gsb6N}MWoX~)e28cZ(s{}8G)!sWDCT2A&QVFk5(os6IJl6 zEc`-O!!I=KnI+1RFS;$U4ephFIBY$|qB{~h5}MC5%bGdOrnOqAJPgV&Pc69 z-2l(ODDOYzKMiAO^KdNZnk!7~n)jcXhu2^0b@#mg>^!{xnzh2;vi}^s|IS;ld*=NY zjCZhk|3k2$HcuZm@4q-t4y^H9pV*5x<6RYeT<%j_OsZ-ZQB8Iji31 znDJYXtLZ>}rDV_LODuW-E`!?>?TLf^?JRNtvMXfz>nkjJ2-zZsnDN_edE{WXtKRrs zs#t?0yM753e^XR^Rr)5TOn^~%O{VZQ${!)TuELJUA2Eo!L@H#m>hmA4)J9ZHm^m35 z)!%~cE^Eeo8xu>rC4^yU#{5UN2o~D|=)><&b2^lmbl3o_7Pi^YlJw`)5~J{SG8t(5 zCFKQ#b4>j{eqPVCzsAp1{cp)=+ae5`bV{(W^a391N!$i+faMN@b)hZ@??AT855Lpj z!}~lXz${y$L}b#89}tLHnf?;*$fUvM3uNi%#fxtIklm_XDxnMDAHhYTKXz*#d357% zS@cNa$jwo1{2d$NWcQ^MJO=+$4946j_8%~eZsJ0~!eEa~oQDyP2Z-p9W|WY}sWuLP zW!IPJ3r3G6j)_3X;~Yg_ycEV=B9@6I|KDTWiRS+Q&bSjFdH;+%QO*Bn7XlLknrit2?Ph{}Fb$t4Y>3>Vj zq-Nu|&`hS|rnjU>aypiA2**i%Q0$mUpNyR}9c;gtqwW>V7@s-1Mvholin&^DQ68uN7f+cXC>AKwQe%mZ*ucDx-J&X!mOj-3U_M7 ztGPJHW;rT9)8#Wk@?Dw8j0@)gQ^an8uNNU$I1-Of`NqiM0qpiqLL>lLpdbRzFJJ({ zhChlWlO%fxF2dvlv_!^!(eBO_baq}=znDJHv)m=*y2r7d<79iqNkPHG2j;E);q<*@ z;)9{^fUBFSQDTrmqEdVwge#$!;)CL6dV+i;n39%R&5y1093Y(~7FC*@KwuDktGqq{3KbQn-Rap8bE<&Jo4@=*D^qwYx3snP zZNU2=w9q%@|P2t9N z`(99OX=`a)wxk7*<(dn(^P8vy6pwn4483%_XZJ)^O4GDYoq1{8O-u z4Ew^G2=%zM2TskUT4~aO2>7FRnm?|%MCOMj@=q=a|KytVDUjwmqmav=f{jY5=m}VT z{0tu8q1O?SAx!!Aprt>Hh^Ru5P*G4=M1#9>4W_0MqxqGRFCfe3@ItQ<9cenOOyfoO zd!D6}E(wE**CKcxC4v0kFf&s++Ve~z>hp+-0&vv8*(_X*et{zJ4Tx~CG(3xckwx(> zNe=n_Vv_QI&a6bE8usD+xA2~%8XP5*{3SD)Q!<%t`ilAPu-|_#t9Unj$$k~@WC9fx zu&68HBIoLGqRh!sx^SDWnQqfL%%YAuMjU%bom-E~Ib z1P9XZhz!Rh7cPaFR3^y~hmjO7Tm+~RWLdOIsY=o-;V*z!!(W19yX1p&1ZfA1k%PbF zmYq*HFtcGC!WdAg>OQON1XgD1HvRxAH(*dF}jyZ(!9tB+XJi18p$hka^xH*r&*{B(HjkxxX=53AE z`&%RR);!`PjuBUEPqib@;R-t)Vjf{ikzsmrP5muMwZPOi=2Z=KhA|-^)DwHr3&vw; zRe46pRzDsmBkA5r#h9c>{5cbQo+;je!JD!pF#Sw7=`zJ&`o;Em$O`$47+kUxj;3G| zTo%KdGFyu5TlRJDGSWy#uP1d<+3_Es67X+-aGp9f;m6})a&^BSlOsbpBMlN|js&E~ z_S75WSMWa1sSwx2qA_ai-zpbdVyh}U4)@K2Mg$& zKAHMz&Kn6SFuz-GT&8BeY&CNo=6P$QjVOs(1>DjoM`{_(8RNAc=#?!5M^s@+T{A7I z-=LmIv=rB&S6(N2rQa%w*+3Q4N?@(E%EDT&D!OTo`HofNgWe#_mf z*S*(-_JmnZbdZ=w)ea7ZA&p7Y8k6^l`j3GV=Q}3$Rrl_wq0C-{(!u?sh^Z z9fV8{0GYVm2PJhUd^>A3@a+sA&<{X2PlCi8gKuYNrF4X_Nw<_&EA>e3O6U|oUSu28 zsc6fL_I66+Av}J9MI+Ip{_~NeY`pj=*~$&dIZva4xDSxRkIlI$NxWibP|?@{Y78e& z>l`K|Vl5$ojVyfSJelm+fHD=0bx>njXBWwmGFh!6rBbe(MCSeX$wow`0CULd6G2*r zph>HExz{Cu-oyV+GDrpaPL>bPFER{zIBZ245R@7`Uci@*Tv!A}M2HY+UGUd~>j@3y zW*sI(jd=l4a~C%p2viw?rqmd;YGQU$2>P3n(L#3J6iyc&2TjrQjTW_#PG8@V zdPlCMzVUQ==|bxFKeci%cXKp`?i~$GEt4x6Y`!01^-v4c0h^mcru2DhC{%oJQ=5}Q zZ-VlFs6E)~5Nh`>uh(1U-AnT4db!Z7*U3zhVhUNpQW8w)l?wyKroE)C&<5n%Hm<>u zAC9m)xm@F{2aUR)>tv%C$V&y9Q8?W<>r7YNkT!=tvZqKYR&W_v2-kuoP!d!ny;j~; zw|kEW_leLi!iEsVhsARvgZn`^5oC(iN%b zl>@}zDbEWObtWrn!1E1?8nmKF2mYMr^AvT#igF?9Afx-YM^;42kCRCGi6~4e zTcY5S=NyQq(@}TAZN-=(zX>e)j z($dmvOp)>^`YlVO+>i65H{lIZKqBR3%Od4>uu0|Er-*|S$db*rH>QH*E4Ft~lLxYz z#ERambhx`aHWM3}NJ26&p0;EaO(|Q~M7oFcd?2DN*{0ib(ku7>eH$S#!4zBleJ0h? z1cRJH)P^n=by)r7vPu@sA%LQQhLs>C;gntOJP2SwP$EK>%n(YXp}evC)i4Sz4P`>q{D1GD2*Ay0`*$t<-LF za81!nTXGV4g|L}$?V&}w2D^?64LL!S*h(Oq)~OKGkELe^(b*|eR}Z&ZxGmp^KF;c` z?19Bd3cwsTgazaynbZNz-pPp)?tgx7gc7o#6;^}@rg&eh9`x_R)-BfWCt6A zu9c}%uzHMhOZJ69&5$dHMh_?wx;V~v6lz7@;__lgiID3ntt|6aIChd5Al;JdV+HV( z06kzBAO}ivawU(eZmt!OL7#G7u!SpCa5&5hU<;_4E8ED#FYNym?%;(rU}y8fUA%BN zFRbGQ_0GM(W+BcSc|j9AGJt@AAen>{8Ce*B!5lAa{WvT3z_u@)!G!vosUjB?&*5fL zi!)W^T1~aMEs);o6sS;(5jL!hfn1?8yQK$5M?45>SS}4gn`n9swEB+_Eul(BK$b*V zKZEB4qnoId9#AQY(Tn|4){r%btj8&2i=HxD}fYH9n#`<)>4r*Ari}wNLCca#T#tph_|G8E5`>+ zTjYTdUx95)kO#s!L6+JX>7Ci;ePytzau5kw(>H*r+Z^dzG2mzu3=eO{UgO%rmWr)F zBTm9tSOColNy|*51v1N`oQg!&o^gLixX&iQZuDPg z*VzpPSK}{jPsu(}i{0d9jo8exnLenZpew`{=~?NexX=$--f@Vc$hpnj|NPy|HnYwY zG0)A7Es*AG2*0g3chi1l-W^{`)l6CA@l2>h8@`kh{MR@g(9czNn^f-1m9Fn|;cO7y+ZN%CU z>x29IzI?Ax08z#2Mz`!`H?rQ#mzgi$mEZmQb{=tF&JkKPR+eTLv-S2#;*I8aG`MXRMslz_iDIxfw7t3K+$9PG9KY}! z0zK4?d)_+;5C8rlp!y9Y2P#>4V4AhDeKq?$bxfxB*VV@kcp5!jv4PlW^L*`j#_fa2 zm|XGINDpflbU=-<3<1gwC=X~F7>0=S4L(XHg3wOTa+Oj2bfEeH(k}|44!8EeA%V0? zx{P>lA-q2w(0(xy>J*`F5$X}4UJ+tkzwn^YVa~>R>V+pI%(EhLPUsiBmr-*?=ncY{ z62>*yK|$=17hFBulw7aiy(2FuYJJm1+Pj2frV<*~Mvjdh=kf%XC%GKsa-7QvE+@G> z!{v)yUcF9zZ}9d@T)xcZE66Jj5aWJOqFPad+?yWl*e$`n?H;NTAF5vc_$@Ej_^1Y1uzkjf zTYjzyP>mn3fab|tL9Pi=4Q2I%N{Ima0+gi*oC{FsPCvcbq&T&8+Iv4Dbqz5}`(=hFSh0vt z;=M1?RoJh=R~Tck#TI_P?HOEHu8P9wk+z9UXS1>fC(rP8M_yFmOW$T?;ixaN3Ddrf zLs{Ve%?TQ66}a1AvAlL}GlTn6Dc~2}ApsSSyqgh^5(&5vqeDjg3H5+=wtC~~(+HW- z7HDF0V!jXn5%S)ba1nUd6G!bu^t+2%%8rSdFK-|Vw}JCoky$$7YQ}`;a_S%%M%}`Java8P$AF)--7CUirTis_*d)bCElqvRGtlE+_IwK*x&5A5pV=xlJ zJFFU$HRcu)g2hM($Y``5nkwty)2H|!M&1clUcJWpE7(Tymf}0o4Vms0kQS6=BS6X2 zyJ4Peg|!8y8N;kZ-8b2@y+Zk>iih_t!cFA#6%KA<0Om&w#(vskP>}?f(QVDOcBeaX z9bB-Vb~}!I6%Gn%K7;~ELy`A0fVc)!cAf81<|z}#530TiCb$G2%!>D}$*Wf{UzJJ} z_%`_Od}>+|KHVsv4m4B>$n`{*cgfJmZI$Qp9($i`G2VkGFzrrOD!lrJ`*bM0NKa{y zx{D&MOx$0no-IuPgNmd&Ek)XASJM?{P1m8c+GIdrlg{=3x%5+E73S2naReq_J9lx? z6-Xj)d}3 zH@B`9*X}&)fnJl8n;sFvqXuM7UcHHgjr8DQnBgk53C!|aPD`Xds;iM zDe$XF8(1&Dn}-ei<_fD2 zGU-ZH*g=?avb%r4NcQyh?a+i10{H-WhHO3d(xOh^U@yk+9%76xV9%J58jq)f#ha)n zMk!t?85krQ6&R@uM94M~8B|P;zZCO}oM2VL2g51Sx&luXno1Z;;|$Q15PGM#Q^VrG zT1GaL8j9fuvu@O^8(JtFJ|I(z5hju58(uJ&t5`r(_RJ$YR*Dlm(MWdHT=lD-~a_`H**l=)c7oTLCM!IRC0WZlm~ZK0F1J{J*%J^qKP#KXiGl7y76`IHJ=ifft5YcRbOu62i~Ln+R12b7t$x|mDOuI^ZVtqagKF0Qr_%3|*R z%6)12UhSlt0s7e~J3+h3foN;1(vVh6D@;_3tz)>ctR0ogRaTl7nB=5+PipGf$XCqA zsPrS{6emo4{dCfag{9k=a_e(0B_o@ymL#5gfBTCO&w@*u-t85W8r9z6K~r8{@4#SB z#WM{{lSt$Xhc33aub<3yrWu66E>sipgqcW2VihrfjC&5oIzTKPTIo{}{{q(m)dF?& zGgw<7Cj7O+s1w2cB6L864vA1)gc?MsQ5c7X(Ikv!VYCRN4H-MV?V2!ea zJJ2huOtW>JQnI1OC@{+P^pQC($u<3IO%?QP)gWJE#J?-CaiUG2a=KbWdNR=RZB!0q zD~8qa7|7OO-UfFJ#EpoW4iw3Mqv0^LSq*Snjw2Qu$}(jV`Y{!SPeb9?Qy^-_RV3de z)0)0+V3|Kdt2{hz@(w}g?6Vsf!9Kr1`i}R>=h}WDV_`r}0dWCTjjArfke9MJ=KC_M zFGONr%A@g^Z0G|~0024ZL!RXHRp3)XXNk;c$j6x?ub>-|KcM}WX`g7rE0N!%{hPE; zSe4U}@6i4&+9#ySnaG!DA3CW`!l{e^62zX$m~bAtM(4=8{U{M4Sub&&i&CjZ`cd~P z*SR>ATBHGWX|8jTD)lYhi@~}VL!wX`7oR?bi}l^7Pv4RLLUXw11u*FS(0deS`mD)Q zu_{qg1tP@(>7CbgYJL{+c(ZSwfmHkEtdzO*zi4~}LU^K>Igkf%JrQ>d5O87j&R(9n zrpVaI`Q_EMy<1F*NoG77*N=6_Y(PT7uz0wDWwe4<6zpG{2e#U6gXU*YYOla5Y(wYB z#({9%$V@>?>M00437AQxXh5{ElN|n35%O8;F7A9Rjub+=A*N59R!)*q*Vbs>CK^?Y z_tmzIhqv%eE2FDwNjSMD+d{uYv9d9hqj(8j6OtX(S z`Y%l0n7lA^Y5d~kRP5PX-_+An7S4J30w<=#kL-A3Ph%XhlS`{3JoV#^^J{DOM!LFQ zpM3-A-DW%CV+MpbLK-K(*yQV7jVEfItGo-KJODVJIM#6U6d)PLZ@S*{)5oS8re)pq zP3_KtSdoZW)^r;GoroNhLyfot3^@bBxJdD+aA!N=)BuBPHcl+W@y3F8s4>n#;Kv(l z8sl9j+|dephuk`T5GRU>uG^mFj=P=*W-CX-wD)ha7||{KCZ~5y5#OX#Hw__^2k@VJ zQ4A%e6Yo%OUwB9J$j-O*d88O#!99chgF%pDJr=C3F%QHJ)*SR4Vq#oV7l#QBJ|og; zMzxUnT4M_vLJvYhLoVzJhE1)5rHlmECd!tjbqWoxyo16T7S@P&7^<*1C@%s!5rSY^ zR`|;%=llq$@GIe=ji4E_%%kh@?1fQ7SFCQW2QoUB{ag-k2?%M_ALH%gYNW!CR3GFj z11%e6K*f)S+^2wu#(qjQ89Q26Bd0-q4bvcL)pW3idev)qo=-2n)xOf7DNV9O^ z@2WV+8 zcYhId95oKX2o%pX z4YL0>P}opoK5VGTZeVA#b@hjK<3>({?&a{4renVbbxI3z?*{u|9Q4|)&|siHsW95@ zHlP-L4;8B)$o=+BXa7gMAHkFoKKn>vmrlY_-ULn>b(78PF5osD``&lA21*2N1T;R{ zZ1M#Vgq;X&K6;lp#LagfJ#nj^$f_srK7x(|H%Qg$9Y?eyE z>6=Wpm=PWVU=47~@fsqM#OH->6U#iS9ux>4tJcu<#$u(bs$iDGGp3Yv@%mqexs=Rr<=GNZ1}gPSLworb>h$&3je zZ*_Tz-Ip8=umB>zSn}9{OD*Sa?8KfgmLm2e4!f|FTmFQGBIoW;=wdlc?h_h|m0veL znFq}eVRC5}h&9{1)vkXs!_&hk82u9(m7Yt%sGrcS;79by56e%hVAzf_?yR%&l%WY_ z3ArRi*!vn;j3VH}oZea^rEW3VpWFhHN02~IzY7=N*W1(UXl(n2hWlj*DbfUYO9gI! zvUku)dNcquVyWRZ278iyfx>sx7=(4sU+`KPg8&;YL~R1x@X&Ce@L?K*gMB?n1ds3x z=Q$XIL;V9K?9Z-26k!Q{y+%w$;=}>)V@R3^`o>O*l#vHYP1yEe*lT3`9)R~C1y2WK z_CSW}W)NY-6byO?Rd|mPBckIEPKS_F5YME0DM(UyXfv?2$%%SoZD^}DEH@PcBN=e@ zH-LX&P#`5FE@0l$@S%NLUH$$8Tpr@Gfy={OHgnm^WjmKgxID^b7neO;_Ho(IcaV&5d zgV|yT0z^tKu*yf-d;EVX)7PHlCeg+I`5FGqP571#flF$$;bhUwcEPuT{~&MkG^Et zyQRok3VKsxA1(f6_n^jpUJJ^eBDePQYEgC{ZvA}c4te$F&#qEThK` zDp|(#3Kvb{{mST4xT;c@Qq)a;6kRSCT#83@n&w}58ana(7i5cVg)-g=q3eErH~2Kk zN4-kPK18dHJ{m89!?U%*Pyb$Mx6z@(fv z4F4LWB<_RZkK!--1`q()kMLkbVjy-8Xn6+QaQa>H-jnQ+T8%+iwwDmD812YW1e<&F*gIr`UO+zf=sCcUQW@xl;V>8Xw-WHvt}RT;*`b>q0k${<6?t6=GWGY40?sG_*aMVp+^R7$&8`iV@2Yj?iY^)@J?I=E1Ga zxZG68WEVM%zb5=Olva@FHt~q55DsHdTKO zu{nX-39vcAeG_1FLUqLEg!dDh6R9UQr{VyyInjf}=2RYGApoBqy5~g|KBmL+O#i#i_$zWx*x+xFOsG@th@+R3iU;#sm_T9QOXH8g)J&DmeCZ4 zY1fnTC>u}ULX`3f7>zN8?v8O&j&YO5xJlun0Xaq*Tk!glV?^v*JH~hoV;JrjSvdwG z8X{`e&OOcr8ZnSm`1O)BR9rQ?Dw9VptfmSCHH!@@+OAcPxM zX^IboRtYI;wHB+bLCRZI^^eC%txe!I+Eoe97Kb&3LiwBQW{QY&JvO(JNw(}vCK*V< z^;7*JCzEWG$s~VhcWmUm_?DhQayx01{B8<-z_y{1k*miJ@6E<9d}?1&!7N4#k*W%2 zf#=|Hl*_BGXe!-29JhVtVDX-Cw-_w;?iQT;`Qo)F9=}PFQkwbHVndQ3=BT>7=)i(f zTJp0r012jP>4V4;ZaFT8MXrrq4!0uGORPE$r@9L~o4;UeXHBXps7e+*gpwcT-d~Sm zA7;TL+J>1{Fte@CbSUXAun<3n2D~xL6fqAR^Sz!x4Ed`OqeX7{Fo~M*VzB(e3JEI$ zSYtqk2~j0HF`uD=lPuuMFjhxz~SAnO)m)fM}5D;n~*=WKC|K10Y4%8;7fyNDv zZXocJ8g$B*AMlsnEo(opdp8rlD#F$1yS=DB0cY198Nyz2GZEzKey%o94ffm~*i3}E zdXTGu|G{7y!@Os~k#7`48(<@<7Gp!(DfrHL(r`vd)WUsm-iM<oK>z?+zG9#hb@|eDhOViFi7$%iQ+q;1zSQ9`gR* zWnnIEA7#h=ag=QzHBD?m!q?-a6cD@L*iR1g5qcAN#8q#D9`~5V+38g^cSO?yPM-2;--aIW+s$+SZaNwG2F$V%;4juT(MoaR$8G#H<@GM6 z(c$Z8ThS_udK{IEV<_H{X;0W<`#Yk)6YC#X};;{ zAFMiu7>K*bbqpsV+`wRk$1NIf^z&6}76xu${bJQgRy~EHVAxewg)%+%W zR%&WJ``B5nF1FtjkF`*Um5i|RsX&?}e>G%BfFcDLPf(-;ghm)o{(9lZK?a2gG>AZ> z2pkrHCJ}5F!B!D$6Tx;7l$MyIaA-r`1fkUlZAh3SFu%aRi~*hqp%NS(5GsXX#A=jU zGx}9L^AiT2Joh$c9FTUAW55EuS_Gw;&EbE_kAoPunrOhtLxMRZ04Q;~Z$k zZfP!RC3~e=3f#~hr_CLxm8LpxF?y8l@fqd^gj#uCio)kf6h={@p@`}zDh(7>CW>kc zMNC{mh*!=kgs#*Hy3)QRW1QB#dAtR{P60-J1CHi|x8PyC1@F2SfCaCTQ~8SkiZ|Ko z{A%QXq>5`?L2l>QBY#X4FL4F@&Vgq6_o(7kt_Y*zCP4&v?KD?JP;qMu-171!a09N8 z3tS#I;5umJ6ycxeaRW}F<_7u7IW-d8@)hzZcWNZK<#f6_SMAj70&eNu|MjO&&7r4H z&pmzm>eHuhJbn5(>8#EQ4_D+h?IR$#$XnP=1&O@%d6&F@PsYCzA}GNs$cVZ_oaTLp{}XdG!Bn8v}= zQzOu2&^UF!(CXR4Kt>M(wy2T83gMi+kEs~+{cs%6$Q#@16MoAI1eK6=z(rYB{np`i zxKiqhhtZTJ)^a;<7a}enmAK2@guC3s+r4T<6!a_t6pS$fNYczPBAPnv{7_RG95Rfp zlxh(@(^49FY=f*-5LvT?Bx`WZB$262k~LCjAGw9(Bg29ioU=~32o=(vxgtLF0<<}|u8WMW8}Aoyhz5e-F!jv{KHs5DVj zSrdrZ%9-{gg-{`knEbn;Kxt3Zy+=`6-a_JLh?EbwH0)Wi3ld!rb*gS~ry?ly(%MyzUr$XKj z`Mc;W*#HcFH{uQ!k1^in$r;OCs=wjJ3l+6YLotCOKpf zHte4Mq|w(8{8^Ztk;Q%xIp$%W9xR8s2^5~#m1$>Y?kvx&Ezc09%M52j1oZdv(#(8z z@gBV41~7x<5c0{W&^y<}Itg~)0PH?ieI%&od~h1X9ZpjZ1vE&37IxxvfFuE62@Ni2 z)k23Dv`?sY!XV6jZ$; z!Uwd2@k0$BuSEpM2e&f=H|i8DvE)Ez zsa2BeH|S(ohH;g~veQHBtC16n=ps;5i2iDnzRb zNKMYL78?+@F2Sa%+m|12!oGUtamE1gc@gQRAx&LOFR*#M6RrerRZXIVHG4{yU2L5tPt6qy?08E z15#X!zrDD8=K>)*yc0jVyXZ*uuI%XBJIq0`_k*w9Rq5`WJzg0{sXJNZrQz0jX>!)ngG&ow1iv3(y5+v;RcQ( z|1Ifp#VFDbIjUSI*^r3odJWQd>h0@bgMvskgqY~Ab&%EV5y^ube>}|P$T5mYKF-@G z)cON{FHoLzXm82154|mH`Wi7GG9nrI0JgNZJCY6cY}ejWvZ1{8Hm|nTWHhk{P$K`B z6tU3T#sTL6m|Gu(6#FTpI6xuAK^;ZNKoK@kL@X2)A_XWBi@XWIy|rho91=SI-h=2d z@C|1(CV3aUA0=z#xg9E7&NgHNU<7fIbT<0}D^ihhi0y`#GA2M@ss73)SqbX^o`oDl z(B@KL@_GQSOBqipq_szW3n<}>rH1I&`-I_$TbHKg{=7@Q!7Tmzsh!L`s*X-HH>3utd^L?cFW>(7Oe@wd* z!a3&^K(Spj+YOySDQYX7k!G(UaY;U}V%x%}CtmaUM&@_PQndoAX+a-UP#9g!Jb5ymS6wWRuT?5 z&(8r%i9F@G(~A398Dw5}UZuvumQ8(wBQhj}GDcimzRXwRq%mXImQ~;6%F6P}BpnZP zQ@2%9H?zvVRZ50H>7KGd=$oL8n|p)T(D05EJ^wQ{`BZB5z>c1Bitc#}frq^l57)A6 zahJ@y$&fVq-PwC!#0i+rflV#I1T5AS8GanUc{ zpIuzYEo5(>LfqFXjjsdd#lN;pU@vO~Bpcjz>!Q5J_td?rs+pPbslJ(+&Ij4Kk&zpC zpO#lT1+eBQgscGQ)|>txlV(K$cq5rl*b9mlu|ri-=bAwvFT`!MfGp#O!Kz zAUP=~<3Xw}YE&8_Z||Q&v9*-WJ)NJR%R3`MFX;jMlX(~pIGM%#W`!q;{Fn_j4Y5-O zxIn|iAM)Sn3C4cq%oQ5kM&U3T5^98uHjMqML)WAU6NK;gxV<7!$NHm zW{Xf;y={Q;Xcu~iFcQM*7J9Ex`-Ivh)TB@c2z^H#6~=L4oD}M~w_W!FQLk4Qo(Z8M z6{T+Y)}i!~v|QCTIS+izqQq@JhF$UcoNGZ8{cciOV9>+AYm&Ax{{EfJd7lNygsCQf~qS306xIdhE(n6EKH$^NRB>d0M!Ic z@SKG|5nm6^d5~gs&Wl){XJ$VW*a z-Unk{f#n=~U|x}*0eK|g*w--^NW|evKw+%`I%VWrngTyR;$927z>}b0585>hKp1|B z4epahpH3vZiqf9lMSy@n4gXMWHo9%(z}!g10P|bVVgz>G@8nuUR@X74qCmR`KHfoP-B)W1@B!RF;iLQ!BoG9usP)m`n-^hqu z6d*20kx{Rs@n}BlgjM}7kac1fXFjn(u(R)zjPjw{BTm{UH|QzZW5k804l3>TIBjJ- zO-ja-JCus+?TLPSf{!*RkNE2LspvcCfj`QS_XYbjX78@&y` zPj~m6Gx_uB#*I{?N-u={0$z|n#{2GEkey93!A#&VUXl;h5#I&-!bZlIi{gE;FJ}A{ zrtm()>Rw96GJZT5I&cLC=z&K8$_&}45(g^P#IaP9nmCbarX~O?K-IsIt2lBM%}5L* z831-biN6gk+p){90^TeZ+5F^j^8N$h_BNsQ6tangNY1&IuFv@0J4s`aD)=SFf_ydO ze-H)AC80KEd>KFP^9#MfK*b2s(FZ*Nd&arg48#5sV4{X^gFe!9>zF#f4_o)sZ2-rq z!`6JdJ>7A>LzzFo%*($PC7Bmi6p+#dfO1@JQHE>FQ{<`Q8Ya)Oa_jS-8VkcSndUi+ z?miQ&;|>v4Jg{)BX5mL%U$kjcZy7EYf!xaSUBI)goB|C!QfjP``%4Q%Xs=2Hy+p~j zOoc)6oyLp}d^LM#@~wN-)6@6cZq6RfjUP>QA01A=olGR(X;<&K$b5n6>9(8G(`!pB znRI(|$&=h)%C64M-pfk1$6@V!MBycLAEh}>G5T?c|kXZ)6{yXuFBM z>FJT<>Cd%IPfxG5)6Wu@D_pK|dHsOtOp#nI+_ZAkj!$tUg8K3z$ zO6uP#&MstEW>>&q0ke$nc6i5&5!(JzscS%)T~b#-bu+MGtk9^Yq&elW&yr`U`WJZsze@+x|+IJ5?~NWSIG|jFmdYAr=NsZ1ki?s z%6da&_C#tg^#FJ(Wb;BAFfAeUqeAZ%YOk>Rg*HI=h1yABi~#~4c1Z6R5xV+C_^4E@ z`+?lox6UcZwQ+6Y^r=ZM&%iCytQl`bVwbR`)+(`-<^iizW$Cp_wPn--1%sSC4iLBV z8XziW2}dxkkvqTM6iI0s(9MWRq9wSB8l}{i=2*Q8Vzt1YCh#Mj$L8!-$XDG zn$T`98pzfOC||$LDv(cWh?NTmUDvk7O*KV0=PN(0r*bkpb_O3NlD>XGSC%zyEC_exS$g z*gK59XeH6_CIR`Kdo%7Kbde#IF2L@|cpgM;Vh`FpkmTFFeTY!>0k-jof5h%hBh`4K zEf+|IaR$ipyx;EZ`zXr7YY${hi14O8nDK#&fO>#9l88nCjA5QTIzIxjxBEdrox?#j z(aBl4z@vd5!VN&D-+55+dr$!kXS|6nzBM5HBcSvZ3T`a8o1a!mi@NSXKE39^ak#|2aqp=%{$5gQdQE>UP!j;V z8;jK;1u~>?1b2~A(*uE91CH%8E;2&qUfCx+$O2ujX|M(%xe7FI0Gwt26pHv#!{gP^ zXKIQEip+13BD0ZGlR#83pwEBjBg8 zX8Md4yCtnccFO4~Ri!cP)+crw7EtIHk)y-RS@hlK1peR;pYz%sD19kE0`Hq$P2^_v z?_=TU#E_UHz!X{OD8Jz)2nMB$6ou~N<5S~TwmSn15JEKWZXbA?yGXe}Je*9!K>b{M z_*TE2ZU9@sTC1NjtG~v6@1t8&!K@vderbkGb{yI2*tNCk2nZk5WL#jZ|ETBQa)0J; z^W!fU7nbih2B29e;WFSHAF>jyQ^F~cx!tkjv+9r|SsjFx=Q__gDXrN?&o+DMR9;qa z-`33~3Go@%%91O}-ru2OD~e=A<*P8gHO2h8GCVY}tHXCWqz7(;!|(@B4!G9y-eKR? zDI7`bIhOOjB%Be4`g=U=8}5UG@jxs=o`BZAEgLx{#C3eZg;v8@b2y3^ru}5EF%Js! zkTB!IY!KREMxSf;9>HqZC9EFc&TY4ht8f!XkdvkB&@5Hyjjn?@xJ$)q(OTQvI=DQ_ zWha;2eN><1Z6wmD1p;xks+(g_8DdeQxF!vtC_63VoK|CpWjyh$WqeSYZ3mHN173GS z@Kn>-$^s7AFv+faLN+*IpM0D)cGz_d6`puwSRQqvs=e^cVyB&t{+FWfn6=WE%!CjR!=g-PO5zhgLdo7z!3RSar5aN<7qjNfZ(2C@D z^x2ga)lK#%%@)C}MomL~d*kywYzToNNjQVL&R7=NMCP*6d#c#-!Hk;Ik?BCmWpVGf zuGq53jMmm&aeNgA1^|I!dSYIfjD1cZJ(eEG-IS~cwqk_Un;~IB^aiL4VnWv8D1qE{ zQLVBXq3z&MjPbL~Mv>6|6=I_fQG%Qw5esFIA@~rnP;omBh6zH}>IWPU2qsEvup2gz zxaQp-*^QedO}`%U2*qIc`08CQaF>1~^0XAq#jmkL?22I;KX16MT~1LTnv@h_$_nP&W&Dy~ z-rk>Su`iErh2yR|z00&*&ZM_zsH_w+mAj0AxPoMl8B=5UjAKA-LR)uj|)gN>tH>Et4%e9Nw!&Svft+&~}u1QdK_*82Kynd|HSQT*@!?C16MfBT;h zQ>L6P%X_oL$i)SkDmTvc!uSMo{G={ly*Pf&x8?7g?|JI-h09l`%5cJMEHMu}Z=fmk z6eAdgU(WND5YA^PxptXzdkekbi|XIbLRI==9nsBMw;-udXxHLwCcAjEoHeY(;9Vt- zIr1J!C1OUdUBzJG3-9+_?hcL@mG@i1yA{o?KHUAcO!O8FP-hshpBr}lgYmhU*uJCd&~NiRNuZ>ubV zvEvpLE|pt^gMyTMhY;14?Csr^JoExd-RK>n)Q!VHXYK0-6o*W9JVYUI!+>#3_6+o~ zPk0h(A@`R!ZE5+oFWH0G=iy|ZOiJZ7eKCKH-xFZJ@L=pj;PAus6g-1O7ujB-94-s0k!EX%zt1dpKRygfLIT0--}z zg%^0J=F0|!NSk^ffCN=BH5%T(UZqAu@%2iSjZ{XO)~iJ{6hh+Qx=`18b4zPmd&dzP zBGjw(^(P0o9OQC{%MmU|xje?@aV}4Ad6LU9F2}h%HA&;7c>9ccAdK+p{i?Tu!>jeG zF{KjpwVHp-*cf#0s^upkTlmQR+!H*7fG}@>V&8oAghJ{^Z3-UZpwbAx4k68ts;6`c z)rJq!0Sdzo1U)348?z)EYs%5BFzoJITEd#whz0MvrNfs|C#MB+A8Gz?W@p)vJZsfOq@;cVcU1drNBQ6*<%9isa{949zky0&f8lYix-cZ&CyS)n zYoJq(7Mv0*Sg>FhUZdnSX%qxYDdws1Q)eeD3R)5W)H}F0nI5Rqt8*(0nd~*3uh3~! zIA~OU&Pu#;pIj}y`KW2v3DvrA`OKNgtA)VS#r!1-paYMbTE4${JHE8M7I#k{f4lpg z_}X%Oc4>C;;c7OHE5A=c|Ep4o%g&dQ7&*?W>LPJ8#jHoHY$&)@UihCveg%mv5kb&i zgl^JchYJR4YcKGWg%5xJ#VJWA_x>^!Ba<}0>4Isvust}Yk0per%y+PF*Le2`r2xBFMoe5MZDgk96yS0 z(=YL&02_tuZRlBHYT|bTP@=%v|5C?~u*_)M;jG&5?p-|#kG;D_R)x!DR(*08>$gr; zenUMB%n5aoVMfWEP1^t-q|l&$ZYp+57H$eT4Ylx8SarZJnx3w%-Zku2)WSJFunP5K zhuaG9MJo1iCb(!C@Zq$Nghq{j9gUB1 zW4)>!jgRl%m^*3cq*JG5bUMMEjH(`VI$63CcLo)kFsL(9S=(dW*{q^ysPXbT^B^!T zPM9>vU+aoJ!Gl;;lNjWb9E5)5t>9l=CI4dL{EIp8FNU#C^OlJLEgJBbFyJJrkbb-B zA_h!31LC*L0nfMt@~?$~1r2-(1E1xAJyoRYKIaUK-!cb2zjI*T7dU~3PViUoj(veo z;H{zvvSmDAv*(4NC_{j9qBYwB;SUY`NPj4*gOts)SJLUe&lfecYl;i=jI zb|P3zSEpl%L-1(^2YxqQlde5qE5Vq4P<03jwi2u&Ye-zx_)cXV6onmSX_X5)<~3?k z*QWG;ER2D*PmNLK3w9v}3g!(S!%=6Ei3)qAy(tQMb`@;z-4|ixqn>?BU$i%81gUE2 z`>3b?$o>I)f3(He#==KCUGPxy?BhVs@W*!$=ouV<8m1>XAj9kXy8HbF=P1uZpt~0! zMaW@eIUWLtsfSJ;ZcdsLL7(>yAl-=L9pF6#fgW-5%|dIz$BdA5#fgkp^Y(uaFoN{h zHv4~_w*Nr=K`tA(Y~-@3mFnAgyIl>20A%OiVgE-+3=|CDWm8ZyKoJ2}G)S6<9rpjG zXYK!8BtKgUTtZ1N(9QP$9tgF0`~QG!FaQUE-R=KFXsvr8k59r-eVkf{l;dRnM-kCb zROl$828v1(MU{0NK_>zZ0ENP}hE&0@CXQpU0$X?ke}V2uapo3y=%*uH$^Cx z60Soy9ksdbTw^iukp$oEGjoM9XZfRn3T{?ODRS>Ou-I~dX-;pSh`SIV&DD?*GbyZE zfiVpTT{4WCW2k{`1#_C|llY5>P9x{SkQT0pP+}j|8b5{)5Sbleife#8H$v~IMkt3m zP!2~O?{TE^0o*(Jd85mAU%&sBZFQxyw=XUOI|lG#DTJy_Bnuy%= z7Lg;@esUR{Cea(QIJ=!5cTYzB^0MaymxS)G0zn>OCJckUYOQ7|`hh*-5S8u(vX)`& zlDQDQ@&^-9+nm@FMI9+jtLQs(;qt`zg_##7UqKvDk!d#d_h};|h`3ylkvrZCmruz) zIxa%(Ean487muiq-9{}mI!!DuFJ@<#ys7(3i2q(*>JoPrmou}AU8O!QM0M;oZpm+B z?~l3Ix4{~g-Dl)}Bt88@eK04$x&Ts_G?4}^g#7h~0BjAH5rl1U_A_Yip-X{ApTO&& z%V$3}t==1h3LJ5zaZUGneg1u4MlZh~9=?=gMK8b~XyI)_A<#qPqxi5qDaCt$J~R9t z#FwKfrlo3sp-IgNyYM2QlD;^oNOX#DHMGNU+ISVYiQrIkqF4a z5WU$%UMr}Eg10XNL;|Bp`)(!0l=f%Ljg3SY?kiM@SOr}}c!FsUL0b&9I|hK(15vz) zJ>ZI*f!QP~6IF0>LA%H}4vpdvRPnNHBPx!|3fZ0sjEyag)ygQ~AXUsFPkpckqObmL-nb#?YmHYJhfvn#x2RdCdg1Po#TAFQ9wzh6>| z2^jn_?l$7y8OV~0-!7-bC*kmS`*uIt)R*+hOTL77K}U{oVHg4q*vQPaSFTL@9Tc^h z+n-sBxCc(Zc#RJlggfoMcJAUN?+V(ze(h9{Yp@|}7vZ*q13^&#nS5L0HuU`7(?Ii< zjcAv;a*f;7gRYG8Mx4XUg~=OmiJ(d8t8eB!zz^pg59j8@UTEs<<*V0b+i>P?!_l!2ha ztTD5+{H7(NC~MxP=t7_^EZ#nWxWUhkAANNueWd*z^-f?0j?;69(!7&>>mGT(`8iVb zYIgPh;+lt2^3p>Qt7|Lti>Q&6i7bSsEL$W@9@QWxSubq$f<>A=4V%(4~M20pnZD8{4z;w9FBM_W(P0!sqFSaI)u#0 z$3$<~#Z?#G9C#AAWyTWSrltFLuMiyVs=8pZl|+48eMd%xGEjuM0K|(+K3pE&5qb^P zMs2!L^=Z>f=KS*gl~r{_++A3@4^#@Vn#Gmh2H_3dU07UPaCSZ14DY7TX-AvRF>Hnl z+hSjzUs*NKa@9baRqgI-9>PQI*1Eerx%-QYuYmhdyG*zdtj+t~3iS1G4K@mq+oflX zp!fUw)eAxTm2WckbiBXnSh2hyMenzi#Xp+A{N9(dl0<&@1q(DC30(V-`US-R3ZUkq zfCGD9?+^-Xq8NZbL9&|)VD0uLhq?i!0SUIRe*jwp$P(K(kQ_jPEU|sa#12OTG=ZZ6 zwlItfn}Quk&hTOzGzJEf!@K#`H0?f1EUP11%j#}#GgjsujkemGJ>7l%LsSk?iStvL z>?!T#kQ~4~`_PUW5B5;^zPFBAaZB&{~BGG`x19CdgPdB-CL}GCv};QK23a z>T#i-5Y`xlzG@S~x+ttmLc1)CD*`Yj+I6A5B(#@>_Nvfs3T;|wX`$T`)|}9C0_h$J zVt7GluM40I8*d2Xt}vE_u`IL|p{)vSO=w?(iUG+|zKNgn-fya!*U}M^1b1MiaeQ5f z#>U3cV~xifKeJwc@YHF~BsdeI;D3i00!=w|Uwd%$&9}IG$mM6be4ES9arq9HpXc%m zT&}D9^JDVoUHS9l^5={4=RNuJ6Y}Rv^5@I)=O^XQSLDxE<0Z?k~vQUsSa*JU~UQ*_TpA!DlKuel=)Z4g8YmXX2NTW5B15 zn#ckp@N2=T68!6^(BYhkUk~77eE9Xk(F69tQJ)T15B&N8dK|=WKv(@C{04P35XNsv zSA!A!hIKVmfnRughNCGw12uy9R5+4U#8L`E+oA`?=+RUj98*sCd}`Gp`CEN*9F8S1 z4G7V2j;QB0HT%buMn$b1A5*|x@2leAfnf#4`Yjsvko|p?uMt=#xtheuT+JiMNxzf7l;5RG@i>?A_i-s6T*~ib)_f^H zg&PX+n>ghR!pp;Vv*KqF4E>K(faNq)p2KPW0CzD}i|}vD{=@v0oAy7>-@yOQy@7v} zKkFYC-oPI?H}Fr%ZALSqhWSk|-q^=@WAT0>W_->Mu%G~bgLr2{_zUANg1-tN1}Ts6 zCLC0$Jf%^2TBp)uP&sK**=SLj5`T{JjOT}HLbLxI^Yz$2#N{2pg?jBDVe8<#6d4cA zEFwU86CxE*JRd}FQPjg(?_Z$%Ip1HZ_Ft&L_H0;1#QI;-%LhD|Bt+8>)${(pQtiLO zLBy2TV8-^p!j&gp$oL;6E<8#dRx^QA6V7X*TgH#ra(~93@m2p5h^zkxPJbv$a^k&+3u0-6sdShGxIPUA)}oH=Qm?r&)hNmjT(O0bRbYZ8GXs#*-e* z4Q}Eccf3^+i|_ITL5J+$<8l4`&i#Fin;yyy@j;r~`+P9K2mb*b9L^2%LHNPhe~5|= z-lKQ-kGN@w)g6C~BTx9qt2pu}d?d_+{3#7Gk{jWJ@OQEQ2_KBGq!=WR}kcN*W+km$va13_-God;c5> zdN(!sN*Vn#XF@ba-k&<~}@A-)$9I`W@&cT02jQ2(&MVBFF& za&Q0O07U`}gxQYmkA+QS`K;ORsb|^vK&-)YIL0XPG8vj*4eg%sQy7G;36Ns#kO;&@ z5a~WS*Jq%G(tQRv-DjXf1c4Dx={^w#&oJ>AY(!Yc9AmiQ`7(SmcV_%dtXD)SDxijh z9l=2GNiHQ!{19(Tu=vsA1QvgSYfe(ePcLQsv^dqLDieQd`bWw5*}dH|P8mPPrQv%V zLGhgNbDRw16EKkL_9+m7yn+0zYPfWpeKc^uP{kjLl7{sleb_ti=P4(&JRG%IO zbGo(zd0r+9y}p?ax{|I)94~RKu=6i?Isq`bS~c-#snohj>LwP)PV& zXYZ`ArM2q{5OXK)udOXF0bsTs8?P;iKS^vK?LsWXSDw4Y|I1cuE<5 zN)i{P*xblP;T+a8T<*?X(H>M!A|2gBjyb!RL3G?|C(^gTh%Lj;BvoOeBwiZ7dgP_|q=(&vFyO)KbWF@%$&h+*V^jGgX0z9vJDe%nn#`Ku4#^>=fsR_h_NKsP92}uAS zB|Kg=08Og~s^SX?UszaC;ja?@nDEyKf2{}rU~oTS4C)7jeo*M_+OG#3$Y$dRM0Z`$ z!;whj&^m@wpgiN6!&*~Ib1Rn}Tqe}23TSwvnp!zdJ|6f>VGYKwj{If#fe#6c-fj-) z{#z=ReNt4YmSvdV(}3D~l9qZ?ISGTciUPV_s2}t(xHEzMe?n<7u`x(SnJQN8%Q-s6Dr}Yv+nYn!F z!Ylrnt+`B;8o(9AcbvOxt9u@~V-F(fiH|b{DOA(x-9Y@0HB3S9{l|?bH+c-lWlS(s z@a3GKTLfP+l5m{xZ`UR%BkxSW3(Y>XWY&PPut#=21dL49-jZ~8@9%ywy2~$^i$Ahs zYA(s;?5GeAcZWuU7zi(~-XwCQg7zX&Doma*==PC8cNlii;ogDpj)6;yhJD?LM28tz zrVk?zOBdyl>2(N@XiY?|UBS=-#xI1 zG^EcCi2#d@YFucIB6wJkB^84aN*dyyg_aarzY7>VBs3UQN4*n><~t9n4jQjOI^6$d zgpSj{+I@9uO;y7>%4o}aty)vrw!Tl)M59fIl!oZYI%38XM?1TyqyLz8{N#x-F2}h% z#pP)(C%H^>K0gz}T2V;T&}`SR4ppnrenAJlv|F z@NjW(TrrVgl=5;%oxpGy1K}g5wvbykQiO9rI42L?84ymwCWYs@k>ELV*5Z8xe^X?5 z&g1MO=$rR_&OQSEct7Cm1MZ*qL$l-$2KI)MpVlC=DwvpB|~#O*R42=3c7~(U{%`XB4B0BpO*&ulG~3igX}^-=~T$;u)ju(Z$4XX4F|R(>q0lB zULfAB1&>1VZn4q=J1=-$FRdV}Wp+jSg)wb)<@C0aD(#V7I__O%SIn7^=Q{fADX}nH zYm9^HA!>K^{$kdLt65!MqD?U)k9lX@gZ`PVqcTdpe{$i@JV2J>v++5~JQQC+$M`bs z%cimdPGl5-p2^1VEFh0lNhV{LTeHh;E+M^pNvh*#;rQ!0ngv!&BN5eV8L3bmCqcnE zORp^#OXudw_I$RIRC~9pat4<|IOYruK}qVp_ZOrz_1^UbaT39GL(4h{SW>`;L18=8 zJ5-X>j6OS5c~MEpL6Gc$?j+%sY7nR^0m$4~3qckX2(bK!kbt{4BH?|6;XpKn77jGFSb9nsuyrnqqP2kqk^a#M^N$ z8=5IFsfD*&)o_ITr{Gh?T3AIJL%=;`KmO4|a?< zi6e(I8+VIKM%(t~dR zjK1A|0c&GkuGQ_s=?c=3c2hxXV>oZ_>!mA(Is%+}*OuvutP~Wv(IvASb}g*3kT|(S z2#2@bg|Y54pPm3FGg@vxFBZ8Hi-oJ|bJmFe%9C0GQ3POi&J8U?;g~2UO7V>v*?yy3 zfh4rOy5y3y_h(sjNs(052I z>x1Y4e1|Q>r=-m$mE$O&0Sh470i7aXi0n3*RFa}Pa$bsKz(1`JT>boWzMDdD^>gE^ z50~aZ7Pjkys+7G8aV7Z#Hz0Y(`Elzds@~hh-l;-__Z^)}@3j{YFj$nWUDfmXl~6|ScRaG>}Cqd=|!1tyKOW~}4g7x)D6#RarrI27Ts@}t3l zHgAUmZTkrN)l=~MU!WMd*~N+bxttRZgj}i6{r<(%r%yi|Kiyx>tNrXapZ)X*TEXKR zedfe?fDZc}ygU0Af-{{QB|G1}fV^J0j+FDH#Y~3p(3y{QKf0coD1Wi#er;PBR>(-0 znk8_ohhraaCQ^XOuN-m#D+_b-nQH)u*)z-0$Gkt=9u`f`H-jsckH=4&&PA8C#yvaH z{8%*f>5q5gwkAP?l;EEnM;d3wW%~ZOK|WGMWUmhr%eJM8De1oiR50gV56ZMI&iTzb zM8mF>l|y6K`{roTpNX3Jn0FD`@YX=SvbRU*@?8FE=Q>)FH0*tCDT_2bb2nV#(A(2P zu%8hBfc*){Zy2#PeQ;4haLzEr;7~LURIE_XA`mAz+&@t9%uwY?e*OIzy}PHEBtIk; zj|F4!a33rGtxwMh_9j`vk#!mJZ|BguWDN$4#?vdGQ`D@75F$~# z9u~eT`0yw-RqgBh>kk|}6mJlt254H}fUkbNO638K%7Z$UhYTv? zCY24=Hz*vW6_SS6wMGLpFI}JBpWEL}1xf96Go(f{$-G!`sl8D8NRgXj5k9H8EOx$d zguQF7$`V-$PFXH*MB}1uY46*Dak~n(nXUO)Bu`qSi>c5!aeP_k%Yw;t?M`fpQ8doaY}GX1cRi-DnN1$LYts@2pxJGG&Ddt zk!YP0B54iykeGU?s>o9hp~DVkDUf>Bfipqa0^DeyR#(6OKqHlhdAkWPYJO4@dS;P0(Fc;r?(kl_Lg$ANhYVUjDy z0W4sp^nKe_j)fIcQWa<|AY&ln?ksjp)4Ef`uLy`FSS1ECyDF7us;;OBa8{u&ZF2M(sK{q6V)9?r^Ad=7Z_ zz>0~VjIXWS&&EgMx!J|l>}dQQ1HLai9d5!;&0BbF14=3^LL}Xm9+4tV=Y~H00{#ss0Akxjr1=mD?7-xE9}~~ zxA%CBV;}i5Du~#S@~@M6p*+gB?Y3+sTeuHt_+t+MBrH<5H^KG4JSUs&@*=!w7uKX_ zY)J|#-Ew6B2Bj!u?^_v_kX7u9y*~JAQaNOn^T3reeyL~fD**7H!g_vOs ziq&cd(aCY8B|qK;08BWw&v!$15VvOtjC_?OPOW*taShU%b6)8Qw2*sPfQPL$FRRlM z_(GeWX6y?$O_;~|zB@}N3gb`7QMg)nvupFqx64v`ZF`H!&Qr7N)dO1{z#VJqNyq)k zgIl$l#e!9}Z2&B4O^_iS!BA^nb#TYErDan(OnvmX-2G&ComWXlXYb2;d5lisr<7-P zU{7^TP;h|i8+Mfhl>ZTecYi+wZ>U*;fH9oh6)+NlI3i&BlEY}zPkvGZB%u!sKrSEd z8={08goBZku{FuQzN9hG+dCZG_B@{BM>W)gj2_(seGv|^5wQwSO{_7Nj2-t()J&N- zV^i!;HBG)$4(9_Qm3*lHc}@rvY_f$YtwC-FS0(MWGTR0ms36|Mdnzt0&a*)X6m67Z zUGxbtAOupZGxCH7$P*`odQzxk!aFXsQ$hv8#iY>A2Dd5#buoN!|-!0*r5x&=ieg~Ko-X-{c0oU?P5%?@b zW~c^2^pUVeazQyIkw`u&(@%B6F$$o!OJ*N4h zV+uUJtxC9Wft?gUT!<^G2jC3&@ivSp&B#s9eq8?Jq$>yTS-yj(I40^6~vV%H=cakxSPC zL9p7;E$qYO*stp#{JL_a9D#Ee{L9Fj%W)G6NoTHu%3Y`((iJ=0#41#vMvx+cRUK6r zj`a}D${Ii=Hs&ks2RLSuAP~T@w-BED@NupZ{JQ1!nSnDW(;>c)Rpam7 z^=x7GXVpyt^3NCqz|ZI0A1l6iP4cJ z9Qleog3O=Jks&%ljtyVMk;E|e?5|TBGrcdjk3G!DQ-LcGPX!+{a_;&mv`Fu#%G7>9 z@S8x(;1=*tct83A8hl%Qd_$S92jInH9I$``@pIHA!d)V4vjs>(>JT9LO@Lu!p@OMj zkKeTbhXE~7gZ+#2Qy=*OhWaHrH2eU!&!#cgh}n=S_*c+^2EL*45Wk9@L<$2()^YHW z?0z_R_z~i-ln}&UXF7Er-TD0uHhbxR?i}p19;l)_QYq4m?QHWGnBrc>{)WoG! ztD0)V7xTN^EkS7To=05s`zn24IUkj%wNmZ)Sl(BGKxsgv@FZCvL6888K%s!blVk%VQ6%_of@F736OE|?st9z01P1`So72-HOlzna zjb4^4jpfx!hP;*~TV6@A6WVpe$etO?581mv2ymzAaM;>cZ0wKSb?3zH`qxHqzjJP8 zA*+BYRyT&)(Dd*k^VZGGn>RD>J&*5vN4}tJtN&5`f7D>;KWaa#e@91-pdPTsNGC8^ zNO4I$ES;3JBT}yfrLa#jVp2jfBT~O)MI;m9DE|LT!3+L<*udWC`4lhs9r^p{60%aD z=g3fjz9ToqXYw1;_htLf@Jsy7C-)WmpHR2ue6mxi=>fyQKgI4WWqy!5<@{iJ0LdOy z`v<55R|aa;{2bCgP8azL?EN$GD?dQ`_0Q?oi}dS1#NIy_ zzw(QR$;JK`^y@5jBwZ&n2oe$c^`iWrC;E(_VKl1adc z)%)uK2ttOO!f;lhm7gdlSG#y1&oH@?XlQB8Q(IAz-v4tfD@nU(AWv6Z8M;3;n*3>i zfiO+Ms`pn12KrBC#V>|wOy`$Z9=JKP3OI#S{*`?%bkT9XC`Q-hjn0IV0ozg6oEVi- zkb$`dKYiw2E(oK{*~jZSq_WOD)?X(@+Oc}LFO-k!)*ixu<*2&_Z9L#!xidN1R_0Zu z9#)}T^O?ennYHU5Jh*|dy4>peW7j67&96LstmOI~CACP;KflC0%9+*2{TAWLV5&z+uMrB8Z`yb=G^A(1<;zzmvm!;fCNsr>L`^W_MLvnaP zOZ5*7nNd}bL~EOBtvZCnB5ODNt1>BeNNlA$gv~Ngd!Qu@L-|0O!Um+mLbz;{5?_H7 z0Kqm!8%5U;L*YTr!^i=$nqd;k=&*?h9nEKINJh{}`3STwQ#(e?g<7|%^_bcTQ;YlB z5o6O!2&9&Xe)2nj5C?;S07d4KqNemIiDdr(mxIuEl$yz2%m84!J4Snw2lOq_4DDC5*{h574VKEF(79Z?a=ReWGB)&AC>;yy6-~~Ksu^rutr1mBAg`L z9VD-#0^vIl7IEqXPHGqT(LFz2l8jb{M*p%K$|1Oi4K@k&Y zY{DhDrf`j>qH7>?6J43Efgg6Rae3z&^kd-~l!yuUa1f#*B~_9ss`-0${{+2-;Cn}+ zCre3?ChFMBq}p?*~{K5zLfV2rtO8 zf3}9VNqY9@@s>a=r~E?dMg$KB#1gp2*~p~`Qk)qZk^5@au<+NW#6nuSM$7Pdm;FCn z%gHD15-f0CM5&X>Yypw845*43bZ;qOIxUy)1a}i=L_`< zxF~)}Hz41cZs&Fe3gO&41GR`FF;L&bDc{Q>Y@FU~dl`pD`IJ!_grI#NJ2X_^K(hx$ zL$w0NJA5n4@+b3VmO~hyuTUG>cE)EF+k1?U#p7dH_-kW0 z-0@jSnnvy4eeuFFEyI4FJAm|3y8R)Y<^4K{(e}r*iGOqv9Yeao8yD3dD*smbOC=Ee zd+ML5|4a>p{#5^8_5WQDG=`c;m~EDn7TIW()JQPcCTV&g&`xU00m*2Q4#H?69fI3l zib=i>1n2uZq$5&5k&x?7hP@>OXCCBcr4|l0nbha5*<+WENsG16?tjqm0kqo+(lV^U zF7HR*RX8GGL3Q_C82#JjG6#5$9V?B|BmrvR zP%lL(f;_X9f3yZ4Q15;G&^r%8Y)hnEoX=T|ct>Q+k-Zy^O?ysuV0Rl@qnsShVU9+? zsV;J7%sJ7ia=}QRJ1QN=(r03UKzkWZxV3Vwrb_2>4yCLvPAD*&!1r@1SouXz(Huhw zPL&ZQD)ovFq&|^n;)|aEJ#iE%<~l*_*Si1yheQ}gfg&on-wEV-G4o(yIky@C1IH9K zxw5nd67$^Z0ms2fb!%hO{M-2@h>q*vv042hy%HQBcn4ggdpVC@rLkE>a`5+aS2$ox z&r#$3*G)>79819}O6B zhRP{@p8mgFT6QG+3R>}wH(jG|)Q=a#@p}c&WgduRvvB$Yn8e>}xn8;Pa(DpjjS~rI zbE~T>tAZF!umcgOl*j1`MrGMUg2T={hhB~8)TX|`tL=J-=~j1!$q7IKBTV zw46BYd1QBYKWv9v4J7Bu1W3+-%S4=fGLbNy`Ged92=3$c!o$Qm;qbtV7Xm>x3SUo@_d4^|C*x zFPgB1;S*8?7$zbcCrS1f`HdjD>q7iT9`;(q=Wy1RCer|llVyJMAUTB8PSXO4^C$s1 zeO>U})OMJ){iZr#s>jT_A;9j;x>2)k%&Y^`^(oUhZK`KXl^~v`I%y)G=6MCUPgA{W zsy9vbmKk`1ai6lJhsmv@k=0T5S-^Wrfhe{D%|L$oBC(AYl!vJdp4&J=IG)gylD>Fh zn#)UEUgq)&m)E$w&gBg*U*~d$%iCPud6UlY7H@w^jvo(%<5rPLSKi2cV}#U{*jph3%izw2e5sNnqycr;$n8@nT|;#J)}m?OKzn8urS><`9>i{bwATPVK*GP>_IE`4 zJIKmTdD2NiGlSf|vLuF<>g1sq|zN4zJF zc+XyTj<~>Wnkg{^$;q`VRN)c$ZwgwewUJX^Pwu9Nfi8&(9iPH^3ASkLXp&+-ireGw#Pe-%~F z?XPX;8=rte`(s2^X0?2DqY1+bnsWqYVt=+dtHqNb>+YF1GtsPRnHxvt zTbP6TXR^jtzP0`s&ba~KO?x0vQu{eb-yA<{yEkCs#Awhe;`UDe?oP??$jMZ z-EYWD(-iqz=*Z9E+Oc_)LhTX1pFf;69WJH}e*BK8JmOT!kt?YDB~jVwR4S2SRQ?T7 zdDN*?BZpD>H$`QaQ>hUbfZ%A-nWIU!%(jnSywICoyg2dV#cMBK%)EGU@5KvL!RLb~ z?ZLtJ!&H4de>_12nX2P>{K8cI>sCeKn=ul^vN}V6C0>GDq5D!#ne~Q`87n%*6vEe< zcJ2DLDX9i4CZFo;<2#w#()UrfIvz@W;?s(<#8QMTu1Y5ZUXGc5HklHq?OBG@H2Inp6Dv8geAKmU)XP%c z!Hs&QwXMBt<4{blt&MM-km^pt*QZGhULz`Rr??#Aa+u2zE=Rc><8qwKQ(T_r@(h=+ zaXG=|B$wy7JkMqN0vT*C^7gb`8$(hQMQfOlkPDMAEFfTjaZ!^ahlr!7Rtf{pasZ;0 zl2!nXLADeXe-u5f;g4#jb^Ot+w1GdmFKyzF;ZGx!Ts8w~AO2Xuv>$(bp>zO${NZ#E ze*!h>5dH*J!oq|QHdK2a5>j2gU_JUDIC4XgdftH%aS>i>RX#~5m`>S&!5B*79S!>m z?E?%0K^Bk}2w@ad4z|Qaz+!yb&^spDpuh4W@EU%7^c{<^7X+1|HXzsy7^mLxp^Zgt zkVzqEzV;3~=x0HlL~Z;)eOqVW37}1c+5`Xq_f5SMBz!jky{SzQcyRy4cS2|rqc$NB zNdzvx6DFZ+78Fa=CJZv);I(&Zh(mX=2J0(gjZ`i1lRBFvxq1nb92rly@{x^N$TlEm-Z@whQ7p{X$J=42RP8+1Kg~oK zT+<-HHT3>}4X5}Do#K%66@Dia9+w6hSx;p%q(oHWk$5Uw><@7J8p+cK(1#6v)F)snhFk`mQ@&v<-|!DGj3n)n z#2iMWpGPAIMEDW41Fbnb|)^|0xdM~#yL&AwG zmz+3Waq{*aasL$y2%c*}4xnIO?&)M<>#Va1pT*K6;NEMo2f5sWLlCr(%Y*(Fi2TBl z0naHb3KhY*`#VFkbAq#j)eeq#Xe%tE9)cls5(}!c*1OYxRct|tsypRf+?^F%um#fU zWvo41UYgInT%NjF_4O(?gk2V{RDPsuVnHXts^X%)|2MAkqV9P;@WvsjIed`)DPUIn zvFMVGWFYA6HJ0_!(W+un`$rR$BfWprhZR|g`av~PZ?!i0iH``NN8nf)+$s{KV-iO8 zbRCtArWQ2~K@E7oR|B|dooNDDdd%meNEikG8AfOW4pGb3un|$LK(KA2shNH;4=RV6 zV;x)`=JE)aom_Sur(e5yyGK?dJ_~D*u1pXgk)-nhYVeP4FX~P#63%!HXXIF<2*o1R%A??!M*t<0|BgQ0P(O`emGIf)+j$K;_9@T{ ziyy~O^M39B0#)wJHgc%*0k2`wRds|y-N9L>sGm*J6j=Ub6t&6)JH3T;xi~%2^`!k; zH)xPH)aMbX+dg~x8C~Ex;!(GM*t7O2H%t3fa3SG`_iO(>u5*Q^bQj#D+J{Q`j>upj z=H>tXCEzBSi~CrL7P5QMhQ zW2GnCH3}7HY>y^VtmrvPlsltVj8WL#2k(qoVIFrT)1B$O;xxAZ$5MG3D;-HZ7Dqiv zQicnPz#kaskCZm6Y9>qe6R+BElEWN*n9Cw!4uh^BV-AA?!RUOIDU-BXUmYebSYq3J zt%RPS8B5hHnz0NsyaBhoBI)%CX((JabJ@~PH3xY6AYvRbXys4b zhzW^89&s9kOnN6IlJGk5(G7_SX^1AIA?#s78pf<6V`6chWxRhI^T=Gi@l`HzRXS#O%gvZv| zpGu9nKRibE;Ec5Pioa?1YK0!68x}geys(;k(7e~L)XBkeKNR=AaQ}QSA@?BO(-)S2 ztU^c`{n7b~({2NAXLgTC;jFuw@wp15EiP=#nG&>zne|06QCaxTnY!Nx=JXt*LYEgT zcszjAdOsIrQBX{)Yt?YxxH6nWdRaUjRh-0J9lzAX=gglk7tNecSYKOQ%CCob@&buQ ze(Bz=6?X!>y5BJ^I_t~lL?POFrss-NB{n!GkFTKn+qn)Zowvijk4Q-imVVu1kcMXt|n~PxQt3=Fm>Qgi@KN?7w{dbPH5&9@0hTow+6G4Zhicuc7KW^{ z^$ZbiL{5rgaA=T{J`W_25ej*dm1twM#X7)tw1d$j`e0K_^I+>BRy_r=X%=L1RX2PT zuaCHSU=+dItvQ4Cb;b9GRfrtmuW69aCwo-X)Z5wdF2|N`8sdk;O*CW z`=+8a`4AZ9!$MEcsURGg!JsVrCM5V(6}UGIiXl_QU(L_edJVff4UpIY!=>fm-*-wqSEP4EagK10ZLVu1mMqzj+Yc^((3i6|sf)Pi}& zqvXJYs@FqK_&RJckA(B{>kueNgiv%%{}O&8V-7%@p8|&|u|Wr9say49l%2{i|E!FF zCpu5q-!|as)@&-;}p0I_+oWq^8#XZ-Aj`2)pIYh)@T3YE2-7PwBMZNL8OGQ4)^K^-NE{6%b;gsvn_jB@vqWAvBg zr?XZTVIsb?r7ZfHdJ?QL*oRN`SMeh(U&vq#dmKcq?q}(cltn@;=?~Ij8G&lQCOGe)MQagO%|clWc8Gq?2PvMXYyh0nKfqY=*9o467Ukv&66W^>3-L)ga8`7I$qK5TK`V)Awj(!qPo@&lc@P_-k=PbZ0T#2qa!qxc9Zu z_$ejGZr$1z!;BwV=+WBm;)Lu@1px_8d3^ThECTxyRJ@7YpCqTR0n}KigCUdhfqn&t z{h%QYi@{3`nd?kF-sfc}WP_&4RuYGl@mxhinadqobdE=_R@in4QoYxNxLuip5zKUm z1Q23Xm~EQ{3OivB?GmSp-G8*U9$U%k{(Q9o%fzd&m0|c zRELTd?meFm4A+jtN%mVO5T~Mmx94e?Z}D>x8t(t&tgI`DN#$SG|t_6QD`i?aQkwGN%`2z%>b7c;kMbocfF}f@aNtelxgu+lR+k*vW!z(0hT;1xF zIHl*cSvBszghT_#5wiuW_Umlbz6nl#Eb~~?IQXA1Z*S2#Yo)jBFLArK@f#5%Fh&j- zHai15)J3u@I~X}$iyNhT_9EKr_7d9bfFA1qfuxfMBo_K9X=;n~0BLAs6m1HMb``|| z4aGqn#UbMX{GNHpe1V2638o$9D!D$DbWoz>?KLDG&xGuCWEszd?MGSbS*FH*A8AT8 zI8i^K9QH=0*8UI`2BJb9Q~uISz5P}EH8TywTnH0-pi}v4(zp;SA`I}j#3jEk(}*zeZ;%iOkEf=Si4Hkh z5)gIjUF0lz6J$9oKaiJY zaOsr^0d&D}_kx&)oq$Lv*f1ElR5s4!D#?e14*v29pXq$|%nc^LU3-|r2bnD^w~$-t zJ&aJ6B}b(Xd-~Ejn2_VXg_TPfkLmnlc~)6kus%B6xthx#9+N-T^GmC1>l|OGeYCVN zro;~`hsT53a0>Wo(1zz8%p-W^A?EdgL317?X$brvdYn`tjMa!5 zmQSw-M@}auHU;=*!aY+)G1HyhF=r!@7)|RPrVc0$0vNj)soZbM129}mzC#-@apUjt zZ)09lRD4d7+M}E}8SmvX!DW&r%+TZzl75CKWGKvX&4l7ZB{YEuW_vv#%l}wK{81|Q z#cBc@oXXw+BnsGHX#edN(Qn?>L2v5?v4p2Zu` z6MO2}thEIV$=t@&1!b7(CbRkvaR{a*t?^$&Yr6O>9;IIYAzh(KzhqxpSO#DPXE*H2 zSp(o*1c=3(k%83MjJHtBZ&1rt;|8W;^QL`m0l{tZ=E4bZG+d`cu@ANDM%VlEX1v{g z9lLbdn;2H#)+h7+2#`@e%$L4{7+`zmS^PlO@($s_P3p6SK2wMJ(4^m2`)^=4@MG${ z{fs+dzxfP{h^%R{au3h%C@ngQbd;VI3Jr>O*Q|eQHgG8*ZNsbi$&Z05p&fKnwvTtx zU&ZdmmFOzyUQdjFIq|GhN(bdQ(!SxQkJ<5O;-2kZx}1XP*a=um<)jH|(ZZ#PWby|M zzh_pQgjs;^a}>H;tC_EUbvY-D?=D2;LRAEiMTeFUK>9vH*!WuJUhZRQ|CC+j;@J2` zTn(?NH<$@XMx1|d8rgzBbdq`MsJWNZ*O%6pbAdTQ5=eny=2ySU)RKX@okK#->7Z0V zp{t`1pa;3NrAPPOaHvAin-9n0I7~EEDNIG(>?_)1P0m@1LL!=K0RdFY2+g12o-=T` zy^OZVE86GHWlh7n#xF3gsv}NBprQ+@z2UqjA7BrgO~^jQgSf|OP!BZw4%GJ zOi?#IJc%4USEat^(a)TuVg9pA>-RGc$?q3}{;;#Bg4U8Wii+Osz59lvJbIciBUD@- z_y2{ITOPe%$lan5bk|BmC6!C@+20SA_asywrrj!AU%k!P80K={pVm>Y!t^m`Y;L{(*oJA= z`~A?OTKt1M={gZ>G@^`-kDcQ3441EQIl<*5muJsYneD!V<5t)m)2`)tpd0Y1t1ogrGj{6bEFONNj>eVX<)ZftXDdNxuQ7(ck%r4yX*4 z6hpc|6a*+N1%*$&U?<5)t{`I=w4>=~UfVO44Exfo3MTZliUsHjG$aivHc6|w_LTD> znOHKIG9DHM$SV0g~V z6L*s&kq`5M?=unIA|1(C`%ReB_}!C`RSWe=Wfj*0{6nyO?QzF z@1Mb^?7}LTn2Iio2_sZgaiGjXB>EhN1(3L77Q_l#$%_pL0VrA|H_Er-RJs3qE<05U z)}o?YbSKIF!NHUoRiip&E**AuvfE&uQa~s`1`wa(4+fCu0UvrA6Ac+k;8anf%qGw= z3&k$3#*MV1z?CB&`=3>Q?5rlwypf)pxp@8Nt-0Gbuec9A_~6y5%0(BYM_yD>Nvrgs zmvbBmB{?!o#Rs|n`zZGyStTs$cHuD&jE)pNMq;?ZV>IwIFfHSG`hC#L@E9kY$G9V7 zAEd{aSCCgjJi%W!Dt@?7#$6JKzL^h}zB;|L?>@>@xv(71v16sj?mwcr+&AyJ>Uhaj zF@6pkL^k^_6=Ih1hgQ0Io3ixpST;WS_3& z@(}z~0`zO0eoZl31KGXy8>CMsoZfk%J=rWE`toI1C=YrsTyZkk|65&dGVnadyCvOr zXN6>PIAs+&o=hc1YD!wRlzqZ4k$wsBlX6T@)xBX6NiO@6v^~JvgUDb+c6nrrr{wZdO9mn- ztx_t|5kW=;+!KOpA>joK{RSa502F|gp2O4$5C9QQ(RJPUSL9uu5JD@=M5a7RLhD(3 za#q>Ot1waJHDVG5{PW4DKm-8OkT*6=$XGzuGsI!m+KxBD8qzbbW#Bruv<-8^d>#SJ z!UsbVWyGZ%xar8hql$}MK_tS{k$+7Um$@Q{iYt+SOcmF-B7}96^*SR8$ikp$| zq9XFE5c$q+pf)IdyVm8g-YruHLCrA}Q2e<+=u^P9@q; z`v%uFI(2)5No#H2K$vv=#f#G~UR-sJZwC&a?a1_#u(U5z!(ga-$pILAjQ9^6y7FPfPT?m77@Cx>?J-VNT z@uk`%zR~3sEE(V(O0TZmhdE^aJ*czuIpL3&LXb9hDr03NQyPFV>XXNt>Uz#Ks;@Xn z?SHJwO;RkeIAbN8pNalqNSjeh@-Yjah#N)Fs2edVhXIH(OiG6a-ym6>@#)qmkvc^6 zA&;#!If54fA3H4C=U^7(pb>?FMiAv__mWaq*NHbXF{{}9vnn#(rZs7+Q`y{3E9w(` zhL|e4oo{r z34p1u+V?ShduD2G=GNr7%eQV$o|}4!G9)y&x%A|@Ti0*C#qU~#PH<)V+SJ^&+gHy{ z-JCIm9#o<@NySQ6wJBZ7$u(l6#%QE?jjqBvwg1Of?!6bCx$8VO$L&9oB7Rs4>t)2M zm%urR&}q{Yaa#z10KyZYTX54LdJD!NL~p5eisF|74NMXPTMt-bnv~|&7P7*~!O2na zCBQbHB?CG6aOLL|LX{*(GYUf)um?%DC-Vut_&W)tQ>6){!)Zz&{hU#J!$h2j9eeZ{ z4EH{0{bQJ}cYP?IR8|y09Ya&KmSX#q>vTJK|e z%0jd+L#*|3Lq8kU*`n@c*s3b|wP$20In3uXRqy{DDmxiah?<-I+S5n8HNvSoS|+JV zHQ+G#d`CgEh4=zS2_=V;BLUAf97XMA(mW_7`bSd3l=W1@{L~aZ7(Lkxi5c;uY(^lz zu`^L&mMSDoabZCE0VgLNnXM1Iln!0KI4lnd(@2`i0Gq01Ni}IQGh{;zZWyMasxVSE zs7D0MydJP(O)?nDEnwqbsCfS%fxc7x>yX^k4ERo?tmwgWw~prNTakZBI=~&b&X2k`BL5ZDz2(+XnvpL> zeuwJrx^+R+%|^aM>cSIT_u4b~jIR@)LDq6q1wf1Kn?%6i)X0%@sJTsMa;HX#^rPlY zQVEf1sLRhSBpKw(K=m|tu z&{VKA|KA7&tRS#?6lJ2wxtb$d$5oG(?}~A6l_T5noPpSV_Bg}GuS@{+T$@;rc3&H_ zh?OI58xSIf=iIGx$jP^Mt<2)!Ip^N@v-dc;3Pm|o@5@hHNMxsFS5}rUec|pE-6gGM zKdnkQXcFu=L} zodYI-^+#*0Bkk{Y-hh52Obaaffj9MGCcA`4J>&}r<~ft++G9+R_x($2{5YXG3cv|l zC5p`v7Nry*lL!3TON2+co|pT4Ymdlm3|n!LA-hETtDwsIcV5=ZUwGg>^s2Z6LhZX2 z=?0=%b>>k7xITb~Ze_J`dF9@Hu`+=9*{NKsXf@gYoL^30^voc{6}IP`FLcUeY5=b| z!YsiiG7>797KwzF_*Vy#h;7{Y%t0MCH0&#Utl$qD9N}I57KH@DT{cO`v3%;C?nw zlC`^q*jtqrVsBMj3A(Pe5p-Q|ryxcHV0KG30cQ8f7HG@-vM)9%1!RB6BuL}~+F2VVP znuZKaO)rzQ=K|QT!uEa*wsV;0bP~W*IQ%xX4@tMd`IXlImxth-H#+4%u0z+hI%V** z6c|z2Wbp=0Xt)Q@ii2nEcV2!l$;>2sn(o0lad3`M7%#gr3D@^&s6NLhpZtu=t@cfI%-PC;?gYV9^SH8WWgd(Bw*gOO7ju z{{(F3H)Ya1@)5FZLA4~$%KLTSC&X-<{I?On+O7O&GE(c!hA)LBoCBHlD0mbI)Zdby zyQBV;+;il@?z6u1$_ zcf%MP)gFN6b0mKxK?P|(hcP(9Ri6cNxq{}yZV@bOH-Qdcx}T#t+|_j!Rb=+HD8mn> zB-%gWWjXs9bz!`?3s6`}G9tZPOqLpUouf0EXy2=H3`_kD+?Qkda7=8bQ>#8kmg5I2 zvDQ5g1)u-?djd?W5wpVQ%xhNX_uT-d6=9Nh;QxU~XDn7;zB1nzZPeJwn4h4qxz$_9 z!CbAC-aO8fuHA~?S2O?c(IoN0tV5J}w3=&)ox5>6#!N9zO$>?6ug{Fdl$Euh-vE8u`_jPZ6nw)R2(q|@QCm>+x29cuAd zX009&0o?7({OZ!fb>{~l)%-q)SID5S9-|u@iv=K9(Jt+QmWq6Z!HFix?a3;2DMjR7M#7OP#GU(Mwn z+{_`#JYGFSE`~m4vdg&{zc1$g56D|=el7M< zzwq-**L0!(|MgwVZiy?-52&qv266T)kD}_uX1~ zG`}crb1YV~zOzR|4|*KD<PVIRCbB%Tyq--=QqqB*VW+nYaCKo9a8JfsETZQ@Lj#7!{Oa zGK5lqld03OBlWDmR z5^)E3b)|MgMWRAOOHgx|pbvj0%S85|$k(xI-+((!mWcrYEDvBDylBtF$OTQtYQ)pX z5~4#D`7&BwnT1^#AQC2mhzFk#??XKF1dWvd8vmsN!!qSJiwZ}K(WA-Ug+qxOY7Ldi8@iX@6%~|Fhj=zQx*|cAq zZKosSlj(NFo^W=~($1u_bB=b-Iy>iS=bW=MMLXx6oiy!CIXf4ybH_|mI)Z7)p(>)? z)Yi}BYtlgs@F9eS9?l;o2`t@#7BRHYR9!KDlO)x8O3n8i;_i%6zmsH-o>2W~@(sD( zZV=BWhv(indo+KPQh*`hakotg%#`nPin}I}&kcc5$0(DtVz z9xmxTrb)*|>|1xo#4jvtwC^i42cehydpRQ^U#Ae0arr)saV1)mceSLA-EF| z7?g%W0(*(|4!~3J9w$eLvRDP=r9Il&EJsg9`+%;*dQZr7_9y!(Pk_vJI;F{k5Fp4U zv^L)nND0SGy$5s>rWq&Pr1Y!shm`L5u&*M#ZhHIOICC~6cG5fw$PhN4bKQE$9P=u(a7rVPM4$%ky8 z7bFO2#bnmvG%aJAV)@8_%^VblbQ$qdv%wPobx;Po#8q0-V017CIIyP^YXCgC33A>h zD4;1ZTc58_5Q2yc7$WF@i!}u|>|dG{jS>7YxUsocR}fj6TXh`_#Y@E*#-+auQnTkN zN*9dd?t<~MHKK6fx$9T1-<%OR^7b-w9tWh>lyo2pAj|?%^6`y1xgOzp2=WQ%u3f)* zb@EDE$uZ)-F%@)<4p#HMzuZvbeCZ+pH6X6dp(_GRvjeUPiQnFsym@W<+J&$G)vS=^ zZr@oajd{|^*RGct_M)X`&r|j7S?ijB=EUi%;%(aVM1A4}1@P48UBx^r>pnWMv#fpL zC0?hfj_TOC_8tf{{7_Whf6bUzr#WLC?*M-6GFV{t48&R_^5{WiR&5`bnj>AVfk9HlSB@@I= zy@|elE|Xjia+%_Ch|6IvN5`qmWVl&+h{FC%496Esn)c(6#Zj$1z z-gUYvK%bx&rie`vxikvAC7h*>v4x~*D5lQ>eJnnuG<_D>z?TDRSq{5BG%I&n5F*7B z`5Vrl=J3Wp0noaagdzM-G5Ny~U&D`HGJ`RvL|^vqI zJW8*O2em8ita9j@ntzHj@#k#g^w6C<|Ajfgi6Ink?Fir7-+Ol+`3;kD|TmAf+T zr1tMBKSrL^cIYi4QZMjnQt;hZJDU|Qh=;aAu2E?c8h@KW3y)TpV-Hr=;phfjHM0y_ z>V@9ejpZENz%h8kmt&5fx0f0mXDC5;?`&119s9rIa*vs(74IE8f<_|g5_S~Czg=t{ zB>u6<(Ift~BL$jPShW5F)Db-5a)HqvBx&FEnXB-E7sxx@Pj##svX0VY_fKn3?(_Ea zc)aAQcv~#uEQeKXtr~22$>F5~z}Ay!Nh4gNvp+dfP7w0+?&a&^L0S-3*6!zfsnfti zQ?La(pN1iQW%aRRs*9Aia`J&u>K`xWg8i&DQW5qhckAaCBHh4%^6^4%&JG6Yjip{;asws zb5n%O&eL&&t~qu@1X7fHgFJB?Jyl(ZVO}gQF?6Q8s)LpYu1(R$)itMJaH+^UT{C%mx zR0IijObV=Sg8PJRYyMVP+Vs|FyLF_QXh>L6BO*Rfm=pXY5TeA?D)Q%mjwDKHsRPVG z5NVtQA~?8yXaf!; zz4Mzfl*hR2=JEuWC%+k^cEMVuuA#n>%O)=SxJ+=_&t;O!0WJqq)P9J!hk1L1w@2aB zLEdSmGyxZ%WM*iA(9}SUq!|IuMXl6l0P|KGGJtt&jTyka^=Jk#Z=)##m^W4pIJo?Z zMaDM-EuKg4q(4B+y}Ni&Qov#-1P@9IrZLEhz{=yk#8Bu)jUPz+6|iyx5;;mF&f z8-&0BB>XcuCS8RgOukCs@Br{Jgn7P>9wClYw0d2%LMD|C`E^Q=LaoARMcnD8+lry& zF$q~xI%LL^Q>z-ZBCDI_wql0$nDiF4BARtRR|KtyYu)FzVq|$tnx$3@FQ;?Wq7^~N z{q$7EA(bJ!Db!7H4<{(~GZ*AQ1G6n~@$N4L*P4u92*g1lr9Wu+K3=g3OFXN@u3s%p z0X#rm0{&P+e4DB&tHSQ%3y!fgd9ZGERdlHBP~(2|$|Oi(0$AsyE#tfUi+&$%DwqDj3kFV$bm8FF*?C?>}%{LI!@4Js4yhn&$igBWM zM66Z3u!#9zOzKs*KX*Kd-Z1^D=m>TXFd4x z={kO_o69(tC%FU}7wQLidk}nGh)-(3+BrezbprPrC&|u@-JQVwC@oDTf%_i*ST}Hg zP%I#W_8|FfQVg#c!n|1s+{g4wi`2M%s!ZViX|(3R{XeF_{j=0OES&}HLPil$P}Hg@ z>NFJfI*JD4ERsO)5x5^&z`|f(Byb|gS}oxFrX!zH)n%v3hpH=)Kc}i|o~rASKcTAE zohsu0x*7Rxs=DP=5d+xmo%sAT#phoj+FB>Bo>PB;wreM1U&QBM^+fE8`1~85hC zKjVqmFB_lV`{Kpqix;0o(SiLsiI@ofGu!Y`UnvM^LBK_i~S2mBU92WZBhR<~>3G&P#-GGk0(5 z!-t`}r|x##ef!+`$y<|m-&XD|RZl8u+?~aLXLjOx13=Jxa8)qh8zRD2i}m*Q)=+&K zId9$Hgt=KhWyH|S(@5-bCUT02>Cc?%5M6n@VRrV+sXnLDL1~`x6@FInv;S0|`-5_- zPdVe)7gjLER+VAu&8t^tR_5Q!tvhd0IRi)0;=41_v>36zRZ(Bw|I1X>0<_aDci|!B z$5}P{hpSm^{5$7}lnzamOUy7*e~%0gnS^Ap8k@VL1GK>CFn42t0frCcZ9pkB5St@X zk}3yG4S)oo5&BJi(9~1DK`bXj0J{--Pf>jYNRR<^Q2dJ0vk?SuwJK?GrLFp92AY~L`=2%Ri5%kvLjSZRf4dg>VNp(>S1WVdsbo+q5UzUqt)(K`_VYoAE=N zTLAHIB9#a-bz*Si9a)XA`~vr{0{4H!zPh=+8SmVr7A9I8-TAG{zP3qcIkt&=xIXIx z0T>pa8?;E0HUp;x#^%^;nAVX5EjV1THWMsN`M>hHbe7zQ1?j0^m8>dHM|#C%p zR5iaBT@+ts!6sQ%mW-mKp=)?<;0sIl4sS49pX!TFiSBg{{dALF`0sz{+B02)j*7>( zW4;55?*W{ng2cpHUzuSp2=mbcPAo1oj=G(bj1e6JOldmooXd|{6=BQOPemW8D#u09 zh!tjkXTkNTC7wLw?Ui)PitEPyU(%}9jiP%hEFB|50@FshuR}#mDk~ZNqrk@X4-bxl zjR9N?ikFQcT5q*Sk3@Tm*cgcK7lt-}gXwQ16PwXQYlG2h2HH&HfEhSw1`e5lm>K9W zgNMyvrx`qI2D{AQF;hhXDp=W0z|cl%1PS^!Xlk^02&0?X(AWszn%Q_9{9&A1gMQKa zlmyr!xJ(Vx_6To}%55!BH)yp=OD&K>fkUHJ1hzW?JQL)XhIKJA=A_Tlbpa37bS+(5*Wwc*4%3F67ot37)VE zs0+Jw7kBiK(P#61dyF*-zlSI6G;0*1M(80E%mF59gdTE^Fy&cMBlM6o0nDA6J@gQN zIi9df&eG&W%Xh<^){<{YP$9r;ZnXS=^Eno!^LrZ6MynDfi;pcaph=CMAf3A;%>StVR8_0Q{c)>?Pcw6h5Ojj(5+ z)(YTn152k0I1zkQE(DzPE2N3S{}}}yY(nl@%E24rtc{42A_zFr?}mH{sAL1Ck>X$} zS&B3g;bwzeA8FlaFzX%Ech?3b)s1x=Ce_RIBy{ZK?S7Hgk;}m$O7}R-+aq#&3y|Ke zvRXGGAz!b;5N{P+LKGq81$Seug|Z-$KvAHS2i!ff1MWWk1;E{Y2kh+ub2Atmnh1lt z!vfqb?1S|PcfxAjAYH)*?1Pjhs>Z&?dLBcu>+KoQU)fpUy#cJtJ{(mAR50Gcv8O|y^`X|b%1w2e+MZ@Hx&q77D6?KRGKgY6DCU@{| zQCHl#vOhJNB;s-dahzeRRzkogh|4iqA&Xss$00;h6%0Ze5`{M?lD`ffas+rZD$(X9 zo(lq#hai4KQirGcCQxcen6CWCL>bm;AEmEiH%WOHebBRdr_5R1bR`~y@nb{&F_V-Z z7bN9BwtJ8>1K#Cb2*#$$oxdGO)%gGa_~ONj&n9Q8Qzc8!W6fnB z<&mxzX@=GoGaz4?p|Uu-R71>**$uHBltIhVdUd38z(a(a8wyR}_JC13q3^0b!T z8D0;O@JMIoM?=_so6~!=gP588>57^8E}MNF zYf2qc4c#YN8E^{l@%7Hb%rW6%j(>AeL-mwm6||Y&_7}=c2ZXhE{T91>wyUP1wZUVR zAX~*3HX=26Zs6A9(i-=I49&+X-3423ga{aNM(0ZI!M*jxAgnq}&qN~vkw^~8Mw~-Kuykoo zkR_a38YqZrAZ>$!z(UFmNY$+-(rwEKtUP3DF;hNnhPqApgh@xAG?A9g>NB-|Q%joK zfN2d;;&kn>sU0!3PE$kr^e$67W@;m*Htsv5_|B=6P+hxf8rNxMghyABgBrLB5SLkd zV57Ok47Rk?wn}X+i48XlvvU}2j*e}NQtO&i%IVk6OmI2LM zLh6)c3!$AyvFkfdBEKKjCru7?p99h?i2byT%p{WWI)Gk?Ttn_2nB5GyhaCB#u*o_A z3~Lnc(EpBnN1^~v90AQIGT8Kv!g+d}7*CGZ6j@xAZV1OfwW->zp9BTJXh=FuqzGs1 zGnDBEo)P(X*FKGOldJr-r;wddxt;e-%4xquo)54CQ4Injh?n!s_K(&+-R^wR^b|o4 z=WuTF9qA`qv$XITf>;-zB%p60R6=3ML174k_;VX-1MzwyCy}OqU&b-ZbWDe| zOhK6_jwmQPRTM`x6kPyqK$5>YietvI{SZ39(|k?vBzp6er}?l~AE-CG?bW9}^H#p@ zhx5LymGx!nERaA%#?g5FX+DJCeEEh)J-<4SMDAICrh#&^&s$l`el*)?KXT4xzn?X- z=F@!iQlpi@;r8RF`6fXs^wHC<#U}e}Gzw4C&G6%V9rvJFpxFGc=^lvXGq%zA>1Xnw z^mBsgvxbKB$B}n2q@T$t`CGF6Ezoy{((Mx3+259*_B_uw{LOg{d=u!7W~uhi%iHhQ zk*n%EXc$Jhojme!PWe z@+){IzlswybV?1daN55nKkbyDef_%p^cZg8H{`C1VE#M?K~E>ut{J`m9rXS)c~trx z8PuJ4PEmW47(@*hlMv)U))V;?iCGk63+%yBLY?{-h=-|wNx&3UNOJksZO235V2@q3 zYO)-ok>}vCT?g-18pEmkAFmc(2Ma5_nF#>u&l=uj9y0j&j#j`ZKx# z=KP^@nv~~KRa+ccu3SS1NM^M+^Ij&Kd64OS{AfwoJs}(r?%vfo1vWzXqkQXxC!jpO zZDj-l3f%A&?jT&8=&FK-tK#w{(DQOuKhF*C9@_$ZKV1C&Rg@F<|9X{^6FhyrTNoAR zlPE|DiQ$nQM<8XT>5r7Q?I^8qUW%Ooa7F49FKxN={<&}%_JKOOh>!%UhT|W5`Q>)7 z_`*9`#UUQ3d=;%3C(l06u9x?w0P?S*E!i<3pe2SOpd|;$wuxe31VySJ#n51XWqB(J z?;5dLfEXh|Lf~j}pq3;r7&et6(`GnYAC2lqtP{;gVB&NWfXeXrQD$kfYzAtG_Av;@ zUV|Be(A8{eAz!OP;udlTg*(j<8X)U^w^`F;YA4N_Ub7}))+EiEL0=7;p|aQ15-fua zI$(TY;WdBD1}1d^_EK1T!MzSRKDgI+;P@c{$2WK2_yx}-46J$+ypJL+DMI`%$ugqA+|;C+rs=Er zd6HsEkp#|?CgGKo&k{)#B_`d_J_W+$yLDJU3DkJrKEL(JbL1N(ql*uS{b#d!UjN>A z->-ww#7@r|&wyM(gXvk5%q-7&4>cm&cp!qHW%^wq>d5A^%NT>9N1V)K%)Dl7E_C_tJwgryfa1YGT zir*z==)jDfWqE3^0~>JQJg#WE2Qr6ymz1LenaI7&6&rD&;BR-j%8Vpk(h?oGWWQVX zicuW+9#GiQHDc2*}FJ*4c4R?SA3~oer1Luy5zX$l9=HGp500dZWYLLsn&BY zT0D{iFP9dC1G1C^%kf%PTv7KwOyyQojtnin7ZDpeG%#wwZzM=`E&9UnGWuPT5UmX! z9yYv+q^W8MPag=muu@YSsiPSPVWUtq@gHSLox>|7+nuvA(dZyJ7?xHdaf@pi4KOa+ z!>``!PVYd7pl9hD5D#JkofSmOGduhC1o}j< zS_ivCIS#$VTeR>Cd&N@aMIKSbJK^5$L|VV}$|gcwZ@i$N;-!QS2mmAFA(JmVbAVKx zie0^8U7}QXmYMq~!n)8mSM}e~N-KWg`ya7#-#2=7x6rG?XPg>IXu^Cg!JDcvEiNc! z{H99zppgk(k{zyF%7l(RcafK6W0LTUpp(HGe;O-1!_^gntE=)U`}CG8*>92Y&z0=Y z%qm-HMaJ4aL9aDJRW(XBv&(n-|3Z_hAXD;W<@!#qW)O3lp|tfJlj**Fua`5A6|YUj z2bY574$``KOK^TW@gQLTF0g##>0X&Tck!k}4j?Q$Yhi(cb5Y8*Xf$F2IYU2|*-`?s%@Gv9d^fbo(s#rxtcrB!*cN{;R;+222kttvY1 zlB@pIU~E3}*ghc3onvNfEV&X!t8=(2T%)u8$xHIQ>Etie-d$OGPy%R&hzfwZIlq`e zV!-8q*u8?3Bg;Zv(Q@~fsv}4*>FMW3&F%mFD>p%T-@m&h{VdE^NoG(T9MH&N3Hx4h zxLUai&O;cVl0#rU#3xN&#QvI+t2hHY1y}K7V-9ZP)yD8j=zsMk+{Y7=sUR)ZE9|5ZEJ@hA_e=Ei~>ZKs>M{W2gzwjSirRGb4Xy}FqDn7 z+D@)QO+Q5TFT*^XRyyEoJd##plI-hDYxv_on%40rAfSYd!B?cg9VlUn+=zJmVHk(gvrG%F}tR1~cmiZ&fZ zyMf|>i9|04t=snHg=K9kA9@1h|COxu-MSo^=MMX>+Eu|1HM5=0=CRdll@lKhhNzJ_$3UR2CIbnTkzds;Khh@TxYQvn9=7l0Bv>tu*o*wP(_Z4gNA35}iP^4vm$-Q5n7vFD$MeUzq8k>ql?ml+c5^AvAd>?Cb1$cYG|iLS4bjk)<_&ygZtk~A*0>PPHaBze`psLEB~I=O zfL>&MS|s{?x%jk`2zZC&?RXiX-8)1;$RMLF@m-YQ=X7np?R%02SH?>TO8$L4sxo z)fsy@aL0HrgudAgNWc>y0yL=`&`S2~6A$+Z`@|Ly7Yc$xsWs0?SR$^NjV(Qqi)XrdC`|_dg{)92cQTTaVs*c($gD-utJ} zLMkXCr1us(X5Ei5@ZgnwXQccVUlj6D^=fP}lUZdZ-TmK0x!05Q(OowoUeZy-Uq?CE zmrc&F@F;i$P(F9~bk#K8C$cc(u_}U3_r$C`t6Wl~Mb~8UDc(kUbaNw#e5yY;G}ON^ zkjl*G7v=|(x&CY-m(2_f4W?2#I3pM4lSA`^{R_i`L&?FB`S}r~ppAy1$L_qM^Z%Fft2uGst2wPTlbe5pfqCrUh=mRu04wIo2j^Gr zgCzLgtGRHW=MvKoZq8icncH08Ya%r$bzu6z8uczGIZ_>UVQo@m%S}yC`r318gkoOKDv62r{j-r#jS29R*prt^QP3Q>7&8Oi<+48BJ zR2=pzpWoocg_asc@%e*K6C|#awTobcNIUMS)3}b}Zmj;N3CNpF%Em5NgcT)-SjV9@ zfX6FA9&iY!BHxjTll`FKcxCC(iN;~}wcaCkyz=&uR!>&^dyP*u)DN-=WXv~@}RB1sdK2#cm6XqD4=NyAG zB4Ti!+hfmQN~ZMnvFEAkkq#|dPaFcV%)pDlywd5dlDCyIBhD-O%r8QLr*{V9HW9)#Sg=*14yj+ zIq&~{DEB#gx_Wo-2jWQBsev7b6#TdYgY|nfKfXsDUMYH}#YD%>)NMNC*hAGG*C#C5 z4qbBT+MvOmvsN{FvbXAs?tmyAM}(Nut3LtHmO4Wvt3PN5S$VSRo)W;a`YUiwHKwkq z{t5)k4Dx_DL8imicW&)`F8k=7bDSHQ6y(AA0w_K|3()8} z+tt63OKa0Rhkc&GxJ4Q5+)I0+c0bRh-`ENE?XUVd&`eCecOs;QgJU>_!eONw| zZD5!Z+M!bw<$J(;DQoNSDB!(A2;EAiMuuv@_v3>s;&1X>0d{u;qoHW6-fnd^w|g0X z{D_j$V6TEp!w;39#tZ-w67EJ`RgW10yLO)$>NnK^ zGc;(1hRo2285%c3hA)f*(b%EfHq~>c+Gnb1Q@v=amrV7Fsa`YH8wlir$%*s|XakyA zw*lFO>=MC48wccY&CzdmpzP-I1eYhd?Bz1?%?_zCDM^hf{2RtUL|iqF;om7qsyWSO zw1x(yu_@ZjWh<9wxO|Pv2`(qOJj>;IE~mJ>z~wZTm$|&k<#jG!=kn$Zo&OeZ-{$Q* zy!{49^?-V@V0f93fNhq1qy>Q>A`q6_v@o11$Wp5SKnzGR0K|Y4!?glP@gas3#~4!F zF)8_F>+qx$kbOrcrJ(HZoRmUx;OL|TA5hn%R3nFuO-d0teEcjzyx>ScyjGp!Cl^el zRwoE@T|LtH72v(>p&@c42#~kdI6eH{WR=)pxR=6dLmW~dbrJ`^auiQ@tE;G)$V;#_Y~ zt2gX7MJuB8ehaODUJ|X|7Omc`I<2{V~B7Vy^V3dMx?R%(QrrIcKLvHN~Y7u%C z$Ty)j?AETK7I9^Pd^2im+}aON`=MJKace(9?boQb1#N4owg!Fp8PskdOe>IYMKv-V zxtQf1Xwd+RCa54!jvtLLc!rrh#VxT7C5{QgVyXNpM$0qE*Ik(X@E)dAY}pIWS( zEYJS$anCg?YtlXn%9){lwwIAx`GkS1bQ25Q(BPUiV>P$-Xn9>;GGW(TU(Y?L2P8$k7!w03zr5ZI;e-6G^+*~yC@vk@6uQJ5dDkg%8Zx>V;7Z1EFBH7 zOJQym?i12veUp8de?``=|CzFO3F3-RJEiBb4P{<~DorW83<-(5Ou3Wz;JUzPp>CyB zgj*il0!9!4mCqedZb&_DAK!YG*Gbv_79DSQ69*?nJoapX05omjt3Q#|wul)N$5ZaE zXPr{>k0?*qthL3g(tU-qo@f7Xy_T>Od{$VzzC|6f`-v$ohO;ISoLP&%1G7q2-@c@P zKjTx5z)WRzN-x&uUg1x1g;9#=*rmQ7dCAAh)+ada@+FNscH>f`+sa@KU9ZcMm#oQ=j!ht{OQ`cn)yq0?OceZ zaqV1vWamO*C<(iNZ#!4AlAUYNwQ~)*cCKM8W4k_--+sx5!mHX0kA%QV^4#fuG;I}W z&c}8iw|@%ivL6}sbbQwp@xeI(zU6E+VvX2!VBfBqii@TPQ|6d9{BW6wAzLeF1uS=I zrj@-VK7k6>C&zfTv#eKK(e^*|Wj_=e`n$WvtFW$(Bu7m5mJpLUSi7sKYpR=@ySFm8 zzA{J2#^x4sdCr=*@?dT;xBL*gUIKZPhKEKBcys701T`J7^D;t;2!_noW@JY~WM&u` z1m>Jmo1h88r%MW!qHk#A&leGwT8$E^tF7m%)MEGRLI^B8WHBu&QZNR}(gjy=urq#aN8FnN?hOn*>_H@$c9gOK zT*J#++HQ*c=uoTUhe}#UD~AdzkNM|NyWbjK*^5ROc2Ay;$v)-sTVKDi^4`rx(l7+a z+7w;V;fn(H_E2b4sHAIs_=QWl9S$0JaHyxgzyta=FdsC!Q~csWs&Qht88<<~{KRxa zHz0{w_t|$IYP>lS3L_oTB1|qo#35P}Hi$bV?&z54g~fBX;|kVm6ybhhS-$7$GSs_cKozF zoM*?7R-9{JJ~R&?Y)u(>-0LLLk(y5b3ngP%r52LXDpK&s*rXI}x>am!4VxYvn_goT zAX?az$_9sP*)TJ?;aUW4MYvWJ5$2yFUaKGLp*vreE$5Di*IEn4Yl$R2?@-qfv_*+` z-W5rF-gArEQ1o4q#OHf%Q5;3z7fF0RaEsbe^gtx>`C*{wM^*Cdw<(Fwy+Ar1k;LbI zARUh&&wiiDvn?jr7UbDKVDfBHBFM8JGI_QrsY9M^F?n{KBtB$@A{>O9#D~bU`^)`V z+K?5e&(G-a0yBSz50}u6_lNu#A#V0Csfq?tv|X~T=N8umv)2ze0g z6F~8HJqzCA8+37%_Q(0k-H)EUy}gAfyq%W@Uvg!jfpLm%SiI&ehlnK`W#FJg7wcYC zEp`d+Sb`d_V#z0x_f%b;A$oXNtMvz%7X?(eOQCaH>*KM}+(!54LER4d^BQ) z*DhE&JdU09ttU~N^U7NhyUp(E>9Tw50gCZcb$kpHp(85AjUY-eX6kWM2Q0V~i~w@C zX>nG!2q)k%j+>FRDW9cy*yt{-w^4f+CVr4K4=E>3ojlFkGjJlP?aNsptKvC$0FY$I zP!gC#NR=M?2a0FDR-D$0aO{LgpMc-J8tk@u7(y~R*Wh&soM6$9N(!Ffr*8~R9B8^4_7`Xx z1ICYny8R8iU-zcy9AV|YJvytJUWnU3y?cO;q$lRHjbvWnI*v9+57dTex^M=u%5s&j z<^k0azqbd#Cf@sXARsgtz{f#i9vZG$iyP-RP~X3w6Nzd#=Ygxv=a6dcw!kK6Yj@Xf z2>e8&6-eNo)g5*eA9#b};CPM*#>a)qwbln~Be@pna5YA-cCZTD9u#rQ?>qo5*_8i~ z0)HoxSVUHe=YE89dSaRgKPM>#jjh!3>G!wW2|H!=1yenP#1nQ5q8|X?6nOJv2$`_V zHl)9_DFbF)#Mgm9pH=WU=&6Rx_z8-vi=Q$TPDn$+dZWZZ)i%av5B4&5+oy$>Sv#wo z8$Lh6+sruaXL)~ulxsIPa}R`8ZyvfeL046wO$)*TFA*|KBf3FORphaRrGuTSl*y7L zqps`5--)XLoKK`)FOY`;epO3ZBDw*}xGc71l_I#nr#MVTOcZB2k&z5RTv}c&XqHB@ zcWJL|?pm*%i(io~vm8kg{o@l&$G-`2~^FZYDH9aa@94iN}_52Ro6vTR|+>YfhLgCjCzoQvV04d%s~ey znFEPXxtvZippKG0>O~h!9${;Bof4HGtirRj_W$IKN%M;9o~UUTk~2i(@uZY1iT}?T z@oNA^jpl-Y{+nqqWr-UFO8nph@=#2`v;>PogUDSa3=;agdEs;0Kl&8Mf+%WN>CuTi$SAXhu{v&yPxWT)}E4Z3_ zVpiZJ$bVRh*OZ37cdZYHM(-Ujo@zQ$ed)5Zu6hWh-2zhh%ummmFuwT-b`&6@uzWgB zvp?`sG7N7PbO7*A1L%~JBi*1QvfF#G?K5@L)S2#EAEYeUGH1b-1C}^FZR%&3%?O;{ z=fVDs&`jy<$0pg?)L~^LGkSry(U-az!Ph67(UQEk365-&R#heWp;nSN709CC`KIQaEy)NW-_)J+C{sU8&d5L*;3Q}dCoyDd+8Hf@hMN!1 zlw{`uV%Vt-BhwB>xQ?>U`}qFs#m7ZPEM(Lb`E`Pr$zd_mP8W@WF;X-KflMHtxK36a zI4GQcCZRXrhjW3#!Hv9r%rY%wQr9}a*x$EwO9S4^8TXs(LNok>cIbvU2&sRK5^yMK zi77N$k_*~br_pcAnLxRUk3RB6r6iAT>O~!fKe~G8HaTPwk4Sml(j9QmtDV0?bHr0U zLkEEveAt{`+ z5<{nnK$DOPV%gd(xn#2T;Pk}Af^@X77u1oBvS6?*4V>2%*Cd%^f#Oh6+WIXE6{Uf0 z_<|ISzXt3~pqK`ajrqyX^~wF1CUFKKt0GbSzlgbl|_Na-~>e4 z5G_s=X~;Rj@pgz1$Lay<{cy(mq#l}Rt%-5vb8~y6s_VyZOk<}9xa2oTV~V-Vj@Bxa z#YN5sN_v-Uv^lK$QxMgRk2TJbPoqiyf1mlbwNzZriwRgAIlR9}{qT zHsi!>yTd?JadJ~!>o zG6{^Ei$9(6k)nc0D6URB)3x~184rIt>*G)7eEjK@hYltD>7s`}g)PZNwO)7erx%@z zwfNIZ9{%*Qi$A?`5P!NX@TW^&3xq$tQ5S#u3;k$KdX_Y@{i@Ysk(O-)D329bk95QQEv*9&E?2?p>OLutet#|Hc;p=#XUly*2u02}dJP=3cs zY7L z>_(!ESKl=q@nu>zI}>9}yLY zD9b~i3E!kPXhx2i8r+kdU@LNx@@GX39BA!gkknP2Mj7*4)CrnzU?p8N2`4y8sSvPdjpV(8RyLuGFvvdps-TxGd??X6 zfjkJ&Z#YvZi4lhc8^EUlJc(0J5*yHGfVxK=IDWZq7Iht5_crS0xGqsnaNRuWI=OBd zb>Jb!amjL$>lRVh#dTP5I-nay9eCJMWKq9}x^Aj-t`tncO?}M)wPKw zNR`^EY?cp1%0T=gx6E8;r+ogJDyF^o^#>C{yu(lild0U|hAIh~v~jGj`034N+8X?4%hxWvax| z?uO@5+-)*44g@1o9Zq2#h9u#gNuUg+?&RrRlJ%f&hwy}X2$oPz4L%l387}wPAS>xn zWIzfbKk&(c78BpX2MVrK_(WB$U99$y43PYuBx^Ke*ZZgPwdTxHl`hS~?c8Lkbo->k<;8Q5lFxr zG;_Em0ttAF@q^+hfdu>x;|E1a9VB3sk$~y_{e}Jgwf+6#{{D;o{a-@&zKi=0i=a=^ zr#bJS#0S-D49c;~cJ?7Q0vo8gGSh=yMtSp{fER!lag zQlP<=t`E5Sjj`Wd#p(Y8<-6^9P)i30Q3ku4lqdlJdnf?_P)h>@6aWYS2mtq7PCozu z0000000000000XB003iQbYX07XD)bcZ0%iHbK6F?e!o@sKdAH`3cUbmu$-BaWy#T8 z$xcLhl02zE5|j{w1PcHoDZl{B%aYHF6U-2p`X!Q~KD+$%kU_IQxnIoDNhX2v1MWmLE+5@8#_?+{>mj zT=>D5e$z5+>-1#m&;03PI#|r-VU&2||1Q6cdPe%~e5?;=6{;U5o|`NpZ#9SF=bz$H znE&GSWbP(UKc5yq_2TErY4MHp%`<8z zGk5C!d>TfNXKsw>(oN|!&*+Lh(ltNxXNmV1xe1;7+4C1=dLLgvRrr{X2 zZJMVivHv*BW_I!9jlRc=DP1@S2mqEz@af5lI;B7MCMW(lBi(x=-784?!bq${3yvCt=LNz^Rsl0_ijYD zr*WPv6En{fG=Kwa3mH)AJZ1FyWqOjY$z3tOwbe+nnVnrcx&AD6pX&=l=pC{hhXBN0 z7>p4n5Icta!t`>|{W@u>LzkZ4^yJ=paqK>NS79`DlSKHsiOuY@n|ROetLm7W<|up7 zMCK@er3C3#jx*~pc6w&hr96wg$IF-bRTNH_OmBHwO&gl@l0w9}#sJy|#`Ky?oz`ss znnc;}e3yN%Ep}ZUZQt0bKb;5O*|)*v!{Gk>;{WdN&Mz)Yn}I3Ugf_AWb29*-4A-%2 z2g$LHjA*}3l>NdV!!YpN*|rykO2V_c^k1%uZ5q(VrpaNbz;K8|ATk}PRblBH(!%@Z z>*vpxcMn(R7x!Q9{%lv{(9#wP1iCpe9ig~t7R ze<~oED-*x|noUM*{*@;)VhYby7D4QJvGrQBiQJRV-$d>s%uU=tfU!MdQ(|q8*pvKa z<~}d>M!Qj_1NvIQ*{92o-#$023IZULgCN6(&_IR-q&P@Ms$Y;|zn?F#nk_TcOf!3# zW`FL(LCWQ-tduGO2m-dyb1)*!|o|Q>m)QN+wAE zCf((HpyJJRMjK>WQwzg!bYcBf`?7iM^@vpjZ?@(6awzY4d1Z4wU2J73_t!ic;bTTA zJ0e?B(<-mV=P6MnV1kwwK-cgQRq!dv8$VWtw$`k>+E7g+TGzIdx?)_x*omf1f5LyA z(Kp8b86>ZP*AxyWfXKjXiwN1!fF_2HoJEvh_l$JEL0X>W`Lj52XNgbsbk)mS*l1F4 zaEn~5tm)AQK6w*==BLp1$Jf+s+$Kwsyc(K%_gZSd_+I2jqbGml24d`6ke>>vU_$J& z1q1qU*_xQnueQaVnGl>?_iGfsyt1EY0mwKHM&XR-*8ptA8vz?)6G9#MzCu@Odn$%Z zH_*E5c=?omKfn8;H^SBV{kgK1mk)7yclY&9Z;0zJS6?@dKp*Wrc`xo`n2NZl>=FEw z{(tLUkHhv*q}XpsVXj<61d!#hEnrM6U=d=;IT&l&xjf>liN9{aKUuM?YOV!nT5La$ zWg$p4N}|Bl`W1q^K@(Ih%eBM(9=Y?m7qu{+#K{pfzCT$&mO>4$$8p^hy)9OWD8mLAG>0h~iK;K?YOM4qX zqrF*}_!DXdpkbX&B zB(o>*9z}Ci19B6Ohl>~-E&^;wEeH%WR=Z602#ahP7D+)3H=Zog2>2d-_cTI3^0Ga- zb!V;y056HGR40+)fCad6olZs%zug(cvYw-C|GMXah~pftdJ6`nS~W(FX)y1&1u%wG zosbOa&chE2Qu4cXJ$T4&_UB7wY%d$<$&1uoHg6%6H`iY-AHICM{djpd&{9I=LI>qy zJGBP;bbWVuasQA{aN}58^T>1=(s=GiSAI~Jc`FSA^Ku+)2#ATGTyS*N7f_|V&Ltuh zn7HmH41T|Q82s|}?*8H1ow96E7MRs&xa@5{`SILMMo+R&e_bT=Me=JDK1OsFVhi(0 z#2=kg$&$HVsl_(!r+YL=BF~+wJ*)QAZ8%nDS=s*3{e2J<|2h4!7k^K}dH)NwSpW9U z=kuU2Ugh>hD=A7@M3;AVLqz4;o!v$h&e}twrGzv$qItX4Vi?NyiIyfZNi#qhnh4tq zYk|~VVGv2QHJ5J%siP)x)I|0|6RE}l*X{0Q_KVFy!vVn<07udd%8tHezwX?PV((g0 z=V&1sOfvTMH=P(QIq=?(zqp(2eC;S!i8!xp&#r>dZ7U>9Fox^PVmcJKK_a1E7gUhN zo9|+4@v~hAjqO>a*FJ=~MvGMGY4|DF6$&86mH{Ae6tc(qIi=l)6mW<8k-QKs80<@% zkD4a9E-?%LF|j%MqQoF3aZ-7zYP7Sc9|7wv1J-}ddozDA&0y-cdK}xqsgLOLW{tLD zEvZCqH~7w+60`hlPL-ezTTM``i(fSKh1uNU>< z0F($~ViQ8Yp%p5#q^hDp%hdCkzAT?!Rn`%U2q=e)fAUA^#NB9f@eu~SjT>BgMpG-4 zQ7qTLpcY#xz0mnEP9vTYLB3I63i^-s;i2*kphV0oW^t>$iirU@MzV=9aR`#+D&=d? zB+cJz`n);mYvuLG$Bt!0m$0AkY3>pXVNNjfDPP~Xui;|z0joWBKA(HDu^@_BCMW!0 zS4r-LLeofcXA41OIHqBnz>*84sOV1WW_K6h*5Lm9?*8Ha`u4K62p9uSKstcX1jqrB z5O40^Dv{H601oFv*y6u`(b{lj(xVggTgZ1?x*|8tMA*MsWgyeAMvJ7Pqm$$BxkS?|^ zvMcHFNTZS_i)fJ>4kgnom|!=iLMF$y<6P#;NaN&@nA+$W`5{2&R3~h~Bj2GRp@T zKnxLEO~w!+Xd`IJIiJc(YKun39_5KV?!>Gvf`rqHOPRr_do3s_qmNmBt4e zWL7bJ26KPp-S|Upju*S+2DOMR3lk_AYgSR)>!govb6K}}oUY&`N8NgXKP>yOE<$?G zUX&yCwp&JL)5{#wyjs5XUkAKU8IA=7%oHAWh=nvR{2NvE<)njj+)+YpJbege!)qQ7 zehUXb#rPAtuG7pVS_`bEmK_K1HX#AjOwcG+UnMNRk)t4-ZC3GjS-v}G^bN-*TYsS1 zg8}HLk{S7Bk+L_4RQ-V6dTD8osLo)QF|7tWKnJJw2`oT>-XW_I|ABNW`p7hY`O-QZ zj{4Ik2QMwfmqBwe@%h~WBz-gcDALS*7%Idg(cb;I^; z*A@w~o)Ovq-!3n+uaJoxV6hDnU|J5gfk{-dqcYlk-VE4FA)H*?Mk||u-P__ov;u*5 z&^^#=_52pQ0%#~5b1DOxS@KK01F@qQQy>Q$Hj)cq$Yf!ao;omA#e!Nma;ii-Je>QI z9h!C`=lHfvhhq@}j4TUVM5SlWvX)bc><@&&>_<9iK7?{J`2E9k-WejFF>k6c(RZIU z^Faxx+X@7R0Zl^=FwT~IMM}p~YflyKlHFZjgh3dk@klp~D!D7ZN7#JNCWt`G1R5pm znB?Cj)>x^Ln@0V6QP%L~(Th-q2{z#W0*2&76;eGM*_Z%B1O8t-5BAEWq55NQnmAe&gSXjRPC=CsxZ7IsWxLyfE^0ol_w!`Euj zvU_H@AG&#(5J4$t*Kc_@>h%W!FM`3c)BYinfMLOyDir0nZWqa*?KFqpRhh{k%)NL<0;!rY0WY-b*3Sh5-XIPDOd+^myIXiqT$S|h9z87=s}#6YRiP! zwlLJF%2%O#v{(<)Vh!@zNtuAuY&)Z@+`Y}C?HWk?32Rl-#uo9@$3>Eav$aWMn3E@* zY7YQPHo>gqjIrsf7`-x;=Tby$J`CJp-1BC1kW%j0CLaw>KpaiFE($zpc;)&V=$!3|>4phb0mgKXMa zr_W^#hm0>=a%(uSx0^s>I|cxf!eUv&p-NN!@*e~F#U^&F;h^Q%KaXE$BfeALGT7EM za%|gy2DUVUnHsd;qv=yE2N%x9e&*x}J}xE`22uwNUK1Wb7u&#+1%|5?;hNOt*-156 zQG+k#1FdR%YUpgs_sd^OZ)(zQa8xIz!Z31vQCqUzg;C0?f%WUY+q;<6SgPN`RVWi1 zFSm$g+5}^bP^Jpm&djW~_Kd+Yy4nsuN)@IisGF`b$UH3ywkW8!IZ--0|k0rpY}h<A-rOA3d zn}mFel~dN=rsk>v2$XZ@u1wYn&7;M7v{?7VV$I?lib^$&FcMB*qO{a3hL){Rnzx`N ze{j{4n#GgJ1(Ot8%{jkX%tk^jy2TTt#a>o4AH7rYeIS|8qSw1qX)Kf=y_yIeSMmpK=| zc)?t*Q`)tj8ZM{HD|cvXzI~B3pRTyZ8fFci(lJDXMO1peschZ4nh2b&+lkCyGc$jE zKesJ_K{b!)k`Gt}0vni)%|^d%%QPV|Y=tVdbsy|*eSq7<2)o+CTaQk0%qT)(BMTu; z7f^x_<>FYf_gB6y6@t6nze9G;=JzkX1^FSX?cmY@WwH|+)7qJ)fefW9*dV%-`z05R zu?KMeejFJ_k&t?39jCfDRozTvnT|^Q27{_P*fdQ!NWNd2?ZocGm5v8`rw6+5fKc+g*XY~S9^gEwd1(iL7rLLO&Hrr82;y#=qN^cXsT@^T2)WfAo{7JOAV+t{!Qz2!s^Tn0ay9DQ9W&7aJi61pV{RgFo{R zq|u1z+itkA%dMgU?QwIy-urZY)|^`PIyZzrcB&W=}TIpD9={+y%T1oG4`|n&-ylLyb4Yj+r+jd+oxCBV%JIYS30I& zokaAVp2O&S0YWc1S~|=&T>*mgk?;}7jJPkD`KCFa5972#NwVCX4VbzHgUI7D8V-0i zLxwix9KdiSGc`*AAiE1d;nk67#il*{vJ-`%es-f-Gc<`~cp}q)hzb)~pduI0IjP+p zh+{;O;9znQN$;Z5<)3cQ%$}hdk%}x)b?+Y{AlOI}G@p8Ifg;`)1Xlim7)m>&0+<{E(WH7PW4$Tph-;-p!dn z*Y;(OCB(0k@U(y7&fTFO_(CCx+HPx6hTL9kcd+V7)*FShnV059#o0WHrN3DIc2^)h z{o!sgOZ=&q##c8(6d>pPIfjXBh~*sIh5lNlxbB!NTZ;%qM|G@gsnilxIp!NMiG0yC z#@JR>FPZpN);{anA63xuQ~tM9V;ecZb8C!eHxa5;e~I^!T=`y5uW_>~vTCv6B8W^2 zP~M^v7_;)2)M@WjIXq0&CO_Y4tMs?!>@0NPHmsCY=N=hO6-Yuwkp;9W^mAr#G)%T{ zm~>uu|FG?py(kW!x3aNUtEgD`*Sk73>%pM z*+fpTRqo&3EI^~5UOk49nx>pWAf^e7w2Y}rL`3k`K2PZm1xnrR)Q8-v|1+X|^Kb9d zVjlP-H%ZNihO2`eqymVYRw+P+LKWpE(N-gv+Dk9nlaQj^_0R;<8(q2Cst$zMNE`LZ zsyaLjf3N&9*(ol$#2X%bPA-HA2&UW@Dl{A^QQKF)``5wk)%sjN_EABW<4~EKMN%r_ ze+|-y99dE$6UNg?InS+Gn24)RaUSo?I=V_(*9HAJY{nn1iiWeLEin;tOnQAZ%F8gQ zAI+kJD;=Ij`2Dq*eILGNIicBh_Af?DTKW=XZ;pn@Utmt*d^1PRurdxVdWRMeW4KX361(jNJuh?CM^0qnM z!EvT)w)hqpnL>8>JF39>iK7Rs3jc9>P+7z@ECe*ttm>5cVrH_47P(t1?u`pk16R-M z9g(9Np>tWeyOULk=!J`9zQ~FuroSv!>7EX$?2>#Bfa{(p8q30Jzq`@wl3Q4f1G?D& z+gZf`wZdu@nmci=AHx`B2K!M8t5tQzG{BshguHSfnjF`Jg9x+%u{D%%2KBDPR@AeX zYrzXou0a1>g)j`&d5Z>Jl;^#7DbxF_P?pifKfhhqz^?|p$Z=;8s!kGYn{p~;8D4DC zmKO=T=x^F?Hue@O(F&*Ocy8=QJ@>mibZ2hwb>ZiFA;;cYd-Mgz62)9Qifyd4)2w2u z5F$=02nGmSmLchbX}0e?Vhf!c6?+f}R|{Q1ELf{>`f-ZHTNn^%+O~%AzGf19OR>X1 zoOcsrw_8bGbR}Q1|q*s@%i)wonWaMOIx8iQHY z6+;}{Jk|Cb2WjBu4chG2&ylB^=Tt2DnfYkGahmwlp@NFd6*&;W&fH3^HnKYe8ksMn z_EcpFI*aZ<2+_7I=~2x&TB+uo9_bFj7*m^7A?avEOa9c<>VRmRKk zgrNt9hBtIAi|Ugeg64Pxtq}%e8(R>Q(TG^k8XJafx#np2ke#T9;zT_R=8>Oxl~@Q9 z5X$tyhv~l`= zlUy+M0)HqJ6e}^t?f~N4w9ZOhXGtA#GY-vUyWYpdGFwb>n~MI~Det&!m)^bVm34B5 zrf9&GPd8(xh3Of(96lMD8@VA-Vd5OYnW`b$4CiKioHp6#&#b2*9rb?pIX?Z`TQ;QZ z-Z@9x}C3DR6Yj=%%dx?8?_@@{1 zr)1Q;I(PZ>w_LL#@qT~Lu!C(==bU+VltD`K8MlmO+#GYK)Znm<`&hQeD|)Q^=@Hd& z0yqkFDfN23Q~8{0S=HMAOB|eb-@3o4K+UN4TWYa&JMWL6xW0}?#S585Cl}q{<-XZg zwCJO3?%Vq%w=Htlq!1SUr%L;E?;HS*()?)p zXD7yG`|EMpX1m>A#cnP2EZnbLCG;}Fyl&6>vl~7iS`sOJ#^C;XtwsDiXEa~)Pt3LE zW8`|3(mJo{SA5^!T*kw9d^0EIJoQ$r`Z@!6t@;@c-> zZUt|i`0-rkx+H6ciWybIxapZ09}nvN_F{qk?_I?5eWFa*McXwMG`D z6)*jB7*B_1i>`jXXJWVI?O?GG!Ht==#(B*CeeqXR+TJE;NHPZ9+-7vsZ)V_Ljhlks zrnmgvX?pjbvE6^8v;S&3XD41`vknkm@>DE0L0jXqt=G!D7 z=jQlx^-ZVe)a#py+)-HO{vh8x;zGY{wN2T|vrhW^+gJWg+QHFt=|k`Xt97|O8+d!R z9A@3HS5U%NZ5?~gb!nj&PVF;?qM#C{w;(HpmU{&f4FasPg{cgpMh zuiW;>y!P;O!N0%%GlC`%`!{waamX+**gJqG5E+?77!Xs4$dikpDMVC|lbKYMSX8N3 kT%Lxm5!p~wjc0-Llt?p?0p6@^AXAxuun0)=xiSJt04W+moB#j- literal 0 HcmV?d00001 diff --git a/lib/iotashan-oath.swc b/lib/iotashan-oath.swc deleted file mode 100644 index 28d6030780eda6a439240f397f281afad4600999..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6299 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 diff --git a/net/systemeD/halcyon/WayUI.as b/net/systemeD/halcyon/WayUI.as index b2e231d..55eec1a 100644 --- a/net/systemeD/halcyon/WayUI.as +++ b/net/systemeD/halcyon/WayUI.as @@ -588,7 +588,7 @@ package net.systemeD.halcyon { var t1:Number = (pathlength/2 - tf.width/2) / pathlength; var p1:Array=pointAt(t1); var t2:Number = (pathlength/2 + tf.width/2) / pathlength; var p2:Array=pointAt(t2); - var mult:Number = (Capabilities.playerType == "Desktop") ? Number(nameformat.size)*1.8 : 1; + var mult:Number = (Capabilities.playerType == "Desktop") ? Number(nameformat.size) : 1; var angleOffset:Number; // so we can do a 180ยบ if we're running backwards var offsetSign:Number; // -1 if we're starting at t2 diff --git a/net/systemeD/halcyon/connection/Connection.as b/net/systemeD/halcyon/connection/Connection.as index bf66a51..04df4ec 100644 --- a/net/systemeD/halcyon/connection/Connection.as +++ b/net/systemeD/halcyon/connection/Connection.as @@ -39,11 +39,6 @@ package net.systemeD.halcyon.connection { public function set apiBase(api:String):void { apiBaseURL = api; } public function get apiDomain():String { return apiBaseURL.replace(/(^https?:\/\/.+?\/).+$/, "$1"); } - // OAuth getters - public function get oauthRequestToken():String { return apiDomain+"oauth/request_token"; } - public function get oauthAccessToken():String { return apiDomain+"oauth/access_token"; } - public function get oauthAuthURL():String { return apiDomain+"oauth/authorize"; } - // connection events public static var LOAD_STARTED:String = "load_started"; public static var LOAD_COMPLETED:String = "load_completed"; @@ -640,21 +635,22 @@ package net.systemeD.halcyon.connection { top:Number, bottom:Number):void { } public function loadEntityByID(type:String, id:Number):void {} - public function setAuthToken(id:Object):void {} - public function deleteAuthToken():void {} - public function setAccessToken(key:String, secret:String):void {} public function createChangeset(tags:Object):void {} public function closeChangeset(callback:Function=null):void {} public function uploadChanges(closeAfterwards:Boolean=false):* {} public function fetchUserTraces(refresh:Boolean=false):void {} public function fetchTrace(id:Number, callback:Function):void {} - public function hasAccessToken():Boolean { return false; } public function fetchHistory(entity:Entity, callback:Function):void {} - public function loadEntity(entity:Entity):void { loadEntityByID(entity.getType(),entity.id); } - + + // OAuth2 support + public var oauthAccessToken:String; // Access token, should be set either from SharedObject (cookie) or from osm.org response + public function setAccessToken(token:String):void {} + public function hasAccessToken():Boolean { return false; } + public function getAccessToken():String { return ""; } + public function deleteAccessToken():void {} } } diff --git a/net/systemeD/halcyon/connection/XMLBaseConnection.as b/net/systemeD/halcyon/connection/XMLBaseConnection.as index 967e143..97e8e08 100644 --- a/net/systemeD/halcyon/connection/XMLBaseConnection.as +++ b/net/systemeD/halcyon/connection/XMLBaseConnection.as @@ -4,7 +4,6 @@ package net.systemeD.halcyon.connection { import flash.system.Security; import flash.net.*; - import org.iotashan.oauth.*; import net.systemeD.halcyon.MapEvent; import net.systemeD.halcyon.connection.bboxes.*; diff --git a/net/systemeD/halcyon/connection/XMLConnection.as b/net/systemeD/halcyon/connection/XMLConnection.as index 07d8c1d..92f8f7d 100644 --- a/net/systemeD/halcyon/connection/XMLConnection.as +++ b/net/systemeD/halcyon/connection/XMLConnection.as @@ -5,7 +5,6 @@ package net.systemeD.halcyon.connection { import mx.rpc.events.*; import flash.system.Security; import flash.net.*; - import org.iotashan.oauth.*; import net.systemeD.halcyon.AttentionEvent; import net.systemeD.halcyon.MapEvent; @@ -95,55 +94,12 @@ package net.systemeD.halcyon.connection { private function mapLoadStatus(event:HTTPStatusEvent):void { } - protected var appID:OAuthConsumer; - protected var authToken:OAuthToken; - override public function setAuthToken(id:Object):void { - authToken = OAuthToken(id); - } - - override public function deleteAuthToken():void { - authToken = null; - var obj:SharedObject = SharedObject.getLocal("access_token","/"); - obj.setProperty("oauth_token", null); - obj.setProperty("oauth_token_secret", null); - try { obj.flush(); } catch (e:Error) {} - } - - override public function hasAccessToken():Boolean { - return !(getAccessToken() == null); - } - - override public function setAccessToken(key:String, secret:String):void { - if (key && secret) { - authToken = new OAuthToken(key, secret); - } - } - - /* Get the stored access token, or try setting it up from loader params */ - private function getAccessToken():OAuthToken { - if (authToken == null) { - var key:String = getParam("oauth_token", null); - var secret:String = getParam("oauth_token_secret", null); - - if ( key != null && secret != null ) { - authToken = new OAuthToken(key, secret); - } - } - return authToken; - } - - private function getConsumer():OAuthConsumer { - if (appID == null) { - var key:String = getParam("oauth_consumer_key", null); - var secret:String = getParam("oauth_consumer_secret", null); - - if ( key != null && secret != null ) { - appID = new OAuthConsumer(key, secret); - } - } - return appID; - } + // OAuth2 config + override public function setAccessToken(token:String):void { oauthAccessToken = token; } + override public function hasAccessToken():Boolean { return oauthAccessToken != null; } + override public function getAccessToken():String { return oauthAccessToken; } + override public function deleteAccessToken():void { oauthAccessToken = null; } // note you may need to blank the SharedObject too private var httpStatus:int = 0; @@ -207,21 +163,14 @@ package net.systemeD.halcyon.connection { dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Couldn't close changeset", 1)); } - private function signedOAuthURL(url:String, method:String):String { - // method should be PUT, GET, POST or DELETE - var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1(); - var oauthRequest:OAuthRequest = new OAuthRequest(method, url, null, getConsumer(), authToken); - var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING); - return String(urlStr); - } - private function sendOAuthPut(url:String, xml:XML, onComplete:Function, onError:Function, onStatus:Function):void { // build the request - var urlReq:URLRequest = new URLRequest(signedOAuthURL(url, "PUT")); + var urlReq:URLRequest = new URLRequest(url); urlReq.method = "POST"; if (xml) { urlReq.data = xml.toXMLString(); } else { urlReq.data = true; } urlReq.contentType = "application/xml"; - urlReq.requestHeaders = [ new URLRequestHeader("X-HTTP-Method-Override", "PUT"), + urlReq.requestHeaders = [ new URLRequestHeader("Authorization", "Bearer "+oauthAccessToken), + new URLRequestHeader("X-HTTP-Method-Override", "PUT"), new URLRequestHeader("X-Error-Format", "XML") ]; var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, onComplete); @@ -231,8 +180,9 @@ package net.systemeD.halcyon.connection { } private function sendOAuthGet(url:String, onComplete:Function, onError:Function, onStatus:Function):void { - var urlReq:URLRequest = new URLRequest(signedOAuthURL(url, "GET")); + var urlReq:URLRequest = new URLRequest(url); urlReq.method = "GET"; + urlReq.requestHeaders = [ new URLRequestHeader("Authorization", "Bearer "+oauthAccessToken) ]; var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, onComplete); loader.addEventListener(IOErrorEvent.IO_ERROR, onError); @@ -266,9 +216,9 @@ package net.systemeD.halcyon.connection { // build the actual request var serv:HTTPService=new HTTPService(); serv.method="POST"; - serv.url=signedOAuthURL(url, "POST"); + serv.url=url; serv.contentType = "text/xml"; - serv.headers={'X-Error-Format':'xml'}; + serv.headers={'X-Error-Format':'xml', "Authorization": "Bearer "+oauthAccessToken}; serv.request=" "; serv.resultFormat="e4x"; serv.requestTimeout=0; diff --git a/net/systemeD/potlatch2/dialogs/OptionsDialog.mxml b/net/systemeD/potlatch2/dialogs/OptionsDialog.mxml index 57177fe..6b652ea 100644 --- a/net/systemeD/potlatch2/dialogs/OptionsDialog.mxml +++ b/net/systemeD/potlatch2/dialogs/OptionsDialog.mxml @@ -57,11 +57,11 @@ - - + + - + @@ -152,14 +152,14 @@ } private function doLogout():void { - conn.deleteAuthToken(); + conn.deleteAccessToken(); logout.enabled = false; } private function doReset():void { - conn.deleteAuthToken(); - userState.setProperty("oauth_consumer_key",null); - userState.setProperty("oauth_consumer_secret",null); + conn.deleteAccessToken(); + userState.setProperty("oauth2_client_id",null); + userState.setProperty("oauth2_client_secret",null); dataServer.text="https://www.openstreetmap.org/api/0.6/"; overviewServer.text="https://tile.openstreetmap.org/"; dataServerSet(); overviewServerSet(); @@ -173,13 +173,13 @@ private function overviewServerSet():void { userState.setProperty("overview_tiles",overviewServer.text); flush(); } - private function oauthKeySet():void { - userState.setProperty("oauth_consumer_key",oauthKey.text); flush(); - conn.deleteAuthToken(); + private function oauthIdSet():void { + userState.setProperty("oauth2_client_id",oauthId.text); flush(); + conn.deleteAccessToken(); } private function oauthSecretSet():void { - userState.setProperty("oauth_consumer_secret",oauthSecret.text); flush(); - conn.deleteAuthToken(); + userState.setProperty("oauth2_client_secret",oauthSecret.text); flush(); + conn.deleteAccessToken(); } ]]> diff --git a/net/systemeD/potlatch2/save/OAuthPanel.mxml b/net/systemeD/potlatch2/save/OAuthPanel.mxml index f6752d5..96cc71c 100644 --- a/net/systemeD/potlatch2/save/OAuthPanel.mxml +++ b/net/systemeD/potlatch2/save/OAuthPanel.mxml @@ -1,159 +1,113 @@ - + xmlns:s="library://ns.adobe.com/flex/spark" + title="Sign in to OpenStreetMap" + creationComplete="init()" + width="900" height="700" > - - - - - - - + + + + + + + + + + + + + -1) { - monitorTimeout = setTimeout(monitorForCallback, 100); + var oauth2:OAuth2 = new OAuth2( + getAuthorizeURL(), + getAccessTokenURL(), + LogSetupLevel.WARN); + var grant:IGrantType = new AuthorizationCodeGrant( + stageWebView, + getClientID(), + getClientSecret(), + getCallbackURL(), + "read_prefs write_prefs write_api read_gpx write_gpx write_notes openid"); + oauth2.addEventListener(GetAccessTokenEvent.TYPE, onGetAccessToken); + oauth2.getAccessToken(grant); + } + + private function onGetAccessToken(getAccessTokenEvent:GetAccessTokenEvent):void { + stageWebView.dispose(); + removeEventListener("render", onRender); + if (!getAccessTokenEvent.accessToken) { + connection.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Couldn't log in - check your username and password")); + return; + } + connection.setAccessToken(getAccessTokenEvent.accessToken); + // also has .tokenType (="Bearer"), .scope (=requested scope) + dispatchEvent(new Event(ACCESS_TOKEN_EVENT)); + PopUpManager.removePopUp(this); + } + + private function onRender(event:Event):void { + var rectangle:Rectangle = viewPort.getBounds(stage); + if (rectangle.width > 0 && rectangle.height > 0) { + stageWebView.viewPort = rectangle; } - // otherwise, if we're not on OSM nor the callback, give up } - - private function getResponseToken(loader:URLLoader):OAuthToken { - var vars:URLVariables = new URLVariables(loader.data); - - // build out request token - var token:OAuthToken = new OAuthToken( - String(vars["oauth_token"]), - String(vars["oauth_token_secret"])); - return token; - } - - private function getAccessToken():void { - tryAccessButton.enabled=false; - clearTimeout(monitorTimeout); - var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1(); - var consumer:OAuthConsumer = getConsumer(); - var oauthRequest:OAuthRequest = new OAuthRequest("GET", connection.oauthAccessToken, - null, consumer, requestToken); - var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING) - var urlReq:URLRequest = new URLRequest(String(urlStr)); - var loader:URLLoader = new URLLoader(); - loader.addEventListener(Event.COMPLETE, loadedAccessToken); - loader.addEventListener(IOErrorEvent.IO_ERROR, accessTokenError); - loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus); - loader.load(urlReq); - } - - private function loadedAccessToken(event:Event):void { - oauthHTML.htmlLoader.loadString("Successfully logged into OpenStreetMap"); - trace("Yay! response: "+URLLoader(event.target).data); - PopUpManager.removePopUp(this); - - _accessToken = getResponseToken(URLLoader(event.target)); - connection.setAuthToken(_accessToken); - dispatchEvent(new Event(ACCESS_TOKEN_EVENT)); - } - - public function get accessToken():OAuthToken { return _accessToken; } - public function get shouldRemember():Boolean { return rememberMe.selected; } - - private function cancelOAuth():void { + public function get shouldRemember():Boolean { return rememberMe.selected; } + private function cancelOAuth():void { + stageWebView.dispose(); + removeEventListener("render", onRender); PopUpManager.removePopUp(this); - clearTimeout(monitorTimeout); } - private function accessTokenError(event:IOErrorEvent):void { - tryAccessButton.enabled=false; - if ( lastHTTPStatus == 401 ) { - oauthHTML.htmlLoader.loadString("Sorry, access was denied. Please check and try again."); - } else { - oauthHTML.htmlLoader.loadString("Sorry, an error occurred ("+lastHTTPStatus+". Please try again."); - } - } - - private function getConsumer():OAuthConsumer { - var key:String = connection.getParam("oauth_consumer_key", ""); - var secret:String = connection.getParam("oauth_consumer_secret", ""); - return new OAuthConsumer(key, secret); - } + private function getAuthorizeURL():String { return connection.getParam("oauth2_authorize_url", ""); } + private function getAccessTokenURL():String { return connection.getParam("oauth2_access_token_url", ""); } + private function getCallbackURL():String { return connection.getParam("oauth2_callback_url", ""); } + private function getClientID():String { return connection.getParam("oauth2_client_id", ""); } + private function getClientSecret():String { return connection.getParam("oauth2_client_secret", ""); } ]]> - + diff --git a/net/systemeD/potlatch2/save/SaveManager.as b/net/systemeD/potlatch2/save/SaveManager.as index 9900d2a..16e5b7a 100644 --- a/net/systemeD/potlatch2/save/SaveManager.as +++ b/net/systemeD/potlatch2/save/SaveManager.as @@ -9,7 +9,6 @@ package net.systemeD.potlatch2.save { import mx.events.CloseEvent; import net.systemeD.halcyon.connection.*; import net.systemeD.potlatch2.controller.*; - import org.iotashan.oauth.*; public class SaveManager { @@ -64,11 +63,9 @@ package net.systemeD.potlatch2.save { oauthPanel.setConnection(_connection); var listener:Function = function(event:Event):void { - var accessToken:OAuthToken = oauthPanel.accessToken; if ( oauthPanel.shouldRemember ) { - var obj:SharedObject = SharedObject.getLocal("access_token","/"); - obj.setProperty("oauth_token", accessToken.key); - obj.setProperty("oauth_token_secret", accessToken.secret); + var obj:SharedObject = SharedObject.getLocal("oauth2_access_token","/"); + obj.setProperty("access_token", _connection.oauthAccessToken); try { obj.flush(); } catch (e:Error) {} } onCompletion(); diff --git a/potlatch2-app-cpu.xml b/potlatch2-app-cpu.xml index 0af40df..6868996 100644 --- a/potlatch2-app-cpu.xml +++ b/potlatch2-app-cpu.xml @@ -1,6 +1,6 @@ - + net.systemed.potlatch - 3.0 + 3.1 Potlatch resources/potlatch2.swf diff --git a/potlatch2-app-linux.xml b/potlatch2-app-linux.xml index e5cb15a..f0d6e6e 100644 --- a/potlatch2-app-linux.xml +++ b/potlatch2-app-linux.xml @@ -1,6 +1,6 @@ net.systemed.potlatch - 3.0 + 3.1 Potlatch resources/potlatch2.swf diff --git a/potlatch2-app.xml b/potlatch2-app.xml index 84b9afd..b397f5e 100644 --- a/potlatch2-app.xml +++ b/potlatch2-app.xml @@ -1,6 +1,6 @@ - + net.systemed.potlatch - 3.0 + 3.1 Potlatch resources/potlatch2.swf diff --git a/potlatch2.mxml b/potlatch2.mxml index 378719e..753a08a 100644 --- a/potlatch2.mxml +++ b/potlatch2.mxml @@ -146,7 +146,7 @@ private var mouseTimer:Timer; private var resizeTimer:Timer; - public var version:String="3.0"; + public var version:String="3.1"; include "build_date.as"; private function startInit():void { @@ -220,8 +220,11 @@ var params:Object = { api: "https://www.openstreetmap.org/api/0.6/", connection: "XML", - oauth_consumer_key: "8IJxvRqJ2b2Rgfv6RCf6Sw", - oauth_consumer_secret: "Ojod3JTQCPCPOQ3HZNlX5bxiRwLTtyzgcHCiTcyI", + oauth2_authorize_url: "https://www.openstreetmap.org/oauth2/authorize", + oauth2_access_token_url: "https://www.openstreetmap.org/oauth2/token", + oauth2_callback_url: "https://www.systemed.net/potlatch/oauth_callback", + oauth2_client_id: "__client_id_here__", + oauth2_client_secret: "__client_secret_here__", lat: 0, lon: 0, zoom: 2, @@ -338,8 +341,8 @@ floatingMap.y=36; // set the access token from saved cookie - var tokenObject:SharedObject = SharedObject.getLocal("access_token","/"); - conn.setAccessToken(tokenObject.data["oauth_token"], tokenObject.data["oauth_token_secret"]); + var tokenObject:SharedObject = SharedObject.getLocal("oauth2_access_token","/"); + conn.setAccessToken(tokenObject.data["access_token"]); // Load any requested GPX track if (loaderInfo.parameters['gpx']) {