@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
899 lines (896 loc) • 42.1 kB
JavaScript
import { filter, take } from 'rxjs/operators';
import { Http } from '../data/http';
import { Net } from '../data/net';
import { RpcLogClient } from '../rpc/log/rpc-log-client';
import { RpcLogSubjectServer } from '../rpc/log/rpc-log-subject-server';
import { RpcTelemetryClient } from '../rpc/telemetry/rpc-telemetry-client';
import { RpcTelemetrySubjectServer } from '../rpc/telemetry/rpc-telemetry-subject-server';
import { LogLevel } from './log-level';
import { LoggingConstants } from './logging-constants';
import { TelemetryControlType } from './telemetry-control-type';
/**
* Logging class.
* @dynamic
*/
export class Logging {
static logMaxRecordLength = 20;
static logMaxWaitTimeMs = 30000;
static telemetryMaxRecordLength = 100;
static telemetryMaxWaitTimeMs = 60000;
static testMode = 'test';
static instance;
static errorLogHeaderStyle = 'font-weight: bold; color: #E81123;';
static warnLogHeaderStyle = 'font-weight: bold; color: #FF8C00;';
static successLogHeaderStyle = 'font-weight: bold; color: ##288928;';
static infoLogHeaderStyle = 'font-weight: bold; color: #0078D7;';
static verboseLogHeaderStyle = 'color: #999';
logSubjectServer;
telemetrySubjectServer;
gateway;
http;
rpc;
logSet;
telemetrySet;
thresholdOfLogLevel = LogLevel.Informational;
verboseTelemetry = false;
/**
* Log a logging event.
*
* @param record the log record to send the gateway.
* @return Promise<any> settle to resolve if buffered.
*/
static log(record) {
return Logging.current.logInternal(record);
}
/**
* Log a critical logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logCritical(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Critical, source, message, params: params });
}
/**
* Log an error logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logError(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Error, source, message, params: params });
}
/**
* Log a warning logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logWarning(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Warning, source, message, params: params });
}
/**
* Log a success logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logSuccess(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Success, source, message, params: params });
}
/**
* Log a informational logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logInformational(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Informational, source, message, params: params });
}
/**
* Log a verbose logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logVerbose(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Verbose, source, message, params: params });
}
/**
* Log a debug logging event.
*
* @param source The record originator of event within a module.
* @param message The message of event.
* @param params The parameters object which will be serialized.
* @return Promise<any> settle to resolve if buffered.
*/
static logDebug(source, message, params) {
return Logging.current.logInternal({ level: LogLevel.Debug, source, message, params: params });
}
/**
* Trace a telemetry event.
*
* @param record the telemetry record to send the gateway.
* @return Promise<any> settle to resolve if buffered.
*/
static trace(record) {
return Logging.current.telemetryInternal(record);
}
/**
* Trace a user action telemetry event.
*
* @param data additional data.
* @return Promise<any> settle to resolve if buffered.
*/
static traceUserAction(data) {
return Logging.trace({
view: LoggingConstants.views.smeUIControl,
instance: LoggingConstants.empty,
action: LoggingConstants.actions.click,
data: data
});
}
/**
* Trace a telemetry event for a button click.
*
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceButton(controlName, controlId, nodeName, nodeType) {
return Logging.traceButtonClick(TelemetryControlType.Button, controlName, controlId, nodeName, nodeType);
}
/**
* Trace a telemetry event for a action pane button click.
*
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceActionPaneButton(controlName, controlId, nodeName, nodeType) {
return Logging.traceButtonClick(TelemetryControlType.ActionPaneButton, controlName, controlId, nodeName, nodeType);
}
/**
* Trace a telemetry event for a action bar button click.
*
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceActionBarButton(controlName, controlId, nodeName, nodeType) {
return Logging.traceButtonClick(TelemetryControlType.ActionBarButton, controlName, controlId, nodeName, nodeType);
}
/**
* Trace a telemetry event on a sme control.
*
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param additionalData Additional key value pairs that can be sent to telemetry.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceControl(controlType, controlName, controlId, additionalData, nodeName, nodeType) {
let data = {};
if (additionalData) {
data = additionalData;
}
data[controlType] = controlType;
data[controlName] = controlName;
data[controlId] = controlId;
const nodeId = Logging.getFormattedNodeId(nodeName, nodeType);
if (nodeId) {
data[LoggingConstants.dataFields.nodeId] = nodeId;
}
return Logging.traceUserAction(data);
}
/**
* Trace a telemetry event on click of an external link.
*
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param url External link that was clicked.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceExternalLink(controlName, url, controlId, nodeName, nodeType) {
const data = {
controlType: TelemetryControlType.ExternalLink,
controlName: controlName,
controlId: controlId,
url: url
};
const nodeId = Logging.getFormattedNodeId(nodeName, nodeType);
if (nodeId) {
data[LoggingConstants.dataFields.nodeId] = nodeId;
}
return Logging.traceUserAction(data);
}
/**
* Trace a telemetry event on click of an internal link.
*
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param route rpc route.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceInternalLink(controlName, route, controlId, nodeName, nodeType) {
const data = {
controlType: TelemetryControlType.InternalLink,
controlName: controlName,
controlId: controlId,
route: route
};
const nodeId = Logging.getFormattedNodeId(nodeName, nodeType);
if (nodeId) {
data[LoggingConstants.dataFields.nodeId] = nodeId;
}
return Logging.traceUserAction(data);
}
/**
* Trace a telemetry event when an async task returns.
*
* @param eventLocation UI location of the event being recorded.
* ex) createVmForm, RegisterAadDialog, stopServiceConfirmationDialog...
* @param eventLabel Description of what event is being logged.
* ex) 'Registered with Azure', 'Added a connection', 'Failed Azure backup'
* @param result Result of event.
* @param additionalData Additional key value pairs that can be sent to telemetry.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceAsyncResult(eventLocation, eventLabel, result, additionalData, nodeName, nodeType) {
let data = {};
if (additionalData) {
data = additionalData;
}
data[LoggingConstants.dataFields.eventLocation] = eventLocation;
data[LoggingConstants.dataFields.eventLabel] = eventLabel;
data[LoggingConstants.dataFields.result] = result;
const nodeId = Logging.getFormattedNodeId(nodeName, nodeType);
if (nodeId) {
data[LoggingConstants.dataFields.nodeId] = nodeId;
}
return Logging.traceUserAction(data);
}
/**
* Log a raw object into the console at debug level of mode.
*/
static debug(object) {
if (Logging.current.consoleLogLevel >= LogLevel.Debug) {
console.log(object);
}
}
/**
* Configure logging mode.
*
* @param thresholdOfLogLevel the log level for gateway.
* @param verboseTelemetry if true, optional telemerty will be collected.
*/
static configureLog(thresholdOfLogLevel, verboseTelemetry) {
Logging.current.thresholdOfLogLevel = thresholdOfLogLevel;
Logging.current.verboseTelemetry = verboseTelemetry;
}
/**
* Wrapper method for tracing a telemetry event for a button click.
*
* @param controlType Type of button that is being recorded
* @param controlName Describes which control was used.
* ex) For a button that says 'Set up' the control name can be 'Set up' to distinguish that from a cancel button.
* @param controlId Unique identifier when a control appears more than once. Specific to how implementation handles uniqueness.
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return Promise<any> settle to resolve if buffered.
*/
static traceButtonClick(controlType, controlName, controlId, nodeName, nodeType) {
const data = {
controlType: controlType,
controlName: controlName,
controlId: controlId
};
const nodeId = Logging.getFormattedNodeId(nodeName, nodeType);
if (nodeId) {
data[LoggingConstants.dataFields.nodeId] = nodeId;
}
return Logging.traceUserAction(data);
}
/**
* Format node information for telemetry
*
* @param nodeName the name of the node currently connected to (this.appContextService.activeConnection.nodeName)
* @param nodeType the type of node currently connected to (this.appContextService.connectionManager.activeConnection.type)
* @return string
*/
static getFormattedNodeId(nodeName, nodeType) {
if (nodeType && nodeName) {
return nodeType + '!' + nodeName;
}
else {
return null;
}
}
/**
* Gets the level of current logging.
*/
get consoleLogLevel() {
const global = window;
return (global.MsftSme && global.MsftSme.Init && global.MsftSme.Init.logLevel) || LogLevel.Warning;
}
/**
* Gets the session Id of shell.
*/
get sessionId() {
const global = window;
return global.MsftSme && global.MsftSme.Init && global.MsftSme.Init.sessionId || Logging.testMode;
}
/**
* Gets the name of current shell or module.
*/
get nameOfModule() {
const global = window;
return global.MsftSme && global.MsftSme.Init && global.MsftSme.Init.moduleName || Logging.testMode;
}
/**
* Initializes a new instance of the Logging class.
*/
constructor() {
this.http = new Http();
this.logSet = {
maxWaitTimeMs: Logging.logMaxWaitTimeMs,
maxRecordLength: Logging.logMaxRecordLength,
api: '/log'
};
this.telemetrySet = {
maxWaitTimeMs: Logging.telemetryMaxWaitTimeMs,
maxRecordLength: Logging.telemetryMaxRecordLength,
api: '/telemetry'
};
}
/**
* Gets the current logging instance.
*/
static get current() {
if (Logging.instance) {
return Logging.instance;
}
Logging.instance = new Logging();
return Logging.instance;
}
/**
* Flush the regular log set, immediately submitting the logs to the gateway
*/
static flushLogs() {
Logging.current.flush(Logging.current.logSet);
}
/**
* Flush the telemetry log set, immediately submitting the logs to the gateway
*/
static flushTelemetry() {
Logging.current.flush(Logging.current.telemetrySet);
}
/**
* Register Rpc object to logging instance.
*
* @param rpc the rpc instance.
*/
registerRpc(rpc, gateway) {
this.rpc = rpc;
this.gateway = gateway;
// start subscribing once after the rpc is ready on the shell.
this.rpc.stateChanged
.pipe(filter(active => active), take(1))
.subscribe(() => {
if (this.rpc.isShell) {
this.logSubjectServer = new RpcLogSubjectServer(this.rpc);
this.logSet.rpcSubscription = this.logSubjectServer.subject
.subscribe(data => {
this.logGateway(this.logSet, data.data);
data.deferred.resolve();
});
this.telemetrySubjectServer = new RpcTelemetrySubjectServer(this.rpc);
this.telemetrySet.rpcSubscription = this.telemetrySubjectServer.subject
.subscribe(data => {
this.logGateway(this.telemetrySet, data.data);
data.deferred.resolve();
});
}
});
}
/**
* Dispose the set of rpc forwarding pipes.
*/
dispose() {
this.disposeSet(this.logSet);
this.disposeSet(this.telemetrySet);
}
/**
* Log a record.
*
* @param record the log record.
* @return Promise<any> the promise object.
*/
logInternal(record) {
const now = new Date();
if (record.level <= this.consoleLogLevel) {
let headerStyle = Logging.verboseLogHeaderStyle;
if (record.level <= LogLevel.Error) {
headerStyle = Logging.errorLogHeaderStyle;
}
else if (record.level <= LogLevel.Warning) {
headerStyle = Logging.warnLogHeaderStyle;
}
else if (record.level <= LogLevel.Success) {
headerStyle = Logging.successLogHeaderStyle;
}
else if (record.level <= LogLevel.Informational) {
headerStyle = Logging.infoLogHeaderStyle;
}
const logSeparator = '--';
const customHeader = record.consoleGroupHeader ? ` ${logSeparator} ${record.consoleGroupHeader}` : '';
if (MsftSme.isEdge() || MsftSme.isInternetExplorer()) {
// Microsoft Edge and IE don't support styles in group headers. so remove if running on Microsoft Edge
// Since there is no color support, also log the severity
console.groupCollapsed(`${LogLevel[record.level]} ${logSeparator} ${this.nameOfModule} ${logSeparator} ${record.source}${customHeader}`);
}
else {
console.groupCollapsed(`%c${this.nameOfModule} ${logSeparator} ${record.source}${customHeader}`, headerStyle);
}
console.log(`logLevel: ${LogLevel[record.level]}`);
console.log(`timestamp: ${now.toISOString()}`);
if (record.message) {
if (MsftSme.isObject(record.message)) {
console.log('message:');
console.log({ ...record.message });
}
else {
console.log(`message: ${record.message}`);
}
}
if (record.params) {
console.group('params');
console.log({ ...record.params });
console.groupEnd();
}
if (record.localParams) {
console.group('params');
console.log({ ...record.localParams });
console.groupEnd();
delete record['localParams'];
}
if (record.stack) {
console.group('stack');
console.log(record.stack);
console.groupEnd();
}
console.groupEnd();
}
if (record.level <= this.thresholdOfLogLevel && this.sessionId !== Logging.testMode) {
if (MsftSme.isObject(record.message)) {
// ensure message is a string.
record.message = JSON.stringify(record.message);
}
const rpcRecord = {
...record,
...{ sessionId: this.sessionId, timestamp: now.getTime(), sourceName: this.nameOfModule }
};
if (this.rpc && this.rpc.stateActive && !this.rpc.isShell) {
return RpcLogClient.log(this.rpc, rpcRecord);
}
this.logGateway(this.logSet, rpcRecord);
}
return Promise.resolve();
}
/**
* Log a telemerty record.
*
* @param record the telemetry record.
* @return Promise<any> the promise object.
*/
telemetryInternal(record) {
if (((!this.verboseTelemetry && !record.optional) || this.verboseTelemetry) && this.sessionId !== Logging.testMode) {
const rpcRecord = { ...record, ...{ sessionId: this.sessionId, timestamp: Date.now(), sourceName: this.nameOfModule } };
if (this.rpc && this.rpc.stateActive && !this.rpc.isShell) {
return RpcTelemetryClient.telemetry(this.rpc, rpcRecord);
}
this.logGateway(this.telemetrySet, rpcRecord);
}
return Promise.resolve();
}
/**
* Dispose the set.
*
* @param set the logger set.
*/
disposeSet(set) {
if (set.rpcSubscription) {
set.rpcSubscription.unsubscribe();
set.rpcSubscription = null;
}
}
/**
* Log to the gateway.
*
* @param set the logger set.
* @param data the record data.
*/
logGateway(set, data) {
if (set.timer == null) {
set.buffer = [];
set.timer = setTimeout(() => this.submitRecords(set), set.maxWaitTimeMs);
}
set.buffer.push(data);
if (set.buffer.length >= set.maxRecordLength) {
clearTimeout(set.timer);
this.submitRecords(set);
set.timer = setTimeout(() => this.submitRecords(set), set.maxWaitTimeMs);
}
}
/**
* Flush a log set, immediately submitting the logs to the gateway
*/
flush(set) {
if (set && set.buffer && set.buffer.length > 0) {
this.submitRecords(set);
}
}
/**
* Submit records to the gateway.
*/
submitRecords(set) {
// Disabled gateway can't log anything in gateway.
if (set.buffer.length > 0 && this.gateway && !this.gateway.disabled) {
this.gateway.post(set.api, set.buffer, { maxRetryCount: 0 }).subscribe({
error: error => {
const message = MsftSme.getStrings().MsftSmeShell.Core.Error.LoggingUnableSubmit.message;
console.error(message.format(Net.getErrorMessage(error)));
}
});
}
set.timer = null;
set.buffer = [];
}
}
//# sourceMappingURL=logging.js.map
// SIG // Begin signature block
// SIG // MIIoNwYJKoZIhvcNAQcCoIIoKDCCKCQCAQExDzANBglg
// SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor
// SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC
// SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg
// SIG // M487nwVsJwQXU58G6WwhM82lgppyLSqWy5bhMHtegbCg
// SIG // gg2FMIIGAzCCA+ugAwIBAgITMwAABAO91ZVdDzsYrQAA
// SIG // AAAEAzANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV
// SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
// SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
// SIG // cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT
// SIG // aWduaW5nIFBDQSAyMDExMB4XDTI0MDkxMjIwMTExM1oX
// SIG // DTI1MDkxMTIwMTExM1owdDELMAkGA1UEBhMCVVMxEzAR
// SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
// SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
// SIG // bjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
// SIG // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
// SIG // n3RnXcCDp20WFMoNNzt4s9fV12T5roRJlv+bshDfvJoM
// SIG // ZfhyRnixgUfGAbrRlS1St/EcXFXD2MhRkF3CnMYIoeMO
// SIG // MuMyYtxr2sC2B5bDRMUMM/r9I4GP2nowUthCWKFIS1RP
// SIG // lM0YoVfKKMaH7bJii29sW+waBUulAKN2c+Gn5znaiOxR
// SIG // qIu4OL8f9DCHYpME5+Teek3SL95sH5GQhZq7CqTdM0fB
// SIG // w/FmLLx98SpBu7v8XapoTz6jJpyNozhcP/59mi/Fu4tT
// SIG // 2rI2vD50Vx/0GlR9DNZ2py/iyPU7DG/3p1n1zluuRp3u
// SIG // XKjDfVKH7xDbXcMBJid22a3CPbuC2QJLowIDAQABo4IB
// SIG // gjCCAX4wHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYB
// SIG // BQUHAwMwHQYDVR0OBBYEFOpuKgJKc+OuNYitoqxfHlrE
// SIG // gXAZMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQLEyRNaWNy
// SIG // b3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQx
// SIG // FjAUBgNVBAUTDTIzMDAxMis1MDI5MjYwHwYDVR0jBBgw
// SIG // FoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0w
// SIG // SzBJoEegRYZDaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
// SIG // L3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx
// SIG // LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYB
// SIG // BQUHMAKGRWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
// SIG // a2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8yMDEx
// SIG // LTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3
// SIG // DQEBCwUAA4ICAQBRaP+hOC1+dSKhbqCr1LIvNEMrRiOQ
// SIG // EkPc7D6QWtM+/IbrYiXesNeeCZHCMf3+6xASuDYQ+AyB
// SIG // TX0YlXSOxGnBLOzgEukBxezbfnhUTTk7YB2/TxMUcuBC
// SIG // P45zMM0CVTaJE8btloB6/3wbFrOhvQHCILx41jTd6kUq
// SIG // 4bIBHah3NG0Q1H/FCCwHRGTjAbyiwq5n/pCTxLz5XYCu
// SIG // 4RTvy/ZJnFXuuwZynowyju90muegCToTOwpHgE6yRcTv
// SIG // Ri16LKCr68Ab8p8QINfFvqWoEwJCXn853rlkpp4k7qzw
// SIG // lBNiZ71uw2pbzjQzrRtNbCFQAfmoTtsHFD2tmZvQIg1Q
// SIG // VkzM/V1KCjHL54ItqKm7Ay4WyvqWK0VIEaTbdMtbMWbF
// SIG // zq2hkRfJTNnFr7RJFeVC/k0DNaab+bpwx5FvCUvkJ3z2
// SIG // wfHWVUckZjEOGmP7cecefrF+rHpif/xW4nJUjMUiPsyD
// SIG // btY2Hq3VMLgovj+qe0pkJgpYQzPukPm7RNhbabFNFvq+
// SIG // kXWBX/z/pyuo9qLZfTb697Vi7vll5s/DBjPtfMpyfpWG
// SIG // 0phVnAI+0mM4gH09LCMJUERZMgu9bbCGVIQR7cT5YhlL
// SIG // t+tpSDtC6XtAzq4PJbKZxFjpB5wk+SRJ1gm87olbfEV9
// SIG // SFdO7iL3jWbjgVi1Qs1iYxBmvh4WhLWr48uouzCCB3ow
// SIG // ggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcNAQEL
// SIG // BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
// SIG // aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
// SIG // ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMT
// SIG // KU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv
// SIG // cml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcw
// SIG // ODIxMDkwOVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgT
// SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
// SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYG
// SIG // A1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0Eg
// SIG // MjAxMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
// SIG // ggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCq
// SIG // uAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJ
// SIG // DXlkh36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/X
// SIG // llnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3WraPPL
// SIG // bfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5
// SIG // pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJXtjt
// SIG // 7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7La4zWMW3
// SIG // Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwP
// SIG // rRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgotswnKDgl
// SIG // mDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I4iVd0yFLPlLE
// SIG // tVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9
// SIG // G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/C
// SIG // HFfbg43sTUkwp6uO3+xbn6/83bBm4sGXgXvt1u1L50kp
// SIG // pxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9A
// SIG // N0/B4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE
// SIG // 2rCIF96eTvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB
// SIG // 6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k
// SIG // 5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAwe
// SIG // CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB
// SIG // /wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h
// SIG // 6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov
// SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj
// SIG // dHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNy
// SIG // bDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0
// SIG // dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv
// SIG // TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDCB
// SIG // nwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYI
// SIG // KwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
// SIG // bS9wa2lvcHMvZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggr
// SIG // BgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABp
// SIG // AGMAeQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkq
// SIG // hkiG9w0BAQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuW
// SIG // EeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79H
// SIG // qaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS
// SIG // 0LD9a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32m
// SIG // kHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPyprWElj
// SIG // HwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMO
// SIG // r5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYKwsat
// SIG // ruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiyWYlobm+n
// SIG // t3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVA
// SIG // KmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX0O5dY0Hj
// SIG // Wwechz4GdwbRBrF1HxS+YWG18NzGGwS+30HHDiju3mUv
// SIG // 7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3
// SIG // HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs
// SIG // 6jeZeRhL/9azI2h15q/6/IvrC4DqaTuv/DDtBEyO3991
// SIG // bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYib
// SIG // V3FWTkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0x
// SIG // ghoKMIIaBgIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEG
// SIG // A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
// SIG // ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
// SIG // MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5n
// SIG // IFBDQSAyMDExAhMzAAAEA73VlV0POxitAAAAAAQDMA0G
// SIG // CWCGSAFlAwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgor
// SIG // BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE
// SIG // AYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBkmKDvLui5kpoQ
// SIG // ShqD1l1YNX0o1EpeOHCIqRONRtaTITBCBgorBgEEAYI3
// SIG // AgEMMTQwMqAUgBIATQBpAGMAcgBvAHMAbwBmAHShGoAY
// SIG // aHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3
// SIG // DQEBAQUABIIBABLgpPIQLue0EOytJVnKA/cG/Pw0eCnj
// SIG // yyvUZRlJsDhdpGnLOe5dQYGg5hXKSbOrO8y5T+rDbDJV
// SIG // 4UKjc2O1a7VATvWxyzeZN1J8hXC8l4BzhhTZLVu6O2dJ
// SIG // TYc6nS+tkh7TpDTQZEaqqeVboHhkCajQnseFuPX/QhvX
// SIG // 91m640jHGE3kg89DRzRM24KA3sK+rq5D8+pclg1A/E4/
// SIG // jnxS73YailltYQ4BmMuoOOV0DWO+70RO5AnkYQlbF5rE
// SIG // PSg9or1gy+TyVB7BP1Wnd7u9MSqQa3nlggKRsoN5WsXL
// SIG // VgFj/wcpW9FATG3rp1A7o9mlnYl417A+ZN0QC/PI4o9F
// SIG // OiShgheUMIIXkAYKKwYBBAGCNwMDATGCF4Awghd8Bgkq
// SIG // hkiG9w0BBwKgghdtMIIXaQIBAzEPMA0GCWCGSAFlAwQC
// SIG // AQUAMIIBUgYLKoZIhvcNAQkQAQSgggFBBIIBPTCCATkC
// SIG // AQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQg
// SIG // hHOQjKfqf9MmeYlA5OIegougmkXCLvmVMXAHV7Bj3GMC
// SIG // BmeuJGuZOBgTMjAyNTAyMjAxNTI4MzkuOTUzWjAEgAIB
// SIG // 9KCB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgT
// SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
// SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMG
// SIG // A1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9u
// SIG // czEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjk2MDAt
// SIG // MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt
// SIG // ZS1TdGFtcCBTZXJ2aWNloIIR6jCCByAwggUIoAMCAQIC
// SIG // EzMAAAHviT9WoVjMqNoAAQAAAe8wDQYJKoZIhvcNAQEL
// SIG // BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
// SIG // bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
// SIG // FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
// SIG // TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN
// SIG // MjMxMjA2MTg0NTQ4WhcNMjUwMzA1MTg0NTQ4WjCByzEL
// SIG // MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
// SIG // EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
// SIG // c29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z
// SIG // b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMe
// SIG // blNoaWVsZCBUU1MgRVNOOjk2MDAtMDVFMC1EOTQ3MSUw
// SIG // IwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2
// SIG // aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
// SIG // AgEAowtY4p8M4B8ITmpGaste6BOASASrJuZF+A1JggVi
// SIG // NJRVaRIiuZmdioefbKC+J7OdqYRTEGBhuZMqQoqbp4MD
// SIG // /TaG+FRlROmqDKOYWfTcrV0eWUYG/WfDUehJiyiAkYQ+
// SIG // LKIzzIP0ZxkU3HX+/02L8jNdIy45i8ihHoDB37yMD5jP
// SIG // gD+4c0C3xMQ3agidruuBneV5Z6xTpLuVPYyzipNcu9HP
// SIG // k8LdOP0S6q7r9Xxj/C5mJrR76weE3AbAA10pnBY4dFYE
// SIG // JF+M1xcKpyBvK4GPsw6iWEDWT/DtWKOJEnJB0+N1wtKD
// SIG // ONMntvvZf602IgxTN55WXto4bTpBgjuhqok6edMSPSE6
// SIG // SV4tLxHpPAHo0+DyjBDtmz8VOt6et7mW43TeS/pYCHAj
// SIG // TAjSNEiKKUuIGlUeEsvyKA79bw1qXviNvPysvI1k3nnd
// SIG // Dtx8TyTGal+EAdyOg58Gax4ip+qBN/LYAUwggCrxKGDk
// SIG // 4O69pRdCLm7f9/lT7yrUwlG2TxThvI2bfaugBaHZb0J7
// SIG // YqJWCGLakqy8lwECJVxoWeIDXL+Hb9WAIpZ21gPQrJ2I
// SIG // fjihBa/+MODOvZSPsmqGdy/7f1H16U//snO4UvxaJXJq
// SIG // xhSUwWJUuJxNXLim5cGf1Dhtuki4QzjVlxmQyjCSjed6
// SIG // Di0kpOJXUdB5bG0+IXi5VpThJSUCAwEAAaOCAUkwggFF
// SIG // MB0GA1UdDgQWBBTtTFqihcKwm7a8PT/AOt2wFUicyzAf
// SIG // BgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
// SIG // BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv
// SIG // c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU
// SIG // aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI
// SIG // KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8v
// SIG // d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p
// SIG // Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEw
// SIG // KDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM
// SIG // MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkq
// SIG // hkiG9w0BAQsFAAOCAgEAGBmWt2gg7nW5PRFXZD/MXEBm
// SIG // biACD0cfStQgO7kcwbfNHwtGlpLmGIUDLxxyUR1KG0jO
// SIG // FMN8ze3xxDfIYWgQ2/TUWhpxVnbR8ZifXjM+iaZ+ioiM
// SIG // ovVOToO0Ak2TJde59sOHnXaub7ZOK0Vjlb6YgwRiQESo
// SIG // l1gfbtosdFh9hDBRh6oyIY1lF4T4EeAujShTVx71r13n
// SIG // Cdll6yZ770BlwHzSRhEyWRqUeNZ1Dd4o34gkoxQ8Wphj
// SIG // 7MuYmLvdOB7/brkl2HeZtCcX9ljSUl5DxpTYaztu6T8Y
// SIG // E9ddZsgEetUt0toXOe9szfcqCRDmxPfFcuShDN2V+d3C
// SIG // 3nzfNRdQvaf3ACpBOrvVeq8spf6koMbtVKnjmQrRv4mh
// SIG // 0ijKMTOzKuEjBbD0//InjncApWKXMNAo2XuSgcdsS2uA
// SIG // dZ3hYm/CfP4EqLIzHRd5x4sh8dWHnWQ7cUkoHoHibItH
// SIG // 21IHc7FTCWL6lcOdlqkDbtBkQu/Wbla3lFSnQiZlDARw
// SIG // aU6elRaKS9CX+Eq4IPs0Q/YsG3Pbma5/vPaHaSJ2852K
// SIG // 5zyh4jtuqntXpDcJf3e66NiLT/5YIc9A6A+5BBnopCiV
// SIG // h3baO3lSaCYZK1HGp07lB9PIPjWMBukvj4wUgfzcjRem
// SIG // x2v8UfnHgGIXI8dIgYr/dDJ9CYhn5wNv4S4+Xr4U3AIw
// SIG // ggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAV
// SIG // MA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET
// SIG // MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
// SIG // bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
// SIG // aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0
// SIG // aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAx
// SIG // ODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYT
// SIG // AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
// SIG // EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
// SIG // cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l
// SIG // LVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEF
// SIG // AAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7V
// SIG // gtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H
// SIG // ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKD
// SIG // RLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gm
// SIG // U3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36M
// SIG // EBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+y
// SIG // OSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoF
// SIG // VZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJi
// SIG // ss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGa
// SIG // RnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+Autuqfjbs
// SIG // Nkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afo
// SIG // mXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9
// SIG // ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk
// SIG // i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y
// SIG // 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV
// SIG // 2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0C
// SIG // AwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEw
// SIG // IwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/
// SIG // LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnp
// SIG // cjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8G
// SIG // CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
// SIG // b20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYD
// SIG // VR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAwe
// SIG // CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB
// SIG // /wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9
// SIG // lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov
// SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj
// SIG // dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoG
// SIG // CCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
// SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS
// SIG // b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcN
// SIG // AQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pc
// SIG // FLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHk
// SIG // wo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF
// SIG // vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4
// SIG // U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2
// SIG // EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8Atq
// SIG // gcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mj
// SIG // dAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZM
// SIG // cm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQE
// SIG // cb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2f
// SIG // pCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBM
// SIG // drVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L
// SIG // +DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJ
// SIG // C4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU
// SIG // 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/
// SIG // 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDTTCC
// SIG // AjUCAQEwgfmhgdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMw
// SIG // EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
// SIG // b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
// SIG // b24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9w
// SIG // ZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
// SIG // Tjo5NjAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
// SIG // b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsO
// SIG // AwIaAxUAS3CPNYMW3mtRMdphW18e3JPtIP+ggYMwgYCk
// SIG // fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
// SIG // Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
// SIG // TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
// SIG // aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkq
// SIG // hkiG9w0BAQsFAAIFAOthNCEwIhgPMjAyNTAyMjAwNDU0
// SIG // NTdaGA8yMDI1MDIyMTA0NTQ1N1owdDA6BgorBgEEAYRZ
// SIG // CgQBMSwwKjAKAgUA62E0IQIBADAHAgEAAgIZHTAHAgEA
// SIG // AgIiMTAKAgUA62KFoQIBADA2BgorBgEEAYRZCgQCMSgw
// SIG // JjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIB
// SIG // AAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQCh0tRfygN1
// SIG // 97uMosyihIbypYP3Rn1LersJkthkKTw8FgRHDz/Xb6Lf
// SIG // MUuR3wlXqsnKFCthLh/EzrZ+Ojao+t/cZ/E+egYRpxze
// SIG // BBM/+BLmoYA24USUgPR9nWHYpx+efue//fboWjW36vli
// SIG // 6mVJryc2/NiUtqGttRVRPBSSmqEJULVmvU+DsXoW0msa
// SIG // UV7s+4+wwJfaJV9LBejiGUG5JgNjfsy7T8pv+cq1lcjQ
// SIG // VHG6wTpcO8Eb1O2avA2ioO18rgbik9+PI3Ik31jfOJ40
// SIG // dUH3FaxVxn6YPnb7qCy7q3WwEf+tTOEp0pf1mwNITaoA
// SIG // Ht8e/eCQ6hPFcQpuuIHC8htDMYIEDTCCBAkCAQEwgZMw
// SIG // fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
// SIG // b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
// SIG // Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWlj
// SIG // cm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAHv
// SIG // iT9WoVjMqNoAAQAAAe8wDQYJYIZIAWUDBAIBBQCgggFK
// SIG // MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkq
// SIG // hkiG9w0BCQQxIgQgA1QstsvdHZnN5L65sE5F1w02W1kA
// SIG // TEh7TCvIWXxnxC0wgfoGCyqGSIb3DQEJEAIvMYHqMIHn
// SIG // MIHkMIG9BCDwYShFuBaN8FM9PTUMdmtA23HbF/I6LzOS
// SIG // 4sx5p8l/ozCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw
// SIG // EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
// SIG // b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
// SIG // b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
// SIG // IFBDQSAyMDEwAhMzAAAB74k/VqFYzKjaAAEAAAHvMCIE
// SIG // IFbk9jFTorFlI4xM2so4TTtuOU/RVAajDSD7CajID8VZ
// SIG // MA0GCSqGSIb3DQEBCwUABIICACQvdZ1kdm00uJaLdaPm
// SIG // Yq7RKvwP57Qkq0pBnwlC8Otwt01QbU0ymFZR8njw0fqS
// SIG // vYJ0cDflpvhxA9g612LNO85heCWUPJlOSKDTyqTniKcw
// SIG // aYhTqw1jQnionEq7/SWS9pTApIDH5RZvo3PK03BMfbdP
// SIG // 0w5XxDi/OfaNJ0ifYHOtAl8+sFKzOdf3kWU7B/x2ZmW1
// SIG // 7aARex7w0EcI1Nh7gZDZZ4AGr8dllFS8CX2oxrsGa7EZ
// SIG // SfaO+kDwaRBZ7YwaOMLOYITRWcFPzswa6LBisdI/SCat
// SIG // aU4mTSP8LcPZ2tBjKCz/LlhGgOpNOvMt0ExRzHT2gpG2
// SIG // qYkQ0ejcLVO3iF4xKv7BgfWB6cPnPnHoTFiOEKHKQ/xG
// SIG // hHYEjTvZ4a+T01yHhEnyuBE3YfUzymI3rfvfrPrCdB4/
// SIG // M+gv1/8D68ItRluDKtbaGnHW1uvgZW5jsdjeN+Bi3CG1
// SIG // 9rSLFL2pmtWo4+qIPL5aLXezNsjlrzq4fIgXRXIdsEJy
// SIG // DKS2ekSkc8vKvxEcN0VeXwm6uwMVBj1cT1efQXAzdlsp
// SIG // bet83iL3mnJ+eA4lUnP6l8S0dbQU115AGA5pM68J8sAm
// SIG // PCjxbNXx14kiSeKzGXfaxzl+bKphX1rma0tKLf+hjLGo
// SIG // 2+dlTv12zVKK3uM3zZqITTQlOv0uaJFft/zqzVBAO2jt
// SIG // FnWQ
// SIG // End signature block