diff --git a/AEPAssurance.podspec b/AEPAssurance.podspec index 5d0ac61..a7bd993 100644 --- a/AEPAssurance.podspec +++ b/AEPAssurance.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "AEPAssurance" - s.version = "4.0.0" + s.version = "4.1.0" s.summary = "AEPAssurance SDK for Adobe Experience Platform Mobile SDK. Written and maintained by Adobe." s.description = <<-DESC diff --git a/AEPAssurance.xcodeproj/project.pbxproj b/AEPAssurance.xcodeproj/project.pbxproj index 3dd8d3d..1b3a990 100644 --- a/AEPAssurance.xcodeproj/project.pbxproj +++ b/AEPAssurance.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 7527DCA22926F15300FE0D8C /* AssuranceConnectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7527DCA12926F15300FE0D8C /* AssuranceConnectionDelegate.swift */; }; 753867C32925735D0021BC3F /* AssuranceAuthorizingPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753867C22925735D0021BC3F /* AssuranceAuthorizingPresentation.swift */; }; 753867C529257CFE0021BC3F /* AssuranceStatusPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753867C429257CFE0021BC3F /* AssuranceStatusPresentation.swift */; }; + 755A475D2A377F2A00FE00AA /* htmlSampleEscaped.txt in Resources */ = {isa = PBXBuildFile; fileRef = 755A475C2A377F2A00FE00AA /* htmlSampleEscaped.txt */; }; 7881FA5928D50C8D0051F902 /* QuickConnectServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7881FA5828D50C8D0051F902 /* QuickConnectServiceTests.swift */; }; B601172227BAE3EF006D3968 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B601172127BAE3EF006D3968 /* Connection.swift */; }; B601172527BAE4AA006D3968 /* AdobeLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B601172427BAE4AA006D3968 /* AdobeLogo.swift */; }; @@ -227,6 +228,7 @@ 7527DCA12926F15300FE0D8C /* AssuranceConnectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssuranceConnectionDelegate.swift; sourceTree = ""; }; 753867C22925735D0021BC3F /* AssuranceAuthorizingPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssuranceAuthorizingPresentation.swift; sourceTree = ""; }; 753867C429257CFE0021BC3F /* AssuranceStatusPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssuranceStatusPresentation.swift; sourceTree = ""; }; + 755A475C2A377F2A00FE00AA /* htmlSampleEscaped.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = htmlSampleEscaped.txt; sourceTree = ""; }; 7881FA5828D50C8D0051F902 /* QuickConnectServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectServiceTests.swift; sourceTree = ""; }; 96C170474540581D23D5C489 /* Pods_TestApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TestApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B601171F27BAE193006D3968 /* connection.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = connection.png; sourceTree = ""; }; @@ -425,6 +427,7 @@ B603FA0827AB1D270033416B /* htmlSample.txt */, B603FA0327A9F51B0033416B /* 40KBString.txt */, B603F9FE27A9EC7D0033416B /* 20KBString.txt */, + 755A475C2A377F2A00FE00AA /* htmlSampleEscaped.txt */, ); path = Resources; sourceTree = ""; @@ -971,6 +974,7 @@ B603FA0427A9F51B0033416B /* 40KBString.txt in Resources */, B6DB487F27B1D73500166FC4 /* 5KBString.txt in Resources */, B603FA0927AB1D270033416B /* htmlSample.txt in Resources */, + 755A475D2A377F2A00FE00AA /* htmlSampleEscaped.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AEPAssurance/Source/AssuranceConstants.swift b/AEPAssurance/Source/AssuranceConstants.swift index 08274ff..fc864ca 100644 --- a/AEPAssurance/Source/AssuranceConstants.swift +++ b/AEPAssurance/Source/AssuranceConstants.swift @@ -15,7 +15,7 @@ import Foundation enum AssuranceConstants { static let EXTENSION_NAME = "com.adobe.assurance" static let FRIENDLY_NAME = "Assurance" - static let EXTENSION_VERSION = "4.0.0" + static let EXTENSION_VERSION = "4.1.0" static let LOG_TAG = FRIENDLY_NAME static let DEFAULT_ENVIRONMENT = AssuranceEnvironment.prod @@ -92,8 +92,8 @@ enum AssuranceConstants { } enum SharedStateKeys { - static let CLIENT_ID = "sessionid" - static let SESSION_ID = "clientid" + static let CLIENT_ID = "clientid" + static let SESSION_ID = "sessionid" static let INTEGRATION_ID = "integrationid" } diff --git a/AEPAssurance/Source/AssuranceEventChunker.swift b/AEPAssurance/Source/AssuranceEventChunker.swift index 31ef950..8951998 100644 --- a/AEPAssurance/Source/AssuranceEventChunker.swift +++ b/AEPAssurance/Source/AssuranceEventChunker.swift @@ -13,8 +13,16 @@ import AEPServices import Foundation +/// +/// An EventChunker used for chunking and stitching of AssuranceEvents +/// +protocol EventChunker { + func chunk(_ event: AssuranceEvent) -> [AssuranceEvent] + func stitch(_ chunkedEvents: [AssuranceEvent]) -> AssuranceEvent? +} + /// Class that brings the capability to chunk the AssuranceEvent if in need to satisfy the socket size limit. -struct AssuranceEventChunker { +struct AssuranceEventChunker: EventChunker { /// The maximum size of data that an `AssuranceEvent` payload can hold after chunking /// @@ -102,4 +110,46 @@ struct AssuranceEventChunker { } return chunkedEvents } + + /// + /// Stitches chunked events together into one `AssuranceEvent` where the stitched events chunkData is now the new event's payload + /// + /// - Parameter chunkedEvents: An array of chunked AssuranceEvents + /// - Returns: An `AssuranceEvent` which has the stitched data as the payload + func stitch(_ chunkedEvents: [AssuranceEvent]) -> AssuranceEvent? { + //exit early if chunkedEvents is empty + guard !chunkedEvents.isEmpty else { return nil } + + var stitchedString = "" + // Stitch chunked payload data together + for event in chunkedEvents { + // Extract the payload string and unescape it. Currently escaped by the services + guard let payloadString = event.payload?[AssuranceConstants.AssuranceEvent.PayloadKey.CHUNK_DATA]?.stringValue else { + Log.warning(label: AssuranceConstants.LOG_TAG, "Error while attempting to stitch chunked event: Chunk payload was not in proper string format.") + return nil + } + + stitchedString += payloadString + } + guard let stitchedStringData = stitchedString.data(using: .utf8) else { + Log.warning(label: AssuranceConstants.LOG_TAG, "Error while attempting to create data from stitched string") + return nil + } + + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .millisecondsSince1970 + var decodedPayload: [String: AnyCodable]? = nil + do { + decodedPayload = try decoder.decode([String:AnyCodable].self, from: stitchedStringData) + } catch { + Log.warning(label: AssuranceConstants.LOG_TAG, "Error while attempting to decode stitched JSON data: \(error)") + return nil + } + let referenceEvent = chunkedEvents[0] + return AssuranceEvent(type: referenceEvent.type, + payload: decodedPayload, + timestamp: referenceEvent.timestamp ?? Date(), + vendor: referenceEvent.vendor) + + } } diff --git a/AEPAssurance/Source/AssuranceSession+SocketDelegate.swift b/AEPAssurance/Source/AssuranceSession+SocketDelegate.swift index 6e29741..742f42a 100644 --- a/AEPAssurance/Source/AssuranceSession+SocketDelegate.swift +++ b/AEPAssurance/Source/AssuranceSession+SocketDelegate.swift @@ -131,9 +131,17 @@ extension AssuranceSession: SocketDelegate { /// - event - the `AssuranceEvent` received from socket func webSocket(_ socket: SocketConnectable, didReceiveEvent event: AssuranceEvent) { Log.trace(label: AssuranceConstants.LOG_TAG, "Received event from assurance session - \(event.description)") - + var eventToQueue = event + // Handle Chunked event before queuing + if event.isChunkedEvent { + guard let stitchedEvent = stitchEvent(event: event) else { + // Exit early if stitching fails + return + } + eventToQueue = stitchedEvent + } // add the incoming event to inboundQueue and process them - inboundQueue.enqueue(newElement: event) + inboundQueue.enqueue(newElement: eventToQueue) inboundSource.add(data: 1) } @@ -147,5 +155,57 @@ extension AssuranceSession: SocketDelegate { stateManager.connectedWebSocketURL = socket.socketURL?.absoluteString } } + + /// + /// Organizes the events into the `chunkedEvents` dictionary by `chunkedID`, and stitches the + /// given event once we have received all of the chunks + /// + /// - Parameter event: The chunked event to be stitched + /// - Returns: An `AssuranceEvent` which has the stitched data as the payload + private func stitchEvent(event: AssuranceEvent) -> AssuranceEvent? { + if let chunkedID = event.chunkedID { + // New chunked event received + if self.chunkedEvents[chunkedID] == nil { + self.chunkedEvents[chunkedID] = [event] + } else { + // Chunked event exists, add chunk + self.chunkedEvents[chunkedID]?.append(event) + // Check if this is the last chunk + if chunkedEvents[chunkedID]?.count == event.chunkedTotal { + // Sort and Stitch + if let sortedChunks = self.chunkedEvents[chunkedID]?.sorted(by: { $0.chunkedSequenceNumber! < $1.chunkedSequenceNumber! }) { + defer { self.chunkedEvents.removeValue(forKey: chunkedID) } + return socket.eventChunker.stitch(sortedChunks) + } + } + } + } + return nil + } + +} +/// +/// Extension on AssuranceEvent used for chunked event properties +/// +fileprivate extension AssuranceEvent { + var isChunkedEvent: Bool { + if self.metadata?[AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID] != nil { + return true + } else { + return false + } + } + + var chunkedID: String? { + return self.metadata?[AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID]?.stringValue + } + + var chunkedSequenceNumber: Int? { + return self.metadata?[AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE]?.intValue + } + + var chunkedTotal: Int? { + return self.metadata?[AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL]?.intValue + } } diff --git a/AEPAssurance/Source/AssuranceSession.swift b/AEPAssurance/Source/AssuranceSession.swift index 3503083..1b9440e 100644 --- a/AEPAssurance/Source/AssuranceSession.swift +++ b/AEPAssurance/Source/AssuranceSession.swift @@ -21,6 +21,7 @@ class AssuranceSession { let sessionOrchestrator: AssuranceSessionOrchestrator let outboundQueue: ThreadSafeQueue = ThreadSafeQueue(withLimit: 200) let inboundQueue: ThreadSafeQueue = ThreadSafeQueue(withLimit: 200) + var chunkedEvents: [String: [AssuranceEvent]] = [:] let inboundSource: DispatchSourceUserDataAdd = DispatchSource.makeUserDataAddSource(queue: DispatchQueue.global(qos: .default)) let outboundSource: DispatchSourceUserDataAdd = DispatchSource.makeUserDataAddSource(queue: DispatchQueue.global(qos: .default)) let pluginHub: PluginHub = PluginHub() diff --git a/AEPAssurance/Source/Socket/SocketConnectable.swift b/AEPAssurance/Source/Socket/SocketConnectable.swift index cf01fd8..b6f27fb 100644 --- a/AEPAssurance/Source/Socket/SocketConnectable.swift +++ b/AEPAssurance/Source/Socket/SocketConnectable.swift @@ -16,6 +16,9 @@ import Foundation protocol SocketConnectable { /// the web socket URL var socketURL: URL? { get } + + /// The event chunker used for chunking and stitching Events + var eventChunker: EventChunker { get } /// the delegate that gets notified on socket events. var delegate: SocketDelegate { get } diff --git a/AEPAssurance/Source/Socket/WebView/WebViewSocket.swift b/AEPAssurance/Source/Socket/WebView/WebViewSocket.swift index ad5c045..b646f80 100644 --- a/AEPAssurance/Source/Socket/WebView/WebViewSocket.swift +++ b/AEPAssurance/Source/Socket/WebView/WebViewSocket.swift @@ -18,7 +18,8 @@ class WebViewSocket: NSObject, SocketConnectable, WKNavigationDelegate, WKScript var delegate: SocketDelegate var socketURL: URL? - let eventChunker = AssuranceEventChunker() + + var eventChunker: EventChunker = AssuranceEventChunker() /// variable tracking the current socket status var socketState: SocketState = .unknown { diff --git a/AEPAssurance/UnitTests/AssuranceEventChunkerTests.swift b/AEPAssurance/UnitTests/AssuranceEventChunkerTests.swift index 509447b..412cb46 100644 --- a/AEPAssurance/UnitTests/AssuranceEventChunkerTests.swift +++ b/AEPAssurance/UnitTests/AssuranceEventChunkerTests.swift @@ -146,6 +146,41 @@ class AssuranceEventChunkerTests: XCTestCase { XCTAssertLessThan(sizeOf(chunkedEvents[3]), ALLOWED_CHUNK_EVENT_SIZE) } + func test_stitch_htmlData() { + // First, chunk a large event + let htmlText = readStringFromFile("htmlSampleEscaped") + let eventPayload = ["htmlMessage" : AnyCodable.init(htmlText)] + let event = AssuranceEvent(type: "type", payload: eventPayload) + + let chunkedEvents = chunker.chunk(event) + + // Next, stitch the chunked event + let stitchedEvent = chunker.stitch(chunkedEvents) + + // Assert + XCTAssertNotNil(stitchedEvent) + XCTAssertEqual(event.payload?["htmlMessage"]?.stringValue, stitchedEvent?.payload?["htmlMessage"]?.stringValue) + + } + + func test_stitch_realChunks() { + let chunks = loadChunks() + let stitchedEvent = chunker.stitch(chunks) + + XCTAssertNotNil(stitchedEvent) + XCTAssertEqual(stitchedEvent?.vendor, "com.adobe.griffon.mobile") + XCTAssertEqual(stitchedEvent?.type, "control") + XCTAssertNotNil(stitchedEvent?.payload) + XCTAssertEqual(stitchedEvent?.payload?["type"], "fakeEvent") + guard let detailDict = stitchedEvent?.payload?["detail"]?.asDictionary() else { + XCTFail("Inner detail dict unexpectedly nil") + return + } + XCTAssertEqual(detailDict["eventType"] as? String, "com.adobe.eventType.rulesEngine") + XCTAssertEqual(detailDict["eventSource"] as? String, "com.adobe.eventSource.responseContent") + XCTAssertNotNil(detailDict["eventData"]) + } + func test_chunk_rulesjson() throws { // prepare @@ -164,6 +199,17 @@ class AssuranceEventChunkerTests: XCTestCase { XCTAssertLessThan(sizeOf(chunkedEvents[2]), ALLOWED_CHUNK_EVENT_SIZE) } + func test_stitch_rulesjson() { + // prepare + let eventPayload = readJsonFromFile("rules") + let event = AssuranceEvent(type: "type", payload: eventPayload) + // test + let chunkedEvents = chunker.chunk(event) + + let stitchedEvent = chunker.stitch(chunkedEvents) + XCTAssertNotNil(stitchedEvent?.payload?["rules"]) + } + private func readStringFromFile(_ fileName: String) -> String { let bundle = Bundle(for: type(of: self)) @@ -189,6 +235,24 @@ class AssuranceEventChunkerTests: XCTestCase { let jsonData = assuranceEvent.jsonData return jsonData.count } + + private func loadChunks() -> [AssuranceEvent] { + // Need to use string vars instead of loading from file because of the escaping characters. Reading from file adds more escaping + let chunk1String = "{\"eventID\":\"e1bac4b4-dd02-4cf1-b0c4-8eeeb07a0e18\",\"vendor\":\"com.adobe.griffon.mobile\",\"type\":\"control\",\"payload\":{\"chunkData\":\"{\\\"detail\\\":{\\\"eventData\\\":{\\\"triggeredconsequence\\\":{\\\"type\\\":\\\"cjmiam\\\",\\\"detail\\\":{\\\"mobileParameters\\\":{\\\"verticalAlign\\\":\\\"center\\\",\\\"dismissAnimation\\\":\\\"top\\\",\\\"verticalInset\\\":0,\\\"backdropOpacity\\\":0.2,\\\"cornerRadius\\\":15,\\\"gestures\\\":{\\\"swipeUp\\\":\\\"adbinapp://dismiss?interaction\\u003dswipeUp\\\",\\\"swipeDown\\\":\\\"adbinapp://dismiss?interaction\\u003dswipeDown\\\",\\\"swipeLeft\\\":\\\"adbinapp://dismiss?interaction\\u003dswipeLeft\\\",\\\"swipeRight\\\":\\\"adbinapp://dismiss?interaction\\u003dswipeRight\\\",\\\"tapBackground\\\":\\\"adbinapp://dismiss?interaction\\u003dtapBackground\\\"},\\\"horizontalInset\\\":0,\\\"uiTakeover\\\":true,\\\"horizontalAlign\\\":\\\"center\\\",\\\"width\\\":80,\\\"displayAnimation\\\":\\\"top\\\",\\\"backdropColor\\\":\\\"#000000\\\",\\\"height\\\":60},\\\"html\\\":\\\"\\u003c!doctype html\\u003e\\\\n\\u003chtml\\u003e\\u003chead\\u003e\\\\n \\u003cmeta type\\u003d\\\\\\\"templateProperties\\\\\\\" name\\u003d\\\\\\\"modal\\\\\\\" label\\u003d\\\\\\\"adobe-label:modal\\\\\\\" icon\\u003d\\\\\\\"adobe-icon:modal\\\\\\\"\\u003e\\\\n \\u003cmeta type\\u003d\\\\\\\"templateZone\\\\\\\" name\\u003d\\\\\\\"default\\\\\\\" label\\u003d\\\\\\\"Default\\\\\\\" classname\\u003d\\\\\\\"body\\\\\\\" definition\\u003d\\\\\\\"[\\u0026quot;CloseBtn\\u0026quot;, \\u0026quot;Image\\u0026quot;, \\u0026quot;Text\\u0026quot;, \\u0026quot;Buttons\\u0026quot;]\\\\\\\"\\u003e\\\\n\\\\n \\u003cmeta type\\u003d\\\\\\\"templateDefaultAnimations\\\\\\\" displayanimation\\u003d\\\\\\\"top\\\\\\\" dismissanimation\\u003d\\\\\\\"top\\\\\\\"\\u003e\\\\n \\u003cmeta type\\u003d\\\\\\\"templateDefaultSize\\\\\\\" width\\u003d\\\\\\\"80\\\\\\\" height\\u003d\\\\\\\"60\\\\\\\"\\u003e\\\\n \\u003cmeta type\\u003d\\\\\\\"templateDefaultPosition\\\\\\\" verticalalign\\u003d\\\\\\\"center\\\\\\\" verticalinset\\u003d\\\\\\\"0\\\\\\\" horizontalalign\\u003d\\\\\\\"center\\\\\\\" horizontalinset\\u003d\\\\\\\"0\\\\\\\"\\u003e\\\\n \\u003cmeta type\\u003d\\\\\\\"templateDefaultGesture\\\\\\\" swipeup\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dswipeUp\\\\\\\" swipedown\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dswipeDown\\\\\\\" swipeleft\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dswipeLeft\\\\\\\" swiperight\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dswipeRight\\\\\\\" tapbackground\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dtapBackground\\\\\\\"\\u003e\\\\n \\u003cmeta type\\u003d\\\\\\\"templateDefaultUiTakeover\\\\\\\" enable\\u003d\\\\\\\"true\\\\\\\"\\u003e\\\\n\\\\n \\u003cmeta name\\u003d\\\\\\\"viewport\\\\\\\" content\\u003d\\\\\\\"width\\u003ddevice-width, initial-scale\\u003d1.0\\\\\\\"\\u003e\\\\n \\u003cmeta charset\\u003d\\\\\\\"UTF-8\\\\\\\"\\u003e\\\\n \\u003cstyle\\u003e\\\\n html,\\\\n body {\\\\n margin: 0;\\\\n padding: 0;\\\\n text-align: center;\\\\n width: 100%;\\\\n height: 100%;\\\\n font-family: adobe-clean, \\u0027Source Sans Pro\\u0027, -apple-system, BlinkMacSystemFont, \\u0027Segoe UI\\u0027,\\\\n Roboto, sans-serif;\\\\n }\\\\n h3 {\\\\n margin: 0.4rem auto;\\\\n }\\\\n p {\\\\n margin: 0.4rem auto;\\\\n }\\\\n\\\\n .body {\\\\n display: flex;\\\\n flex-direction: column;\\\\n background-color: #fff;\\\\n border-radius: 0.3rem;\\\\n color: #333333;\\\\n width: 100vw;\\\\n height: 100vh;\\\\n text-align: center;\\\\n align-items: center;\\\\n background-size: \\u0027cover\\u0027;\\\\n }\\\\n\\\\n .content {\\\\n width: 100%;\\\\n height: 100%;\\\\n display: flex;\\\\n justify-content: center;\\\\n flex-direction: column;\\\\n position: relative;\\\\n }\\\\n\\\\n a {\\\\n text-decoration: none;\\\\n }\\\\n\\\\n .image {\\\\n height: 1rem;\\\\n flex-grow: 4;\\\\n flex-shrink: 1;\\\\n display: flex;\\\\n justify-content: center;\\\\n width: 90%;\\\\n flex-direction: column;\\\\n align-items: center;\\\\n }\\\\n .image img {\\\\n max-height: 100%;\\\\n max-width: 100%;\\\\n }\\\\n\\\\n .image.empty-image {\\\\n display: none;\\\\n }\\\\n\\\\n .empty-image ~ .text {\\\\n flex-grow: 1;\\\\n }\\\\n\\\\n .text {\\\\n text-align: center;\\\\n color: #333333;\\\\n line-height: 1.25rem;\\\\n font-size: 0.875rem;\\\\n padding: 0 0.8rem;\\\\n width: 100%;\\\\n box-sizing: border-box;\\\\n }\\\\n .title {\\\\n line-height: 1.3125rem;\\\\n font-size: 1.025rem;\\\\n }\\\\n\\\\n .buttons {\\\\n width: 100%;\\\\n display: flex;\\\\n flex-direction: column;\\\\n font-size: 1rem;\\\\n line-height: 1.3rem;\\\\n text-decoration: none;\\\\n text-align: center;\\\\n box-sizing: border-box;\\\\n padding: 0.8rem;\\\\n padding-top: 0.4rem;\\\\n gap: 0.3125rem;\\\\n }\\\\n\\\\n .button {\\\\n flex-grow: 1;\\\\n background-color: #1473e6;\\\\n color: #ffffff;\\\\n border-radius: 0.25rem;\\\\n cursor: pointer;\\\\n padding: 0.3rem;\\\\n gap: 0.5rem;\\\\n }\\\\n\\\\n .btnClose {\\\\n color: #000000;\\\\n }\\\\n\\\\n .closeBtn {\\\\n align-self: flex-end;\\\\n color: #000000;\\\\n width: 1.8rem;\\\\n height: 1.8rem;\\\\n margin-top: 1rem;\\\\n margin-right: 0.3rem;\\\\n }\\\\n .closeBtn img {\\\\n width: 100%;\\\\n height: 100%;\\\\n }\\\\n \\u003c/style\\u003e\\\\n \\u003cstyle type\\u003d\\\\\\\"text/css\\\\\\\" id\\u003d\\\\\\\"editor-styles\\\\\\\"\\u003e\\\\n\\\\n\\u003c/style\\u003e\\\\n \\u003c/head\\u003e\\\\n\\\\n \\u003cbody\\u003e\\\\n HELLOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"metadata\":{\"chunkTotal\":4,\"chunkSequenceNumber\":0,\"chunkId\":\"2afeacc7-ed13-454b-a70a-d4c2689a2ebc\"}}" + + let chunk2String = "{\"eventID\":\"0f1f577d-c92b-4432-b308-d1ea1c412e88\",\"vendor\":\"com.adobe.griffon.mobile\",\"type\":\"control\",\"payload\":{\"chunkData\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"},\"metadata\":{\"chunkTotal\":4,\"chunkSequenceNumber\":1,\"chunkId\":\"2afeacc7-ed13-454b-a70a-d4c2689a2ebc\"}}" + + let chunk3String = "{\"eventID\":\"8cfe60dd-8528-4d91-ae4a-ce318085bd93\",\"vendor\":\"com.adobe.griffon.mobile\",\"type\":\"control\",\"payload\":{\"chunkData\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHELLO\\\\n \\u003cdiv class\\u003d\\\\\\\"body\\\\\\\"\\u003e\\u003cdiv class\\u003d\\\\\\\"closeBtn\\\\\\\" data-uuid\\u003d\\\\\\\"362ef3b3-41ed-4b2b-8ef6-57e332a953c8\\\\\\\" data-btn-style\\u003d\\\\\\\"plain\\\\\\\"\\u003e\\u003ca aria-label\\u003d\\\\\\\"Close\\\\\\\" class\\u003d\\\\\\\"btnClose\\\\\\\" href\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dcancel\\\\\\\"\\u003e\\u003csvg xmlns\\u003d\\\\\\\"http://www.w3.org/2000/svg\\\\\\\" height\\u003d\\\\\\\"18\\\\\\\" viewbox\\u003d\\\\\\\"0 0 18 18\\\\\\\" width\\u003d\\\\\\\"18\\\\\\\" class\\u003d\\\\\\\"close\\\\\\\"\\u003e\\\\n \\u003crect id\\u003d\\\\\\\"Canvas\\\\\\\" fill\\u003d\\\\\\\"#ffffff\\\\\\\" opacity\\u003d\\\\\\\"0\\\\\\\" width\\u003d\\\\\\\"18\\\\\\\" height\\u003d\\\\\\\"18\\\\\\\"\\u003e\\u003c/rect\\u003e\\\\n \\u003cpath fill\\u003d\\\\\\\"currentColor\\\\\\\" xmlns\\u003d\\\\\\\"http://www.w3.org/2000/svg\\\\\\\" d\\u003d\\\\\\\"M13.2425,3.343,9,7.586,4.7575,3.343a.5.5,0,0,0-.707,0L3.343,4.05a.5.5,0,0,0,0,.707L7.586,9,3.343,13.2425a.5.5,0,0,0,0,.707l.707.7075a.5.5,0,0,0,.707,0L9,10.414l4.2425,4.243a.5.5,0,0,0,.707,0l.7075-.707a.5.5,0,0,0,0-.707L10.414,9l4.243-4.2425a.5.5,0,0,0,0-.707L13.95,3.343a.5.5,0,0,0-.70711-.00039Z\\\\\\\"\\u003e\\u003c/path\\u003e\\\\n\\u003c/svg\\u003e\\u003c/a\\u003e\\u003c/div\\u003e\\u003cdiv class\\u003d\\\\\\\"image\\\\\\\" data-uuid\\u003d\\\\\\\"1676d303-988b-4c8c-8e54-f7d2aa7d0adc\\\\\\\"\\u003e\\u003cimg src\\u003d\\\\\\\"https://d14dq8eoa1si34.cloudfront.net/2a6ef2f0-1167-11eb-88c6-b512a5ef09a7/urn:aaid:aem:d6c49a4a-8545-4d6d-812c-035dba81c4a6/oak:1.0::ci:b3cbed1b260ed90dff6198ff6c1d1f37/d4455e35-71bb-3bdb-9417-ed2fd5d8698b\\\\\\\" alt\\u003d\\\\\\\"\\\\\\\"\\u003e\\u003c/div\\u003e\\u003cdiv class\\u003d\\\\\\\"text\\\\\\\" data-uuid\\u003d\\\\\\\"d2bd1254-cf5c-4783-ba2f-27e8a09e2469\\\\\\\"\\u003e\\u003ch3\\u003e\\u003c/h3\\u003e\\u003cp\\u003eHello み\\u003c/p\\u003e\\u003c/div\\u003e\\u003cdiv class\\u003d\\\\\\\"buttons\\\\\\\" data-uuid\\u003d\\\\\\\"59fdd469-ac93-4c70-9d39-940834088e6c\\\\\\\"\\u003e\\u003ca class\\u003d\\\\\\\"button\\\\\\\" data-uuid\\u003d\\\\\\\"193aa7a6-fdd2-42b2-a61f-9c9a50c45acb\\\\\\\" href\\u003d\\\\\\\"adbinapp://dismiss?interaction\\u003dclicked\\\\\\\"\\u003eGreat\\u003c/a\\u003e\\u003c/div\\u003e\\u003c/div\\u003e\\\\n \\\\n\\\\n\\u003cscript type\\u003d\\\\\\\"text/javascript\\\\\\\"\\u003e(document.querySelectorAll(\\u0027a[href^\\u003d\\\\\\\"adbinapp://\\\\\\\"]\\u0027) || []).forEach(\\\\n n \\u003d\\u003e n.addEventListener(\\u0027click\\u0027, e \\u003d\\u003e {e.stopPropagation(); e.preventDefault();}, true)\\\\n );\\u003c/script\\u003e\\u003c/body\\u003e\\u003c/html\\u003e\\\",\\\"remoteAssets\\\":[\\\"https://d14dq8eoa1si34.cloudfront.net/2a6ef2f0-1167-11eb-88c6-b512a5ef09a7/urn:aaid:aem:d6c49a4a-8545-4d6d-812c-035dba81c4a6/oak:1.0::ci:b3cbed1b260ed90dff6198ff6c1d1f37/d4455e35-71bb-3bdb-9417-ed2fd5d8698b\\\"]},\\\"id\\\":\\\"2d8b96e3-42b7-4dea-b416-4c66c7004d07\\\"}},\\\"eventSource\\\":\\\"com.adobe.eventSource.responseContent\\\",\\\"eventType\\\":\\\"com.adobe.eventType.rulesEngine\\\",\\\"eventName\\\":\\\"Rule Consequence Event (Spoof)\\\"},\\\"type\\\":\\\"fakeEvent\\\"}\"},\"metadata\":{\"chunkTotal\":4,\"chunkSequenceNumber\":2,\"chunkId\":\"2afeacc7-ed13-454b-a70a-d4c2689a2ebc\"}}" + + let chunk4String = "{\"eventID\":\"1feafa7c-144e-47ac-8dcc-af82b684b6e8\",\"vendor\":\"com.adobe.griffon.mobile\",\"type\":\"control\",\"payload\":{\"chunkData\":\"\"},\"metadata\":{\"chunkTotal\":4,\"chunkSequenceNumber\":3,\"chunkId\":\"2afeacc7-ed13-454b-a70a-d4c2689a2ebc\"}}" + + guard let encodedChunk1 = chunk1String.data(using: .utf8), let encodedChunk2 = chunk2String.data(using: .utf8), let encodedChunk3 = chunk3String.data(using: .utf8), let encodedChunk4 = chunk4String.data(using: .utf8) else { + XCTFail("Failed to encode chunk strings") + return [] + } + + return [encodedChunk1, encodedChunk2, encodedChunk3, encodedChunk4].map { AssuranceEvent.from(jsonData: $0)!} + } } diff --git a/AEPAssurance/UnitTests/AssuranceSessionTests.swift b/AEPAssurance/UnitTests/AssuranceSessionTests.swift index 8a16e06..cee73be 100644 --- a/AEPAssurance/UnitTests/AssuranceSessionTests.swift +++ b/AEPAssurance/UnitTests/AssuranceSessionTests.swift @@ -199,6 +199,74 @@ class AssuranceSessionTests: XCTestCase { wait(for: [stateManager.expectation!], timeout: 2.0) XCTAssertTrue(stateManager.getAllExtensionStateDataCalled) } + + func test_session_receives_chunkedEvents() { + let mockEventChunker = MockEventChunker() + mockSocket.eventChunker = mockEventChunker + mockEventChunker.eventToReturn = tacoEvent + session.pluginHub.registerPlugin(mockPlugin, toSession: session) + mockPlugin.expectation = XCTestExpectation(description: "sends inbound event to respective plugin") + let chunkEvent1 = AssuranceEvent(type: "control", + payload: ["type":"Test"], + metadata: [AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID: "testChunkID", + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE: 1, + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL: 3]) + + let chunkEvent2 = AssuranceEvent(type: "control", + payload: ["type":"Test"], + metadata: [AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID: "testChunkID", + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE: 2, + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL: 3]) + + let chunkEvent3 = AssuranceEvent(type: "control", + payload: ["type":"Test"], + metadata: [AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID: "testChunkID", + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE: 3, + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL: 3]) + + session.webSocket(mockSocket, didReceiveEvent: chunkEvent1) + session.webSocket(mockSocket, didReceiveEvent: chunkEvent2) + session.webSocket(mockSocket, didReceiveEvent: chunkEvent3) + + wait(for: [mockPlugin.expectation!], timeout: 2.0) + XCTAssertTrue(mockPlugin.eventReceived) + XCTAssertTrue(mockEventChunker.stitchCalled) + XCTAssertEqual(0, session.inboundQueue.size()) + } + + func test_session_receives_chunkedEvent_stitchFails() { + let mockEventChunker = MockEventChunker() + mockSocket.eventChunker = mockEventChunker + session.pluginHub.registerPlugin(mockPlugin, toSession: session) + mockPlugin.expectation = XCTestExpectation(description: "sends inbound event to respective plugin") + mockPlugin.expectation?.isInverted = true + let chunkEvent1 = AssuranceEvent(type: "control", + payload: ["type":"Test"], + metadata: [AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID: "testChunkID", + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE: 1, + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL: 3]) + + let chunkEvent2 = AssuranceEvent(type: "control", + payload: ["type":"Test"], + metadata: [AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID: "testChunkID", + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE: 2, + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL: 3]) + + let chunkEvent3 = AssuranceEvent(type: "control", + payload: ["type":"Test"], + metadata: [AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_ID: "testChunkID", + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_SEQUENCE: 3, + AssuranceConstants.AssuranceEvent.MetadataKey.CHUNK_TOTAL: 3]) + + session.webSocket(mockSocket, didReceiveEvent: chunkEvent1) + session.webSocket(mockSocket, didReceiveEvent: chunkEvent2) + session.webSocket(mockSocket, didReceiveEvent: chunkEvent3) + + wait(for: [mockPlugin.expectation!], timeout: 2.0) + XCTAssertFalse(mockPlugin.eventReceived) + XCTAssertTrue(mockEventChunker.stitchCalled) + XCTAssertEqual(0, session.inboundQueue.size()) + } func test_session_disconnect() throws { diff --git a/AEPAssurance/UnitTests/Mocks/MockSocket.swift b/AEPAssurance/UnitTests/Mocks/MockSocket.swift index 0d8a039..6f3790d 100644 --- a/AEPAssurance/UnitTests/Mocks/MockSocket.swift +++ b/AEPAssurance/UnitTests/Mocks/MockSocket.swift @@ -20,6 +20,7 @@ class MockSocket: SocketConnectable { var socketURL: URL? var delegate: SocketDelegate var socketState: SocketState + var eventChunker: EventChunker = MockEventChunker() required init(withDelegate delegate: SocketDelegate) { self.delegate = delegate @@ -51,3 +52,22 @@ class MockSocket: SocketConnectable { self.socketState = state } } + +class MockEventChunker: EventChunker { + + var chunkCalled = false + var chunkedEventsToReturn: [AssuranceEvent] = [] + func chunk(_ event: AEPAssurance.AssuranceEvent) -> [AEPAssurance.AssuranceEvent] { + chunkCalled = true + return chunkedEventsToReturn + } + + var stitchCalled = false + var eventToReturn: AssuranceEvent? = nil + func stitch(_ chunkedEvents: [AEPAssurance.AssuranceEvent]) -> AEPAssurance.AssuranceEvent? { + stitchCalled = true + return eventToReturn + } + + +} diff --git a/AEPAssurance/UnitTests/PluginHubTests.swift b/AEPAssurance/UnitTests/PluginHubTests.swift index 41f7229..580c745 100644 --- a/AEPAssurance/UnitTests/PluginHubTests.swift +++ b/AEPAssurance/UnitTests/PluginHubTests.swift @@ -252,9 +252,11 @@ class PluginTaco: AssurancePlugin { } var eventReceived = false + var receivedEvent: AssuranceEvent? = nil func receiveEvent(_ event: AssuranceEvent) { expectation?.fulfill() eventReceived = true + receivedEvent = event } var isSessionConnectedCalled = false diff --git a/AEPAssurance/UnitTests/Resources/htmlSampleEscaped.txt b/AEPAssurance/UnitTests/Resources/htmlSampleEscaped.txt new file mode 100644 index 0000000..686ce68 --- /dev/null +++ b/AEPAssurance/UnitTests/Resources/htmlSampleEscaped.txt @@ -0,0 +1 @@ +\r\n\r\n\r\nthis is where the page title would go!<\/title>\r\n<style>\r\nbody {\r\n background-color: #000000;\r\n font-family: Helvetica, Arial, sans-serif;\r\n font-size: 14px;\r\n color: white;\r\n}\r\nh1 {\r\n font-size: 2em;\r\n}\r\na:hover {\r\n color: #cccccc;\r\n}\r\np {\r\n color: blue;\r\n}\r\n.redtext {\r\n color: red;\r\n}\r\np.redtext {\r\n width: 100px;\r\n}\r\n<\/style>\r\n<\/head>\r\n<body>\r\n<h1>a header!<\/h1>\r\n<p>this is just a paragraph on the page<\/p>\r\n<p class=\"redtext\">this is just another paragraph on the page<\/p>\r\n<p>a third paragraph <a href=\"http:\/\/adobe.com\">with a link!<\/a><\/p>\r\n<p>Here is a quote from WWF's website:<\/p>\r\n<blockquote cite=\"http:\/\/www.worldwildlife.org\/who\/index.html\">\r\nFor 50 years, WWF has been protecting the future of nature.\r\nThe world's leading conservation organization,\r\nWWF works in 100 countries and is supported by\r\n1.2 million members in the United States and\r\nclose to 5 million globally.\r\n<\/blockquote>\r\n<p>Here we specify the width and height of an image with the width and height attributes:<\/p>\r\n<img src=\"img_girl.jpg\" alt=\"Girl in a jacket\" width=\"500\" height=\"600\">\r\n<h1>Heading 1<\/h1>\r\n<h2>Heading 2<\/h2>\r\n<h3>Heading 3<\/h3>\r\n<h4>Heading 4<\/h4>\r\n<h5>Heading 5<\/h5>\r\n<h6>JnUQfvpqkskQXyTVTJjhGWQgStdgFBDHHsuOMjInVpgHxToDBsBiPrSjaOlknWEOZUCiPrpZafcCvTIAYqYogPZqiBIFklkLgdCjykgoWptJHBOHahGopXGtdyfuGtjaVCIAWnuAdePfycnFeQrpPflqQncCYImtsdZxgBZYrEWWESqnQsvggrWvhDFQrfFtJHNKZWhPwRzMcySsRpNOgFuPFNPvyfOHoKKhQtIaWxRBwpJJJcPucnvkXAmgkBRCBIKLUUqTkDHOxExoGxNlOkhRXhmGlJkRvzwUvVxHTJwaQdHUQXMEZIrWHOCEHSwjRgzNTwsqbWQrvLlLNTrfZjnJzIhYzXXrwowGAOCkOxYoOQKXJjRTFJnWKCNgfztGgeYxQEcDMKjZXnVLKGVWdMEGoTCZcxxNIBywZYjakILmrJPIijSHBmCXuMDuRAfupSbJgoJmmHbOFOpvcCyNPFLUKhuVXKzmAhXPGihgiiXsqbnQafuvNgVCTUXpHCapVHVhgSrlmgcpAAKGiQcRNGuMIgEBwxsBLOriEvZiuHIAEFONuwxmJVeNQtFrTeYDaPnDdQOAiadCyCZQOqIbFPenthXHJENSjhHaFICTzpDhZEZmumcSdCNmFfoKmBVXhaSpRfmrmOlSvYXChtJJuVlaYmZJuXHrPkJQmVAKQHfKHjulwwswszCBplLiauYXBPmZAkxNnaGRAtrvOcFJqYpZSUiqQgbWUrjckKiDcPcFEztMYBbsvscukojEUbrcSFOFmQVEnJITuVihQTPYxqfRLzlopcdSSFoqtwJIflbaRSsxaekIZFdPZrrUaHnYhCWdtdLUHMTYRvoQKvVEeiwfeMqgVWrGAhBWVpVoJtBVdtpCdLFBcPbTqHXWdxIHffMBwegtKRhAsbTTcZGxopLKdcmgLpFigasAEQadpBDvCHhgRPtWDkfSqSTcdSNgLRlYDpVmhLnzSpZotsjQJfRxagZzEVXRbDpNOnFSFeEzyPPOtYuwINeQuTbapIbiWDVVCrFJQvkVuMbcbskspWEVJFCWEHQbOVhnoVSdjTkCmJChaUOeTYMIiXKXaRfnxzOsEXuMJWhfqTEKExzEtEDmUvWePuQKnYKJTvckVzsslfDhvpKOpwaagrCTXkfzLLhlXjaEtSUFTWFExAzZqxYQOPUAteALKaYmdObfiDJRuPAiXtZKMyeLaDDBtKKZQiKKRzdOePvlJpKVvrMvwxAYQuKyvlBDHbagMIUoZViRHzxTCxSnQoDowQdYCdTOcHoClxcOzIQDpJBRmGZipGNYFVThDtRdvDujlDctIlivQKYcfFIMRnWZLoTkNmnxgTHGHlPHaLhRMsnZRHiyXPogNhlbkIqTVFYmlntfmNursSEqaxoOfbOsTvJzrBdTGGUgtPhKALvVeBVCXDCsQDIyImJmCIglekUEsOsemDVGwnojZStIkcRJICPNtiqYWNPjPoCnwdezacvtNXyGshLEkbkMLuAJqLiCMDMXExuWcNEwxXSVXMmSovzFwxqYDtGDuXHTuuiWyGbuCBMtUHkMZmfQqsivilBywgVQIHkhgGyCfeqzQFTmmgbnZNsWCZhMVWpCfuqkNMcrYJyABYucdZUQehcdjUYxLoQlFtFwZCgqlEerVztuhJwnQLzcQNyGuybDBkEaPuqSAARghseIyExuJfkBYeVSojxYVbwPZNyksHnpZVnILIOehVIZlUegocSpsdARywITlUydfgVeXCoXoWHumynwxNWVJyJKAizkeUazaSadFVaxYUROLBeLUcAKhYKlurzROlNlzRGdwsxirakwMjJCemdiRjynpjGqgUyNdwABIwdOKrbftCiqcDWQcBVQVzRKEkMXztAKVUHyOXoOcrftzWkavwUmSpKZegBNemKZqDbyZadWeEJOrXBUitUkoqpfZjKlfoZEGwVTpVIaGTDdTujsDdfFjrHGfzyRusKEQveYgomMCkjZmnYqbFTdYIxByrgZZeBVomuJvIOQujsuZgOYearjGYNBWRIvcdJoBnSVVOOHQpjFGhkAfNKnqCoWJhVPCMLKcnpZPYkowweziqiIfagwXMzvJXSXIKjMIuEObVswOPtGTVkuvvTiTdahnyMkOfxoegpZElsghuFRTEvsFyFGqKllMxhCdCAkBsrkSTVrRmTHdBQcNVqtpWSnKYpdsCcMUjueTEfSVoFshqClPctiPqCkxHyckSCsRVrIpuvGbZAErTmjjIoZOOsCqxizlTCYaZKcCIENEDkvSCmINpPxfvxlITccoNdLnVRTlbcGSqtwFDQrNBNhccDCRtbipkiARuydcCYnMAePOXFHDjtkQUEoUEVghjnCLWBJGmKpdTYbJgPflHTPfZMbxvjopTjMudigyGxFwWiMjmyBarwAQXdvtkZEARvjvlZQLRGcjpnXzUsIrkOhBdtPdbyHivxgtWFWUDHPuViHSEWcWDypWjPPfBrYXdToJFYTeuKsgEsZgFVroMFyATsSIPRHOPmNlrHvENWCqvkcpNKjnstWHilagOFlRqnxoasNdZBXQNezOfoSQKMBnsKrwPkLeOGfCBqgaKWvupfcpOugxqPElPUZqfDmOMfjZygYVlvNqFLrqyzAnoWqpMsQffVnsQMRWtAyHBmKOdlzcQsxXrKSKGLFscYuQrMOlMexZmkPPenkzfXlRVeqTcljpugsAcdxHAgODtTEvIXgdtsAYqZcjPvIINPcVFTdhKwNzwjaluyfeTgViUUFMTvxtcWDhsRkvAiztMCWhSYkvhbDCVneUjweGfTdEFChqYTNDvlfoZYIxOAeMmtJxXycEOizktUpSjYCzJUAYfqXZulcwnZxrXZCESKBpHKCkdJZWATKatJEPEQDuSBROJRfyXPYVSpruWIJpOYMnxbdAIEPqxqlMLVBbsjPTrETXcDmEtSAEqMzcUFHddsBVcGFICptpxRdNfDfXXVGQtjmANpkFSjLbuQnKNRSXoEohToKLSoVRzmYhnkSLhsoiKrCMoCdUVmCCNrVqtRFeVNcImOgAIIZTvGMACrSiHCwysxVlXEqgbvVBvaPoSzGSfpOtLFZPCIdKhbxomqUFwhxIYCcDtgLExTWekCgOkcHwLWMPlFSvZGOjFUtwELUvGaRgwcHbZwmacVnzWWLNXmMLdBmNOscDagCQOFKQRwRAWqcugHiFbsZzDaCbYpGKFPIJeoWPoXJaZRLIjHAKKgIRnbarTDuyPLMhXvBoDdosiyNnNSJnWbNhTbPCdzKtXaPjqVmgFccAbuzCgoBtDnTZYAqhXQQxzNRHxssCWwyVpRFhQgrXrSaUdNvpChRSMkcrkwJzbRWXzKXKMaePYsLKIOidlOkLPsXTyylTRKwcFNdOGjuraDxFAWJLsAMAOHrffeOQsEVHbOqNgJGxJzasQxljCGgbodgjxqPkQBAWrzVnHZKhGJEVPCBXDgzbqgLUCQDtTqhPVYJNGGDIhqKwUABIKqPggRtxCpDjTwfLLuXLBRJTjzCIshMsHhpknxlTQlgZyDwyXKVzUjVnxkvgLRwwwLJdqpCfgRqtIHSNURoZSgQXTwtUGeuawMkQMxyPAhYxJnSoKZcsrPUXIWbnFdjuAVjiVnUQrPcvmHZxRLXTCsHExteNuRTsgcOHJbyUJZcdGjlsIHrnJIGshPgeCqkbMvnRaaYdlKjWSvviDUXWUYVdTFQLXTpigEDVgdmVXNjvVigWBUvsYEOAyTBrpCoUWIjvSqGrHuVuwGmmLOtxEGTrlhSriUZQumHAmiwSPTQnyLBHMORihkstzMGXVWndTfeqFLcwimvYfcxLwFAWKcqGECzAsWxhKnVbUhPEivtlsByPRwQyBEvBMGGNqpqTlcIMgvyWRhGOMKfxVavzCVErrgGqEihQblvdzJDsIgmqARIefcabrZTJfIExMZJGIVVbWBQtraKjOOjJIZVoUHIOeVSHAojqyNHsGeWqhiARmkJZwshTeGPFQRrzndMUBTJjAQhmKSgWKEwYArrqupHApaFbGNLeHKGJibFishDObPWflXItIAvXzywGRxCiClXaYxzisnSkvEzsJeWKICgICCkHmNRzjERZCZUiVFquMZuAHUcoRKqBvLexxcHGrPveVFBsNmuDyQKcCuKNBnKqWocBgTcJRkctPTlZYiiODLONaifpLQHWIlwuqrrzZXJlhLhSUQvoCTnDVDelmbCwBhtLGkyzvdipGUrTQFWbxmeozfkFhcVtIdXKczRXpZlrqSLBKcTlBiCPxQcOduyvgQJUYyASoTjXhLfuNefBxIdDTRVMVtlJPgwJZPbqLWIAzVoVTOdazRMmyJNhUXkvXCKpnpAbNvXnAVgpIjVegYERIizASaTDOXSEGnOvotwjVZlIlfiNftediCrXmIWqgruQIUnwAKaujjmcCmktEPqCQOvKBBvouIDtMjWLzBBQydVUZQYkvAKXJUEvAvRalAvjKqiRfGHPjcOJKNSzziwQhsZdMITTgLCNBYpaTKksskYfScRkcWjgwXkeijDsWpisvNGqiyjsBUhanztORREtAEmVyCfsbRiZpZZxoQlGNTaDPaHPJkRvJfpuLfWtRoxcbQcncBDzGjCPrtRqUmFhtPQMClMzQzefuBhoZgDDoaGlTQgZuCdRtkCizMGScAOTcejQYQUEgeMFvUOzEIMmWrgkXwuzeHKhyoAtRuKVLEsirzmUsRYfeExhlbzLgLibwsgQiQcDuioBtCvBejMpCDPztcadmjsknHfHXnIYkyaKmPcwldCHfTZpJTsxBUcxUWhBWuWaUIBoRjmKpqdXGQJGOnnkNxzVyNpwZzMBoZAGnzHFXNeJoihIWrNPLBlCccATxnxEPjolApYhqGgUEaWlOdFDqKjKtNXpayoKEZMBRDRnopSnBLOFBUHQPxcJDlAujlxvolNJDifQSiLzTuBndUSlkUhaQCGtYcyoRiUQLTXpEUTXmSfwDJiMwIxiWMekSuEJKhUzWVrpmAZLyJzqSPQMtWDfaQxQBesIVCTPUYxHHbOOiMDrXjmjSpvioIuqvkYhLAIwLUwpETmZoGCXytcOZDEcqqYLGNTbqhriFmxXLkaraEfqwBvVuORqZcEzrNNdEYKTvwivcitCldaSWEgVibpPPkFGWCafzxCLjsCTSNYHqISfFElQMOVsHzXJVhwznoBRjBSdmOitHDuSfYWiauckmwcMbklYmbHdpVussmwkOGaqeZRhFyTtsqINmxFlTaHLTAXRyrPBQfEdARghPhOkdvvLcdtWqlhwgFIQtmmbZvMWQOUNEiMXyBpJAflFgMSuFJqMnZiEjusqKdYrERdKspmzuqOpOxRKylqQIXdAMZhCZyVgKtYxvKxIZhOjcKOcYiqxtMBYFaRjXbVHCKtcHvqhJlVaeMrauLbxLKpCTJNondVLLpJJaxPBGHaYjqnYAsUxRRhbQvVIVZZMdPkfDwtvNLXFBplUVbnMvZPYoKvVqhHnWiuRGkoGbrkmQpBkqcmYFslZAfLnFbNegmuFoDugHrBphupteAVfSyeMKeGsjeYrhEqlJcNCORDqUUAVomnCvIwvWXhbapLREKPkLukCAYAUzMOMSgkDWnWfvZIWkiPTSXfJHUWIEyNYdPvYflCxyTTfZxoSGlIgYpuhSKtETrCHDETZCFSsqQrtFRZTiuHPMaylkzGCpBjOVyoXCJbLGZreqdXdihcrGtbdBjqPNuardpTnIaFEjsLtnJDUKIqBMmEHkNkraktyXoPmHcMaqPoRgfPpWcEwLezJJpdQivOdsHKcVAjPGugtGwejCNfMSleQAzmYTrBwudcVlyXQljmubZVNNFFlsirEkHgmnNdGqPRzGoBPibHJvnUPldVBinaImdwriXIFWjNcsYFujOJIhqzkVbsWJMFuGAqPQrVByGOqPXsagKrdGTYUXTnVUiicZdrdiCweVFZoeiLNlEFFEmkxZoVtQdfYemjeGpQTwPYUmexWuKELrNxSRCFxaTWlTzaePkDHrRgqmuhHuDWDPLKlaYfeXQwxbOdlWuQPiBekhqRKPpmqaaeILirKpzuNzIQxVFgxWBaLLNBfNsyNNSZVxemwIPHpntGwzCdBRemnbXJTiPRJpFiwHRJpFTKTcKDhsrEqgjFNkZultiytHoXWnxdPNMjDsfNytJLkaiGnCaTdVhkCIJYrynCvERmfLZitecZgxvWaRYQDCyUJDUxLHJKrpNJrJYmdySCETEbpAvxdqeYlRYrPvPMNnSHGroqpuXLqcsywfEkuXolLIABxNDrfXNJJXNtRFDFoJZbFtYXRpsInKOMBUwcEOiwGpYdlurrZMtXYqPWZmmidiWGLGWlTSQoZDxQculpvbhCBqnyfpPdSlwVhzXyDVfOcHdwuhwWQgiwusbUSqGSQjaSDpdsNGCVmxvfaxXomHyJeLJyDWnzDMBqXYIsvsilLrGJczWmxKgxDwZxvlgdVzqFzueLgxCJhoxEfCFkwaEaiZJbXDZGkmimLeTbXyOJIzGDTHnyAeIvZajPBgDtNaaiZpCYXOBNEKKewInEIeWzJliZaxzmssTDcYITgmCUidObhkLWSDkgkHixmqXmoqXdLlwunNjnhcxtdtpBdQXNaTEPyZLkibZCGsupaeEWSTmoigmtDnDKvvbWZSHXKdnjqUDbuqtqUzCuiWPptXRKBqIDHpjAkWhDTvKOOvubsGuyuOJggrDQLtjWXsasEuQmLQiBJqppfZrfqbjcQxrzremCfgSvYRAipCvcoPNvnUXsjSKEFzrvTavYRpmZrYpYEKqWNALxqrRgzYoHEJowvvCreOwrqzBTASLqobTNhqYEVHDMPVFOEzqDPmhWADtjkUfUQKNksBeDIiUZaeVhFdSzhYNdVdchdlEosiQxDbvpLlqibtJOAgzwUSGBDoCMvfugzEewPVidcPQlEILgfYueEgyjjYZYIgpBNCCBBnBRiDjPcYCBwpeljyhAvNpaieLHzIvMeHrZxXTECMHvIJnMHtwsuaTgynIlqUZrEbeITZUUdxakBvnbVzSFrExgRaQIQnoJFwXuMXMjhhHGAmvrRkNPqKhxoosoQcKStqYPhewWPhUXSXQzQcvOaLDcEFKTgFAKmZROdyzrdebOzKGtyQubeMvCDvPVoelRhAVYuvVWIXkUqgOymbeyqKrJfplbjHoqilzXWkzrxhXEWERKDEzZQJkhKgsSzcOwEZTMHokpwlWVBsQsUuqWqGqEojpCNULZsZvjHmQgyzJYctpLjdxPuvzzatFYuRWjJINBCJirQVpbHoJdDjTCigUGoTzqODqXmBoTqVeNGiEynghFyuUNdgnmUdprUXTpkRQFFetYcjCZgBrKQfqKTrZmnXAQfMsSCXcvBxLfevVYsxaRSMeJmHdngGmSLZuHgpKwefNtLzvBMIJlWezYxPmTTJOuKvzhIBlwJdeadFYySJEOknVIDcUgyYsPauROqJZbimlnkRLtbXGjAYxovlVgmnFsCCNJQEsRMLFVlKKHZzFuZeieAuOdnGQqMnMOgrfZMQWvjSvFsrVhkhRcHkURUQEOneZlsQCJwKxfQlUWohBTAlFLMXjKqvKOEszEgIbdsZsEYsCbhBGAVSsrXHEHdadewRisWcamvLSDPONELYPcQRMznTivHOCjlfAKazRhNxaEVyUQuGPFSKnRWswFZGtCYWfZBTSpULdBDSIltEAiwtGxLKMOxufXkfCaHcJcJPpYFHLgKImUaDTvaMhBraKNVtQWePGaASZoHbUsQnyhDNymajGvFRiChqPBLQFuMUAefgIhUCuKTrVvBRjgxmYSUWTidbGuHfbFFMlZLshVlbvKmeBQtAPPPaGoJcYSrZELdcmKhhRUFXcuZdeCuhBovsnAORwIamjwspOXRQTSInjcOaQrHIACRHbccfDnNauwTINdHpXWtanSJYyZORmDWKuZErVTghmchQopoQvHViZBdIFWKhKaYMIgAEGkSipxHDXlrgJCcaQIogCfQaCKXyCqLTOuLxnAbHOYSoEFBCqDjelpoxWYHiVmdkbbGEYCKzuACbBjSzdAgnsjwmgZlSoBsWyeZkMpGPhAYAiQuXePAmPhHKdtLVWiXPlVYpvwwzfbLBhJdvzLygRnRbWqrsVPzszPCivEAtoxibXpWWmwjzfBaXjgqNgSnHuQnlzPczYUtIakrFgmZmLTmIASHdufebxPvKBAmpIsVfOPqyDTPYayQmPsFoOjiXnNkazFgHsdsLhSnABTHGaYNIYSVdUENaiYtfLCDgNULHuYvXUsCppafVlvbjFrikLfACuqwhXswxRjoLagraLtqeVhorELuytsanVpAKZbDqyBdsBTWUCfSGxJvZotKozStJQIPSwOjhAVPcbwSKMCgYkeVjzkObKEUzIhEaFgMGvRECbVHtVjMgzGtPOTdPwapGNLrIzKMfWOXMZCsvzRtmfNWudrrqvteJGAgbQAIEUqrmvcmfHkHlkPHWvOoFSbpsDrAdMOEMeGAeAoGbNsKmxzVVHUQDhHjFESEiEydfxToFinasfKYeNFoIwcDhwiRixlpgXsfwzsGoAHQNOfBgcCjHKllOkunVxmsSvtTaKMKWVNqboszSUFiXHhyibGgFdnDvHnmcZCuHLZCcaUOXFqJqVvHzxThzwSlwdAJzFlvkHaSvlaknfFbNOEBSQPUZnmpVeeuTihwtPVKwKLllvPPWLHqYUTdLuKLRWktdYhmmnAVcmtUBKDodJOUOeKcDZeiBqiyrNzKuYVoNvtSzjyyJLxXkkhGaiRuGZCJasSFvpBZpBgRBjEmdnkirNMFFriYKLduhlElMYvlCHAHbkQqgzLLMFoXXJiFSXtnAoZxlJUyszMNTxPvpwsKNcRZYesAQyPmkxSJgRNGZKhjvLJSzPbiUxxtVPmZxsRrVJyxQoQWSXmpflIBETTYJokfSxVLYqsDkKzrYLxbJdxxOxYYPHlDBIjUhuYqsHfAwIItmKXTbhGUZnDJDYOumLtNcmkeSSnAEEJdRufJrnkOcdCpUmCvXYcsFHsRecDNRyDYecFpmZzkboDkooNuqkzPzrDsDaReNIkZRWsNpiAYCVvphqBJNwtyQtjPeguEJYPIOErMMyAaZKyNtVIqIwvqyEvmtFNndSYMAmuqTeWTQHleujrcPymnBpgWmHqwBpKnNSvNWBKXLPwqFexiaIOrJxQfLFZeHjsNhDeEDEqobSsjjNUnVqbzBwPVGpYDaQZEOTqOXHNuJAVIhUNNjXpdmeKPsfEolaCzCiaZaRQmtAPCBBPNtRwQlNweCiJThOlkFpTqaemJWGGrvtAAasrCWyQHLJYLXnHStJnVIgjqUhZpMaxrrxtGgflUqTioOrThsdLNnNlWusNnWvsKTsmQnZAMlBMMFgAkNgYmDTsBwmwXMdaTxVNwOQncgTdNEaGqHNpSzOVOlsSUolhBDechjhTMjJpGQQdoISdPoYgyLWEXgZafnFCTEPxWBzCygKJmOcgHguAZGUdWrzRJejGJraKZWEQlMrSRFqBbGtUBzHNPWrGqgIPPkqNQTWCwjMuAnFkCbbSkXBmFOcqcTtilAzbxwFGkphrkXfBtxRSJGINYBAdZmujpeUqlBGkpRhaZdaWgqhJVViLtCTgWThDOQiVOfOnhXPxLNhXNFhOygKyepqzkrDQuMaofNHGwCCtyIcFHlhNoIsBJVUzAIhpIpeYucnnObeZcscLAQWvhMqshEONmaIFZxbaYCcukEEEzuGELqEbXEAGQtzVMCJdfEcHRjlPqrixTlZcPSPZhVzQguqmIvNMwvmdXlxigWjsIDFsbisaWJIXRuPxjBTBAWnMDDrmeZwkjLxGKBUrGjwZdaFcHSeopcBKidppghzxMTQQrhJiErmQwUzRQQEDhTyPHlotLXAXQPlVxtSwwIfmFTusncicWsPQZhNcXxGmyxIhNWaLiXdthqGlLhHRpXMhdBEnZHkgtCMALRDpoUWPRVZDQNsZVCvmWLBPwUdjJwqJYlHEXskuhiMxGuRNpUvKvBULXbCmcZwQPqtMnTqTVxgRiaFRRHlBCVvVOUnURATBDpUtPhlJiFCTKGUhqHylFogppXfGYcHOzFFtnrUzwsRpPwAnGUdASSbitipNqcNOuYpGjEanwtKnWsUIFsFBdMMgFIrMrDucqMqwiLHqlNHplopWnASAsZuDJGIbFqZnrOmWVqcYmCbvYwljYLeRNZQrAGQByYDLwZkrhobyjxsFBhlZfDChveksWKcNMErCPYhaRYaDClUcGwyhfoaXPSKlbLoEpgItMPwKHugENBXcgCjVhkbVoQClmVLgTIYcMSQANNOYwsWKqgRnTZDRgURoeMzsyaTYkILZKZJKNPEOHcooLvaejitszdukkFnqHZxoXtNljXPtiWMakUvHLsJXYwCvrSaLRRdErhoIfeIUWvDVIyPIEgFPavtiWpmURnYnFilWPlCXKjuYYohpuHXvHrTnBWOQFNWdzYTviwJDCEHUKERCsWCbntEXlIuQEtHBqWBebYPWsPHaRmNtIotHzjbiinqYFrcIBunlsYJjrQSDJQiQvQQoQguBkBLTWzhsaCpQRJxicDuHCXcRodmfqmdqkRIaTJlRhetoEAEkDgASYOvLxLOxAPeqaadYrYCBxxpcJrGccBUKWTAtFZKWpGEcFzdXTuvzvajwndVlIJArEWiGZscSqwftwDpOajHqcpysakoHWgTAKJMjXOrCcMKnTpaxjjZBsALJdlLnOLcCEEEyNubHWdrCLmutMUSybIoJeQRvEnaTMrFYliAMOFImgHTcxUYJWSuWneedcZUaLXtUEiJKyNxaZeysrFtjDBwqqBYumIYpNSlBibsvbpVPoivXUcjTBrEGpEwOXWRgMGJsWeSwSvXrgnpZxuZudTftPPrDuYIMyPPXAxbhLTjMZeYxjpUwHKuuxbfonVSrfZDLWSLTkwzSsGFBihGhwAsjWXcsgifVqDmLLaeIwOWbqUBIUBEdhmKGbcjPeCRjijmOJyCwHYqqSqExZeIUdTXZVRvxRDGUThdzdKClJuvpnPosHMlXHUvHHiRFEmUJhkndwhFJOFTCPoydRpXcuCaAiAUPXJIKqYMkZiZaMEbFpyIvEbEnztcDPZEipbZtbjgVsoOuFUmVxvhOfpJARouNJoNdClLEQsKZqQOWdxnskuvDdhCPrApJavEHTAXCyLerhNTPWiJeormjclVjWtmXnhSzZXDnLETIRYmCHQOSUFMYzumFtVYzzouwaksonYIquiSfYxNwelpRbSAoSPyNnjQbXAEeuYkbSyJlWGBCyAfmrTGgJGuQeGIaMlcYZyBKHaDbawuEKgaleHMgubuDGceXHWdJgoaKnKhRnpriIOosjpfYBvZjhAmFLVNarjkFGndljhsEXnpNUwHMUqKyeFoNzUJtlkqnmiLlbOKdHBiiWMPkoiVEIslahzGhODYGrNVFTwQTlrcwFyaggUQnwvbBfTtcySHerTBOgDBJKjqXIlLAjAXFSTJiqHxZoRKVersyRUsWXVusVkCFFnDOEpJDluRZSmgcOgEiOYMkXipimbxsejWOjEiOPhOYWabRBZFgBWEqBkoQpQYDocjoypVHrErGHgNHgcLbJmYTAZqYscPlhoCRGDUpEiqUEKscuGwAvlfIAgrGFIDMndcrcbqQGWmAPQqjztoLWGBskupevJkQHSRWeAtjqnsCnwckEYJCPEvWeJTXQJkysrZUYlyRHgUGyyyzkUDtECEXmCqtmlWBhtSWaTGghtkHJrbBAWEqpaReCGhjtUjtDGEwKbhqReQkzjHperrJKZkxVxPkMQIvTtdOsMzJKACSGHLSowTgVXSePCabyDNBPAwTgtDKtsgSYiPgivgqBtXgxFWJHZfOzvDlfGhScwOvATFtHcpSCkmJYkvMYqbRHIkDIYBNeHWmJuzHpZoDABcxiTlJGjroloQKvnRalxeCwaCrxNZIWzYSXyuXNBaPnANSOFklZBDvSauUvXmUxhCpmZtwcmsVJRUyYcfWYtaPbMixmhpMxSCWNuSCJRfZQydyQeCyAveDUfjWgcnheQQmJMoWiOlWyxLHhLuvFvRChMemghLeFHNagQkqltOJSwhVOhuQNhyyOuAfokhBYHbXsxgVprkuXFkyfYBuigmbizxkaaLEnElZzJVXxVoxnmHGNQiKhcyiYOjDkhSHusuVDyxmwoZVkdESNtzCpuqNhHrEMSTcRcHNSVBanYpNfZuWwVCxtXUxQHYDkIOpsQMRevYKdtOJZVNGphaVXAtiVDxQAGOSqCpqYkyubSbntKzeZBmCRODQEbbLIbkRfvXSqjuMrWizYuGnnhbJajRbBVpMPauoLkdYvGSDTNsevqmFGDmAZHQRYkELiOANPXatxZIENIigRxKwdQdUaCKCWhDeLzlzYdSZBaMjbpqoJzsHQygrQvzCBNYLojamuPXlSqexbULLAeQGkPfYakGdYPlrAxFUYJsJHSLeuHoMvpSEmLYiEfqggkzESmhBIcPsxbxuAmCHjEoRqsFVYUAusISNmunQBFmdrRIFRZeuQHOLgkXHkDMDkdbLUEheUOgYOBYMqktNlQIjcJHEYYxFVvIdYAUFgMIeBeRGlsFWyHoldQkkbMpgKMEiAmtdnVrpDUKWMElPRZcVYlPKAhWcFdDhLPlWrLJrCNZBwvUQClYnXDJAhHFJcHjZQfLiKjlFuagKYJXUqVEqWpyrodatGlRCHRiHQopnazgONDZKklYkwErvPeoWRmlUeVepFaHgiUGxCLcnjDThytQonSPNLYyWpFKHnaXyFsOitffqpBWsMIvwYGWIoxfVMEJsnoJcVWPOVDlSKlHzDnYXfWGHhbjRgSTWhhAqtTsJyAywgOubsFTtzWncbusvujoXlGCHdeykiXpXisGhpzWnKfOlKHdDoQTYGuThVoZbItNXSUMuKGSorXMftxeRKtNhpQzDhJNuEMVLMBLWHwlXZergLUcHBDIsOpbFwZdhalLfGzdzYoKhRHMzxscQoUHMdiVCYweSXvGOFQZSbgjfuhIKFcAieNezQrgWJxyoneJvxyCHgkTCoKFuyuftSmMabjVOxucpPmfSLnokhSQaPMnyZBAejpKLtHLqzDLVaiddIYZAWjFIjWwqTLikpfZHKkoxrPjeCOnafdyBqxRujTUsyIDNaoejzpjsAnSFTekfoFPyTJRSWQJrGldtfeDAEgvnoSqeCPCOqlGNEWSoJjhuhZekrkxBLrDtevQNsLwwbDwwJpePQQxMPlqvVUxtWjdPCJjfGLXzgucrHDVrMAzEJmdlGrrAgDIerltmsbJJDGQfgqtAUCczufvZuCjFcmackphEftBtspjQWUHFFKeASAYpjhSWQhrHxcWhFWAdGCmUTobdAJayRRnIgAThXJSpiVFTyYyVFUHunKClTFiTdSNDMoqMDXJeOvETDfduJRgDbGtUoCMcovNmvHPwmTVouaDQgjHSGhuFLKilXiIlhnmthdSZzPxQGMZsaMrqEfsmqkfRpgCfUcZSZnFyKYpSiULiGLSgvGhwNjmoyzWEToNVYaEpQGbccMLumuqkMbJXooirivUZrYhLDbbudGvdLUfBAoEbnTMntFrYHZJgrFGvGwCoWjzWwihuVcJVRvtZBuBnTKjrePcRPSEvttXUBLKRvXNRezEEPNbwUXvBEzLyzZeTErrveUmBMRXivarhxNbACdhmclAbKlqOjsEgVzcUNQxFGHSYjjZDpBfhwFSLOgoZuSLEwEOPUVyuaLJfrzkYzEeqPJaxfoHlZQuJLhtDeKdTHSEjkzAzSOgSlQpAFvJtejHJQJVVgbRebrNULLbqvgplEAdJVQNOoaWTETXxlHSorTTJbHwLzmTbOZyClQRfEEnviSNoCtEomywVXpdwieBfwOARNIQYFICGXtuyHVgqguLotqbeFDtRSrvuXdapnxdILjLeLvYhgnNgGHyZqcabrNoKtDKUATKYtdrkDwlKWgnunNgfxMrKzsrlgZuvfXBhvBYtzjvCVCfLKDPkOYsoMbwaPNbCtzKzaAHVNmVhdCrROKvBVOkPCFzpCBDdlGwdfcSSYKgqhKstbvPqXcQgHzZAvKmxwRGgPHCErONCYbYktLsxYHWeqXzJpNkcyaiZvbRNwvtaxEPMFGJdxxJpkMDXDTDCfwwXeCgouPIAvZEEqiEcgVrJgoYktkQfkgPGSmmgtwbhsDvSJUblPUOKoQnxfFldZiNNuxHvXsKwZMSHrWwFIZQOguzlQoBPBFHWBcRVJLofbFIvIdygzMOEylKnVqJXZypyMRyhWvbmsbBXeFNhpjrnzZzjamcltkbckWokKkRdAEbENQNiKgaSpvoVvcufWzJfxkQUTaTSBaDTbJAYNtvalletecqXbBmdZzjsSzfRxDtcrimFaSLmhgGDcRNbNKxFauaevSODndgNSMEWkUxoUzbJNnDXOAWvujMAyTusBEXIsXnJGefUCKPKFCKAIzOKhkleYgbQForPmNGyqBNEouQhkTsnheCqgGspvUgiUdXfEVObekbvCSIOIxxpyVVbbRmOEWTALKenLYqChbjkoYIIlIDYmwQDmGDLIFzuLXGjBMjTvBFUkDLugoXHFJNcxGYCnkTCkLyFNltFWOZcpHUquGrsgUXpWkZDkGBKyyQLmaXjNENImcogvjplfOWwINtXdhMrXUrewSItAGKTzeKaMHeXFGrZFNUmotOcmiJSlFhVBUQsXQfNqTVbRmoCxzsCwGMyfPjxfgJqkyGGWJldcRIQeYaHNMscuxGMYAGmQSUTkxSJvwfMEiyxCJpxePQTlmSVgUdGDVJGNKNPsxihibXjkEebcYWhwpIBwYCEWHoaVEuSGJlExoezrvEJdavfrNEyoPTUwlpyOgJdUkjWTAhnSIvRMamPzUHNXujXyAvdUBuTYvwwqWkgJRkkqCpDvoaDDXaTNiELBtpQmgsXgqbqaHPCZQdcOgQZMpBbMZaIHMhwQRtfZoPJEcpTSLQRimAHboJhhfmpXgLGpBWEHBewotfOdkmkiqdSbbxhagyfmfKvxdyHKssQMNUHdqsSDyYqSIfpfknAFNrsQMSAlHgjWGGIeDyEDaxWRZJDeGLLKdtOEGTYFffvKrdIfeNZtWSxUgneInxvQvmsVBeHJdsDMKjpxUSXBMMviJmtAFErwmxdtdkGyYyqRVUSSvLVnQRxXjIjvLbrBlVYqvzGGbfymrAFPGVNPSLyAiKUzWqYgdRJOChcKkNdjxYtWmoWezhudnXSEtiWGMJtwabOHSnDfXCOGlqidiJWQAjbdLcCGDSEfXvrkgXfrPhrFTvdVRPVdnJJuaaeBCVCqcaXqhGmXSNedjhkwlXmNFFefZrFOteJbdlBNjoWJxCPZHuRzusurelIMkOFrQwkdRmvQAcbrmnIutujurDtvHmulExwNTlxOgDAqSzDBFbGnkowaffnDXwdcoxTKMSorVKbtAqaswnRMSxHhdkqWyQFUZBitmOASqabGqPbEtwPotsyjCoiaJiyiwmsVXTCZDJDuxNiPwIkZItKcMACwzfsdJmDtFQlxVTKZsCoZwuAxHxXgtynLZrJzgMYzUaUDYFLzoOaplHeRTUuyNRiXbsLMySHjRnKiJZddCnfXYMlgrFXHapWkSCNQYUIoQdBqWREDdkctthYsJTcrKqClPCcJaECtlHIIvMIilphwJmyXlfdjxMJhsdDScEKRihgiwDAXwZxiYZeimWtylKQbHRwhrhOBzsIiayvlRMByYOyIoXIPfoTSbMaGDBWTqUcSHUXhiBqzkmQZpqacoCKkWPENmHVhvAcOmmSVyyFawiARspTRpVuCfldSQqBgXgQApVwzYydzWGvAXtyoufrYxzaxQbpdHMuTDsnlhOxNkSGvyalWaPptqcrRRODdzqYrTggtdnECBvidtDgEaNCvKTrjneGkRAJvlGrZjyuKLzjZRHuGkNoMcWFEAlkcIHkBadAPIKuuwAoeVvLCsHekNhlLtRQqKZwRXCkahpPmepZqONfUJQeqIHKebYfPzEdQjzHKMCoyJWaRihgpiDLkZFZFnPNHYZOZvlouubErBnkIcGBVOKeVFXpchXUEMMrmdvepVCJcCtTUQdRKlsKtIZFQPKWscsbqSvbFLpBkHcQYtDELRlBotooYNcsRziqwbwKGaUwxFkBwBIgJkZMpnkesncHtdDWdyMDpWXOdjJwuDRyfDKirQAhZUNQKlMYvQiIqMevpSawMxMvLHtLeeApkNsixsilzSjatpqhbGCAGonhgXaqpNyWpkoCcRHIDQFiyddruPdPHeXnzJkJsmmIhpLeVOJmugQfrJLiWnzzJPApFKhAkLDhkeZzIomNVCbuUGmMmObBHHEFMATYNYMlqmFIfeTrcUnjpJDyZWlIauNZDaRsBWxMUjcQzDDEvknyasNybnlGnRgHvmcGwBbTwUpihhRuFrXcUxrRTQpzMHesKRMjxHJUOEYcQyGYYJRgNLEAZtrGIqJjNNaNXGMUaBOkJGIxbUGAvWNcFggvboGJaaYXdXnqGQxxhmfyqYXOgfYDNtboAtgFewctKGKXmOkvkIxaEmfqObnZnQHfIFLAjHOeKZjLncTfRJQhDHgdfbttGXCWllyqndLOkpaALzmIcmCzmFelpPqgxDEJIadsaMnEhssTXLgyyCxlubswRQJCDpheoiAzoQkIzsZcmzHWWZHYdAhdehNpbvJBATfLownUEsRMgEKtAmyDQOXiekvUDdMYSwExPIqDaClnmFPpVNfnqObEhQQqcWDplMumNqjwKjNoAtLoaYQbBrVTXpjdIgSbtyOhVXYczmrCyrISHEaBZVpRiSdsYCUdlmkLNElwOKeHjCYNMvEQzvublHXPmrOgxpFeokhxVHoBgiYPKlZpmXdJrMVSETbfSflZrgqcPalrAuazfvQvTdtszuMNWPExYAkkAvZLbZIeLfGlmKWexqIeaLqJsYfUFIoMyZbRkMMiqJokMCCJDmVjBnNqrWVIZJAQvHvRwtzmvYHzOvgdnLtaXuTlTaIdRNsjgriZdDiaCgUSZOsBZLbNiojTDronRlZIdHSiLGqwWSrGexDpEbyPAiKllYXLpUvxEyuPWRxydANYeZTbPzQykBkbHYSRmnsDsMmGfBRYKWbmTEtNOkmqmIpuvdKJuCMRKsbslICazHpuLkQliLpcayZpMSIJlaunSlcJjidcpJIcQwhlRJYSZsuILlJHUXlMVqRSrkmNQFcxLrDTIlOxNiCWyTvgxttQAysBEFHInqYUDTNgJzIjWevMgdyOOpyOkeTZoOxJCKTEKkyQCsMrjRgHQFoIDcxXsqbxHMriHSjEsXHOeyZIVaBmKXiaFfWJevVtdTjmTDGLMxlCiIJDoJPkCDaPtOsKprfFUtaRotZNgABfeyqFGqtkqKmVslBCSMkNyfDIHPDVnzIsmIIdBtEfxVJOgqqKvFMVXEaEGJJpwSQZEnxhaxzYcGuUKfrnTwmDfKfcKXNzDOQgWVycHkqMqUWLBqbfgiqSRpDykrTNMDNnMysqIrVIAbmsaERNBwZgcVuGeKdNbCqNRPwEkNWxTTwMpISgdZOnwQPglRvtfeYuxhKVnpUiolurlPkxABLcicIpSdgmKQdwSqCllAPWDxghAHSQHLjddDBNUrFGIBORVaFErrlpKwzdyKArfFRAfvYdIhpoKkJUuNztLBSnPVoapmabcrWxMfPBPBdhrEFEXEAIpCQxXhnrDWDfxFsaFuZVlTgOxISFPJImjbgpgIZJTKzwtNcLrlWgOexIHLOVjKMWGFpPAzSHIjAUppbrnkUITRsKMASABpnNJTHzXzMIQgtuKDCNEbbgCepyXsDPGzCkaEujclGuAqWnLCmVcMUYqHqzohtlTsWtdmPudORdREUUTwaaAELjycrhnZkvyICIoLlVhzDiySjVqLwEfdaebvHIgwJnqoOsEmPmVFdVKvbkDCkAfxIMmRLCCaCWOnrMkLFhMeGslibztnzpEANhanSuIkaglCRzdAJQSxWbagQZJeIdQnfLvkWyPKFRczCTEdgYHJzHoRzowitfwRBzzgZzxaKUwKEaZCMcYaknyzrbBccvPGmSNphfrkgxEGIJYHJjhpRHxQjliopysHXoCLVnunXdXgylJeYnAgTolZsCeISadVRVNQAXPnVRuKOPOBCxquxYeNnSMFWZuUfbXeViOrxfqmRSGDdqbQkqDsBsDmnXjXajBMscgTaQJpwFHTOkgSluWdEZyynrozynEUqBdRorKkZZfFvKUakwoSNawckVFgxWfhrxsXxCStTbUriuVIASfIystSoYeLpLgFkfpfZNpQqqqvzDvKFnydcfIrMnuadMcSKsAxogWxiMcEKpqQmGsSWiiWkGFZfZuCxkUefuWLwPPRXXFFqBRlBUITuIrBYVoCGJpGzWwcLyjTmWuViWgFsFxpwfUcGSEpOBVEYbWLhjCipOXecCdOVxsbYyjKmGnvTokUaZcWRCOgnVdFKeXVDYiuneRHnAuZFBohbKSrvPvdDHxDtlVaXDAPKqzWVskDWvieipBhqNawwARdDPchJiHLWfxdBvFvGejySUWNTPhGTGkrcJJWCnEPnEdcjorTXyQBxpRdLBxRxMYeIOVWsToFzpOYnWCLrOQnoqWWytKWXPpfaahfEakEpzrBkBhnDZpQZgAqpOJLISjdwdlSUWqywIeiltVozLPkeOwgeiThyzTsXUxMsFQyfGKpfRjCFHYkLqMEhQAtiYqeCVhxIZXfqFYcaeDJtPKZTqgzjHcoOFCCwWVcLXIdYfcjtFXfmBwtdhqQUUMcdvglOvSxXqwRoZrlWkLvoIRFYsuNqbMklIQBxLCrlvnoJDGWmyXPttyVsHVMCTbJPoPlFQhhPUPqwKdfxfxfMrJtteBPnpzzWhONdNTlZnRjafbIoJCvbTJRIpNJrltEbHkxoVeZCLCHqaPIwaUqLRTyztTrQAusecIPDNhUJQsAVaMMiOmmHuYUGraucAoYaSsBGRtqbUkHHANWsKgacRZgDiSJMWmROWikWHnBUVrlhLLdtuklDSeGMQEziuYIdWlovtHyFjvthasixqZhHMHjhOpiGzTgqJKVJFWhBdKWSgQGQzFISFNbpxQJCUhBLeiOxqFUIkgDOcOXIuWwbBrihpnonXqEZdyYvWrUHGndLeGayRTlBeYpJuVggRJeortNtGGHfxCkFrDWWUSdzCshXfzZGjzCYKTdwbdeHxRBgExUphfuIwMplHxcnZcDBqLMsvijOviphsxWXRyresIyCXfYJRvrwaZkhLMxFmouLWskoZdqYIdUtaJRUslUUfcoHIUXcxzPmdLvlDvWbcrkRNTDLDuGywfgSBvejVgLhwEOwGDKyHCKAkRIzfzJWTtcvxcwwdcaLasWwimiUQDXXCvuVRJRJGhJKRNpwHIcftQkpitwUXTcZhxGXJFrbHLhUFwCaUsLgSExzcyoWxMsQZXaJUdUrIPiipMotuKdWOOhYFHTjupsbMgQlleheiWgQWLOAQhHBbvtngUmzCoXLDQTbcZGXBfaCdgLcbFuIpycFUZyyBZegHXyDFUckwGiLlyIjAEdjHGpWsSkotZhYrpbOtAgCFVXGGxtykaEJGKotcFYeyBvggFVwsPzbkOWIPxWBgCTIEhuJyrKQHBjVnKHzWWPLZBzmKcxgxDNeJcyIFetGOZJYSwazroziKHNdZGALNMOdOFBMYWUaeYyiZFdwRpYLduZFutdPOPztquwZdQAXZKWeIqHyjnEDKjRIrYphwXLoLmzQNtczWSqKLxnblfnQNaIJiHSgUsERRPzlxjMJucjXhRIPipMqIIHBnmicLRbJlCTBkfDEDwqitFjZKalhDWvBOhovXcFQlnOlBIAFugrpAquOcvAcYQJwMNvVAItvdcaRMbBppXmOFoOkLfNdVDeQhaFjtyclsUyszBOFOjNSDMmgzMbfAcqCuVSDwcnlECrbXmqoYzTvWzUnRWLzikwhqGnZHOsMelkADSZsFsOtZnAHUUJYtQAAFZJUeNRPHXPKbtGnOLVVrNaHbtksTmeJeTTRRgqbCfYCERzbRwATyLnlHLrPFzGwOTIClFbGzxHBmBYrARYWIxFiXhLOPcpiwZjreFjWBlZbCWbTrHAoYVyCdhsPmGZWxKcQjYRKYLnGkFuMAUUwvndUhgDhSCbprMbGPYHDFeshMVcoAYriYuxMlSqUwvRRtxdbtVCmiVotssMBpXmFcqcWrcOcyVhtxUUetGDHFsIQLpFfhPRPLLZAElqzbbMUKDcsaYzqUpoMJVLMwpuXFWVBWxXjnCfjBqumBJWJPKKmbGxExSNoZzkomasilRtPjdgFuiuVsTIFecgQOFGCCiiZYVSTygugXuEgMhRpEthlACvNmYAqZfzzjWWrZUwGSoTZWJEWMmXqWVfRBApyzSvuxzNJqSFiLZlqVPjHRrFikwUaFTlcJzmJlHtwaicvYYtSVGbWhRyReHaAuvrPCnVuVbVmMUROPKnakREdfWYmvoShyhlGyqhhwOckNumExaUhIWpkkPaHeODGZHXSBhpRuPMBNPgAFoSremxCUbasqJKKyogemhtiQLiQFYkVykOkNkNURrffNAbstXfYpUQyPyQstowWkOTWuOegajCacSGGOlRxNWHcoTuwSIcZJuawGxUpluvAkrDerRGjByuzQfeXrJfBsTIykDpCGCiQRtjuZqCqwRConCpkvJGEdQqCusBlZKUoWNxdMolEIAJMbApsZAbqlpkpbwgAkvmSTxmZbKSOaUgtKKUAEgJWfDNerAMicYpcNKtGZKreoriMaDLAJdAGwqcQraSHEsZVpEWZmLDAalSNBySnPmoZQRxWeVGFCpLsPtGpsDSWNAAlauJlhYAIcDzVIEQbAoBgaGtGaEYBSIvGEsVWkQPhRvEvUNrMtYjHhPWNTtAXRgvsHmfiVNkSoznZCqDADfNteedtFQacluISYMHTGoAgpcOZsAAOHumwlTFZRjxylgLMkHnijtcGVLJCPDjPyGxjqPDLRfLpTbeVHcBZxluZDmyzITBsTAQbnjIYLezqnKIVSoWtckXuFzwqPBteKLyDKBeWoahWAJPNsJqXQsOAETJxsEhQhORweJwwXTFLgJuJJYsncHqYBBQTflLTqDCEoKfdiGIGjUQKYsRIPsOiqpseahNlSyjjoZjPeIQUxRsLuRgqdhVOiZSyaPoHAeYjGFmPASLGSZvvJzKHIdLuBPCQQoRPdAennjetYoSpbFDyWuqQMggrJgzVCSurbyGbywFyHXvlUxIabxtLyBhNLWbVsLRnhrAsgOSKZEgPWmaozUgrOVBHJVdJeGUkNEXWIYUhBKQedQtPUjXgoEnVeKNmwjTIGncZHILOdfZqqSfqjdyzUxAQLVNQmGOBZbSYqcVhAmOWjGebxgwJtDunvxSqPcEwgUQkzcJXkhGfboFyToImmvSgGhPvEtUgiBximkkhcjXZatGQVmXhZRhOZdIbUouBiPHKCYcLofdEsqySbPrGxAudIkHvPPNEtCCdXkFKmbriTMUCnHuZLMIVMyaMqFGUcWuIdbiGhMLyQXWpHbdVCZLrvibRNUmNBjRhhiIowKZBCnaGHYvTpwAToMrhPjJZdrpKEaKDJHZspNoLoLKbJmRaClSnADBHLymUCpYaGdfVpjdxqnrOQaQpYVoRhQFmKrHJGXGVzAzrzixPLbHkzIXiItnQaCHgshddUMVmNbxcxsiXUyLBFEhIcErLwtueVseCIpEiOuFiQYkrFkVYMKZLFWfinmZkrFOiMutRIFgcyzhbuevNqoFJUxCyUIqxBojCjLUlkVVYDwWJKBBLCJRgzpOxxXwhKSzmEJyCBuKmeCXukxaxCgZFmTcjPVesEwUiAJXCbxjxJiCFcQRPoEbMihldsWTIQKqoCMAEzBKJtRPMZQFdVRFdIYAAmBFKvXhvTpDRIxwVaHDxgZHnOzskxGmDgJFdoSMPFFdHEdtToxEnuARFcdOCeAJStZARXauFqcEtoHaodPXGAqLpTBwImqIAMjsbwWpXzSpWnIPvfbdRnYtkPgojbbBKosLtFUlkdEzWDOdFsbBPxMfjTxhLwPqUytbQZeIqDpHklhSvWqGQTLXfihrcfpbthniKysYmJbkBwCJbmHSQMKyZOWhAqGymngjoomxXcGJpVwXzZDvmtPThhzjiNtOHQjujChNanzqGldxdOPNaVtYTNFvUtmgRBcQXKSrzeKoAVLuQSUENkeNCrrLSptzCptYYbAHxnsbSHvOgJDRMxXUTXfaEbxmrGmfCaXsefdXSOLqKARoTNyLuISODtkOkrZRDOzvYRcfuVIcjdRaRsbatHZjLkRYpfElOEeUYZYfPHFMtfhxsUUcfgFRsEvyBTWnRVRLfXEodIcQxHUkzHjkTvgfsWYeYGvZoEVwNlxvxPrKxrhZCnmrHthyGVVoZVbsWMnUpNErpuugvnlqUcnIMYMwimVKOQcAcNRitvdzysbHnOmzgdHxmyOfeLLONZlfSxZfMehKMfTncYXftUSZEfUUQpBLlNhxUZcsVSzfIEirEeWVhFmQqrDNIJErlCorkoayXZNbopdshrfPkdnFqPdLjyAGcnZOCuEfhrGyacODcvYQuUflofrPbNcbgYXyGTFNHIiIahHsNbnfKyAeOzwigsYcemGdFKNivgcOLhYMqPBUwicYZxmoxDAphlDBWfGdWmAgAOraeNmSnYuLJkLUCFtNbzeQmjbFEZDkdcVxAPqBfTsYaGujTdrPkerswvBkLwVynosiBWokwRSFQPzRdcKBfZfatdkZEsJyfUjEXGxYHKxFpInkbqAbqnuRgMxoYhZQgQJxhcTflNIYkqaBdSmdCJquBrRtaYXGgCoacoKCarWlhdRnembBKSBKFHGQRBclyVSifnHhkgiGEWmGJgmQOQvAqyJgwhQLAKdyyTBHlFezdVUDdSrhegBGeoJMckusbZknmpnxBhgjavhSIgBiKvUfrTKNMUxYJfsrALvaartGYVfjpqwQZShOxbAPpWFoPPGzuIDBFrWFyNtBhzVpmIOOSCyjvtkwLiECrebgFjwUmsbioTRDIFiOuhaTlvmArtkDacQJxzxckfUuYJsTIBUiUoNOFFpDdbuFNFDCfGnxrEIUIwHlAtJKGvzHTDytsqRjLmPJGuJgnzsyXtQAEaYlSnQUdjbFlcCqcluUeEBqhTkOXyMDfPmJwbDCdvKtfcJHMjlTjNzczUeTvHpLquMZpoDlzpecbjRlQFTxNxuHbwPlrlknCvgbfOUIGiyUBKZBKwmbWosTXyASsCMyuzhfmvXQVZqLdSOBMPcSjztwSsSFFeIWWTbElQjxPpUzohFeBoLIBXraQjDOLDynUepamceLvVdvOMWhzAtjrELDOpasGTFYKtIuoEUAEEMyavqMrxUPWMPmOKEddJSRoekcBXpbraUKPDarnRssvdgmWDcybJBbFIaUoDBijaPNApgJkpIyySMBURGEpbnkGgePoZAFHLpLuRwaiBuejPeaiRVrWJaUOasPUyDgimoOqQtxhCqAUwcLycQxDLjYoIGEnyZszoFSoYShZqTsZOUwfNxHeswbfwBfIfEvtifOJXrspZnBERobBSFPkmadCAFsKEhrohKneNCdDpITbONTXBOuDGKBcrITsStqlVIeCPwWjnpohMKBXYROpepHouIXdCfJZMSXiVdlVPtAfbYESGRgrtQmXHQgPJPxiUoOlweJWfUiBXCIIKAspctMDoXrKwfgUyJkIdKzeSGDoAyXwkiuEjxVShElyTvWlTQzGNREdYcWuOqKBeDWNqHCBtCLpbEnerkXWgJCOkUhRosYxuHptTCmBEJrfEkwltESVnTcimCeLNvNLcnHpNSvNHodRjKztxxpjVszByrXQtoYbLfjGAqrsyUAedlLYnAoGEToFCGmOZMcyKRirqVBcQFcwiIHytGgCePdlfhBhZVJGglTAXivsQaDSLjfikNfUmULVAhtAcuUMWtRRlBHUxSSctAyPXIqWGcUSyGIfkHpMYpgENDUihXwuObwOyhVthTQdhDOFmJCHgiqrYMrTyamyKnBHCYSVNdqdapColSdCKIcCFDDkbWcNuIjWPsdrnQxhpsbyXWmyeuejjpGkXjBJfdmhdlCAyEYnkLBZIdSUSkkjxcOzjyrspuFdCFKCmJCamNqZlLBSQHAzAewAOpAlaBHpysAvrHpKYUsDUVnunsItdChJNOmtBdOQuOBhGETJSQFaehGtgRMswajIbxAHvhsQTvweMOqhaRmeYUVqvYFOXJyjuKaVVUKYSjdhnJKYBovjpVpcIpXZeHObIDNjpdHuKilgkoFBmhqVMNLFwQFKYXyqmDdndwpIfsitEHWdMsnBfZhIyyfGfqJlnVaDCROjuyQbIhMABJcseWLyvTwhspPkTjTPuHEYcoyVUGAkWsKdPHjYfqnMfDbLPlcZlKnDKWfZvlIScPLxHTrOmQlEOrVOBvxCkXDhyEyKwbCKGUTTcCwQVXvtLgtzSDzkqcDznbodSqPFlDvXgzJsIISqJntfwHXKIZnLRVHUDuWpGSYpLLVyCSPZHpsPxrDKNEIJiAeGsJdUOIQuLBRGGDoXSnVCRzRbvQSUDZAKOWHvCbzaqwHfENUhlUSAIxzxVbMGNWFJmqUUdFepGfxsbqdiADBOlYLILwJTHgLNoXxnLxYsRnKyikLHkLfRGffephmfHuVgIZBDaUrbdXqPAMzGvZpgGpCxMgWedPUDHEwXzjXlgpsNFaVXmqqcYmzCuvngFYykbvOkpVoyMUKrKbVqBkTfdMWsdBnvKyykdBVWyKntkOQprRIZpqMGVqsqhyhhfiZuGJAHYzoZhHQSguQnfwyKcBTNlCMDdDphsKMtLSXmqZQSyLIkcUyeozvEUYXDrbURFtpElASvEoooPnHKyxUhJHayDWPxALgcSTJEawBpqfoMVBJedlZEQTYSDOJBZqwdAarbsCZIWntkYElCLeVFjgMRyCsIlLomlrfTMTYdCEbAYzAgMDdIOTAVOFyaDUppfkcoqPEBAEBNwWPEYXpDambZAYzzIvrtUaRmPHpfQGMgsAQFXrjLYjTwXbEORboelSBMoFebaSbWuGdYVxznhZeSUMUDgaiYooOEPzRoSKCxQvTyRUxGPdAXtrhexskGmYwSpxCURjuSXmDEqFuKxgThEeIBZSQlqAXtfKLwsHPguwaMiLFNKNdkstmnfgWBvHdSNGhdXfYIawDhmVYWqylMChtcweaRGXoxHOnRlWQxBTubviNkfFddTeDgIKDGnNwCelTOMADSjXUxpHCypFxeKbtIIillIKrOFfYmUWKVBXqHtmUNxpOWmdaKemuIpXcyUmPCrFzPZweErCQRMuTSOqDUADghgFjydPcvyZuOGvMCzfMDckYeBSjarOzYcckUFEZZmAsiEDOvFmSyCZPURjkMpPdCnYzzeLniExbGZCPwWEYnXwumfgiUVykxtKLFquwSYuotxAnRwVzegUhBqqjhFcluevelyiCPutaeaFafYPGnREGAjcJvleiWCdDmLDWsidRWOCNzzCfDrORxobKHIEKkCGMFxdsLdRBpRVRzyTwFHvXwOwxYnfGreVgmieSHUvWZZbZTNWgLPHGFWIVeJGVMqOjjGEdrJobagxSHyRnRiHFBevmVXdcklaxZSKjfbbaxaCpUKDHncPpiqmDoDFPOXjSUslWWJmcIaihfQwOtKqHopZUgrekcpenrDVbGLojnNKJreucYOpqlsuSSqzuOJCdNLcZLpjSKRgEGsSkCbJAspMyagKmSBWtYWPSJkKRmsznxbHxBirblJvWBwmRUQpcvezUfUXkRgXYwOVGqjkRplfguEUGHHrcZNAqqBEtDqrzMspCxnsrJPOLJwHHttzaTpeNdtiFniAfiqXwwFJcxbqzvixroUEcsclcndADfaKslUbiGyRcXdtFjkVIzQEFHGXnkfWDsxPYaHveGfADHpzhXcaqbEQwWHwSUTJoMQYhLhJNCptDYNOqCyTSDhaxdMEEQQTQewBQOiHopQGHdYajUXGzCSZoHzYGUaTWWfoEQKSiQivWtYYdquqrjYanMHnHKbBTGXIyVuGTQjfniIxZCDJcoISogUEHayCGVPejyMFGIaDzDUDLYeEdBMkPvFuCevUEBNDhjHlUSZywvTbmiWWxKqGQuEBGkEnoSLNbJHQhunwUUFitQJnPXGhpqrVssGotuPsCRLTesRNkKQGDUuEsLcMPvPWuLkbcPueKAyvouKKYZWnvUCecbkMwzLsFpeJJgAJABMRIJNWgcgPIjqEeVspKoaqpjcMHBapiwWGyFjzcmJGoLPkWtbrKKArHnWfsFypARqytRLXlEPOKfhxhaVjXOjHUTdEBtsWnAzkgXzqnlvJhfKFcMRTezcysIXQgqvVhOASbaomaFZkLVswxVJXLFejNVYoiFrcoRNTfyGmdHrPsOIFdKjFNDUYlAwHtqEgdvgiKqEkXnmlbHgyECTInPrwnIXgXNyjFxoBObKZoqmqzwQmTXDkmVDJJTIqiHMTBFuMFhzGxfWcEavLzfzOfgsrXLqtscUVWoHVsczDSahBVuqDRUjpnNLASVSTDaODKOgwmnDmxwmdCwqxSEexuDZMJUFhWiDytUTXeSNtXLQGQGKYMUJmbmrTVJjbhcyctXUwPhATfnqVMqThFtfKqbXfTdbvHpQWFGwTFPQwKUtNmizqhTmjVoTiLVQKDctzQbZsPuRpZSywVZqRXdSuKyeJdwfksaPfYYKmvNwkMnKpotpAKEIQlsNIkgbUQWGcAGZpEVreqxhREWMllvXbhbVlDJZqWOaZvYOceAXowSIusyQzjMQcgyBBPwzopRiYVQOGOvRTtfqJuhyiKufphLvYtFGtQgCVANxqCdGSFQiMdRZqOfokquuAfKnpIzCTDnGuLIwXhiFPUIJXKEfcwuppBgCcglDNgkittmUfsXCLQowwhfyrGhrATIKXMgqhaXrCPbtJCfciQqMNoMxUgybFoiXoTuzcxSOlchUFbbrbNkoFsqWZWhwwnxBrcqPHJojgKkWVHGWakBmFBdGkjgehdsyunOuRcvgAzaLNZLyBNsOeNjjLgknAIXcduNTGknZBiPbHebWvtavBDGYwFXhWZGrXkwwoPRExZSlSLzuGmNxkGOwabFXRVUtiVidQMjDCDYXIktkanfcbIykXNMuSGvMyilaHFbbfZFPNLfuvtFSVeYySFxCDaFspEFiAFcEakyABvpCnPBtUllTFJSLNmYVuqWoOWrIRoaGxNDBlSCggpRneRyOFkuwMjGrXnILgdoqXMoUkeffYBKwqahmqgotGwaHToXTHnhmjQVaNFQqtpOtTDmKAwNrquTrxtRLRfOULDiqoZDzTZrQIpAxZLHyWlhEPJDeQGEsVQfpYGKomgwsEgjaSomASsmRqwlyZHUVRHCPUgpHRaCmoJjjIiQZmQGbiXKjLVQvwzWLbAerpmCQFjEfTxpcTpKguTbJfEYvSqaXADtChNLTQClVDObscoqjPUfjClNjkSAnfBoLcaFYarIhQByTkfSkGffHxUqJWPRGKlZTAXpBTcVKFrYyJidkmflvJBiTaTwlUWHwefhUYTxKeBmzbYgTGkCVjTuEJhOdZOUJxpzkDwrhegYWMarUJjYxXdyHhessRSHnmpFOlflxbkoJvrxmZpajybupMyGDpiqHgCLdVTHvWBjfKGMSlHPKdGrhkfdDhPeiZNWrgewtsoMguwobOYKyKgdBulYMRCbkRxVawFjkaZsUsWpxDBUnVcooWLKpMCMaUjRYMNuxSmcivXsrjFizmGFobSHfgaRwyfYOMenkCJVFgCIRyOrycUFkDKrkRzcdloMNpPYhnrPwnlURnePqtTmqyQsGiQxlDFpZfrjQYeMlerwLeKAIJZJDlPYoXdUjufhaIvBRHTdkCNposycXagKLMQbOOHxvXyqwgpguTCoGZcEqboSXfZVyoZRoUuLQWxaeYkUunBsaXSoXbiOrbnLxUuURbxVYWNVySMZCAEpPkfEAaIJArZcwOThSyYzVbUCcJgOeBcomSKaqfPDKFCGLQoQCTWAvTjGTfnIjhiJuNPOUtmGAKUVeplsXBBpQlryNfXAiPqPvccsPGDdGIbMLTMWNGfBPsVMLaujxABiVqdkFyQFzqOGxLRSXLYBeCeQGZzamrSIMSyazALKSxEkyXNGZDZXImOQkKeZHUGBmOFpOIvCBoKBxWpLEnuuWHjXSPZwUHIvlzfAOPAkUSwvZrozUylVbByPcdpkujCialfJbpZIqPbfDkPwRUOOSqgCQfbQxLobihKPYILjsqelFXKpKZDACoCTEEeRcGiuwDSHXxEOuUCLiFjShOlQuBiuOqdPbJybxCastQBDWapRkVAkISBJcsPFJnlzWJXZglRHPfdCHjINZQlaVEOuahbloOCNCCYoLSUTXjxoQyjacGXyRvMXuyzSdfJCWzzNsSAqaYqXjehCNcLoobsJfMwkbToqfwEPdQIaiLxDsKEEZDiZodPKyDDmnyzyZKRaSzNqcDcQdcPNSldqXrCnbQTzrgiLyEJGKiUJJlVfxKzqXfyXqDkFnToGzCgBchvMPJcahFYmwcyjHCMopaugDotmVieFXJIvPaVaOFtVGiJyKHdZPCVUKPLNIvhLIbmeNlwUZfdpyzKmjusfiLDmqYVvzhMtLlMYnwFqmmqzgcYbIFLWwUhtxDbvvuYrqSxrcaeSpabMulTVOHedVrbbvtIDwYyMbMuBuLGwAJYZQyiZDTrZGBIyglluzutjTyVrYbZUtQJOWPbKvzcfezmSwBllFyVkrnKYxBusEpCRrmKmhitAfSsjrCtMleWJLeXJioppcUTSXeExDNKJnYLLQDBEDYTUqWzvGmvSUYfvSHRKpeNgSFuJFqmLDdjPNmeFiotgkrACtBGMwreqrJEFlPQuCGOVqzjfldNEpUWDvSjQxIGoYkeBnKEYjfqtCfpMMqzIGEwyIoZMdnyySGazZCRdVehmGHyHAmbjjchbsqLsyLDvCoPbUiwFJSgWDSGbRPdiqsZFezigzUDaCIFfLqCyinjYkhCnYAyQrQyTLpIbnQUVGIperyDWCYHRdXGIePTjFcNFoTzpKvEIryGwHCfTsmfvDenzuuOfbGMlrVncpgSvXAdiLxIFESITbVYsbcxHmpQYaGQjxQsszCcjTngKvEsbZhurpgBelONXeGVmkfDItHEsXAVKvERDVqlCQVaTOEVknCUfAFwhKyCbpNVZJfQfXTTWJRgeWuiXUwRpAJHbZAhjKjcdDFsEpCvuvxmnMyOlSuhzDHFzrCiaVCBfvxmCyONXvPJEDiElRANcAtRBNbZsgrEzMWnLNvGgIwcycbmaaHpdMEfRRIceiLfSuFlXZWkazEQHQPprOnXIGAoZWMuzXVysDqvUHiJUuZtxncWbbzJKECYituORkiYMpuOaHVlJMEWyMZJYmJVOKxTBhkrNbqPdaVRweireTQCdHfDODckmgDBSBPaZkcAXORqcHLMPLKXhiJEGtIOoAlESpBJVRPNYiSgCeoqSpVTWybNbywJVpqrkAjccwTEYhsrGEBWScrZGZtsmXCpZOmDpaJMICHbPodpZVmFeQrAReOaJIXLykUSPryqSFxxzNiFHONZLTzYJVJWUQWgDTJttmUkpuIgKZbeLvXbBOeIcqqtfEXgjbPqrtxECyzXUzfRHFVYqJfmoteYyOMaabWISxlnogSVoLKLHfjTlyRnmcymgGTXfHdxgyBFdBXUtfIwSXcBCmnYboGNnwZYpqqoXZZuovPJDrcwsxMBynQBGpOQqWbyXSmAZrYvSRstTjjfgMxvLPLcctowDmYGFQDKZzjTstBSCqfPPjDQJPTUuDkRBJgZzyCrtWQEaPjYInuSJXqkdgIJZRBQqazDBkDUwdXPbAtdHckZkrOEDdGTddrhHCrZVQUmlfESGvZJsaWkXHBRQiRZHpNXcCNPlJVmzbExpTikAADntBeMlVGccimEeKQaHwwKMWXWDeLWhtkrvIDFmDuHMQbAcfhtEOXKnbpomPDjrRhJEVVhGUxEFEdXkuFLiPzwvobILxiDvZFxYZCIKQWnuQHMsDvgbrVghDmZDcICtcvQfTQDlzkBdrydNKDvYXVtMZeGTMnakBkrjJLGXchhnsMBAYJxpRaQnOpTupKFywSBzrZLLnJtwPbYimqLGSomzADmwlRUtjpipqchYIecVcDreiLhAvDVyKkNgqJZGJzQwCVJvwNHckFozMIcqrgWJwJBHXnnBKCTfBPnNVaZEJVvrRYTwqNNUHqcIjsMCDwPiAJoDmBcXfeUSfaZCDvCLECDGYoZHahfCCTXOCUxGilyNwsQeEEdnBhTuFPvkHVPMmwUdNdtCrVbmINjUSmKduWPMpSSReBfmopKBTGXKGpcQHIkBLvqmLgkszGnEvfaDsMtXCWxosjwfWDOpHUbWjjBCfgPRLptpcFVnYFOVRsfuBffAyTjhfjHWNGFWJSXCnKrYSkXYWwcMcytmDQggUpDUhJsceYuCMngCuxYXBgqqsfBENAZbwxAcPpgPlfdDrrmyuHcCDxGCvYCANOKWWEEARBvdemwEAFQwdNvtwBJFwOgepVlPgptyoCfmyTzYgdhGgRkgdVOGhIDFmFkQLytcHZOHQfTmcvoJZbIkRZtBOhTeNJDMmSkMcEvUKpiHKDkUihcyDLfCyytmtNVTEGzPwwVxFkhRSBqLIjpcAUnrykUAKJvozNvMiuFgEPHKfUFPmxpoBmIkaeAQRmiXkoBKhPzMNLJRNsuUlaJzFZSkfNAsGujxHNzegAaMRRraJyQNYufxUJZjFDFYGpJgaYUusdOZevduXEWfhtBgVrcldggGvNkhFdzKarHWcyNzDcLQwMkgDkgvCjJbCTDhMHyYSeFakFHasCsfHLtRkpFGdjuzvUHvZRmERSiFYWpkyyQEFlNmAbZIUaRZaZQfYLqQIOrNWAdhtgawKlwbfUNxPyoVFJGafOzpOIQTOsmsoHglXVYzYvyhflJdGjXVNIMkOKFuJeqOrPAaCrKyWovYRfydyqdNcLgEwuuelairkrPwqgDDzxWPLITfEwvEgeYYIbkLXXmwtJqSCuIixWiuMowaLpHuSOWwFhpUrPjAwTuOXIivPBiOELXnozjxiWyRhreRdIwCjhacWMUsEkKkLjhMdNrbxDwOJeRspYLQVYIVuZcENjAcegneHGhOUWLYiXwvbYkACQItdZMtBakdpgSLIJndktmILhEAAtEIljuRnEmMLVUwCcGlgYveQKjqeCJdQuLMQjkvKByZKHJDNuIoonTwdUdcniYDwnhvfVzwUioqHbRRHzXhRvKMQtcnxBWneRkjUdMSbACWGrwgvTAUcuZjnjUALDDXMupYACcrqteeYyfgWaRRMJtufaCIjqKJKdJHYOcAgbNtsansncNHCeYigEByMqBYBalOMBWKdctIArLVJddNiRTkExpVlvuOmiLfRapmrFeVnmzNlyctzZWESCzRLizFVNMHIidtrlPqRfzGLGboCAONySlqGJVsyygoeTbHJQnBhjFqXDpduNBBcqXqZmbzZlaSQBGstAeuhtwidfPYhVOLnlwcIGlNYuuxyYPfTnsypFDtjkjwolgXkaDbHGojqKIedgmBnYVXhNuURnQfYBLAxRPEQvEtCnPgHDOMLslNDCikVRkPkTwNsrEkiHqPwXlnRjTWDIHShXGHtiKcQPcwMhdQataLatIpEGXJRAsbaXRquwgRUWrCixngfNRdwDnvgbrzcvNXVWHYXrZnrgvCLDHabVzDOZBDHqpThnqmfXfDcwIaAodlxpjQzzdHjpApPOOKQyKkpDybOoYMmAsWfZeHsnCfUrwMAEhhmFQPdlOsuAqPOMerNfXvJeFScMCuoJfrbJxhCOiXKBKoawtiGnTmcInfwCLITmdMnFIuLXgmGTpMEXFDrcvRDlXgVncAzIoHRbWkmfivBOLMtkhasUbqrSaITlMkFDShTQrfinpQoDLjhWTsnEJQzDPQbpHCloAkhStnIiLixHMCiRDZvxoKXDwYFRgFPhlGcXsGDaktRoGiGFjzGpYshSPsYPYLbuLuPoXmualRQyIMNjGREXQDvRRvUxKJQjdVYUcnkZJlVfknyvRKkIjFWmifAFIHusFKDheWYMEFVUzazKZObZaZOcMZPLWZZuzKJxYHdwEbCjvJgcrkAIczRqITcIoCPfsqwhJjbmDkxKoTaOSiAYEggCZNTbZJiSSZQUZMcoAMOYVKZUNyvVQCxoftiALFZqXMPaYYqPmUqgdtQXyIDGFlkWDVKoGSgkTsdPlmmuyPHEerqDIWEDieGVXFDYoteenQRzQVyuzYRFDXxvQhIhBkgQMavCUWUoBfwCvgcnxPBceaPJcZRcTzATOsUavcrRyfcZbAvKJbRlnKweevBekdNqkHuPqcFtPeMXClVrrgGRBzlZjyuPUkcirglzVVupbikkKhONaHGFVAJGxwlxwRhnRxKwUAIHUIHyfpvoabVpHhwcIZOpnEnUtYUNQoPOAoZBJsbfdJOXTWykPbwEWQPmpaoBbMBLmmbnajpEjqjAwHZGQpWXktsGJTVKSmPZIjyQWIkbDwwhPLtlNsJSkZwxoAoiDROmVmQhxzFtWNaSXtQsKrxHtScCjLBityJFjrsttjLowLZaLHTLwbBhUowjpzDZXwaXPoFxvPBSvnMmAyNpNBwEslCxuTQmeKdLlzXQhnewpVBkRHzgtVjFdpRXKYUnoLZDqLYTeRbQAedbbNzJrYJwBlCxqQbqnUjJQavRzvXzrkcSIWTIavvCShoXndiemIlKPNKMedHXSEZDQGfqjnsCoDnGMEHimNLrbyGazfmcGwfsMvxZVlrIqNfqqODWHPCbKFcRpwvjGXStltPxIHtSpipRsGmXwPVerCFFRfUWAZUCcQrDAIrzWeYxpmvkHgUkDOBmQeYHnTAmjTxvoyQvgoaZEMEwGoJQLPpvHzUZHXYVtuxXrvvJryGMLKoUqjyTslMlzlHwAlDOTZgJwIPADVecmvcQgTqvcfbydYzFTeSNDeSCSWWNCdePqfWhEiCkfdKzeHAmnxuLmeEjHjfPVdVuJssgnXhzXTrWwpkYgpvEnoqNSrapFlfibtqwBvHjDtPJfNOOYZFhTGKJIgxdoWYUCJnHgrOyUsfHNeFKOtnGGAumuyPZRDGKApiQdeZshhzTuvTRkFRGzKYYAeynUkQuPNjgzvaOiQXALoMGViwCBYWbPqwTqeMNXRTxqtjDLlzwIrxBlzRkTSEZpgyaeTlnBdZAcEOvjpcEYAsZdvjuBCVlsbtFzdalcUMBrJlffjnWWcQrmafaxEsborLXjQIoiSKMwWsSVkacgzAlKZxSbhVCEQJgnHotxVjhiMQAMyQrAwJRakQKcuofUUpTilQJKnsIjaQiZQFMlIBPvNEHYVjowsrFXFahKAeuJhghCfYdITnBagLUyVBcTuUqFUfIcIKwsdIOeNHjoiwytrjtwwBJcnabwMvjzOsXybLtRPeupmFUTiKhuYWiJHDcxtNlbJJAtukEwSCCqbukBFQPyJXOVhjZYzRkvMVRqpVTbOrcNgUkknrNGKjyLosHdZsUpfAGvosIfJYukEHGGWcpbESaxvBtBAfOwoaeJoIdZrhcOLoIUZNROPzvNEuzIMQqkpIalGSOrzWksHubCURGrsOnymyXZypzECcrxqKPpmiWlMoaxGpqAQsKHUCuOWLwsqdnoAbNroYssJozsJmGvRhGQBBgmaUENoakYfPjKhIcoHlaokykAwRBbQeoWYheKMwNkKdZLaakZNqLZxzLMSuLJJxltLUSBHVryuQleEKytWLUJtRCXBjwgFfZMPmkaLIQXqlgYjCUdLjWiFRHtIcPWsjOHHmhQmEbofXvMGSQjDcFTSVTosiCGoFedRpFhygOrjEYqUNMuQgJBFoPktrenAUBwFpHSxpTfZnunaiIQijHwOGjBXQjZNthYpoVxNZzSomGtKdHbdvuLdtNJdsfIOOWiWpfPDPgURNGKzzvcZLBGwHAgDZntTSynxufsnoIsHGRBxeFesCUpuCJVGPXuhGNZDfmTogxmgagYgwZcVSCaphtCnndrVDLJTODFAXepBtGhXqdoEmkbiDGdSKxAPTMGOHYxnvlMiZeAuOjEPNGYoSwCXkeOErGnFXrZbkljHMuPuqPhMyEwUGnWjxsaBSpHYgBtbeTsLIGpoyvXMROnGXCEgCbDFfJvSEcXsIYdCFgQERuzNlzXMGdWNLGfBnODnUWfkkPtrELrlWQwSHWzlwPfNyxHVaBIIRkHtuWUoyPivTMxjUnbCTSXyXUIcMIpsqdNeywTYObqVtXPRjUFEQmMiuLzTotYzEwizIAIipePduDHTnAOwwIuxgPFtfzpurJLQNQuDdhSWatHnNUKewOUooYVJYElIGRKMwYTYyOHEcEahOakyQtTvCYrJDyIBilEjhsSKegeVJWqxgNlVNgFlnsmTWZifrImCjMIUJfTdNTfNqiEqAAqXospwcpxjtnFlBbQGIuBFmEvqBOsISXcIYCODFsZzDCExnmefUlmbhvcafZprnsgazWVXhNtqRHxHIDoLBFOxLEncQySpFMBCLNKeuBoRsHniykhYNOKLdsEGyPhgqpKUQFWSKEskGHDYXucbHOAEBoiRtVsTKJhelsJJRjKIjVgFlaFBVWaJrHkxnUrERaNWOvPvpzMANLkUTgLZqdWmJBtElHFFMGfmirXKLMhYsCsBItYhVWmMvtoAhQHyxCSyoLVWFobtHwmIvPiLmAocvtWCSsCpKHIeMshhWbRJyelDMaJflWHIYpjfIdJGVjJvrEQsflXRbbUpMqXejBexwnpaNJLGDIVqtnrRtEvdGPHYehsIVXuSBcVotMYtLXXqxgquWRNVGyiIXXmXBpNovdttWGyLcSSqkUUfJbZcYpPhymuZlfrHnFmIAUBqLWwZxaLoWhGWJUEbIPBiLKWHYuzGZivzDAFTpdGwHnMajLhuLrYZdimYPKMVagdAUveXzilZUIRuLPWnaOIXKdsHXZWzdFvkWTtgWuJMrbJczKhfDLqLVZCkhRLmkhzFdLKbNHTrEKXdGevoVQbkyUMwKXrhCXTzBXCfWRwqKlaLpMVMNygMnOmFVKHWUAxLJkdkhcgDufWFEFMhnRoNhkrevKRRcekVnwYBEgvCsBUBBYyrxAFOBwiySgKTljqOPYrAfJQUZuraNYBfcPTykWZpgJDSrEuZVNBjNPQpYponuelhqlzOiEkehBpXuwVqOwxwASLiuNJoKRanMNOsgiZFMozWABfufhCTXlJmOyQFunJxMplpMBCmHpjuWkUnYPLvGfuIdjETJtzwXGEQnNmETEQiguCLJhfuodtCJwRkCzIyAuhFYbBBAzgNDJAKpEIbOSxvqLYeQXJZLusNUDtbPgHnBwcBQulYQLjQZfXqEavNmAaxMzWtVBVKFdqoxbWxbQFdgISmyCQbaoFzTeqjLCLjmWHfgRveOSryvvStbYNkaDOPTfQFgENnGcNPFHAPbqMxdftdTtKQxyCcQhLrcZCzEGFeGBtxiwwBBpzDgTXDHajtqtAwnecBlLiLrszdyocZTnPkpSRCQsdDeqDMjfuNiMRMxmpvVbstFySFUQOvHErwzDxnrrTKheeQycbBeyezsvPwoZhuwEeKNjnmaFvEBhXHRZJkDdrQNYYEziAjewYrILMIsZZIpRNvUqljbFMipxMkPvGqAaxJjTooeyXDaBzkUmlrflAkVPhqseMpZHhpcgnxkSofJVwbCnxiiurvMdSbuTXsYEjUgvWAnyPmfZRZfJcFAFwSXymKXpcegksLkOIzFRKZTJAvehwYPDDDvTPCoDnkAJoOotcnVadnAeGLnyvXFxkiEwncIZChnTQzSwoAQqCCTcociqNPlcaQzbpOYscGYHogYlqTurpVFiQodLtHvcpulMCUPyQqkWjVkAKfwRFMooPtPgplEJtjMfgUnLLTbnLXzUYXCuWffKMyZIzizjyDHopPSDmEWrSkusKOsNanDFSNfDgDedbLGYjvhMhXTOgDpuzGhPkrDmYdTlimCQqHAaLsEiIgidgoFkiDgSLjyaAdmYoXsvoceivCjREZOqMrzAlCjZOmWqsiQneGGmkunodnNeFytjiTQXiNiShWohcCAHnyZwuQxEVUEWsXCYSvmdSvWFpwOtgRozPbFKlAqlpikhLtDfLbAvRoYAOaCUgDJzlEeszrlnRmWRKUqPVsiFjbAsKvFFRkMQgZqyGIbsADIwvrERJFAQPmCjipdhFlUobKQUXbHDFeFXTUHMUmtoAotNFWwJnYRcNxyfrQgPLNWfeOmKuRHVMUJUiWFoZzFwvLuJgVXBZflnPkYejUNGwMHGsdgUyTsXzGELeWmRHNoQzbqrYJFqfYMnAHBIOGWNsJqYlvXqbXczcphPWQqYWhSatzBkBiTchKZPbVEoQYeRyArhDDMeMpswmMCcLUmJyxwjcZrSWttgRdNSqxOGmxwKZlebvjoCUemDlweHzJqPPnpGKQTxGjwLCGeVygmvDdhIOpOjSJHmLYTCjVZWzGYSvvwgQrcJoFJjmmXVxQsuwyDFALKbwPAXnhOSRjKqBjGEAzFVpQxVKonvyrZJmLToWHnRujTzWJEkmyyRVebsSYaPwQFLHdWLtPlNQPqMRbXsGCmEMCjkIavkmNEYcbXfdrEeLhEIlLNHbKaDbrBnlCWMcGHXPosfpKKoZlkLGzYOQHkPQduhORwSxQLUosmWkqqyZbWiKOancRfElmUNlGYfVAwERHPjsdUjFbneAUgPDaLAXuJSCtqwHRqQDGVqCvlNJBnYvHirOeQqcCdYylNolgXjYYSkLxKdmUNlVRwjFLTbLBIBalioRRBgJxQXLgnTqlrEsbkUEJDDIQYyPtEqmCUxawtCIAvKmeBmyNLFglaBtyBWznpfmBASuQCsHUXwkuJLbXrwyFZbghyzmUZLvDVGqKyWLQDGOKMJqAPWbMlwkeVcivOipRtFRpipNxezPfxTjgucTCCZhanMfGbbxqvtjpbQbxtSXxNnQzevecMuaegVSVZamMKZNFwsJldrkdqosfNHzspsnOMeEvtdWZmSXpwUxZLKThxpvtiqNROLoKZqQgQcFeiRfpwCArPNaiqePCdoiRdvRLOOgizxAnXBRHSLyaaZVMBnqvcYnAcEveGhlxvVKzUoToZhVbuCnSASupDeTPurKGNjVxDUDldijgnniZieATdXaoleEHsIGhIOmVjllGXfQJzIFoNPeDFPdKGuyZGjsJFXJkKnKhOQMBYVaMSjkqYHeBFiaeqTexpApJyOHWZYgkAqQGaljJeDaVdRSIjmgDOGGPHJsBZbVUugkRxpcSwcHnITEEYLHplzlwtBVSLCKgmahFJPCupkMBLqxAnFcUSUqEyqqctEElUVdrDZklVnYJnGrxeMzQuqQpybbmCLYmKnzMXptOuMsQqicjgjdUNlLlEnRxVKVULBlvtmNjUzafEEekXdLbeLHooTBLPJEoXvkjDvQDhIkRGJIXhyYefMWQaZIjsOePsnfYImOwzhgQmZyXKdGzlLFrsXatUjkcfweXUXcSMdOOQQgIjPMIPEKLebECIlyqWwhAGJWEnSVNTnetaaoJLIOIweoAgoXetksBiYxnABvnzbcbRTSEUfPHqkjPsoQKTTipXSrbCczIMhocgXszyDoFBKZaVnjjnTeQLRqAUGVqTTdxIxfwmeTMzCwChaPsgyECiMmjPalKwtmNNuvOzMereLvALbJMrtbpvLoIqJUFVvsCMXXtdbjkspnuTgcOxqHXmXUbesUseTWnuMOikrCOtBBtEHBpBRMhEcpXplANwiokWzVOEMYqbQhcdTJnFqUrwWezwEhwvzwHBJQZCpFIaOdUPulKPMBTQlLoZSzfWOKXAdAKGGDAMdBBvuWpAyuaLwbkciZaxJPYtcOvAXjBtUFRtiDqTPDRqIbyhTfpjoDMbsgspuSSQqGUPeTmjWHsUPoaOMCNwLnRgyRaKAOMNxuMdmvEAxFewrHIqzWsrFAfzhJJEgSFSleqiSXheFfnziAClXFFRJeReKLsUOuTXZnjOBqINwYXbjcDpBWitEyxZZzVCNezKHALjWpKrwhHVQBPjcGSFDCvqtgeNDjAjkgkubKyTtxZTimQnhjvdjUALXWPuYeKcmgYYZnBnQhWdWGABaKgpCYjzrSeFEXtEgDOFAHCHhILrGRafJVyIaqAUtzZFXDhWNIMxyBBmUWsyVoQpJwVHDGPTNdFWRnwZjvTObZVKYRXkeLmtfpRGWHBszZckXjViKvZRHfaNytKXGVdiVaeQrWxcdWDMOfcezaRjSJeBitVroGrmmxDhiapvikcZBzbWrLezJQLMRBVRoVFVhWmEhjbmdYPTqkMbFIomMfyEfgZReMCTWOimtWnrwxsKiTkDEhlAZLLDmqHlqMdeXgRrQTshOgLhlvrNbzkQfDgZyAZTkdPXRmDndyLkEfbXQidULrZmnXesQuzAFlEwZxnfdNEHiYsJpIoQWMEnZFsahtXpIGkodFiYAxmZkHqTSTIchIAeFTVvFHHniUlVwlyDBXTRLclOnQObLyyrhjDaPnpddwQZCChBcoiYWmTQPxYxzKMcDGpAoOjrCNyRLxwJYnfJkHvYLFXZTLcepYKkEucBarzcxfuLfxBQGZncabCQwPjFGsVHASUokOSqcqsUPwSMumZWuTylloVMJZhgpNPRbTweFHqYVQoRxxxXAORMdakaNslOsuGHxcNjiIQHspUDcdGATRVXDLFCAnpsVZlCcwVgiRoZnuOegxdXwOAlurfsaAntHMPmSFkbCXJTGoPgKNOALHnwgfDJeqHdXoIgSYfeASVKVRsOBhDtadWKrZvIAftVqWMkFqTpRGaQrSOSsnxeIgEnoEOkWggqDTTWfdoYaqlJHjTvFTSIgxuqLXhRovmOiTAXPeSIKwKgmkbxHSfJXSRIUnJVQlXavAdZpXeEGSeuYXpjcoPzdBPDniEZZOTqaHHowBEliDhbyzAloSCRolwMrMzInArfwMPCpqSMAAPunRhIYpCTaIbsAXqUqZMLMYzDRxzCrlwjTJRIpNkNIAxDWAxYxmgSMkChEdbeUdSjpIVwtfAasytbddXchLEFRqqVorvMWJxPwJhcVGssUsTxEYdlZWFrPjbkhGtbMyOIoWHxelyTYDaikmVPvStIxexwehlHoHiJwEfMEuJjSSetSDOrfkfmmxClOQhAbaTnXtYjaccfOEdiHqiofDAHVwxlHRmZNGKFimuPVGKQVnAQZZxzasnyHislZwGMdiFElFfYWzXwVeDLRkxnAlkJQWiIubcaVXonDnaphSuUizwvvQGsCjppgjynGIgOMsVzpRDYDIxAOagPTdHkrsCzvLzwbExcuviussrMWgfzWBYzJTdFNZpZUuTdRySTipufGgifhsRRmaApfOKPdfVPJITZwBuwYAgLhvPydKSlFDqZKpSMKjbHyPLVRrSTNAqMFochPQhTHgEuLZEKgShmlLPddMPSakXUmgyLyGoDRBKAUysApvZMRgTTLVbgYpVxjZsNztdsDjrosgazAvldMXPABfoAXwNLfSeCRPgfMfxNpgsOjfyNqXWnnnpXsXzPazzoWXTHrHilbDRZzSLeNLhvGYBCTzXKadqDIUiCKNFGvDickUafNuZYTEHHFgNsrqCTNRvoAfKukWeTTiKiHMFLJiOvgRWKjbKUMRQpODspinEnyymlkbbsOuCaCpLSfdOGxdks\r\n<\/h6>\r\n<\/body>\r\n<\/html>\r\n diff --git a/Podfile.lock b/Podfile.lock index 0368495..3993276 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,37 +1,37 @@ PODS: - - AEPAnalytics (3.2.0): - - AEPCore (>= 3.7.0) - - AEPServices (>= 3.7.0) - - AEPCore (4.0.0): - - AEPRulesEngine (>= 4.0.0) + - AEPAnalytics (4.0.0): + - AEPCore (>= 4.0.0) - AEPServices (>= 4.0.0) - - AEPEdge (1.6.0): - - AEPCore (>= 3.7.0) - - AEPEdgeIdentity (>= 1.2.0) - - AEPEdgeConsent (1.1.0): - - AEPCore (>= 3.7.0) - - AEPEdge (>= 1.6.0) - - AEPEdgeIdentity (1.2.0): - - AEPCore (>= 3.7.0) - - AEPIdentity (4.0.0): + - AEPCore (4.1.0): + - AEPRulesEngine (>= 4.0.0) + - AEPServices (>= 4.1.0) + - AEPEdge (4.2.0): + - AEPCore (>= 4.1.0) + - AEPEdgeIdentity (>= 4.0.0) + - AEPEdgeConsent (4.0.0): + - AEPCore (>= 4.0.0) + - AEPEdge (>= 4.0.0) + - AEPEdgeIdentity (4.0.0): - AEPCore (>= 4.0.0) - - AEPLifecycle (4.0.0): + - AEPIdentity (4.1.0): + - AEPCore (>= 4.1.0) + - AEPLifecycle (4.1.0): + - AEPCore (>= 4.1.0) + - AEPMessaging (4.0.0): - AEPCore (>= 4.0.0) - - AEPMessaging (1.1.4): - - AEPCore (>= 3.8.1) - - AEPEdge (>= 1.5.0) - - AEPEdgeIdentity (>= 1.1.0) - - AEPServices (>= 3.8.1) - - AEPPlaces (3.0.3): - - AEPCore (>= 3.0.0) - - AEPServices (>= 3.0.0) + - AEPEdge (>= 4.0.0) + - AEPEdgeIdentity (>= 4.0.0) + - AEPServices (>= 4.0.0) + - AEPPlaces (4.1.0): + - AEPCore (>= 4.0.0) + - AEPServices (>= 4.0.0) - AEPRulesEngine (4.0.0) - - AEPServices (4.0.0) - - AEPSignal (4.0.0): + - AEPServices (4.1.0) + - AEPSignal (4.1.0): + - AEPCore (>= 4.1.0) + - AEPTarget (4.0.1): - AEPCore (>= 4.0.0) - - AEPTarget (3.3.1): - - AEPCore (>= 3.1.0) - - AEPUserProfile (3.0.1): + - AEPUserProfile (4.0.0): - AEPCore - SwiftLint (0.52.0) @@ -70,20 +70,20 @@ SPEC REPOS: - SwiftLint SPEC CHECKSUMS: - AEPAnalytics: b83efee29b4323537cbce4b8f146d26cee6cdc80 - AEPCore: dd7cd69696c768c610e6adc0307032985a381c7e - AEPEdge: e4364a56d358c517f7d4cef87570ac4e7652d3a2 - AEPEdgeConsent: d10d4232615b880d484050edf47b2e3fbfb787bb - AEPEdgeIdentity: 6bb2c1e62d48cdc988b4d492e8e6d563f0ced73d - AEPIdentity: 45ee1c3717e08ff3ca60930caf4a869d60d7bf08 - AEPLifecycle: 59be1b5381d8ec4939ece43516ea7d2de4aaba65 - AEPMessaging: bc711037b3989843f925649874225f8f3a4d8462 - AEPPlaces: 561e22d5ee6570fcb0b721a47aa7cda2c4f00ec0 + AEPAnalytics: a510eb9653fac7f913965ad4291c8d51f74ffdcd + AEPCore: 20fb832a7467b25ca4aca186c0a5a1e3c0c6abc3 + AEPEdge: c31a1c31d8466f964efeb9a4f94eebc52a0b55a5 + AEPEdgeConsent: 54c1b6a30a3d646e3d4bc4bae1713755422b471e + AEPEdgeIdentity: c2396b9119abd6eb530ea11efc58ec019b163bd4 + AEPIdentity: 88671626d6043a488896ee7d71483a8bcec80739 + AEPLifecycle: 97693ea99ef9deb818b726a4e429ef96abb1353e + AEPMessaging: b5693ae07e7bfb27f375a2d86640b60989b8338f + AEPPlaces: 9c5e8ba1292e1d6bf09c743a9e56c7ef62c33d9a AEPRulesEngine: 458450a34922823286ead045a0c2bd8c27e224c6 - AEPServices: ca493988df250d84fda050124ff7549bcc43c65f - AEPSignal: b2b332adf4d8a9af6a1b57f5dd8c2e1ea6d5c112 - AEPTarget: 90c732ef32cee5733897e395afdab7242df9762d - AEPUserProfile: 2ddb5ba8e2c22dd8f942992306b050f4be2c2403 + AEPServices: d94555679870311d2f1391c5d7a5de590fd1f3c0 + AEPSignal: 9152e68bae462276f57ac63666e879cc7ff7c302 + AEPTarget: 914857b1e7bfbffd032848a38cdf2e321377498d + AEPUserProfile: 5c1d90014627cacff99efb193ce9acd287905661 SwiftLint: 13280e21cdda6786ad908dc6e416afe5acd1fcb7 PODFILE CHECKSUM: 71eff368dd285422aa7446618ee8ed088faeec87 diff --git a/TestApp/AppDelegate.swift b/TestApp/AppDelegate.swift index 70965e0..e0868b5 100644 --- a/TestApp/AppDelegate.swift +++ b/TestApp/AppDelegate.swift @@ -34,6 +34,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD requestNotificationPermission() MobileCore.track(state: "Before SDK Init", data: nil) MobileCore.setLogLevel(.trace) + + let launchID = "" let extensions = [AEPIdentity.Identity.self, Lifecycle.self, Signal.self, @@ -50,7 +52,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ] let appState = application.applicationState MobileCore.registerExtensions(extensions, { - MobileCore.configureWith(appId: "94f571f308d5/f986c2be4925/launch-e96cdeaddea9-development") + MobileCore.configureWith(appId: launchID) if appState != .background { MobileCore.lifecycleStart(additionalContextData: nil) }