Repository: FLEXTool/FLEX Branch: master Commit: eee24be7b7e0 Files: 431 Total size: 2.6 MB Directory structure: gitextract_243iz5jb/ ├── .clang-format ├── .github/ │ ├── FUNDING.yml │ └── ISSUE_TEMPLATE/ │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── .vscode/ │ └── settings.json ├── CONTRIBUTING.md ├── Classes/ │ ├── Core/ │ │ ├── Controllers/ │ │ │ ├── FLEXFilteringTableViewController.h │ │ │ ├── FLEXFilteringTableViewController.m │ │ │ ├── FLEXNavigationController.h │ │ │ ├── FLEXNavigationController.m │ │ │ ├── FLEXTableViewController.h │ │ │ └── FLEXTableViewController.m │ │ ├── FLEXSingleRowSection.h │ │ ├── FLEXSingleRowSection.m │ │ ├── FLEXTableViewSection.h │ │ ├── FLEXTableViewSection.m │ │ └── Views/ │ │ ├── Carousel/ │ │ │ ├── FLEXCarouselCell.h │ │ │ ├── FLEXCarouselCell.m │ │ │ ├── FLEXScopeCarousel.h │ │ │ └── FLEXScopeCarousel.m │ │ ├── Cells/ │ │ │ ├── FLEXCodeFontCell.h │ │ │ ├── FLEXCodeFontCell.m │ │ │ ├── FLEXKeyValueTableViewCell.h │ │ │ ├── FLEXKeyValueTableViewCell.m │ │ │ ├── FLEXMultilineTableViewCell.h │ │ │ ├── FLEXMultilineTableViewCell.m │ │ │ ├── FLEXSubtitleTableViewCell.h │ │ │ ├── FLEXSubtitleTableViewCell.m │ │ │ ├── FLEXTableViewCell.h │ │ │ └── FLEXTableViewCell.m │ │ ├── FLEXTableView.h │ │ └── FLEXTableView.m │ ├── Editing/ │ │ ├── ArgumentInputViews/ │ │ │ ├── FLEXArgumentInputColorView.h │ │ │ ├── FLEXArgumentInputColorView.m │ │ │ ├── FLEXArgumentInputDateView.h │ │ │ ├── FLEXArgumentInputDateView.m │ │ │ ├── FLEXArgumentInputFontView.h │ │ │ ├── FLEXArgumentInputFontView.m │ │ │ ├── FLEXArgumentInputFontsPickerView.h │ │ │ ├── FLEXArgumentInputFontsPickerView.m │ │ │ ├── FLEXArgumentInputNotSupportedView.h │ │ │ ├── FLEXArgumentInputNotSupportedView.m │ │ │ ├── FLEXArgumentInputNumberView.h │ │ │ ├── FLEXArgumentInputNumberView.m │ │ │ ├── FLEXArgumentInputObjectView.h │ │ │ ├── FLEXArgumentInputObjectView.m │ │ │ ├── FLEXArgumentInputStringView.h │ │ │ ├── FLEXArgumentInputStringView.m │ │ │ ├── FLEXArgumentInputStructView.h │ │ │ ├── FLEXArgumentInputStructView.m │ │ │ ├── FLEXArgumentInputSwitchView.h │ │ │ ├── FLEXArgumentInputSwitchView.m │ │ │ ├── FLEXArgumentInputTextView.h │ │ │ ├── FLEXArgumentInputTextView.m │ │ │ ├── FLEXArgumentInputView.h │ │ │ └── FLEXArgumentInputView.m │ │ ├── FLEXArgumentInputViewFactory.h │ │ ├── FLEXArgumentInputViewFactory.m │ │ ├── FLEXDefaultEditorViewController.h │ │ ├── FLEXDefaultEditorViewController.m │ │ ├── FLEXFieldEditorView.h │ │ ├── FLEXFieldEditorView.m │ │ ├── FLEXFieldEditorViewController.h │ │ ├── FLEXFieldEditorViewController.m │ │ ├── FLEXMethodCallingViewController.h │ │ ├── FLEXMethodCallingViewController.m │ │ ├── FLEXVariableEditorViewController.h │ │ └── FLEXVariableEditorViewController.m │ ├── ExplorerInterface/ │ │ ├── Bookmarks/ │ │ │ ├── FLEXBookmarkManager.h │ │ │ ├── FLEXBookmarkManager.m │ │ │ ├── FLEXBookmarksViewController.h │ │ │ └── FLEXBookmarksViewController.m │ │ ├── FLEXExplorerViewController.h │ │ ├── FLEXExplorerViewController.m │ │ ├── FLEXViewControllersViewController.h │ │ ├── FLEXViewControllersViewController.m │ │ ├── FLEXWindow.h │ │ ├── FLEXWindow.m │ │ ├── FLEXWindowManagerController.h │ │ ├── FLEXWindowManagerController.m │ │ └── Tabs/ │ │ ├── FLEXTabList.h │ │ ├── FLEXTabList.m │ │ ├── FLEXTabsViewController.h │ │ └── FLEXTabsViewController.m │ ├── FLEX-Categories.h │ ├── FLEX-Core.h │ ├── FLEX-ObjectExploring.h │ ├── FLEX-Runtime.h │ ├── FLEX.h │ ├── GlobalStateExplorers/ │ │ ├── DatabaseBrowser/ │ │ │ ├── FLEXDBQueryRowCell.h │ │ │ ├── FLEXDBQueryRowCell.m │ │ │ ├── FLEXDatabaseManager.h │ │ │ ├── FLEXMultiColumnTableView.h │ │ │ ├── FLEXMultiColumnTableView.m │ │ │ ├── FLEXRealmDatabaseManager.h │ │ │ ├── FLEXRealmDatabaseManager.m │ │ │ ├── FLEXRealmDefines.h │ │ │ ├── FLEXSQLResult.h │ │ │ ├── FLEXSQLResult.m │ │ │ ├── FLEXSQLiteDatabaseManager.h │ │ │ ├── FLEXSQLiteDatabaseManager.m │ │ │ ├── FLEXTableColumnHeader.h │ │ │ ├── FLEXTableColumnHeader.m │ │ │ ├── FLEXTableContentViewController.h │ │ │ ├── FLEXTableContentViewController.m │ │ │ ├── FLEXTableLeftCell.h │ │ │ ├── FLEXTableLeftCell.m │ │ │ ├── FLEXTableListViewController.h │ │ │ ├── FLEXTableListViewController.m │ │ │ ├── FLEXTableRowDataViewController.h │ │ │ ├── FLEXTableRowDataViewController.m │ │ │ └── LICENSE │ │ ├── FLEXAPNSViewController.h │ │ ├── FLEXAPNSViewController.m │ │ ├── FLEXAddressExplorerCoordinator.h │ │ ├── FLEXAddressExplorerCoordinator.m │ │ ├── FLEXCookiesViewController.h │ │ ├── FLEXCookiesViewController.m │ │ ├── FLEXLiveObjectsController.h │ │ ├── FLEXLiveObjectsController.m │ │ ├── FLEXObjectListViewController.h │ │ ├── FLEXObjectListViewController.m │ │ ├── FLEXObjectRef.h │ │ ├── FLEXObjectRef.m │ │ ├── FLEXWebViewController.h │ │ ├── FLEXWebViewController.m │ │ ├── FileBrowser/ │ │ │ ├── FLEXActivityViewController.h │ │ │ ├── FLEXActivityViewController.m │ │ │ ├── FLEXFileBrowserController.h │ │ │ ├── FLEXFileBrowserController.m │ │ │ ├── FLEXFileBrowserSearchOperation.h │ │ │ └── FLEXFileBrowserSearchOperation.m │ │ ├── Globals/ │ │ │ ├── FLEXGlobalsEntry.h │ │ │ ├── FLEXGlobalsEntry.m │ │ │ ├── FLEXGlobalsSection.h │ │ │ ├── FLEXGlobalsSection.m │ │ │ ├── FLEXGlobalsViewController.h │ │ │ └── FLEXGlobalsViewController.m │ │ ├── Keychain/ │ │ │ ├── FLEXKeychain.h │ │ │ ├── FLEXKeychain.m │ │ │ ├── FLEXKeychainQuery.h │ │ │ ├── FLEXKeychainQuery.m │ │ │ ├── FLEXKeychainViewController.h │ │ │ ├── FLEXKeychainViewController.m │ │ │ └── SSKeychain_LICENSE │ │ ├── RuntimeBrowser/ │ │ │ ├── DataSources/ │ │ │ │ ├── FLEXRuntimeClient.h │ │ │ │ ├── FLEXRuntimeClient.m │ │ │ │ ├── FLEXRuntimeController.h │ │ │ │ ├── FLEXRuntimeController.m │ │ │ │ ├── FLEXRuntimeExporter.h │ │ │ │ └── FLEXRuntimeExporter.m │ │ │ ├── FLEXKBToolbarButton.h │ │ │ ├── FLEXKBToolbarButton.m │ │ │ ├── FLEXKeyPathSearchController.h │ │ │ ├── FLEXKeyPathSearchController.m │ │ │ ├── FLEXKeyboardToolbar.h │ │ │ ├── FLEXKeyboardToolbar.m │ │ │ ├── FLEXObjcRuntimeViewController.h │ │ │ ├── FLEXObjcRuntimeViewController.m │ │ │ ├── FLEXRuntimeBrowserToolbar.h │ │ │ ├── FLEXRuntimeBrowserToolbar.m │ │ │ ├── FLEXRuntimeKeyPath.h │ │ │ ├── FLEXRuntimeKeyPath.m │ │ │ ├── FLEXRuntimeKeyPathTokenizer.h │ │ │ ├── FLEXRuntimeKeyPathTokenizer.m │ │ │ ├── FLEXSearchToken.h │ │ │ └── FLEXSearchToken.m │ │ └── SystemLog/ │ │ ├── ActivityStreamAPI.h │ │ ├── FLEXASLLogController.h │ │ ├── FLEXASLLogController.m │ │ ├── FLEXLogController.h │ │ ├── FLEXOSLogController.h │ │ ├── FLEXOSLogController.m │ │ ├── FLEXSystemLogCell.h │ │ ├── FLEXSystemLogCell.m │ │ ├── FLEXSystemLogMessage.h │ │ ├── FLEXSystemLogMessage.m │ │ ├── FLEXSystemLogViewController.h │ │ ├── FLEXSystemLogViewController.m │ │ └── LLVM_LICENSE.TXT │ ├── Info.plist │ ├── Manager/ │ │ ├── FLEXManager+Extensibility.h │ │ ├── FLEXManager+Extensibility.m │ │ ├── FLEXManager+Networking.h │ │ ├── FLEXManager+Networking.m │ │ ├── FLEXManager.h │ │ ├── FLEXManager.m │ │ └── Private/ │ │ └── FLEXManager+Private.h │ ├── Network/ │ │ ├── FLEXFirebaseTransaction.mm │ │ ├── FLEXHTTPTransactionDetailController.h │ │ ├── FLEXHTTPTransactionDetailController.m │ │ ├── FLEXMITMDataSource.h │ │ ├── FLEXMITMDataSource.m │ │ ├── FLEXNetworkCurlLogger.h │ │ ├── FLEXNetworkCurlLogger.m │ │ ├── FLEXNetworkMITMViewController.h │ │ ├── FLEXNetworkMITMViewController.m │ │ ├── FLEXNetworkRecorder.h │ │ ├── FLEXNetworkRecorder.m │ │ ├── FLEXNetworkSettingsController.h │ │ ├── FLEXNetworkSettingsController.m │ │ ├── FLEXNetworkTransaction.h │ │ ├── FLEXNetworkTransaction.m │ │ ├── FLEXNetworkTransactionCell.h │ │ ├── FLEXNetworkTransactionCell.m │ │ ├── Firestore.h │ │ ├── OSCache/ │ │ │ ├── LICENSE.md │ │ │ ├── OSCache.h │ │ │ └── OSCache.m │ │ └── PonyDebugger/ │ │ ├── FLEXNetworkObserver.h │ │ ├── FLEXNetworkObserver.m │ │ └── LICENSE │ ├── ObjectExplorers/ │ │ ├── FLEXObjectExplorer.h │ │ ├── FLEXObjectExplorer.m │ │ ├── FLEXObjectExplorerFactory.h │ │ ├── FLEXObjectExplorerFactory.m │ │ ├── FLEXObjectExplorerViewController.h │ │ ├── FLEXObjectExplorerViewController.m │ │ └── Sections/ │ │ ├── FLEXCollectionContentSection.h │ │ ├── FLEXCollectionContentSection.m │ │ ├── FLEXColorPreviewSection.h │ │ ├── FLEXColorPreviewSection.m │ │ ├── FLEXDefaultsContentSection.h │ │ ├── FLEXDefaultsContentSection.m │ │ ├── FLEXMetadataSection.h │ │ ├── FLEXMetadataSection.m │ │ ├── FLEXMutableListSection.h │ │ ├── FLEXMutableListSection.m │ │ ├── FLEXObjectInfoSection.h │ │ └── Shortcuts/ │ │ ├── FLEXBlockShortcuts.h │ │ ├── FLEXBlockShortcuts.m │ │ ├── FLEXBundleShortcuts.h │ │ ├── FLEXBundleShortcuts.m │ │ ├── FLEXClassShortcuts.h │ │ ├── FLEXClassShortcuts.m │ │ ├── FLEXImageShortcuts.h │ │ ├── FLEXImageShortcuts.m │ │ ├── FLEXLayerShortcuts.h │ │ ├── FLEXLayerShortcuts.m │ │ ├── FLEXNSDataShortcuts.h │ │ ├── FLEXNSDataShortcuts.m │ │ ├── FLEXNSStringShortcuts.h │ │ ├── FLEXNSStringShortcuts.m │ │ ├── FLEXShortcut.h │ │ ├── FLEXShortcut.m │ │ ├── FLEXShortcutsFactory+Defaults.h │ │ ├── FLEXShortcutsFactory+Defaults.m │ │ ├── FLEXShortcutsSection.h │ │ ├── FLEXShortcutsSection.m │ │ ├── FLEXUIAppShortcuts.h │ │ ├── FLEXUIAppShortcuts.m │ │ ├── FLEXViewControllerShortcuts.h │ │ ├── FLEXViewControllerShortcuts.m │ │ ├── FLEXViewShortcuts.h │ │ ├── FLEXViewShortcuts.m │ │ ├── FLEXWindowShortcuts.h │ │ └── FLEXWindowShortcuts.m │ ├── Toolbar/ │ │ ├── FLEXExplorerToolbar.h │ │ ├── FLEXExplorerToolbar.m │ │ ├── FLEXExplorerToolbarItem.h │ │ └── FLEXExplorerToolbarItem.m │ ├── Utility/ │ │ ├── APPLE_LICENSE │ │ ├── Categories/ │ │ │ ├── CALayer+FLEX.h │ │ │ ├── CALayer+FLEX.m │ │ │ ├── FLEXRuntime+Compare.h │ │ │ ├── FLEXRuntime+Compare.m │ │ │ ├── FLEXRuntime+UIKitHelpers.h │ │ │ ├── FLEXRuntime+UIKitHelpers.m │ │ │ ├── NSArray+FLEX.h │ │ │ ├── NSArray+FLEX.m │ │ │ ├── NSDateFormatter+FLEX.h │ │ │ ├── NSDateFormatter+FLEX.m │ │ │ ├── NSObject+FLEX_Reflection.h │ │ │ ├── NSObject+FLEX_Reflection.m │ │ │ ├── NSTimer+FLEX.h │ │ │ ├── NSTimer+FLEX.m │ │ │ ├── NSUserDefaults+FLEX.h │ │ │ ├── NSUserDefaults+FLEX.m │ │ │ ├── Private/ │ │ │ │ ├── Cocoa+FLEXShortcuts.h │ │ │ │ ├── Cocoa+FLEXShortcuts.m │ │ │ │ ├── NSDictionary+ObjcRuntime.h │ │ │ │ ├── NSDictionary+ObjcRuntime.m │ │ │ │ ├── NSMapTable+FLEX_Subscripting.h │ │ │ │ ├── NSMapTable+FLEX_Subscripting.m │ │ │ │ ├── NSString+FLEX.h │ │ │ │ ├── NSString+FLEX.m │ │ │ │ ├── NSString+ObjcRuntime.h │ │ │ │ ├── NSString+ObjcRuntime.m │ │ │ │ ├── UIView+FLEX_Layout.h │ │ │ │ └── UIView+FLEX_Layout.m │ │ │ ├── UIBarButtonItem+FLEX.h │ │ │ ├── UIBarButtonItem+FLEX.m │ │ │ ├── UIFont+FLEX.h │ │ │ ├── UIFont+FLEX.m │ │ │ ├── UIGestureRecognizer+Blocks.h │ │ │ ├── UIGestureRecognizer+Blocks.m │ │ │ ├── UIMenu+FLEX.h │ │ │ ├── UIMenu+FLEX.m │ │ │ ├── UIPasteboard+FLEX.h │ │ │ ├── UIPasteboard+FLEX.m │ │ │ ├── UITextField+Range.h │ │ │ └── UITextField+Range.m │ │ ├── FLEXAlert.h │ │ ├── FLEXAlert.m │ │ ├── FLEXColor.h │ │ ├── FLEXColor.m │ │ ├── FLEXHeapEnumerator.h │ │ ├── FLEXHeapEnumerator.m │ │ ├── FLEXMacros.h │ │ ├── FLEXResources.h │ │ ├── FLEXResources.m │ │ ├── FLEXUtility.h │ │ ├── FLEXUtility.m │ │ ├── Keyboard/ │ │ │ ├── FLEXKeyboardHelpViewController.h │ │ │ ├── FLEXKeyboardHelpViewController.m │ │ │ ├── FLEXKeyboardShortcutManager.h │ │ │ └── FLEXKeyboardShortcutManager.m │ │ └── Runtime/ │ │ ├── FLEXRuntimeUtility.h │ │ ├── FLEXRuntimeUtility.m │ │ ├── Objc/ │ │ │ ├── FLEXObjcInternal.h │ │ │ ├── FLEXObjcInternal.mm │ │ │ ├── FLEXRuntimeConstants.h │ │ │ ├── FLEXRuntimeConstants.m │ │ │ ├── FLEXRuntimeSafety.h │ │ │ ├── FLEXRuntimeSafety.m │ │ │ ├── FLEXSwiftInternal.h │ │ │ ├── FLEXSwiftInternal.mm │ │ │ ├── FLEXTypeEncodingParser.h │ │ │ ├── FLEXTypeEncodingParser.m │ │ │ └── Reflection/ │ │ │ ├── FLEXBlockDescription.h │ │ │ ├── FLEXBlockDescription.m │ │ │ ├── FLEXClassBuilder.h │ │ │ ├── FLEXClassBuilder.m │ │ │ ├── FLEXIvar.h │ │ │ ├── FLEXIvar.m │ │ │ ├── FLEXMetadataExtras.h │ │ │ ├── FLEXMetadataExtras.m │ │ │ ├── FLEXMethod.h │ │ │ ├── FLEXMethod.m │ │ │ ├── FLEXMethodBase.h │ │ │ ├── FLEXMethodBase.m │ │ │ ├── FLEXMirror.h │ │ │ ├── FLEXMirror.m │ │ │ ├── FLEXProperty.h │ │ │ ├── FLEXProperty.m │ │ │ ├── FLEXPropertyAttributes.h │ │ │ ├── FLEXPropertyAttributes.m │ │ │ ├── FLEXProtocol.h │ │ │ ├── FLEXProtocol.m │ │ │ ├── FLEXProtocolBuilder.h │ │ │ └── FLEXProtocolBuilder.m │ │ ├── flex_fishhook.c │ │ └── flex_fishhook.h │ └── ViewHierarchy/ │ ├── FLEXHierarchyViewController.h │ ├── FLEXHierarchyViewController.m │ ├── FLEXImagePreviewViewController.h │ ├── FLEXImagePreviewViewController.m │ ├── SnapshotExplorer/ │ │ ├── FHSRangeSlider.h │ │ ├── FHSRangeSlider.m │ │ ├── FHSSnapshotView.h │ │ ├── FHSSnapshotView.m │ │ ├── FHSView.h │ │ ├── FHSView.m │ │ ├── FHSViewController.h │ │ ├── FHSViewController.m │ │ ├── FHSViewSnapshot.h │ │ ├── FHSViewSnapshot.m │ │ └── Scene/ │ │ ├── FHSSnapshotNodes.h │ │ ├── FHSSnapshotNodes.m │ │ ├── SceneKit+Snapshot.h │ │ └── SceneKit+Snapshot.m │ └── TreeExplorer/ │ ├── FLEXHierarchyTableViewCell.h │ ├── FLEXHierarchyTableViewCell.m │ ├── FLEXHierarchyTableViewController.h │ └── FLEXHierarchyTableViewController.m ├── Example/ │ ├── FLEXample/ │ │ ├── App/ │ │ │ ├── Commit.swift │ │ │ ├── CommitListViewController.h │ │ │ ├── CommitListViewController.m │ │ │ ├── Person.h │ │ │ └── Person.m │ │ ├── AppDelegate.swift │ │ ├── MiscNetworkRequests.h │ │ ├── MiscNetworkRequests.m │ │ ├── SceneDelegate.swift │ │ └── Supporting Files/ │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ └── LaunchScreen.storyboard │ │ ├── Bundle/ │ │ │ ├── dogs.realm │ │ │ └── image │ │ ├── FLEXample-Bridging-Header.h │ │ ├── Info.plist │ │ └── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ ├── FLEXample-Cocoapods.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── FLEXample.xcscheme │ ├── FLEXample-SPM.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── FLEXample.xcscheme │ ├── Podfile │ └── Realm/ │ ├── Dog.h │ ├── Dog.m │ ├── Owner.h │ └── Owner.m ├── FLEX.podspec ├── FLEX.xcodeproj/ │ ├── project.pbxproj │ └── xcshareddata/ │ └── xcschemes/ │ ├── FLEX.xcscheme │ └── FLEXTests.xcscheme ├── FLEXTests/ │ ├── FLEXTests.m │ ├── FLEXTestsMethodsList.m │ ├── FLEXTypeEncodingParserTests.m │ ├── Info.plist │ └── Supporting Files/ │ ├── FLEXNewRootClass.h │ └── FLEXNewRootClass.m ├── Graphics/ │ ├── FLEX-Icons.sketch │ ├── filetypes/ │ │ └── old/ │ │ ├── audio.pxm │ │ ├── binary.pxm │ │ ├── html.pxm │ │ ├── js.pxm │ │ ├── json.pxm │ │ ├── plist.pxm │ │ ├── text.pxm │ │ ├── textplain.pxm │ │ └── video.pxm │ ├── image_to_code.py │ ├── images_to_hex.sh │ └── iterate_hex_to_pasteboard.sh ├── LICENSE ├── Package.swift ├── README.md ├── SM/ │ └── Surfile └── generate-spm-headers.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- BasedOnStyle: WebKit AccessModifierOffset: -2 AlignEscapedNewlinesLeft: false AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackParameters: true BreakBeforeBinaryOperators: false BreakBeforeBraces: Linux BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 0 ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerBinding: false ExperimentalAutoDetectBinPacking: false IndentCaseLabels: true IndentFunctionDeclarationAfterType: false IndentWidth: 4 MaxEmptyLinesToKeep: 3 NamespaceIndentation: None ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 60 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerBindsToType: false SpaceAfterControlStatementKeyword: true SpaceBeforeAssignmentOperators: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false Standard: Auto TabWidth: 4 UseTab: Never ... ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [NSExceptional] ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Report a bug in FLEX title: '' labels: bug assignees: '' --- ### Environment - Platform+version: **iOS 14** - FLEX version: **9.9.9** ### Bug Report Here, you can provide a description of the bug. Some tips: - Please do not paste an entire crash log. Upload the crash log to something like [ghostbin.co](https://ghostbin.co/) or another paste service. Alternatively, you can cut out the relevant stack trace and paste that inside a ` ```code block``` ` - If the bug is more complex than "this button is broken" or a crash, consider including a sample project. For example, if your app's requests aren't showing up in the network history page. - Providing steps to reproduce is always helpful! - If you want to include a screenshot or GIF, consider modifying the default markdown for uploaded images to use this code to make the image smaller on desktop: ``` ``` This template is a suggestion. You may format your issue however you want, but generally you should at least include your iOS version and FLEX version. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest a new feature for FLEX title: '' labels: enhancement assignees: '' --- ================================================ FILE: .gitignore ================================================ # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate .DS_Store /Example/Pods Podfile.lock IDEWorkspaceChecks.plist *.xcworkspace .build ================================================ FILE: .travis.yml ================================================ language: objective-c xcode_workspace: FLEX.xcworkspace xcode_sdk: iphonesimulator before_install: - gem install xcpretty matrix: include: - xcode_scheme: UICatalog - xcode_scheme: FLEX script: - set -o pipefail - xcodebuild -workspace $TRAVIS_XCODE_WORKSPACE -scheme $TRAVIS_XCODE_SCHEME -sdk $TRAVIS_XCODE_SDK build | xcpretty ================================================ FILE: .vscode/settings.json ================================================ { "search.exclude": { "Classes/Headers": true } } ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to FLEX # We welcome contributions! Please open a pull request with your changes. ================================================ FILE: Classes/Core/Controllers/FLEXFilteringTableViewController.h ================================================ // // FLEXFilteringTableViewController.h // FLEX // // Created by Tanner on 3/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" #pragma mark - FLEXTableViewFiltering @protocol FLEXTableViewFiltering /// An array of visible, "filtered" sections. For example, /// if you have 3 sections in \c allSections and the user searches /// for something that matches rows in only one section, then /// this property would only contain that on matching section. @property (nonatomic, copy) NSArray *sections; /// An array of all possible sections. Empty sections are to be removed /// and the resulting array stored in the \c section property. Setting /// this property should immediately set \c sections to \c nonemptySections /// /// Do not manually initialize this property, it will be /// initialized for you using the result of \c makeSections. @property (nonatomic, copy) NSArray *allSections; /// This computed property should filter \c allSections for assignment to \c sections @property (nonatomic, readonly, copy) NSArray *nonemptySections; /// This should be able to re-initialize \c allSections - (NSArray *)makeSections; @end #pragma mark - FLEXFilteringTableViewController /// A table view which implements \c UITableView* methods using arrays of /// \c FLEXTableViewSection objects provied by a special delegate. @interface FLEXFilteringTableViewController : FLEXTableViewController /// Stores the current search query. @property (nonatomic, copy) NSString *filterText; /// This property is set to \c self by default. /// /// This property is used to power almost all of the table view's data source /// and delegate methods automatically, including row and section filtering /// when the user searches, 3D Touch context menus, row selection, etc. /// /// Setting this property will also set \c searchDelegate to that object. @property (nonatomic, weak) id filterDelegate; /// Defaults to \c NO. If enabled, all filtering will be done by calling /// \c onBackgroundQueue:thenOnMainQueue: with the UI updated on the main queue. @property (nonatomic) BOOL filterInBackground; /// Defaults to \c NO. If enabled, one • will be supplied as an index title for each section. @property (nonatomic) BOOL wantsSectionIndexTitles; /// Recalculates the non-empty sections and reloads the table view. /// /// Subclasses may override to perform additional reloading logic, /// such as calling \c -reloadSections if needed. Be sure to call /// \c super after any logic that would affect the appearance of /// the table view, since the table view is reloaded last. /// /// Called at the end of this class's implementation of \c updateSearchResults: - (void)reloadData; /// Invoke this method to call \c -reloadData on each section /// in \c self.filterDelegate.allSections. - (void)reloadSections; #pragma mark FLEXTableViewFiltering @property (nonatomic, copy) NSArray *sections; @property (nonatomic, copy) NSArray *allSections; /// Subclasses can override to hide specific sections under certain conditions /// if using \c self as the \c filterDelegate, as is the default. /// /// For example, the object explorer hides the description section when searching. @property (nonatomic, readonly, copy) NSArray *nonemptySections; /// If using \c self as the \c filterDelegate, as is the default, /// subclasses should override to provide the sections for the table view. - (NSArray *)makeSections; @end ================================================ FILE: Classes/Core/Controllers/FLEXFilteringTableViewController.m ================================================ // // FLEXFilteringTableViewController.m // FLEX // // Created by Tanner on 3/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXFilteringTableViewController.h" #import "FLEXTableViewSection.h" #import "NSArray+FLEX.h" #import "FLEXMacros.h" @interface FLEXFilteringTableViewController () @end @implementation FLEXFilteringTableViewController @synthesize allSections = _allSections; #pragma mark - View controller lifecycle - (void)loadView { [super loadView]; if (!self.filterDelegate) { self.filterDelegate = self; } else { [self _registerCellsForReuse]; } } - (void)_registerCellsForReuse { for (FLEXTableViewSection *section in self.filterDelegate.allSections) { if (section.cellRegistrationMapping) { [self.tableView registerCells:section.cellRegistrationMapping]; } } } #pragma mark - Public - (void)setFilterDelegate:(id)filterDelegate { _filterDelegate = filterDelegate; filterDelegate.allSections = [filterDelegate makeSections]; if (self.isViewLoaded) { [self _registerCellsForReuse]; } } - (void)reloadData { [self reloadData:self.nonemptySections]; } - (void)reloadData:(NSArray *)nonemptySections { // Recalculate displayed sections self.filterDelegate.sections = nonemptySections; // Refresh table view if (self.isViewLoaded) { [self.tableView reloadData]; } } - (void)reloadSections { for (FLEXTableViewSection *section in self.filterDelegate.allSections) { [section reloadData]; } } #pragma mark - Search - (void)updateSearchResults:(NSString *)newText { NSArray *(^filter)(void) = ^NSArray *{ self.filterText = newText; // Sections will adjust data based on this property for (FLEXTableViewSection *section in self.filterDelegate.allSections) { section.filterText = newText; } return nil; }; if (self.filterInBackground) { [self onBackgroundQueue:filter thenOnMainQueue:^(NSArray *unused) { if ([self.searchText isEqualToString:newText]) { [self reloadData]; } }]; } else { filter(); [self reloadData]; } } #pragma mark Filtering - (NSArray *)nonemptySections { return [self.filterDelegate.allSections flex_filtered:^BOOL(FLEXTableViewSection *section, NSUInteger idx) { return section.numberOfRows > 0; }]; } - (NSArray *)makeSections { return @[]; } - (void)setAllSections:(NSArray *)allSections { _allSections = allSections.copy; // Only display nonempty sections self.sections = self.nonemptySections; } - (void)setSections:(NSArray *)sections { // Allow sections to reload a portion of the table view at will [sections enumerateObjectsUsingBlock:^(FLEXTableViewSection *s, NSUInteger idx, BOOL *stop) { [s setTable:self.tableView section:idx]; }]; _sections = sections.copy; } #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return self.filterDelegate.sections.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.filterDelegate.sections[section].numberOfRows; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return self.filterDelegate.sections[section].title; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *reuse = [self.filterDelegate.sections[indexPath.section] reuseIdentifierForRow:indexPath.row]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuse forIndexPath:indexPath]; [self.filterDelegate.sections[indexPath.section] configureCell:cell forRow:indexPath.row]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewAutomaticDimension; } - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { if (self.wantsSectionIndexTitles) { return [NSArray flex_forEachUpTo:self.filterDelegate.sections.count map:^id(NSUInteger i) { return @"⦁"; }]; } return nil; } #pragma mark - UITableViewDelegate - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { return [self.filterDelegate.sections[indexPath.section] canSelectRow:indexPath.row]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section]; void (^action)(UIViewController *) = [section didSelectRowAction:indexPath.row]; UIViewController *details = [section viewControllerToPushForRow:indexPath.row]; if (action) { action(self); [tableView deselectRowAtIndexPath:indexPath animated:YES]; } else if (details) { [self.navigationController pushViewController:details animated:YES]; } else { [NSException raise:NSInternalInconsistencyException format:@"Row is selectable but has no action or view controller"]; } } - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { [self.filterDelegate.sections[indexPath.section] didPressInfoButtonAction:indexPath.row](self); } - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) { FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section]; NSString *title = [section menuTitleForRow:indexPath.row]; NSArray *menuItems = [section menuItemsForRow:indexPath.row sender:self]; if (menuItems.count) { return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu *(NSArray *suggestedActions) { return [UIMenu menuWithTitle:title children:menuItems]; } ]; } return nil; } @end ================================================ FILE: Classes/Core/Controllers/FLEXNavigationController.h ================================================ // // FLEXNavigationController.h // FLEX // // Created by Tanner on 1/30/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface FLEXNavigationController : UINavigationController + (instancetype)withRootViewController:(UIViewController *)rootVC; @end @interface UINavigationController (FLEXObjectExploring) /// Push an object explorer view controller onto the navigation stack - (void)pushExplorerForObject:(id)object; /// Push an object explorer view controller onto the navigation stack - (void)pushExplorerForObject:(id)object animated:(BOOL)animated; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Core/Controllers/FLEXNavigationController.m ================================================ // // FLEXNavigationController.m // FLEX // // Created by Tanner on 1/30/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXNavigationController.h" #import "FLEXExplorerViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXTabList.h" @interface UINavigationController (Private) - (void)_gestureRecognizedInteractiveHide:(UIGestureRecognizer *)sender; @end @interface UIPanGestureRecognizer (Private) - (void)_setDelegate:(id)delegate; @end @interface FLEXNavigationController () @property (nonatomic, readonly) BOOL toolbarWasHidden; @property (nonatomic) BOOL waitingToAddTab; @property (nonatomic, readonly) BOOL canShowToolbar; @property (nonatomic) BOOL didSetupPendingDismissButtons; @property (nonatomic) UISwipeGestureRecognizer *navigationBarSwipeGesture; @end @implementation FLEXNavigationController + (instancetype)withRootViewController:(UIViewController *)rootVC { FLEXNavigationController *nav = [[self alloc] initWithRootViewController:rootVC]; return nav; } - (void)viewDidLoad { [super viewDidLoad]; self.waitingToAddTab = YES; // Add gesture to reveal toolbar if hidden UITapGestureRecognizer *navbarTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleNavigationBarTap:) ]; // Don't cancel touches to work around bug on versions of iOS prior to 13 navbarTapGesture.cancelsTouchesInView = NO; [self.navigationBar addGestureRecognizer:navbarTapGesture]; // Add gesture to dismiss if not presented with a sheet style if (@available(iOS 13, *)) { switch (self.modalPresentationStyle) { case UIModalPresentationAutomatic: case UIModalPresentationPageSheet: case UIModalPresentationFormSheet: break; default: [self addNavigationBarSwipeGesture]; break; } } else { [self addNavigationBarSwipeGesture]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (@available(iOS 15.0, *)) { UISheetPresentationController *presenter = self.sheetPresentationController; presenter.detents = @[ UISheetPresentationControllerDetent.mediumDetent, UISheetPresentationControllerDetent.largeDetent, ]; presenter.prefersScrollingExpandsWhenScrolledToEdge = NO; presenter.selectedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge; presenter.largestUndimmedDetentIdentifier = UISheetPresentationControllerDetentIdentifierLarge; } if (self.beingPresented && !self.didSetupPendingDismissButtons) { for (UIViewController *vc in self.viewControllers) { [self addNavigationBarItemsToViewController:vc.navigationItem]; } self.didSetupPendingDismissButtons = YES; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (self.waitingToAddTab) { // Only add new tab if we're presented properly if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) { // New navigation controllers always add themselves as new tabs, // tabs are closed by FLEXExplorerViewController [FLEXTabList.sharedList addTab:self]; self.waitingToAddTab = NO; } } } - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { [super pushViewController:viewController animated:animated]; [self addNavigationBarItemsToViewController:viewController.navigationItem]; } - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { // Workaround for UIActivityViewController trying to dismiss us for some reason if (![self.viewControllers.lastObject.presentedViewController isKindOfClass:UIActivityViewController.self]) { [super dismissViewControllerAnimated:flag completion:completion]; } } - (void)dismissAnimated { // Tabs are only closed if the done button is pressed; this // allows you to leave a tab open by dragging down to dismiss if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) { [FLEXTabList.sharedList closeTab:self]; } [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } - (BOOL)canShowToolbar { return self.topViewController.toolbarItems.count > 0; } - (void)addNavigationBarItemsToViewController:(UINavigationItem *)navigationItem { if (!self.presentingViewController) { return; } // Check if a done item already exists for (UIBarButtonItem *item in navigationItem.rightBarButtonItems) { if (item.style == UIBarButtonItemStyleDone) { return; } } // Give root view controllers a Done button if it does not already have one UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissAnimated) ]; // Prepend the button if other buttons exist already NSArray *existingItems = navigationItem.rightBarButtonItems; if (existingItems.count) { navigationItem.rightBarButtonItems = [@[done] arrayByAddingObjectsFromArray:existingItems]; } else { navigationItem.rightBarButtonItem = done; } // Keeps us from calling this method again on // the same view controllers in -viewWillAppear: self.didSetupPendingDismissButtons = YES; } - (void)addNavigationBarSwipeGesture { UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleNavigationBarSwipe:) ]; swipe.direction = UISwipeGestureRecognizerDirectionDown; swipe.delegate = self; self.navigationBarSwipeGesture = swipe; [self.navigationBar addGestureRecognizer:swipe]; } - (void)handleNavigationBarSwipe:(UISwipeGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateRecognized) { [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } } - (void)handleNavigationBarTap:(UIGestureRecognizer *)sender { // Don't reveal the toolbar if we were just tapping a button CGPoint location = [sender locationInView:self.navigationBar]; UIView *hitView = [self.navigationBar hitTest:location withEvent:nil]; if ([hitView isKindOfClass:[UIControl class]]) { return; } if (sender.state == UIGestureRecognizerStateRecognized) { if (self.toolbarHidden && self.canShowToolbar) { [self setToolbarHidden:NO animated:YES]; } } } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 { if (g1 == self.navigationBarSwipeGesture && g2 == self.barHideOnSwipeGestureRecognizer) { return YES; } return NO; } - (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateRecognized) { BOOL show = self.canShowToolbar; CGFloat yTranslation = [sender translationInView:self.view].y; CGFloat yVelocity = [sender velocityInView:self.view].y; if (yVelocity > 2000) { [self setToolbarHidden:YES animated:YES]; } else if (show && yTranslation > 20 && yVelocity > 250) { [self setToolbarHidden:NO animated:YES]; } else if (yTranslation < -20) { [self setToolbarHidden:YES animated:YES]; } } } @end @implementation UINavigationController (FLEXObjectExploring) - (void)pushExplorerForObject:(id)object { [self pushExplorerForObject:object animated:YES]; } - (void)pushExplorerForObject:(id)object animated:(BOOL)animated { UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object]; if (explorer) { [self pushViewController:explorer animated:animated]; } } @end ================================================ FILE: Classes/Core/Controllers/FLEXTableViewController.h ================================================ // // FLEXTableViewController.h // FLEX // // Created by Tanner on 7/5/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import "FLEXTableView.h" @class FLEXScopeCarousel, FLEXWindow, FLEXTableViewSection; typedef CGFloat FLEXDebounceInterval; /// No delay, all events delivered extern CGFloat const kFLEXDebounceInstant; /// Small delay which makes UI seem smoother by avoiding rapid events extern CGFloat const kFLEXDebounceFast; /// Slower than Fast, faster than ExpensiveIO extern CGFloat const kFLEXDebounceForAsyncSearch; /// The least frequent, at just over once per second; for I/O or other expensive operations extern CGFloat const kFLEXDebounceForExpensiveIO; @protocol FLEXSearchResultsUpdating /// A method to handle search query update events. /// /// \c searchBarDebounceInterval is used to reduce the frequency at which this /// method is called. This method is also called when the search bar becomes /// the first responder, and when the selected search bar scope index changes. - (void)updateSearchResults:(NSString *)newText; @end @interface FLEXTableViewController : UITableViewController < UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate > /// A grouped table view. Inset on iOS 13. /// /// Simply calls into \c initWithStyle: - (id)init; /// Subclasses may override to configure the controller before \c viewDidLoad: - (id)initWithStyle:(UITableViewStyle)style; @property (nonatomic) FLEXTableView *tableView; /// If your subclass conforms to \c FLEXSearchResultsUpdating /// then this property is assigned to \c self automatically. /// /// Setting \c filterDelegate will also set this property to that object. @property (nonatomic, weak) id searchDelegate; /// Defaults to NO. /// /// Setting this to YES will initialize the carousel and the view. @property (nonatomic) BOOL showsCarousel; /// A horizontally scrolling list with functionality similar to /// that of a search bar's scope bar. You'd want to use this when /// you have potentially more than 4 scope options. @property (nonatomic) FLEXScopeCarousel *carousel; /// Defaults to NO. /// /// Setting this to YES will initialize searchController and the view. @property (nonatomic) BOOL showsSearchBar; /// Defaults to NO. /// /// Setting this to YES will make the search bar appear whenever the view appears. /// Otherwise, iOS will only show the search bar when you scroll up. @property (nonatomic) BOOL showSearchBarInitially; /// Defaults to NO. /// /// Setting this to YES will make the search bar activate whenever the view appears. @property (nonatomic) BOOL activatesSearchBarAutomatically; /// nil unless showsSearchBar is set to YES. /// /// self is used as the default search results updater and delegate. /// The search bar will not dim the background or hide the navigation bar by default. /// On iOS 11 and up, the search bar will appear in the navigation bar below the title. @property (nonatomic) UISearchController *searchController; /// Used to initialize the search controller. Defaults to nil. @property (nonatomic) UIViewController *searchResultsController; /// Defaults to "Fast" /// /// Determines how often search bar results will be "debounced." /// Empty query events are always sent instantly. Query events will /// be sent when the user has not changed the query for this interval. @property (nonatomic) FLEXDebounceInterval searchBarDebounceInterval; /// Whether the search bar stays at the top of the view while scrolling. /// /// Calls into self.navigationItem.hidesSearchBarWhenScrolling. /// Do not change self.navigationItem.hidesSearchBarWhenScrolling directly, /// or it will not be respsected. Use this instead. /// Defaults to NO. @property (nonatomic) BOOL pinSearchBar; /// By default, we will show the search bar's cancel button when /// search becomes active and hide it when search is dismissed. /// /// Do not set the showsCancelButton property on the searchController's /// searchBar manually. Set this property after turning on showsSearchBar. /// /// Does nothing pre-iOS 13, safe to call on any version. @property (nonatomic) BOOL automaticallyShowsSearchBarCancelButton; /// If using the scope bar, self.searchController.searchBar.selectedScopeButtonIndex. /// Otherwise, this is the selected index of the carousel, or NSNotFound if using neither. @property (nonatomic) NSInteger selectedScope; /// self.searchController.searchBar.text @property (nonatomic, readonly, copy) NSString *searchText; /// A totally optional delegate to forward search results updater calls to. /// If a delegate is set, updateSearchResults: is not called on this view controller. @property (nonatomic, weak) id searchResultsUpdater; /// self.view.window as a \c FLEXWindow @property (nonatomic, readonly) FLEXWindow *window; /// Convenient for doing some async processor-intensive searching /// in the background before updating the UI back on the main queue. - (void)onBackgroundQueue:(NSArray *(^)(void))backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock; /// Adds up to 3 additional items to the toolbar in right-to-left order. /// /// That is, the first item in the given array will be the rightmost item behind /// any existing toolbar items. By default, buttons for bookmarks and tabs are shown. /// /// If you wish to have more control over how the buttons are arranged or which /// buttons are displayed, you can access the properties for the pre-existing /// toolbar items directly and manually set \c self.toolbarItems by overriding /// the \c setupToolbarItems method below. - (void)addToolbarItems:(NSArray *)items; /// Subclasses may override. You should not need to call this method directly. - (void)setupToolbarItems; @property (nonatomic, readonly) UIBarButtonItem *shareToolbarItem; @property (nonatomic, readonly) UIBarButtonItem *bookmarksToolbarItem; @property (nonatomic, readonly) UIBarButtonItem *openTabsToolbarItem; /// Whether or not to display the "share" icon in the middle of the toolbar. NO by default. /// /// Turning this on after you have added custom toolbar items will /// push off the leftmost toolbar item and shift the others leftward. @property (nonatomic) BOOL showsShareToolbarItem; /// Called when the share button is pressed. /// Default implementation does nothign. Subclasses may override. - (void)shareButtonPressed:(UIBarButtonItem *)sender; /// Subclasses may call this to opt-out of all toolbar related behavior. /// This is necessary if you want to disable the gesture which reveals the toolbar. - (void)disableToolbar; @end ================================================ FILE: Classes/Core/Controllers/FLEXTableViewController.m ================================================ // // FLEXTableViewController.m // FLEX // // Created by Tanner on 7/5/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" #import "FLEXExplorerViewController.h" #import "FLEXBookmarksViewController.h" #import "FLEXTabsViewController.h" #import "FLEXScopeCarousel.h" #import "FLEXTableView.h" #import "FLEXUtility.h" #import "FLEXResources.h" #import "UIBarButtonItem+FLEX.h" #import @interface Block : NSObject - (void)invoke; @end CGFloat const kFLEXDebounceInstant = 0.f; CGFloat const kFLEXDebounceFast = 0.05; CGFloat const kFLEXDebounceForAsyncSearch = 0.15; CGFloat const kFLEXDebounceForExpensiveIO = 0.5; @interface FLEXTableViewController () @property (nonatomic) NSTimer *debounceTimer; @property (nonatomic) BOOL didInitiallyRevealSearchBar; @property (nonatomic) UITableViewStyle style; @property (nonatomic) BOOL hasAppeared; @property (nonatomic, readonly) UIView *tableHeaderViewContainer; @property (nonatomic, readonly) BOOL manuallyDeactivateSearchOnDisappear; @property (nonatomic) UIBarButtonItem *middleToolbarItem; @property (nonatomic) UIBarButtonItem *middleLeftToolbarItem; @property (nonatomic) UIBarButtonItem *leftmostToolbarItem; @end @implementation FLEXTableViewController @dynamic tableView; @synthesize showsShareToolbarItem = _showsShareToolbarItem; @synthesize tableHeaderViewContainer = _tableHeaderViewContainer; @synthesize automaticallyShowsSearchBarCancelButton = _automaticallyShowsSearchBarCancelButton; #pragma mark - Initialization - (id)init { if (@available(iOS 13.0, *)) { self = [self initWithStyle:UITableViewStyleInsetGrouped]; } else { self = [self initWithStyle:UITableViewStyleGrouped]; } return self; } - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { _searchBarDebounceInterval = kFLEXDebounceFast; _showSearchBarInitially = YES; _style = style; _manuallyDeactivateSearchOnDisappear = ( NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11 ); // We will be our own search delegate if we implement this method if ([self respondsToSelector:@selector(updateSearchResults:)]) { self.searchDelegate = (id)self; } } return self; } #pragma mark - Public - (FLEXWindow *)window { return (id)self.view.window; } - (void)setShowsSearchBar:(BOOL)showsSearchBar { if (_showsSearchBar == showsSearchBar) return; _showsSearchBar = showsSearchBar; if (showsSearchBar) { UIViewController *results = self.searchResultsController; self.searchController = [[UISearchController alloc] initWithSearchResultsController:results]; self.searchController.searchBar.placeholder = @"Filter"; self.searchController.searchResultsUpdater = (id)self; self.searchController.delegate = (id)self; if (@available(iOS 9.1, *)) { self.searchController.obscuresBackgroundDuringPresentation = NO; } else { self.searchController.dimsBackgroundDuringPresentation = NO; } self.searchController.hidesNavigationBarDuringPresentation = NO; /// Not necessary in iOS 13; remove this when iOS 13 is the minimum deployment target self.searchController.searchBar.delegate = self; self.automaticallyShowsSearchBarCancelButton = YES; if (@available(iOS 13, *)) { self.searchController.automaticallyShowsScopeBar = NO; } [self addSearchController:self.searchController]; } else { // Search already shown and just set to NO, so remove it [self removeSearchController:self.searchController]; } } - (void)setShowsCarousel:(BOOL)showsCarousel { if (_showsCarousel == showsCarousel) return; _showsCarousel = showsCarousel; if (showsCarousel) { _carousel = ({ weakify(self) FLEXScopeCarousel *carousel = [FLEXScopeCarousel new]; carousel.selectedIndexChangedAction = ^(NSInteger idx) { strongify(self); [self.searchDelegate updateSearchResults:self.searchText]; }; // UITableView won't update the header size unless you reset the header view [carousel registerBlockForDynamicTypeChanges:^(FLEXScopeCarousel *_) { strongify(self); [self layoutTableHeaderIfNeeded]; }]; carousel; }); [self addCarousel:_carousel]; } else { // Carousel already shown and just set to NO, so remove it [self removeCarousel:_carousel]; } } - (NSInteger)selectedScope { if (self.searchController.searchBar.showsScopeBar) { return self.searchController.searchBar.selectedScopeButtonIndex; } else if (self.showsCarousel) { return self.carousel.selectedIndex; } else { return 0; } } - (void)setSelectedScope:(NSInteger)selectedScope { if (self.searchController.searchBar.showsScopeBar) { self.searchController.searchBar.selectedScopeButtonIndex = selectedScope; } else if (self.showsCarousel) { self.carousel.selectedIndex = selectedScope; } [self.searchDelegate updateSearchResults:self.searchText]; } - (NSString *)searchText { return self.searchController.searchBar.text; } - (BOOL)automaticallyShowsSearchBarCancelButton { if (@available(iOS 13, *)) { return self.searchController.automaticallyShowsCancelButton; } return _automaticallyShowsSearchBarCancelButton; } - (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)value { if (@available(iOS 13, *)) { self.searchController.automaticallyShowsCancelButton = value; } _automaticallyShowsSearchBarCancelButton = value; } - (void)onBackgroundQueue:(NSArray *(^)(void))backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSArray *items = backgroundBlock(); dispatch_async(dispatch_get_main_queue(), ^{ mainBlock(items); }); }); } - (void)setsShowsShareToolbarItem:(BOOL)showsShareToolbarItem { _showsShareToolbarItem = showsShareToolbarItem; if (self.isViewLoaded) { [self setupToolbarItems]; } } - (void)disableToolbar { self.navigationController.toolbarHidden = YES; self.navigationController.hidesBarsOnSwipe = NO; self.toolbarItems = nil; } #pragma mark - View Controller Lifecycle - (void)loadView { self.view = [FLEXTableView style:self.style]; self.tableView.dataSource = self; self.tableView.delegate = self; self.tableView.estimatedRowHeight = 10; _shareToolbarItem = FLEXBarButtonItemSystem(Action, self, @selector(shareButtonPressed:)); _bookmarksToolbarItem = [UIBarButtonItem flex_itemWithImage:FLEXResources.bookmarksIcon target:self action:@selector(showBookmarks) ]; _openTabsToolbarItem = [UIBarButtonItem flex_itemWithImage:FLEXResources.openTabsIcon target:self action:@selector(showTabSwitcher) ]; self.leftmostToolbarItem = UIBarButtonItem.flex_fixedSpace; self.middleLeftToolbarItem = UIBarButtonItem.flex_fixedSpace; self.middleToolbarItem = UIBarButtonItem.flex_fixedSpace; } - (void)viewDidLoad { [super viewDidLoad]; self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag; // Toolbar self.navigationController.toolbarHidden = self.toolbarItems.count > 0; self.navigationController.hidesBarsOnSwipe = YES; // On iOS 13, the root view controller shows it's search bar no matter what. // Turning this off avoids some weird flash the navigation bar does when we // toggle navigationItem.hidesSearchBarWhenScrolling on and off. The flash // will still happen on subsequent view controllers, but we can at least // avoid it for the root view controller if (@available(iOS 13, *)) { if (self.navigationController.viewControllers.firstObject == self) { _showSearchBarInitially = NO; } } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (@available(iOS 11.0, *)) { // When going back, make the search bar reappear instead of hiding if ((self.pinSearchBar || self.showSearchBarInitially) && !self.didInitiallyRevealSearchBar) { self.navigationItem.hidesSearchBarWhenScrolling = NO; } } // Make the keyboard seem to appear faster if (self.activatesSearchBarAutomatically) { [self makeKeyboardAppearNow]; } [self setupToolbarItems]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Allow scrolling to collapse the search bar, only if we don't want it pinned if (@available(iOS 11.0, *)) { if (self.showSearchBarInitially && !self.pinSearchBar && !self.didInitiallyRevealSearchBar) { // All this mumbo jumbo is necessary to work around a bug in iOS 13 up to 13.2 // wherein quickly toggling navigationItem.hidesSearchBarWhenScrolling to make // the search bar appear initially results in a bugged search bar that // becomes transparent and floats over the screen as you scroll [UIView animateWithDuration:0.2 animations:^{ self.navigationItem.hidesSearchBarWhenScrolling = YES; [self.navigationController.view setNeedsLayout]; [self.navigationController.view layoutIfNeeded]; }]; } } if (self.activatesSearchBarAutomatically) { // Keyboard has appeared, now we call this as we soon present our search bar [self removeDummyTextField]; // Activate the search bar dispatch_async(dispatch_get_main_queue(), ^{ // This doesn't work unless it's wrapped in this dispatch_async call [self.searchController.searchBar becomeFirstResponder]; }); } // We only want to reveal the search bar when the view controller first appears. self.didInitiallyRevealSearchBar = YES; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.manuallyDeactivateSearchOnDisappear && self.searchController.isActive) { self.searchController.active = NO; } } - (void)didMoveToParentViewController:(UIViewController *)parent { [super didMoveToParentViewController:parent]; // Reset this since we are re-appearing under a new // parent view controller and need to show it again self.didInitiallyRevealSearchBar = NO; } #pragma mark - Toolbar, Public - (void)setupToolbarItems { if (!self.isViewLoaded) { return; } self.toolbarItems = @[ self.leftmostToolbarItem, UIBarButtonItem.flex_flexibleSpace, self.middleLeftToolbarItem, UIBarButtonItem.flex_flexibleSpace, self.middleToolbarItem, UIBarButtonItem.flex_flexibleSpace, self.bookmarksToolbarItem, UIBarButtonItem.flex_flexibleSpace, self.openTabsToolbarItem, ]; for (UIBarButtonItem *item in self.toolbarItems) { [item _setWidth:60]; // This does not work for anything but fixed spaces for some reason // item.width = 60; } // Disable tabs entirely when not presented by FLEXExplorerViewController UIViewController *presenter = self.navigationController.presentingViewController; if (![presenter isKindOfClass:[FLEXExplorerViewController class]]) { self.openTabsToolbarItem.enabled = NO; } } - (void)addToolbarItems:(NSArray *)items { if (self.showsShareToolbarItem) { // Share button is in the middle, skip middle button if (items.count > 0) { self.middleLeftToolbarItem = items[0]; } if (items.count > 1) { self.leftmostToolbarItem = items[1]; } } else { // Add buttons right-to-left if (items.count > 0) { self.middleToolbarItem = items[0]; } if (items.count > 1) { self.middleLeftToolbarItem = items[1]; } if (items.count > 2) { self.leftmostToolbarItem = items[2]; } } [self setupToolbarItems]; } - (void)setShowsShareToolbarItem:(BOOL)showShare { if (_showsShareToolbarItem != showShare) { _showsShareToolbarItem = showShare; if (showShare) { // Push out leftmost item self.leftmostToolbarItem = self.middleLeftToolbarItem; self.middleLeftToolbarItem = self.middleToolbarItem; // Use share for middle self.middleToolbarItem = self.shareToolbarItem; } else { // Remove share, shift custom items rightward self.middleToolbarItem = self.middleLeftToolbarItem; self.middleLeftToolbarItem = self.leftmostToolbarItem; self.leftmostToolbarItem = UIBarButtonItem.flex_fixedSpace; } } [self setupToolbarItems]; } - (void)shareButtonPressed:(UIBarButtonItem *)sender { } #pragma mark - Private - (void)debounce:(void(^)(void))block { [self.debounceTimer invalidate]; self.debounceTimer = [NSTimer scheduledTimerWithTimeInterval:self.searchBarDebounceInterval target:block selector:@selector(invoke) userInfo:nil repeats:NO ]; } - (void)layoutTableHeaderIfNeeded { if (self.showsCarousel) { self.carousel.frame = FLEXRectSetHeight( self.carousel.frame, self.carousel.intrinsicContentSize.height ); } self.tableView.tableHeaderView = self.tableView.tableHeaderView; } - (void)addCarousel:(FLEXScopeCarousel *)carousel { if (@available(iOS 11.0, *)) { self.tableView.tableHeaderView = carousel; } else { carousel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; CGRect frame = self.tableHeaderViewContainer.frame; CGRect subviewFrame = carousel.frame; subviewFrame.origin.y = 0; // Put the carousel below the search bar if it's already there if (self.showsSearchBar) { carousel.frame = subviewFrame = FLEXRectSetY( subviewFrame, self.searchController.searchBar.frame.size.height ); frame.size.height += carousel.intrinsicContentSize.height; } else { frame.size.height = carousel.intrinsicContentSize.height; } self.tableHeaderViewContainer.frame = frame; [self.tableHeaderViewContainer addSubview:carousel]; } [self layoutTableHeaderIfNeeded]; } - (void)removeCarousel:(FLEXScopeCarousel *)carousel { [carousel removeFromSuperview]; if (@available(iOS 11.0, *)) { self.tableView.tableHeaderView = nil; } else { if (self.showsSearchBar) { [self removeSearchController:self.searchController]; [self addSearchController:self.searchController]; } else { self.tableView.tableHeaderView = nil; _tableHeaderViewContainer = nil; } } } - (void)addSearchController:(UISearchController *)controller { if (@available(iOS 11.0, *)) { self.navigationItem.searchController = controller; } else { controller.searchBar.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin; [self.tableHeaderViewContainer addSubview:controller.searchBar]; CGRect subviewFrame = controller.searchBar.frame; CGRect frame = self.tableHeaderViewContainer.frame; frame.size.width = MAX(frame.size.width, subviewFrame.size.width); frame.size.height = subviewFrame.size.height; // Move the carousel down if it's already there if (self.showsCarousel) { self.carousel.frame = FLEXRectSetY( self.carousel.frame, subviewFrame.size.height ); frame.size.height += self.carousel.frame.size.height; } self.tableHeaderViewContainer.frame = frame; [self layoutTableHeaderIfNeeded]; } } - (void)removeSearchController:(UISearchController *)controller { if (@available(iOS 11.0, *)) { self.navigationItem.searchController = nil; } else { [controller.searchBar removeFromSuperview]; if (self.showsCarousel) { // self.carousel.frame = FLEXRectRemake(CGPointZero, self.carousel.frame.size); [self removeCarousel:self.carousel]; [self addCarousel:self.carousel]; } else { self.tableView.tableHeaderView = nil; _tableHeaderViewContainer = nil; } } } - (UIView *)tableHeaderViewContainer { if (!_tableHeaderViewContainer) { _tableHeaderViewContainer = [UIView new]; self.tableView.tableHeaderView = self.tableHeaderViewContainer; } return _tableHeaderViewContainer; } - (void)showBookmarks { UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[FLEXBookmarksViewController new] ]; [self presentViewController:nav animated:YES completion:nil]; } - (void)showTabSwitcher { UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[FLEXTabsViewController new] ]; [self presentViewController:nav animated:YES completion:nil]; } #pragma mark - Search Bar #pragma mark Faster keyboard static UITextField *kDummyTextField = nil; /// Make the keyboard appear instantly. We use this to make the /// keyboard appear faster when the search bar is set to appear initially. /// You must call \c -removeDummyTextField before your search bar is to appear. - (void)makeKeyboardAppearNow { if (!kDummyTextField) { kDummyTextField = [UITextField new]; kDummyTextField.autocorrectionType = UITextAutocorrectionTypeNo; } kDummyTextField.inputAccessoryView = self.searchController.searchBar.inputAccessoryView; [UIApplication.sharedApplication.keyWindow addSubview:kDummyTextField]; [kDummyTextField becomeFirstResponder]; } - (void)removeDummyTextField { if (kDummyTextField.superview) { [kDummyTextField removeFromSuperview]; } } #pragma mark UISearchResultsUpdating - (void)updateSearchResultsForSearchController:(UISearchController *)searchController { [self.debounceTimer invalidate]; NSString *text = searchController.searchBar.text; void (^updateSearchResults)(void) = ^{ if (self.searchResultsUpdater) { [self.searchResultsUpdater updateSearchResults:text]; } else { [self.searchDelegate updateSearchResults:text]; } }; // Only debounce if we want to, and if we have a non-empty string // Empty string events are sent instantly if (text.length && self.searchBarDebounceInterval > kFLEXDebounceInstant) { [self debounce:updateSearchResults]; } else { updateSearchResults(); } } #pragma mark UISearchControllerDelegate - (void)willPresentSearchController:(UISearchController *)searchController { // Manually show cancel button for < iOS 13 if (!@available(iOS 13, *) && self.automaticallyShowsSearchBarCancelButton) { [searchController.searchBar setShowsCancelButton:YES animated:YES]; } } - (void)willDismissSearchController:(UISearchController *)searchController { // Manually hide cancel button for < iOS 13 if (!@available(iOS 13, *) && self.automaticallyShowsSearchBarCancelButton) { [searchController.searchBar setShowsCancelButton:NO animated:YES]; } } #pragma mark UISearchBarDelegate /// Not necessary in iOS 13; remove this when iOS 13 is the deployment target - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope { [self updateSearchResultsForSearchController:self.searchController]; } #pragma mark Table View /// Not having a title in the first section looks weird with a rounded-corner table view style - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (@available(iOS 13, *)) { if (self.style == UITableViewStyleInsetGrouped) { return @" "; } } return nil; // For plain/gropued style } @end ================================================ FILE: Classes/Core/FLEXSingleRowSection.h ================================================ // // FLEXSingleRowSection.h // FLEX // // Created by Tanner Bennett on 9/25/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewSection.h" NS_ASSUME_NONNULL_BEGIN /// A section providing a specific single row. /// /// You may optionally provide a view controller to push when the row /// is selected, or an action to perform when it is selected. /// Which one is used first is up to the table view data source. @interface FLEXSingleRowSection : FLEXTableViewSection /// @param reuseIdentifier if nil, kFLEXDefaultCell is used. + (instancetype)title:(nullable NSString *)sectionTitle reuse:(nullable NSString *)reuseIdentifier cell:(void(^)(__kindof UITableViewCell *cell))cellConfiguration; @property (nullable, nonatomic) UIViewController *pushOnSelection; @property (nullable, nonatomic) void (^selectionAction)(UIViewController *host); /// Called to determine whether the single row should display itself or not. @property (nonatomic) BOOL (^filterMatcher)(NSString *filterText); @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Core/FLEXSingleRowSection.m ================================================ // // FLEXSingleRowSection.m // FLEX // // Created by Tanner Bennett on 9/25/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXSingleRowSection.h" #import "FLEXTableView.h" @interface FLEXSingleRowSection () @property (nonatomic, readonly) NSString *reuseIdentifier; @property (nonatomic, readonly) void (^cellConfiguration)(__kindof UITableViewCell *cell); @property (nonatomic) NSString *lastTitle; @property (nonatomic) NSString *lastSubitle; @end @implementation FLEXSingleRowSection #pragma mark - Public + (instancetype)title:(NSString *)title reuse:(NSString *)reuse cell:(void (^)(__kindof UITableViewCell *))config { return [[self alloc] initWithTitle:title reuse:reuse cell:config]; } - (id)initWithTitle:(NSString *)sectionTitle reuse:(NSString *)reuseIdentifier cell:(void (^)(__kindof UITableViewCell *))cellConfiguration { NSParameterAssert(cellConfiguration); self = [super init]; if (self) { _title = sectionTitle; _reuseIdentifier = reuseIdentifier ?: kFLEXDefaultCell; _cellConfiguration = cellConfiguration; } return self; } #pragma mark - Overrides - (NSInteger)numberOfRows { if (self.filterMatcher && self.filterText.length) { return self.filterMatcher(self.filterText) ? 1 : 0; } return 1; } - (BOOL)canSelectRow:(NSInteger)row { return self.pushOnSelection || self.selectionAction; } - (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row { return self.selectionAction; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { return self.pushOnSelection; } - (NSString *)reuseIdentifierForRow:(NSInteger)row { return self.reuseIdentifier; } - (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row { cell.textLabel.text = nil; cell.detailTextLabel.text = nil; cell.accessoryType = UITableViewCellAccessoryNone; self.cellConfiguration(cell); self.lastTitle = cell.textLabel.text; self.lastSubitle = cell.detailTextLabel.text; } - (NSString *)titleForRow:(NSInteger)row { return self.lastTitle; } - (NSString *)subtitleForRow:(NSInteger)row { return self.lastSubitle; } @end ================================================ FILE: Classes/Core/FLEXTableViewSection.h ================================================ // // FLEXTableViewSection.h // FLEX // // Created by Tanner on 1/29/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import "NSArray+FLEX.h" @class FLEXTableView; NS_ASSUME_NONNULL_BEGIN #pragma mark FLEXTableViewSection /// An abstract base class for table view sections. /// /// Many properties or methods here return nil or some logical equivalent by default. /// Even so, most of the methods with defaults are intended to be overriden by subclasses. /// Some methods are not implemented at all and MUST be implemented by a subclass. @interface FLEXTableViewSection : NSObject { @protected /// Unused by default, use if you want NSString *_title; @private __weak UITableView *_tableView; NSInteger _sectionIndex; } #pragma mark - Data /// A title to be displayed for the custom section. /// Subclasses may override or use the \c _title ivar. @property (nonatomic, readonly, nullable, copy) NSString *title; /// The number of rows in this section. Subclasses must override. /// This should not change until \c filterText is changed or \c reloadData is called. @property (nonatomic, readonly) NSInteger numberOfRows; /// A map of reuse identifiers to \c UITableViewCell (sub)class objects. /// Subclasses \e may override this as necessary, but are not required to. /// See \c FLEXTableView.h for more information. /// @return nil by default. @property (nonatomic, readonly, nullable) NSDictionary *cellRegistrationMapping; /// The section should filter itself based on the contents of this property /// as it is set. If it is set to nil or an empty string, it should not filter. /// Subclasses should override or observe this property and react to changes. /// /// It is common practice to use two arrays for the underlying model: /// One to hold all rows, and one to hold unfiltered rows. When \c setFilterText: /// is called, call \c super to store the new value, and re-filter your model accordingly. @property (nonatomic, nullable) NSString *filterText; /// Provides an avenue for the section to refresh data or change the number of rows. /// /// This is called before reloading the table view itself. If your section pulls data /// from an external data source, this is a good place to refresh that data entirely. /// If your section does not, then it might be simpler for you to just override /// \c setFilterText: to call \c super and call \c reloadData. - (void)reloadData; /// Like \c reloadData, but optionally reloads the table view section /// associated with this section object, if any. Do not override. /// Do not call outside of the main thread. - (void)reloadData:(BOOL)updateTable; /// Provide a table view and section index to allow the section to efficiently reload /// its own section of the table when something changes it. The table reference is /// held weakly, and subclasses cannot access it or the index. Call this method again /// if the section numbers have changed since you last called it. - (void)setTable:(UITableView *)tableView section:(NSInteger)index; #pragma mark - Row Selection /// Whether the given row should be selectable, such as if tapping the cell /// should take the user to a new screen or trigger an action. /// Subclasses \e may override this as necessary, but are not required to. /// @return \c NO by default - (BOOL)canSelectRow:(NSInteger)row; /// An action "future" to be triggered when the row is selected, if the row /// supports being selected as indicated by \c canSelectRow:. Subclasses /// must implement this in accordance with how they implement \c canSelectRow: /// if they do not implement \c viewControllerToPushForRow: /// @return This returns \c nil if no view controller is provided by /// \c viewControllerToPushForRow: — otherwise it pushes that view controller /// onto \c host.navigationController - (nullable void(^)(__kindof UIViewController *host))didSelectRowAction:(NSInteger)row; /// A view controller to display when the row is selected, if the row /// supports being selected as indicated by \c canSelectRow:. Subclasses /// must implement this in accordance with how they implement \c canSelectRow: /// if they do not implement \c didSelectRowAction: /// @return \c nil by default - (nullable UIViewController *)viewControllerToPushForRow:(NSInteger)row; /// Called when the accessory view's detail button is pressed. /// @return \c nil by default. - (nullable void(^)(__kindof UIViewController *host))didPressInfoButtonAction:(NSInteger)row; #pragma mark - Context Menus /// By default, this is the title of the row. /// @return The title of the context menu, if any. - (nullable NSString *)menuTitleForRow:(NSInteger)row API_AVAILABLE(ios(13.0)); /// Protected, not intended for public use. \c menuTitleForRow: /// already includes the value returned from this method. /// /// By default, this returns \c @"". Subclasses may override to /// provide a detailed description of the target of the context menu. - (NSString *)menuSubtitleForRow:(NSInteger)row API_AVAILABLE(ios(13.0)); /// The context menu items, if any. Subclasses may override. /// By default, only inludes items for \c copyMenuItemsForRow:. - (nullable NSArray *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13.0)); /// Subclasses may override to return a list of copiable items. /// /// Every two elements in the list compose a key-value pair, where the key /// should be a description of what will be copied, and the values should be /// the strings to copy. Return an empty string as a value to show a disabled action. - (nullable NSArray *)copyMenuItemsForRow:(NSInteger)row API_AVAILABLE(ios(13.0)); #pragma mark - Cell Configuration /// Provide a reuse identifier for the given row. Subclasses should override. /// /// Custom reuse identifiers should be specified in \c cellRegistrationMapping. /// You may return any of the identifiers in \c FLEXTableView.h /// without including them in the \c cellRegistrationMapping. /// @return \c kFLEXDefaultCell by default. - (NSString *)reuseIdentifierForRow:(NSInteger)row; /// Configure a cell for the given row. Subclasses must override. - (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row; #pragma mark - External Convenience /// For use by whatever view controller uses your section. Not required. /// @return An optional title. - (nullable NSString *)titleForRow:(NSInteger)row; /// For use by whatever view controller uses your section. Not required. /// @return An optional subtitle. - (nullable NSString *)subtitleForRow:(NSInteger)row; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Core/FLEXTableViewSection.m ================================================ // // FLEXTableViewSection.m // FLEX // // Created by Tanner on 1/29/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewSection.h" #import "FLEXTableView.h" #import "FLEXUtility.h" #import "UIMenu+FLEX.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wincomplete-implementation" @implementation FLEXTableViewSection - (NSInteger)numberOfRows { return 0; } - (void)reloadData { } - (void)reloadData:(BOOL)updateTable { [self reloadData]; if (updateTable) { NSIndexSet *index = [NSIndexSet indexSetWithIndex:_sectionIndex]; [_tableView reloadSections:index withRowAnimation:UITableViewRowAnimationNone]; } } - (void)setTable:(UITableView *)tableView section:(NSInteger)index { _tableView = tableView; _sectionIndex = index; } - (NSDictionary *)cellRegistrationMapping { return nil; } - (BOOL)canSelectRow:(NSInteger)row { return NO; } - (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row { UIViewController *toPush = [self viewControllerToPushForRow:row]; if (toPush) { return ^(UIViewController *host) { [host.navigationController pushViewController:toPush animated:YES]; }; } return nil; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { return nil; } - (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row { return nil; } - (NSString *)reuseIdentifierForRow:(NSInteger)row { return kFLEXDefaultCell; } - (NSString *)menuTitleForRow:(NSInteger)row { NSString *title = [self titleForRow:row]; NSString *subtitle = [self menuSubtitleForRow:row]; if (subtitle.length) { return [NSString stringWithFormat:@"%@\n\n%@", title, subtitle]; } return title; } - (NSString *)menuSubtitleForRow:(NSInteger)row { return @""; } - (NSArray *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13.0)) { NSArray *copyItems = [self copyMenuItemsForRow:row]; NSAssert(copyItems.count % 2 == 0, @"copyMenuItemsForRow: should return an even list"); if (copyItems.count) { NSInteger numberOfActions = copyItems.count / 2; BOOL collapseMenu = numberOfActions > 4; UIImage *copyIcon = [UIImage systemImageNamed:@"doc.on.doc"]; NSMutableArray *actions = [NSMutableArray new]; for (NSInteger i = 0; i < copyItems.count; i += 2) { NSString *key = copyItems[i], *value = copyItems[i+1]; NSString *title = collapseMenu ? key : [@"Copy " stringByAppendingString:key]; UIAction *copy = [UIAction actionWithTitle:title image:copyIcon identifier:nil handler:^(__kindof UIAction *action) { UIPasteboard.generalPasteboard.string = value; } ]; if (!value.length) { copy.attributes = UIMenuElementAttributesDisabled; } [actions addObject:copy]; } UIMenu *copyMenu = [UIMenu flex_inlineMenuWithTitle:@"Copy…" image:copyIcon children:actions ]; if (collapseMenu) { return @[[copyMenu flex_collapsed]]; } else { return @[copyMenu]; } } return @[]; } - (NSArray *)copyMenuItemsForRow:(NSInteger)row { return nil; } - (NSString *)titleForRow:(NSInteger)row { return nil; } - (NSString *)subtitleForRow:(NSInteger)row { return nil; } @end #pragma clang diagnostic pop ================================================ FILE: Classes/Core/Views/Carousel/FLEXCarouselCell.h ================================================ // // FLEXCarouselCell.h // FLEX // // Created by Tanner Bennett on 7/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface FLEXCarouselCell : UICollectionViewCell @property (nonatomic, copy) NSString *title; @end ================================================ FILE: Classes/Core/Views/Carousel/FLEXCarouselCell.m ================================================ // // FLEXCarouselCell.m // FLEX // // Created by Tanner Bennett on 7/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXCarouselCell.h" #import "FLEXColor.h" #import "UIView+FLEX_Layout.h" @interface FLEXCarouselCell () @property (nonatomic, readonly) UILabel *titleLabel; @property (nonatomic, readonly) UIView *selectionIndicatorStripe; @property (nonatomic) BOOL constraintsInstalled; @end @implementation FLEXCarouselCell - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { _titleLabel = [UILabel new]; _selectionIndicatorStripe = [UIView new]; self.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; self.selectionIndicatorStripe.backgroundColor = self.tintColor; if (@available(iOS 10, *)) { self.titleLabel.adjustsFontForContentSizeCategory = YES; } [self.contentView addSubview:self.titleLabel]; [self.contentView addSubview:self.selectionIndicatorStripe]; [self installConstraints]; [self updateAppearance]; } return self; } - (void)updateAppearance { self.selectionIndicatorStripe.hidden = !self.selected; if (self.selected) { self.titleLabel.textColor = self.tintColor; } else { self.titleLabel.textColor = FLEXColor.deemphasizedTextColor; } } #pragma mark Public - (NSString *)title { return self.titleLabel.text; } - (void)setTitle:(NSString *)title { self.titleLabel.text = title; [self.titleLabel sizeToFit]; [self setNeedsLayout]; } #pragma mark Overrides - (void)prepareForReuse { [super prepareForReuse]; [self updateAppearance]; } - (void)installConstraints { CGFloat stripeHeight = 2; self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; self.selectionIndicatorStripe.translatesAutoresizingMaskIntoConstraints = NO; UIView *superview = self.contentView; [self.titleLabel flex_pinEdgesToSuperviewWithInsets:UIEdgeInsetsMake(10, 15, 8 + stripeHeight, 15)]; [self.selectionIndicatorStripe.leadingAnchor constraintEqualToAnchor:superview.leadingAnchor].active = YES; [self.selectionIndicatorStripe.bottomAnchor constraintEqualToAnchor:superview.bottomAnchor].active = YES; [self.selectionIndicatorStripe.trailingAnchor constraintEqualToAnchor:superview.trailingAnchor].active = YES; [self.selectionIndicatorStripe.heightAnchor constraintEqualToConstant:stripeHeight].active = YES; } - (void)setSelected:(BOOL)selected { super.selected = selected; [self updateAppearance]; } @end ================================================ FILE: Classes/Core/Views/Carousel/FLEXScopeCarousel.h ================================================ // // FLEXScopeCarousel.h // FLEX // // Created by Tanner Bennett on 7/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import /// Only use on iOS 10 and up. Requires iOS 10 APIs for calculating row sizes. @interface FLEXScopeCarousel : UIControl @property (nonatomic, copy) NSArray *items; @property (nonatomic) NSInteger selectedIndex; @property (nonatomic) void(^selectedIndexChangedAction)(NSInteger idx); - (void)registerBlockForDynamicTypeChanges:(void(^)(FLEXScopeCarousel *))handler; @end ================================================ FILE: Classes/Core/Views/Carousel/FLEXScopeCarousel.m ================================================ // // FLEXScopeCarousel.m // FLEX // // Created by Tanner Bennett on 7/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXScopeCarousel.h" #import "FLEXCarouselCell.h" #import "FLEXColor.h" #import "FLEXMacros.h" #import "UIView+FLEX_Layout.h" const CGFloat kCarouselItemSpacing = 0; NSString * const kCarouselCellReuseIdentifier = @"kCarouselCellReuseIdentifier"; @interface FLEXScopeCarousel () @property (nonatomic, readonly) UICollectionView *collectionView; @property (nonatomic, readonly) FLEXCarouselCell *sizingCell; @property (nonatomic, readonly) id dynamicTypeObserver; @property (nonatomic, readonly) NSMutableArray *dynamicTypeHandlers; @property (nonatomic) BOOL constraintsInstalled; @end @implementation FLEXScopeCarousel - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = FLEXColor.primaryBackgroundColor; self.autoresizingMask = UIViewAutoresizingFlexibleWidth; self.translatesAutoresizingMaskIntoConstraints = YES; _dynamicTypeHandlers = [NSMutableArray new]; CGSize itemSize = CGSizeZero; if (@available(iOS 10.0, *)) { itemSize = UICollectionViewFlowLayoutAutomaticSize; } // Collection view layout UICollectionViewFlowLayout *layout = ({ UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new]; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.sectionInset = UIEdgeInsetsZero; layout.minimumLineSpacing = kCarouselItemSpacing; layout.itemSize = itemSize; layout.estimatedItemSize = itemSize; layout; }); // Collection view _collectionView = ({ UICollectionView *cv = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout ]; cv.showsHorizontalScrollIndicator = NO; cv.backgroundColor = UIColor.clearColor; cv.delegate = self; cv.dataSource = self; [cv registerClass:[FLEXCarouselCell class] forCellWithReuseIdentifier:kCarouselCellReuseIdentifier]; [self addSubview:cv]; cv; }); // Sizing cell _sizingCell = [FLEXCarouselCell new]; self.sizingCell.title = @"NSObject"; // Dynamic type weakify(self); _dynamicTypeObserver = [NSNotificationCenter.defaultCenter addObserverForName:UIContentSizeCategoryDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { strongify(self) [self.collectionView setNeedsLayout]; [self setNeedsUpdateConstraints]; // Notify observers for (void (^block)(FLEXScopeCarousel *) in self.dynamicTypeHandlers) { block(self); } } ]; } return self; } - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self.dynamicTypeObserver]; } #pragma mark - Overrides - (void)drawRect:(CGRect)rect { [super drawRect:rect]; CGFloat width = 1.f / UIScreen.mainScreen.scale; // Draw hairline CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, FLEXColor.hairlineColor.CGColor); CGContextSetLineWidth(context, width); CGContextMoveToPoint(context, 0, rect.size.height - width); CGContextAddLineToPoint(context, rect.size.width, rect.size.height - width); CGContextStrokePath(context); } + (BOOL)requiresConstraintBasedLayout { return YES; } - (void)updateConstraints { if (!self.constraintsInstalled) { self.collectionView.translatesAutoresizingMaskIntoConstraints = NO; [self.collectionView flex_pinEdgesToSuperview]; self.constraintsInstalled = YES; } [super updateConstraints]; } - (CGSize)intrinsicContentSize { return CGSizeMake( UIViewNoIntrinsicMetric, [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height ); } #pragma mark - Public - (void)setItems:(NSArray *)items { NSParameterAssert(items.count); _items = items.copy; // Refresh list, select first item initially [self.collectionView reloadData]; self.selectedIndex = 0; } - (void)setSelectedIndex:(NSInteger)idx { NSParameterAssert(idx < self.items.count); _selectedIndex = idx; NSIndexPath *path = [NSIndexPath indexPathForItem:idx inSection:0]; [self.collectionView selectItemAtIndexPath:path animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; [self collectionView:self.collectionView didSelectItemAtIndexPath:path]; } - (void)registerBlockForDynamicTypeChanges:(void (^)(FLEXScopeCarousel *))handler { [self.dynamicTypeHandlers addObject:handler]; } #pragma mark - UICollectionView - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { // if (@available(iOS 10.0, *)) { // return UICollectionViewFlowLayoutAutomaticSize; // } self.sizingCell.title = self.items[indexPath.item]; return [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.items.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { FLEXCarouselCell *cell = (id)[collectionView dequeueReusableCellWithReuseIdentifier:kCarouselCellReuseIdentifier forIndexPath:indexPath]; cell.title = self.items[indexPath.row]; return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { _selectedIndex = indexPath.item; // In case self.selectedIndex didn't trigger this call if (self.selectedIndexChangedAction) { self.selectedIndexChangedAction(indexPath.row); } // TODO: dynamically choose a scroll position. Very wide items should // get "Left" while smaller items should not scroll at all, unless // they are only partially on the screen, in which case they // should get "HorizontallyCentered" to bring them onto the screen. // For now, everything goes to the left, as this has a similar effect. [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES]; [self sendActionsForControlEvents:UIControlEventValueChanged]; } @end ================================================ FILE: Classes/Core/Views/Cells/FLEXCodeFontCell.h ================================================ // // FLEXCodeFontCell.h // FLEX // // Created by Tanner on 12/27/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXMultilineTableViewCell.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXCodeFontCell : FLEXMultilineDetailTableViewCell @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Core/Views/Cells/FLEXCodeFontCell.m ================================================ // // FLEXCodeFontCell.m // FLEX // // Created by Tanner on 12/27/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXCodeFontCell.h" #import "UIFont+FLEX.h" @implementation FLEXCodeFontCell - (void)postInit { [super postInit]; self.titleLabel.font = UIFont.flex_codeFont; self.subtitleLabel.font = UIFont.flex_codeFont; self.titleLabel.adjustsFontSizeToFitWidth = YES; self.titleLabel.minimumScaleFactor = 0.9; self.subtitleLabel.adjustsFontSizeToFitWidth = YES; self.subtitleLabel.minimumScaleFactor = 0.75; // Disable mutli-line pre iOS 11 if (@available(iOS 11, *)) { self.subtitleLabel.numberOfLines = 5; } else { self.titleLabel.numberOfLines = 1; self.subtitleLabel.numberOfLines = 1; } } @end ================================================ FILE: Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.h ================================================ // // FLEXKeyValueTableViewCell.h // FLEX // // Created by Tanner Bennett on 1/23/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewCell.h" @interface FLEXKeyValueTableViewCell : FLEXTableViewCell @end ================================================ FILE: Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.m ================================================ // // FLEXKeyValueTableViewCell.m // FLEX // // Created by Tanner Bennett on 1/23/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXKeyValueTableViewCell.h" @implementation FLEXKeyValueTableViewCell - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { return [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]; } @end ================================================ FILE: Classes/Core/Views/Cells/FLEXMultilineTableViewCell.h ================================================ // // FLEXMultilineTableViewCell.h // FLEX // // Created by Ryan Olson on 2/13/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewCell.h" /// A cell with both labels set to be multi-line capable. @interface FLEXMultilineTableViewCell : FLEXTableViewCell + (CGFloat)preferredHeightWithAttributedText:(NSAttributedString *)attributedText maxWidth:(CGFloat)contentViewWidth style:(UITableViewStyle)style showsAccessory:(BOOL)showsAccessory; @end /// A \c FLEXMultilineTableViewCell initialized with \c UITableViewCellStyleSubtitle @interface FLEXMultilineDetailTableViewCell : FLEXMultilineTableViewCell @end ================================================ FILE: Classes/Core/Views/Cells/FLEXMultilineTableViewCell.m ================================================ // // FLEXMultilineTableViewCell.m // FLEX // // Created by Ryan Olson on 2/13/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXMultilineTableViewCell.h" #import "UIView+FLEX_Layout.h" #import "FLEXUtility.h" @interface FLEXMultilineTableViewCell () @property (nonatomic, readonly) UILabel *_titleLabel; @property (nonatomic, readonly) UILabel *_subtitleLabel; @property (nonatomic) BOOL constraintsUpdated; @end @implementation FLEXMultilineTableViewCell - (void)postInit { [super postInit]; self.titleLabel.numberOfLines = 0; self.subtitleLabel.numberOfLines = 0; } + (UIEdgeInsets)labelInsets { return UIEdgeInsetsMake(10.0, 16.0, 10.0, 8.0); } + (CGFloat)preferredHeightWithAttributedText:(NSAttributedString *)attributedText maxWidth:(CGFloat)contentViewWidth style:(UITableViewStyle)style showsAccessory:(BOOL)showsAccessory { CGFloat labelWidth = contentViewWidth; // Content view inset due to accessory view observed on iOS 8.1 iPhone 6. if (showsAccessory) { labelWidth -= 34.0; } UIEdgeInsets labelInsets = [self labelInsets]; labelWidth -= (labelInsets.left + labelInsets.right); CGSize constrainSize = CGSizeMake(labelWidth, CGFLOAT_MAX); CGRect boundingBox = [attributedText boundingRectWithSize:constrainSize options:NSStringDrawingUsesLineFragmentOrigin context:nil ]; CGFloat preferredLabelHeight = FLEXFloor(boundingBox.size.height); CGFloat preferredCellHeight = preferredLabelHeight + labelInsets.top + labelInsets.bottom + 1.0; return preferredCellHeight; } @end @implementation FLEXMultilineDetailTableViewCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { return [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; } @end ================================================ FILE: Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.h ================================================ // // FLEXSubtitleTableViewCell.h // FLEX // // Created by Tanner on 4/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewCell.h" /// A cell initialized with \c UITableViewCellStyleSubtitle @interface FLEXSubtitleTableViewCell : FLEXTableViewCell @end ================================================ FILE: Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.m ================================================ // // FLEXSubtitleTableViewCell.m // FLEX // // Created by Tanner on 4/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXSubtitleTableViewCell.h" @implementation FLEXSubtitleTableViewCell - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { return [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; } @end ================================================ FILE: Classes/Core/Views/Cells/FLEXTableViewCell.h ================================================ // // FLEXTableViewCell.h // FLEX // // Created by Tanner on 4/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface FLEXTableViewCell : UITableViewCell /// Use this instead of .textLabel @property (nonatomic, readonly) UILabel *titleLabel; /// Use this instead of .detailTextLabel @property (nonatomic, readonly) UILabel *subtitleLabel; /// Subclasses can override this instead of initializers to /// perform additional initialization without lots of boilerplate. /// Remember to call super! - (void)postInit; @end ================================================ FILE: Classes/Core/Views/Cells/FLEXTableViewCell.m ================================================ // // FLEXTableViewCell.m // FLEX // // Created by Tanner on 4/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewCell.h" #import "FLEXUtility.h" #import "FLEXColor.h" #import "FLEXTableView.h" @interface UITableView (Internal) // Exists at least since iOS 5 - (BOOL)_canPerformAction:(SEL)action forCell:(UITableViewCell *)cell sender:(id)sender; - (void)_performAction:(SEL)action forCell:(UITableViewCell *)cell sender:(id)sender; @end @interface UITableViewCell (Internal) // Exists at least since iOS 5 @property (nonatomic, readonly) FLEXTableView *_tableView; @end @implementation FLEXTableViewCell - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [self postInit]; } return self; } - (void)postInit { UIFont *cellFont = UIFont.flex_defaultTableCellFont; self.titleLabel.font = cellFont; self.subtitleLabel.font = cellFont; self.subtitleLabel.textColor = FLEXColor.deemphasizedTextColor; self.titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; self.subtitleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; self.titleLabel.numberOfLines = 1; self.subtitleLabel.numberOfLines = 1; } - (UILabel *)titleLabel { return self.textLabel; } - (UILabel *)subtitleLabel { return self.detailTextLabel; } @end ================================================ FILE: Classes/Core/Views/FLEXTableView.h ================================================ // // FLEXTableView.h // FLEX // // Created by Tanner on 4/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN #pragma mark Reuse identifiers typedef NSString * FLEXTableViewCellReuseIdentifier; /// A regular \c FLEXTableViewCell initialized with \c UITableViewCellStyleDefault extern FLEXTableViewCellReuseIdentifier const kFLEXDefaultCell; /// A \c FLEXSubtitleTableViewCell initialized with \c UITableViewCellStyleSubtitle extern FLEXTableViewCellReuseIdentifier const kFLEXDetailCell; /// A \c FLEXMultilineTableViewCell initialized with \c UITableViewCellStyleDefault extern FLEXTableViewCellReuseIdentifier const kFLEXMultilineCell; /// A \c FLEXMultilineTableViewCell initialized with \c UITableViewCellStyleSubtitle extern FLEXTableViewCellReuseIdentifier const kFLEXMultilineDetailCell; /// A \c FLEXTableViewCell initialized with \c UITableViewCellStyleValue1 extern FLEXTableViewCellReuseIdentifier const kFLEXKeyValueCell; /// A \c FLEXSubtitleTableViewCell which uses monospaced fonts for both labels extern FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell; #pragma mark - FLEXTableView @interface FLEXTableView : UITableView + (instancetype)flexDefaultTableView; + (instancetype)groupedTableView; + (instancetype)plainTableView; + (instancetype)style:(UITableViewStyle)style; /// You do not need to register classes for any of the default reuse identifiers above /// (annotated as \c FLEXTableViewCellReuseIdentifier types) unless you wish to provide /// a custom cell for any of those reuse identifiers. By default, \c FLEXTableViewCell, /// \c FLEXSubtitleTableViewCell, and \c FLEXMultilineTableViewCell are used, respectively. /// /// @param registrationMapping A map of reuse identifiers to \c UITableViewCell (sub)class objects. - (void)registerCells:(NSDictionary *)registrationMapping; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Core/Views/FLEXTableView.m ================================================ // // FLEXTableView.m // FLEX // // Created by Tanner on 4/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableView.h" #import "FLEXUtility.h" #import "FLEXSubtitleTableViewCell.h" #import "FLEXMultilineTableViewCell.h" #import "FLEXKeyValueTableViewCell.h" #import "FLEXCodeFontCell.h" FLEXTableViewCellReuseIdentifier const kFLEXDefaultCell = @"kFLEXDefaultCell"; FLEXTableViewCellReuseIdentifier const kFLEXDetailCell = @"kFLEXDetailCell"; FLEXTableViewCellReuseIdentifier const kFLEXMultilineCell = @"kFLEXMultilineCell"; FLEXTableViewCellReuseIdentifier const kFLEXMultilineDetailCell = @"kFLEXMultilineDetailCell"; FLEXTableViewCellReuseIdentifier const kFLEXKeyValueCell = @"kFLEXKeyValueCell"; FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell = @"kFLEXCodeFontCell"; #pragma mark Private @interface UITableView (Private) - (CGFloat)_heightForHeaderInSection:(NSInteger)section; - (NSString *)_titleForHeaderInSection:(NSInteger)section; @end @implementation FLEXTableView + (instancetype)flexDefaultTableView { if (@available(iOS 13.0, *)) { return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped]; } else { return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; } } #pragma mark - Initialization + (id)groupedTableView { if (@available(iOS 13.0, *)) { return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped]; } else { return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; } } + (id)plainTableView { return [[self alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; } + (id)style:(UITableViewStyle)style { return [[self alloc] initWithFrame:CGRectZero style:style]; } - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { self = [super initWithFrame:frame style:style]; if (self) { [self registerCells:@{ kFLEXDefaultCell : [FLEXTableViewCell class], kFLEXDetailCell : [FLEXSubtitleTableViewCell class], kFLEXMultilineCell : [FLEXMultilineTableViewCell class], kFLEXMultilineDetailCell : [FLEXMultilineDetailTableViewCell class], kFLEXKeyValueCell : [FLEXKeyValueTableViewCell class], kFLEXCodeFontCell : [FLEXCodeFontCell class], }]; } return self; } #pragma mark - Public - (void)registerCells:(NSDictionary *)registrationMapping { [registrationMapping enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, Class cellClass, BOOL *stop) { [self registerClass:cellClass forCellReuseIdentifier:identifier]; }]; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.h ================================================ // // FLEXArgumentInputColorView.h // Flipboard // // Created by Ryan Olson on 6/30/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputView.h" @interface FLEXArgumentInputColorView : FLEXArgumentInputView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m ================================================ // // FLEXArgumentInputColorView.m // Flipboard // // Created by Ryan Olson on 6/30/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputColorView.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" @protocol FLEXColorComponentInputViewDelegate; @interface FLEXColorComponentInputView : UIView @property (nonatomic) UISlider *slider; @property (nonatomic) UILabel *valueLabel; @property (nonatomic, weak) id delegate; @end @protocol FLEXColorComponentInputViewDelegate - (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView; @end @implementation FLEXColorComponentInputView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.slider = [UISlider new]; [self.slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; [self addSubview:self.slider]; self.valueLabel = [UILabel new]; self.valueLabel.backgroundColor = self.backgroundColor; self.valueLabel.font = [UIFont systemFontOfSize:14.0]; self.valueLabel.textAlignment = NSTextAlignmentRight; [self addSubview:self.valueLabel]; [self updateValueLabel]; } return self; } - (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; self.slider.backgroundColor = backgroundColor; self.valueLabel.backgroundColor = backgroundColor; } - (void)layoutSubviews { [super layoutSubviews]; const CGFloat kValueLabelWidth = 50.0; [self.slider sizeToFit]; CGFloat sliderWidth = self.bounds.size.width - kValueLabelWidth; self.slider.frame = CGRectMake(0, 0, sliderWidth, self.slider.frame.size.height); [self.valueLabel sizeToFit]; CGFloat valueLabelOriginX = CGRectGetMaxX(self.slider.frame); CGFloat valueLabelOriginY = FLEXFloor((self.slider.frame.size.height - self.valueLabel.frame.size.height) / 2.0); self.valueLabel.frame = CGRectMake(valueLabelOriginX, valueLabelOriginY, kValueLabelWidth, self.valueLabel.frame.size.height); } - (void)sliderChanged:(id)sender { [self.delegate colorComponentInputViewValueDidChange:self]; [self updateValueLabel]; } - (void)updateValueLabel { self.valueLabel.text = [NSString stringWithFormat:@"%.3f", self.slider.value]; } - (CGSize)sizeThatFits:(CGSize)size { CGFloat height = [self.slider sizeThatFits:size].height; return CGSizeMake(size.width, height); } @end @interface FLEXColorPreviewBox : UIView @property (nonatomic) UIColor *color; @property (nonatomic) UIView *colorOverlayView; @end @implementation FLEXColorPreviewBox - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.layer.borderWidth = 1.0; self.layer.borderColor = UIColor.blackColor.CGColor; self.backgroundColor = [UIColor colorWithPatternImage:[[self class] backgroundPatternImage]]; self.colorOverlayView = [[UIView alloc] initWithFrame:self.bounds]; self.colorOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.colorOverlayView.backgroundColor = UIColor.clearColor; [self addSubview:self.colorOverlayView]; } return self; } - (void)setColor:(UIColor *)color { self.colorOverlayView.backgroundColor = color; } - (UIColor *)color { return self.colorOverlayView.backgroundColor; } + (UIImage *)backgroundPatternImage { const CGFloat kSquareDimension = 5.0; CGSize squareSize = CGSizeMake(kSquareDimension, kSquareDimension); CGSize imageSize = CGSizeMake(2.0 * kSquareDimension, 2.0 * kSquareDimension); UIGraphicsBeginImageContextWithOptions(imageSize, YES, UIScreen.mainScreen.scale); [UIColor.whiteColor setFill]; UIRectFill(CGRectMake(0, 0, imageSize.width, imageSize.height)); [UIColor.grayColor setFill]; UIRectFill(CGRectMake(squareSize.width, 0, squareSize.width, squareSize.height)); UIRectFill(CGRectMake(0, squareSize.height, squareSize.width, squareSize.height)); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } @end @interface FLEXArgumentInputColorView () @property (nonatomic) FLEXColorPreviewBox *colorPreviewBox; @property (nonatomic) UILabel *hexLabel; @property (nonatomic) FLEXColorComponentInputView *alphaInput; @property (nonatomic) FLEXColorComponentInputView *redInput; @property (nonatomic) FLEXColorComponentInputView *greenInput; @property (nonatomic) FLEXColorComponentInputView *blueInput; @end @implementation FLEXArgumentInputColorView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.colorPreviewBox = [FLEXColorPreviewBox new]; [self addSubview:self.colorPreviewBox]; self.hexLabel = [UILabel new]; self.hexLabel.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.9]; self.hexLabel.textAlignment = NSTextAlignmentCenter; self.hexLabel.font = [UIFont systemFontOfSize:12.0]; [self addSubview:self.hexLabel]; self.alphaInput = [FLEXColorComponentInputView new]; self.alphaInput.slider.minimumTrackTintColor = UIColor.blackColor; self.alphaInput.delegate = self; [self addSubview:self.alphaInput]; self.redInput = [FLEXColorComponentInputView new]; self.redInput.slider.minimumTrackTintColor = UIColor.redColor; self.redInput.delegate = self; [self addSubview:self.redInput]; self.greenInput = [FLEXColorComponentInputView new]; self.greenInput.slider.minimumTrackTintColor = UIColor.greenColor; self.greenInput.delegate = self; [self addSubview:self.greenInput]; self.blueInput = [FLEXColorComponentInputView new]; self.blueInput.slider.minimumTrackTintColor = UIColor.blueColor; self.blueInput.delegate = self; [self addSubview:self.blueInput]; } return self; } - (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; self.alphaInput.backgroundColor = backgroundColor; self.redInput.backgroundColor = backgroundColor; self.greenInput.backgroundColor = backgroundColor; self.blueInput.backgroundColor = backgroundColor; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat runningOriginY = 0; CGSize constrainSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX); self.colorPreviewBox.frame = CGRectMake(0, runningOriginY, self.bounds.size.width, [[self class] colorPreviewBoxHeight]); runningOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) + [[self class] inputViewVerticalPadding]; [self.hexLabel sizeToFit]; const CGFloat kLabelVerticalOutsetAmount = 0.0; const CGFloat kLabelHorizontalOutsetAmount = 2.0; UIEdgeInsets labelOutset = UIEdgeInsetsMake(-kLabelVerticalOutsetAmount, -kLabelHorizontalOutsetAmount, -kLabelVerticalOutsetAmount, -kLabelHorizontalOutsetAmount); self.hexLabel.frame = UIEdgeInsetsInsetRect(self.hexLabel.frame, labelOutset); CGFloat hexLabelOriginX = self.colorPreviewBox.layer.borderWidth; CGFloat hexLabelOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) - self.colorPreviewBox.layer.borderWidth - self.hexLabel.frame.size.height; self.hexLabel.frame = CGRectMake(hexLabelOriginX, hexLabelOriginY, self.hexLabel.frame.size.width, self.hexLabel.frame.size.height); NSArray *colorComponentInputViews = @[self.alphaInput, self.redInput, self.greenInput, self.blueInput]; for (FLEXColorComponentInputView *inputView in colorComponentInputViews) { CGSize fitSize = [inputView sizeThatFits:constrainSize]; inputView.frame = CGRectMake(0, runningOriginY, fitSize.width, fitSize.height); runningOriginY = CGRectGetMaxY(inputView.frame) + [[self class] inputViewVerticalPadding]; } } - (void)setInputValue:(id)inputValue { if ([inputValue isKindOfClass:[UIColor class]]) { [self updateWithColor:inputValue]; } else if ([inputValue isKindOfClass:[NSValue class]]) { const char *type = [inputValue objCType]; if (strcmp(type, @encode(CGColorRef)) == 0) { CGColorRef colorRef; [inputValue getValue:&colorRef]; UIColor *color = [[UIColor alloc] initWithCGColor:colorRef]; [self updateWithColor:color]; } } else { [self updateWithColor:UIColor.clearColor]; } } - (id)inputValue { return [UIColor colorWithRed:self.redInput.slider.value green:self.greenInput.slider.value blue:self.blueInput.slider.value alpha:self.alphaInput.slider.value]; } - (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView { [self updateColorPreview]; } - (void)updateWithColor:(UIColor *)color { CGFloat red, green, blue, white, alpha; if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) { self.alphaInput.slider.value = alpha; [self.alphaInput updateValueLabel]; self.redInput.slider.value = red; [self.redInput updateValueLabel]; self.greenInput.slider.value = green; [self.greenInput updateValueLabel]; self.blueInput.slider.value = blue; [self.blueInput updateValueLabel]; } else if ([color getWhite:&white alpha:&alpha]) { self.alphaInput.slider.value = alpha; [self.alphaInput updateValueLabel]; self.redInput.slider.value = white; [self.redInput updateValueLabel]; self.greenInput.slider.value = white; [self.greenInput updateValueLabel]; self.blueInput.slider.value = white; [self.blueInput updateValueLabel]; } [self updateColorPreview]; } - (void)updateColorPreview { self.colorPreviewBox.color = self.inputValue; unsigned char redByte = self.redInput.slider.value * 255; unsigned char greenByte = self.greenInput.slider.value * 255; unsigned char blueByte = self.blueInput.slider.value * 255; self.hexLabel.text = [NSString stringWithFormat:@"#%02X%02X%02X", redByte, greenByte, blueByte]; [self setNeedsLayout]; } - (CGSize)sizeThatFits:(CGSize)size { CGFloat height = 0; height += [[self class] colorPreviewBoxHeight]; height += [[self class] inputViewVerticalPadding]; height += [self.alphaInput sizeThatFits:size].height; height += [[self class] inputViewVerticalPadding]; height += [self.redInput sizeThatFits:size].height; height += [[self class] inputViewVerticalPadding]; height += [self.greenInput sizeThatFits:size].height; height += [[self class] inputViewVerticalPadding]; height += [self.blueInput sizeThatFits:size].height; return CGSizeMake(size.width, height); } + (CGFloat)inputViewVerticalPadding { return 10.0; } + (CGFloat)colorPreviewBoxHeight { return 40.0; } + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); // We don't care if currentValue is a color or not; we will default to +clearColor return (strcmp(type, @encode(CGColorRef)) == 0) || (strcmp(type, FLEXEncodeClass(UIColor)) == 0); } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.h ================================================ // // FLEXArgumentInputDataView.h // Flipboard // // Created by Daniel Rodriguez Troitino on 2/14/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputView.h" @interface FLEXArgumentInputDateView : FLEXArgumentInputView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.m ================================================ // // FLEXArgumentInputDataView.m // Flipboard // // Created by Daniel Rodriguez Troitino on 2/14/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputDateView.h" #import "FLEXRuntimeUtility.h" @interface FLEXArgumentInputDateView () @property (nonatomic) UIDatePicker *datePicker; @end @implementation FLEXArgumentInputDateView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.datePicker = [UIDatePicker new]; self.datePicker.datePickerMode = UIDatePickerModeDateAndTime; // Using UTC, because that's what the NSDate description prints self.datePicker.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; self.datePicker.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; [self addSubview:self.datePicker]; } return self; } - (void)setInputValue:(id)inputValue { if ([inputValue isKindOfClass:[NSDate class]]) { self.datePicker.date = inputValue; } } - (id)inputValue { return self.datePicker.date; } - (void)layoutSubviews { [super layoutSubviews]; self.datePicker.frame = self.bounds; } - (CGSize)sizeThatFits:(CGSize)size { CGFloat height = [self.datePicker sizeThatFits:size].height; return CGSizeMake(size.width, height); } + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); return strcmp(type, FLEXEncodeClass(NSDate)) == 0; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.h ================================================ // // FLEXArgumentInputFontView.h // Flipboard // // Created by Ryan Olson on 6/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputView.h" @interface FLEXArgumentInputFontView : FLEXArgumentInputView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.m ================================================ // // FLEXArgumentInputFontView.m // Flipboard // // Created by Ryan Olson on 6/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputFontView.h" #import "FLEXArgumentInputViewFactory.h" #import "FLEXRuntimeUtility.h" #import "FLEXArgumentInputFontsPickerView.h" @interface FLEXArgumentInputFontView () @property (nonatomic) FLEXArgumentInputView *fontNameInput; @property (nonatomic) FLEXArgumentInputView *pointSizeInput; @end @implementation FLEXArgumentInputFontView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.fontNameInput = [[FLEXArgumentInputFontsPickerView alloc] initWithArgumentTypeEncoding:FLEXEncodeClass(NSString)]; self.fontNameInput.targetSize = FLEXArgumentInputViewSizeSmall; self.fontNameInput.title = @"Font Name:"; [self addSubview:self.fontNameInput]; self.pointSizeInput = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:@encode(CGFloat)]; self.pointSizeInput.targetSize = FLEXArgumentInputViewSizeSmall; self.pointSizeInput.title = @"Point Size:"; [self addSubview:self.pointSizeInput]; } return self; } - (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; self.fontNameInput.backgroundColor = backgroundColor; self.pointSizeInput.backgroundColor = backgroundColor; } - (void)setInputValue:(id)inputValue { if ([inputValue isKindOfClass:[UIFont class]]) { UIFont *font = (UIFont *)inputValue; self.fontNameInput.inputValue = font.fontName; self.pointSizeInput.inputValue = @(font.pointSize); } } - (id)inputValue { CGFloat pointSize = 0; if ([self.pointSizeInput.inputValue isKindOfClass:[NSValue class]]) { NSValue *pointSizeValue = (NSValue *)self.pointSizeInput.inputValue; if (strcmp([pointSizeValue objCType], @encode(CGFloat)) == 0) { [pointSizeValue getValue:&pointSize]; } } return [UIFont fontWithName:self.fontNameInput.inputValue size:pointSize]; } - (BOOL)inputViewIsFirstResponder { return [self.fontNameInput inputViewIsFirstResponder] || [self.pointSizeInput inputViewIsFirstResponder]; } #pragma mark - Layout and Sizing - (void)layoutSubviews { [super layoutSubviews]; CGFloat runningOriginY = self.topInputFieldVerticalLayoutGuide; CGSize fontNameFitSize = [self.fontNameInput sizeThatFits:self.bounds.size]; self.fontNameInput.frame = CGRectMake(0, runningOriginY, fontNameFitSize.width, fontNameFitSize.height); runningOriginY = CGRectGetMaxY(self.fontNameInput.frame) + [[self class] verticalPaddingBetweenFields]; CGSize pointSizeFitSize = [self.pointSizeInput sizeThatFits:self.bounds.size]; self.pointSizeInput.frame = CGRectMake(0, runningOriginY, pointSizeFitSize.width, pointSizeFitSize.height); } + (CGFloat)verticalPaddingBetweenFields { return 10.0; } - (CGSize)sizeThatFits:(CGSize)size { CGSize fitSize = [super sizeThatFits:size]; CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX); CGFloat height = fitSize.height; height += [self.fontNameInput sizeThatFits:constrainSize].height; height += [[self class] verticalPaddingBetweenFields]; height += [self.pointSizeInput sizeThatFits:constrainSize].height; return CGSizeMake(fitSize.width, height); } #pragma mark - + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); return strcmp(type, FLEXEncodeClass(UIFont)) == 0; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontsPickerView.h ================================================ // // FLEXArgumentInputFontsPickerView.h // FLEX // // Created by 啟倫 陳 on 2014/7/27. // Copyright (c) 2014年 f. All rights reserved. // #import "FLEXArgumentInputTextView.h" @interface FLEXArgumentInputFontsPickerView : FLEXArgumentInputTextView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontsPickerView.m ================================================ // // FLEXArgumentInputFontsPickerView.m // FLEX // // Created by 啟倫 陳 on 2014/7/27. // Copyright (c) 2014年 f. All rights reserved. // #import "FLEXArgumentInputFontsPickerView.h" #import "FLEXRuntimeUtility.h" @interface FLEXArgumentInputFontsPickerView () @property (nonatomic) NSMutableArray *availableFonts; @end @implementation FLEXArgumentInputFontsPickerView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.targetSize = FLEXArgumentInputViewSizeSmall; [self createAvailableFonts]; self.inputTextView.inputView = [self createFontsPicker]; } return self; } - (void)setInputValue:(id)inputValue { self.inputTextView.text = inputValue; if ([self.availableFonts indexOfObject:inputValue] == NSNotFound) { [self.availableFonts insertObject:inputValue atIndex:0]; } [(UIPickerView *)self.inputTextView.inputView selectRow:[self.availableFonts indexOfObject:inputValue] inComponent:0 animated:NO]; } - (id)inputValue { return self.inputTextView.text.length > 0 ? [self.inputTextView.text copy] : nil; } #pragma mark - private - (UIPickerView*)createFontsPicker { UIPickerView *fontsPicker = [UIPickerView new]; fontsPicker.dataSource = self; fontsPicker.delegate = self; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // Deprecated in iOS 13; from then on, selection is always shown fontsPicker.showsSelectionIndicator = YES; #pragma clang diagnostic pop return fontsPicker; } - (void)createAvailableFonts { NSMutableArray *unsortedFontsArray = [NSMutableArray new]; for (NSString *eachFontFamily in UIFont.familyNames) { for (NSString *eachFontName in [UIFont fontNamesForFamilyName:eachFontFamily]) { [unsortedFontsArray addObject:eachFontName]; } } self.availableFonts = [NSMutableArray arrayWithArray:[unsortedFontsArray sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]]; } #pragma mark - UIPickerViewDataSource - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 1; } - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { return self.availableFonts.count; } #pragma mark - UIPickerViewDelegate - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view { UILabel *fontLabel; if (!view) { fontLabel = [UILabel new]; fontLabel.backgroundColor = UIColor.clearColor; fontLabel.textAlignment = NSTextAlignmentCenter; } else { fontLabel = (UILabel*)view; } UIFont *font = [UIFont fontWithName:self.availableFonts[row] size:15.0]; NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]; NSAttributedString *attributesString = [[NSAttributedString alloc] initWithString:self.availableFonts[row] attributes:attributesDictionary]; fontLabel.attributedText = attributesString; [fontLabel sizeToFit]; return fontLabel; } - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { self.inputTextView.text = self.availableFonts[row]; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.h ================================================ // // FLEXArgumentInputNotSupportedView.h // Flipboard // // Created by Ryan Olson on 6/18/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputTextView.h" @interface FLEXArgumentInputNotSupportedView : FLEXArgumentInputTextView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.m ================================================ // // FLEXArgumentInputNotSupportedView.m // Flipboard // // Created by Ryan Olson on 6/18/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputNotSupportedView.h" #import "FLEXColor.h" @implementation FLEXArgumentInputNotSupportedView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.inputTextView.userInteractionEnabled = NO; self.inputTextView.backgroundColor = [FLEXColor secondaryGroupedBackgroundColorWithAlpha:0.5]; self.inputPlaceholderText = @"nil (type not supported)"; self.targetSize = FLEXArgumentInputViewSizeSmall; } return self; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.h ================================================ // // FLEXArgumentInputNumberView.h // Flipboard // // Created by Ryan Olson on 6/15/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputTextView.h" @interface FLEXArgumentInputNumberView : FLEXArgumentInputTextView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.m ================================================ // // FLEXArgumentInputNumberView.m // Flipboard // // Created by Ryan Olson on 6/15/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputNumberView.h" #import "FLEXRuntimeUtility.h" @implementation FLEXArgumentInputNumberView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation; self.targetSize = FLEXArgumentInputViewSizeSmall; } return self; } - (void)setInputValue:(id)inputValue { if ([inputValue respondsToSelector:@selector(stringValue)]) { self.inputTextView.text = [inputValue stringValue]; } } - (id)inputValue { return [FLEXRuntimeUtility valueForNumberWithObjCType:self.typeEncoding.UTF8String fromInputString:self.inputTextView.text]; } + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); static NSArray *supportedTypes = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ supportedTypes = @[ @FLEXEncodeClass(NSNumber), @FLEXEncodeClass(NSDecimalNumber), @(@encode(char)), @(@encode(int)), @(@encode(short)), @(@encode(long)), @(@encode(long long)), @(@encode(unsigned char)), @(@encode(unsigned int)), @(@encode(unsigned short)), @(@encode(unsigned long)), @(@encode(unsigned long long)), @(@encode(float)), @(@encode(double)), @(@encode(long double)) ]; }); return type && [supportedTypes containsObject:@(type)]; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.h ================================================ // // FLEXArgumentInputObjectView.h // Flipboard // // Created by Ryan Olson on 6/15/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputTextView.h" @interface FLEXArgumentInputObjectView : FLEXArgumentInputTextView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.m ================================================ // // FLEXArgumentInputJSONObjectView.m // Flipboard // // Created by Ryan Olson on 6/15/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputObjectView.h" #import "FLEXRuntimeUtility.h" static const CGFloat kSegmentInputMargin = 10; typedef NS_ENUM(NSUInteger, FLEXArgInputObjectType) { FLEXArgInputObjectTypeJSON, FLEXArgInputObjectTypeAddress }; @interface FLEXArgumentInputObjectView () @property (nonatomic) UISegmentedControl *objectTypeSegmentControl; @property (nonatomic) FLEXArgInputObjectType inputType; @end @implementation FLEXArgumentInputObjectView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { // Start with the numbers and punctuation keyboard since quotes, curly braces, or // square brackets are likely to be the first characters type for the JSON. self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation; self.targetSize = FLEXArgumentInputViewSizeLarge; self.objectTypeSegmentControl = [[UISegmentedControl alloc] initWithItems:@[@"Value", @"Address"]]; [self.objectTypeSegmentControl addTarget:self action:@selector(didChangeType) forControlEvents:UIControlEventValueChanged]; self.objectTypeSegmentControl.selectedSegmentIndex = 0; [self addSubview:self.objectTypeSegmentControl]; self.inputType = [[self class] preferredDefaultTypeForObjCType:typeEncoding withCurrentValue:nil]; self.objectTypeSegmentControl.selectedSegmentIndex = self.inputType; } return self; } - (void)didChangeType { self.inputType = self.objectTypeSegmentControl.selectedSegmentIndex; if (super.inputValue) { // Trigger an update to the text field to show // the address of the stored object we were given, // or to show a JSON representation of the object [self populateTextAreaFromValue:super.inputValue]; } else { // Clear the text field [self populateTextAreaFromValue:nil]; } } - (void)setInputType:(FLEXArgInputObjectType)inputType { if (_inputType == inputType) return; _inputType = inputType; // Resize input view switch (inputType) { case FLEXArgInputObjectTypeJSON: self.targetSize = FLEXArgumentInputViewSizeLarge; break; case FLEXArgInputObjectTypeAddress: self.targetSize = FLEXArgumentInputViewSizeSmall; break; } // Change placeholder switch (inputType) { case FLEXArgInputObjectTypeJSON: self.inputPlaceholderText = @"You can put any valid JSON here, such as a string, number, array, or dictionary:" "\n\"This is a string\"" "\n1234" "\n{ \"name\": \"Bob\", \"age\": 47 }" "\n[" "\n 1, 2, 3" "\n]"; break; case FLEXArgInputObjectTypeAddress: self.inputPlaceholderText = @"0x0000deadb33f"; break; } [self setNeedsLayout]; [self.superview setNeedsLayout]; } - (void)setInputValue:(id)inputValue { super.inputValue = inputValue; [self populateTextAreaFromValue:inputValue]; } - (id)inputValue { switch (self.inputType) { case FLEXArgInputObjectTypeJSON: return [FLEXRuntimeUtility objectValueFromEditableJSONString:self.inputTextView.text]; case FLEXArgInputObjectTypeAddress: { NSScanner *scanner = [NSScanner scannerWithString:self.inputTextView.text]; unsigned long long objectPointerValue; if ([scanner scanHexLongLong:&objectPointerValue]) { return (__bridge id)(void *)objectPointerValue; } return nil; } } } - (void)populateTextAreaFromValue:(id)value { if (!value) { self.inputTextView.text = nil; } else { if (self.inputType == FLEXArgInputObjectTypeJSON) { self.inputTextView.text = [FLEXRuntimeUtility editableJSONStringForObject:value]; } else if (self.inputType == FLEXArgInputObjectTypeAddress) { self.inputTextView.text = [NSString stringWithFormat:@"%p", value]; } } // Delegate methods are not called for programmatic changes [self textViewDidChange:self.inputTextView]; } - (CGSize)sizeThatFits:(CGSize)size { CGSize fitSize = [super sizeThatFits:size]; fitSize.height += [self.objectTypeSegmentControl sizeThatFits:size].height + kSegmentInputMargin; return fitSize; } - (void)layoutSubviews { CGFloat segmentHeight = [self.objectTypeSegmentControl sizeThatFits:self.frame.size].height; self.objectTypeSegmentControl.frame = CGRectMake( 0.0, // Our segmented control is taking the position // of the text view, as far as super is concerned, // and we override this property to be different super.topInputFieldVerticalLayoutGuide, self.frame.size.width, segmentHeight ); [super layoutSubviews]; } - (CGFloat)topInputFieldVerticalLayoutGuide { // Our text view is offset from the segmented control CGFloat segmentHeight = [self.objectTypeSegmentControl sizeThatFits:self.frame.size].height; return segmentHeight + super.topInputFieldVerticalLayoutGuide + kSegmentInputMargin; } + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); // Must be object type return type[0] == FLEXTypeEncodingObjcObject || type[0] == FLEXTypeEncodingObjcClass; } + (FLEXArgInputObjectType)preferredDefaultTypeForObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type[0] == FLEXTypeEncodingObjcObject || type[0] == FLEXTypeEncodingObjcClass); if (value) { // If there's a current value, it must be serializable to JSON // to display the JSON editor. Otherwise display the address field. if ([FLEXRuntimeUtility editableJSONStringForObject:value]) { return FLEXArgInputObjectTypeJSON; } else { return FLEXArgInputObjectTypeAddress; } } else { // Otherwise, see if we have more type information than just 'id'. // If we do, make sure the encoding is something serializable to JSON. // Properties and ivars keep more detailed type encoding information than method arguments. if (strcmp(type, @encode(id)) != 0) { BOOL isJSONSerializableType = NO; // Parse class name out of the string, // which is in the form `@"ClassName"` Class cls = NSClassFromString(({ NSString *className = nil; NSScanner *scan = [NSScanner scannerWithString:@(type)]; NSCharacterSet *allowed = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$" ]; // Skip over the @" then scan the name if ([scan scanString:@"@\"" intoString:nil]) { [scan scanCharactersFromSet:allowed intoString:&className]; } className; })); // Note: we can't use @encode(NSString) here because that drops // the class information and just goes to @encode(id). NSArray *jsonTypes = @[ [NSString class], [NSNumber class], [NSArray class], [NSDictionary class], ]; // Look for matching types for (Class jsonClass in jsonTypes) { if ([cls isSubclassOfClass:jsonClass]) { isJSONSerializableType = YES; break; } } if (isJSONSerializableType) { return FLEXArgInputObjectTypeJSON; } else { return FLEXArgInputObjectTypeAddress; } } else { return FLEXArgInputObjectTypeAddress; } } } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.h ================================================ // // FLEXArgumentInputStringView.h // Flipboard // // Created by Ryan Olson on 6/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputTextView.h" @interface FLEXArgumentInputStringView : FLEXArgumentInputTextView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.m ================================================ // // FLEXArgumentInputStringView.m // Flipboard // // Created by Ryan Olson on 6/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputStringView.h" #import "FLEXRuntimeUtility.h" @implementation FLEXArgumentInputStringView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { FLEXTypeEncoding type = typeEncoding[0]; if (type == FLEXTypeEncodingConst) { // A crash here would mean an invalid type encoding string type = typeEncoding[1]; } // Selectors don't need a multi-line text box if (type == FLEXTypeEncodingSelector) { self.targetSize = FLEXArgumentInputViewSizeSmall; } else { self.targetSize = FLEXArgumentInputViewSizeLarge; } } return self; } - (void)setInputValue:(id)inputValue { if ([inputValue isKindOfClass:[NSString class]]) { self.inputTextView.text = inputValue; } else if ([inputValue isKindOfClass:[NSValue class]]) { NSValue *value = (id)inputValue; NSParameterAssert(strlen(value.objCType) == 1); // C-String or SEL from NSValue FLEXTypeEncoding type = value.objCType[0]; if (type == FLEXTypeEncodingConst) { // A crash here would mean an invalid type encoding string type = value.objCType[1]; } if (type == FLEXTypeEncodingCString) { self.inputTextView.text = @((const char *)value.pointerValue); } else if (type == FLEXTypeEncodingSelector) { self.inputTextView.text = NSStringFromSelector((SEL)value.pointerValue); } } } - (id)inputValue { NSString *text = self.inputTextView.text; // Interpret empty string as nil. We loose the ability to set empty string as a string value, // but we accept that tradeoff in exchange for not having to type quotes for every string. if (!text.length) { return nil; } // Case: C-strings and SELs if (self.typeEncoding.length <= 2) { FLEXTypeEncoding type = [self.typeEncoding characterAtIndex:0]; if (type == FLEXTypeEncodingConst) { // A crash here would mean an invalid type encoding string type = [self.typeEncoding characterAtIndex:1]; } if (type == FLEXTypeEncodingCString || type == FLEXTypeEncodingSelector) { const char *encoding = self.typeEncoding.UTF8String; SEL selector = NSSelectorFromString(text); return [NSValue valueWithBytes:&selector objCType:encoding]; } } // Case: NSStrings return self.inputTextView.text.copy; } // TODO: Support using object address for strings, as in the object arg view. + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); unsigned long len = strlen(type); BOOL isConst = type[0] == FLEXTypeEncodingConst; NSInteger i = isConst ? 1 : 0; BOOL typeIsString = strcmp(type, FLEXEncodeClass(NSString)) == 0; BOOL typeIsCString = len <= 2 && type[i] == FLEXTypeEncodingCString; BOOL typeIsSEL = len <= 2 && type[i] == FLEXTypeEncodingSelector; BOOL valueIsString = [value isKindOfClass:[NSString class]]; BOOL typeIsPrimitiveString = typeIsSEL || typeIsCString; BOOL typeIsSupported = typeIsString || typeIsCString || typeIsSEL; BOOL valueIsNSValueWithCorrectType = NO; if ([value isKindOfClass:[NSValue class]]) { NSValue *v = (id)value; len = strlen(v.objCType); if (len == 1) { FLEXTypeEncoding type = v.objCType[i]; if (type == FLEXTypeEncodingCString && typeIsCString) { valueIsNSValueWithCorrectType = YES; } else if (type == FLEXTypeEncodingSelector && typeIsSEL) { valueIsNSValueWithCorrectType = YES; } } } if (!value && typeIsSupported) { return YES; } if (typeIsString && valueIsString) { return YES; } // Primitive strings can be input as NSStrings or NSValues if (typeIsPrimitiveString && (valueIsString || valueIsNSValueWithCorrectType)) { return YES; } return NO; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.h ================================================ // // FLEXArgumentInputStructView.h // Flipboard // // Created by Ryan Olson on 6/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputView.h" @interface FLEXArgumentInputStructView : FLEXArgumentInputView /// Enable displaying ivar names for custom struct types + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding; @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.m ================================================ // // FLEXArgumentInputStructView.m // Flipboard // // Created by Ryan Olson on 6/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputStructView.h" #import "FLEXArgumentInputViewFactory.h" #import "FLEXRuntimeUtility.h" #import "FLEXTypeEncodingParser.h" @interface FLEXArgumentInputStructView () @property (nonatomic) NSArray *argumentInputViews; @end @implementation FLEXArgumentInputStructView static NSMutableDictionary *> *structFieldNameRegistrar = nil; + (void)initialize { if (self == [FLEXArgumentInputStructView class]) { structFieldNameRegistrar = [NSMutableDictionary new]; [self registerDefaultFieldNames]; } } + (void)registerDefaultFieldNames { NSDictionary *defaults = @{ @(@encode(CGRect)): @[@"CGPoint origin", @"CGSize size"], @(@encode(CGPoint)): @[@"CGFloat x", @"CGFloat y"], @(@encode(CGSize)): @[@"CGFloat width", @"CGFloat height"], @(@encode(CGVector)): @[@"CGFloat dx", @"CGFloat dy"], @(@encode(UIEdgeInsets)): @[@"CGFloat top", @"CGFloat left", @"CGFloat bottom", @"CGFloat right"], @(@encode(UIOffset)): @[@"CGFloat horizontal", @"CGFloat vertical"], @(@encode(NSRange)): @[@"NSUInteger location", @"NSUInteger length"], @(@encode(CATransform3D)): @[@"CGFloat m11", @"CGFloat m12", @"CGFloat m13", @"CGFloat m14", @"CGFloat m21", @"CGFloat m22", @"CGFloat m23", @"CGFloat m24", @"CGFloat m31", @"CGFloat m32", @"CGFloat m33", @"CGFloat m34", @"CGFloat m41", @"CGFloat m42", @"CGFloat m43", @"CGFloat m44"], @(@encode(CGAffineTransform)): @[@"CGFloat a", @"CGFloat b", @"CGFloat c", @"CGFloat d", @"CGFloat tx", @"CGFloat ty"], }; [structFieldNameRegistrar addEntriesFromDictionary:defaults]; if (@available(iOS 11.0, *)) { structFieldNameRegistrar[@(@encode(NSDirectionalEdgeInsets))] = @[ @"CGFloat top", @"CGFloat leading", @"CGFloat bottom", @"CGFloat trailing" ]; } } - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { NSMutableArray *inputViews = [NSMutableArray new]; NSArray *customTitles = [[self class] customFieldTitlesForTypeEncoding:typeEncoding]; [FLEXRuntimeUtility enumerateTypesInStructEncoding:typeEncoding usingBlock:^(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset) { FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:fieldTypeEncoding]; inputView.targetSize = FLEXArgumentInputViewSizeSmall; if (fieldIndex < customTitles.count) { inputView.title = customTitles[fieldIndex]; } else { inputView.title = [NSString stringWithFormat:@"%@ field %lu (%@)", structName, (unsigned long)fieldIndex, prettyTypeEncoding ]; } [inputViews addObject:inputView]; [self addSubview:inputView]; }]; self.argumentInputViews = inputViews; } return self; } #pragma mark - Superclass Overrides - (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; for (FLEXArgumentInputView *inputView in self.argumentInputViews) { inputView.backgroundColor = backgroundColor; } } - (void)setInputValue:(id)inputValue { if ([inputValue isKindOfClass:[NSValue class]]) { const char *structTypeEncoding = [inputValue objCType]; if (strcmp(self.typeEncoding.UTF8String, structTypeEncoding) == 0) { NSUInteger valueSize = 0; if (FLEXGetSizeAndAlignment(structTypeEncoding, &valueSize, NULL)) { void *unboxedValue = malloc(valueSize); [inputValue getValue:unboxedValue]; [FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset) { void *fieldPointer = unboxedValue + fieldOffset; FLEXArgumentInputView *inputView = self.argumentInputViews[fieldIndex]; if (fieldTypeEncoding[0] == FLEXTypeEncodingObjcObject || fieldTypeEncoding[0] == FLEXTypeEncodingObjcClass) { inputView.inputValue = (__bridge id)fieldPointer; } else { NSValue *boxedField = [FLEXRuntimeUtility valueForPrimitivePointer:fieldPointer objCType:fieldTypeEncoding]; inputView.inputValue = boxedField; } }]; free(unboxedValue); } } } } - (id)inputValue { NSValue *boxedStruct = nil; const char *structTypeEncoding = self.typeEncoding.UTF8String; NSUInteger structSize = 0; if (FLEXGetSizeAndAlignment(structTypeEncoding, &structSize, NULL)) { void *unboxedStruct = malloc(structSize); [FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset) { void *fieldPointer = unboxedStruct + fieldOffset; FLEXArgumentInputView *inputView = self.argumentInputViews[fieldIndex]; if (fieldTypeEncoding[0] == FLEXTypeEncodingObjcObject || fieldTypeEncoding[0] == FLEXTypeEncodingObjcClass) { // Object fields memcpy(fieldPointer, (__bridge void *)inputView.inputValue, sizeof(id)); } else { // Boxed primitive/struct fields id inputValue = inputView.inputValue; if ([inputValue isKindOfClass:[NSValue class]] && strcmp([inputValue objCType], fieldTypeEncoding) == 0) { [inputValue getValue:fieldPointer]; } } }]; boxedStruct = [NSValue value:unboxedStruct withObjCType:structTypeEncoding]; free(unboxedStruct); } return boxedStruct; } - (BOOL)inputViewIsFirstResponder { BOOL isFirstResponder = NO; for (FLEXArgumentInputView *inputView in self.argumentInputViews) { if ([inputView inputViewIsFirstResponder]) { isFirstResponder = YES; break; } } return isFirstResponder; } #pragma mark - Layout and Sizing - (void)layoutSubviews { [super layoutSubviews]; CGFloat runningOriginY = self.topInputFieldVerticalLayoutGuide; for (FLEXArgumentInputView *inputView in self.argumentInputViews) { CGSize inputFitSize = [inputView sizeThatFits:self.bounds.size]; inputView.frame = CGRectMake(0, runningOriginY, inputFitSize.width, inputFitSize.height); runningOriginY = CGRectGetMaxY(inputView.frame) + [[self class] verticalPaddingBetweenFields]; } } + (CGFloat)verticalPaddingBetweenFields { return 10.0; } - (CGSize)sizeThatFits:(CGSize)size { CGSize fitSize = [super sizeThatFits:size]; CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX); CGFloat height = fitSize.height; for (FLEXArgumentInputView *inputView in self.argumentInputViews) { height += [inputView sizeThatFits:constrainSize].height; height += [[self class] verticalPaddingBetweenFields]; } return CGSizeMake(fitSize.width, height); } #pragma mark - Class Helpers + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); if (type[0] == FLEXTypeEncodingStructBegin) { return FLEXGetSizeAndAlignment(type, nil, nil); } return NO; } + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding { NSParameterAssert(typeEncoding); NSParameterAssert(names); structFieldNameRegistrar[typeEncoding] = names; } + (NSArray *)customFieldTitlesForTypeEncoding:(const char *)typeEncoding { return structFieldNameRegistrar[@(typeEncoding)]; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.h ================================================ // // FLEXArgumentInputSwitchView.h // Flipboard // // Created by Ryan Olson on 6/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputView.h" @interface FLEXArgumentInputSwitchView : FLEXArgumentInputView @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m ================================================ // // FLEXArgumentInputSwitchView.m // Flipboard // // Created by Ryan Olson on 6/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputSwitchView.h" @interface FLEXArgumentInputSwitchView () @property (nonatomic) UISwitch *inputSwitch; @end @implementation FLEXArgumentInputSwitchView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.inputSwitch = [UISwitch new]; [self.inputSwitch addTarget:self action:@selector(switchValueDidChange:) forControlEvents:UIControlEventValueChanged]; [self.inputSwitch sizeToFit]; [self addSubview:self.inputSwitch]; } return self; } #pragma mark Input/Output - (void)setInputValue:(id)inputValue { BOOL on = NO; if ([inputValue isKindOfClass:[NSNumber class]]) { NSNumber *number = (NSNumber *)inputValue; on = [number boolValue]; } else if ([inputValue isKindOfClass:[NSValue class]]) { NSValue *value = (NSValue *)inputValue; if (strcmp([value objCType], @encode(BOOL)) == 0) { [value getValue:&on]; } } self.inputSwitch.on = on; } - (id)inputValue { BOOL isOn = [self.inputSwitch isOn]; NSValue *boxedBool = [NSValue value:&isOn withObjCType:@encode(BOOL)]; return boxedBool; } - (void)switchValueDidChange:(id)sender { [self.delegate argumentInputViewValueDidChange:self]; } #pragma mark - Layout and Sizing - (void)layoutSubviews { [super layoutSubviews]; self.inputSwitch.frame = CGRectMake(0, self.topInputFieldVerticalLayoutGuide, self.inputSwitch.frame.size.width, self.inputSwitch.frame.size.height); } - (CGSize)sizeThatFits:(CGSize)size { CGSize fitSize = [super sizeThatFits:size]; fitSize.height += self.inputSwitch.frame.size.height; return fitSize; } #pragma mark - Class Helpers + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { NSParameterAssert(type); // Only BOOLs. Current value is irrelevant. return strcmp(type, @encode(BOOL)) == 0; } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.h ================================================ // // FLEXArgumentInputTextView.h // FLEXInjected // // Created by Ryan Olson on 6/15/14. // // #import "FLEXArgumentInputView.h" @interface FLEXArgumentInputTextView : FLEXArgumentInputView // For subclass eyes only @property (nonatomic, readonly) UITextView *inputTextView; @property (nonatomic) NSString *inputPlaceholderText; @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m ================================================ // // FLEXArgumentInputTextView.m // FLEXInjected // // Created by Ryan Olson on 6/15/14. // // #import "FLEXColor.h" #import "FLEXArgumentInputTextView.h" #import "FLEXUtility.h" @interface FLEXArgumentInputTextView () @property (nonatomic) UITextView *inputTextView; @property (nonatomic) UILabel *placeholderLabel; @property (nonatomic, readonly) NSUInteger numberOfInputLines; @end @implementation FLEXArgumentInputTextView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithArgumentTypeEncoding:typeEncoding]; if (self) { self.inputTextView = [UITextView new]; self.inputTextView.font = [[self class] inputFont]; self.inputTextView.backgroundColor = FLEXColor.secondaryGroupedBackgroundColor; self.inputTextView.layer.cornerRadius = 10.f; self.inputTextView.contentInset = UIEdgeInsetsMake(0, 5, 0, 0); self.inputTextView.autocapitalizationType = UITextAutocapitalizationTypeNone; self.inputTextView.autocorrectionType = UITextAutocorrectionTypeNo; self.inputTextView.delegate = self; self.inputTextView.inputAccessoryView = [self createToolBar]; if (@available(iOS 11, *)) { self.inputTextView.smartQuotesType = UITextSmartQuotesTypeNo; [self.inputTextView.layer setValue:@YES forKey:@"continuousCorners"]; } else { self.inputTextView.layer.borderWidth = 1.f; self.inputTextView.layer.borderColor = FLEXColor.borderColor.CGColor; } self.placeholderLabel = [UILabel new]; self.placeholderLabel.font = self.inputTextView.font; self.placeholderLabel.textColor = FLEXColor.deemphasizedTextColor; self.placeholderLabel.numberOfLines = 0; [self addSubview:self.inputTextView]; [self.inputTextView addSubview:self.placeholderLabel]; } return self; } #pragma mark - Private - (UIToolbar *)createToolBar { UIToolbar *toolBar = [UIToolbar new]; [toolBar sizeToFit]; UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil ]; UIBarButtonItem *pasteItem = [[UIBarButtonItem alloc] initWithTitle:@"Paste" style:UIBarButtonItemStyleDone target:self.inputTextView action:@selector(paste:) ]; UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self.inputTextView action:@selector(resignFirstResponder) ]; toolBar.items = @[spaceItem, pasteItem, doneItem]; return toolBar; } - (void)setInputPlaceholderText:(NSString *)placeholder { self.placeholderLabel.text = placeholder; if (placeholder.length) { if (!self.inputTextView.text.length) { self.placeholderLabel.hidden = NO; } else { self.placeholderLabel.hidden = YES; } } else { self.placeholderLabel.hidden = YES; } [self setNeedsLayout]; } - (NSString *)inputPlaceholderText { return self.placeholderLabel.text; } #pragma mark - Superclass Overrides - (BOOL)inputViewIsFirstResponder { return self.inputTextView.isFirstResponder; } #pragma mark - Layout and Sizing - (void)layoutSubviews { [super layoutSubviews]; self.inputTextView.frame = CGRectMake(0, self.topInputFieldVerticalLayoutGuide, self.bounds.size.width, [self inputTextViewHeight]); // Placeholder label is positioned by insetting then origin // by the content inset then the text container inset CGSize s = self.inputTextView.frame.size; self.placeholderLabel.frame = CGRectMake(0, 0, s.width, s.height); self.placeholderLabel.frame = UIEdgeInsetsInsetRect( UIEdgeInsetsInsetRect(self.placeholderLabel.frame, self.inputTextView.contentInset), self.inputTextView.textContainerInset ); } - (NSUInteger)numberOfInputLines { switch (self.targetSize) { case FLEXArgumentInputViewSizeDefault: return 2; case FLEXArgumentInputViewSizeSmall: return 1; case FLEXArgumentInputViewSizeLarge: return 8; } } - (CGFloat)inputTextViewHeight { return ceil([[self class] inputFont].lineHeight * self.numberOfInputLines) + 16.0; } - (CGSize)sizeThatFits:(CGSize)size { CGSize fitSize = [super sizeThatFits:size]; fitSize.height += [self inputTextViewHeight]; return fitSize; } #pragma mark - Class Helpers + (UIFont *)inputFont { return [UIFont systemFontOfSize:14.0]; } #pragma mark - UITextViewDelegate - (void)textViewDidChange:(UITextView *)textView { [self.delegate argumentInputViewValueDidChange:self]; self.placeholderLabel.hidden = !(self.inputPlaceholderText.length && !textView.text.length); } @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.h ================================================ // // FLEXArgumentInputView.h // Flipboard // // Created by Ryan Olson on 5/30/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import typedef NS_ENUM(NSUInteger, FLEXArgumentInputViewSize) { /// 2 lines, medium-sized FLEXArgumentInputViewSizeDefault = 0, /// One line FLEXArgumentInputViewSizeSmall, /// Several lines FLEXArgumentInputViewSizeLarge }; @protocol FLEXArgumentInputViewDelegate; @interface FLEXArgumentInputView : UIView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding; /// The name of the field. Optional (can be nil). @property (nonatomic, copy) NSString *title; /// To populate the filed with an initial value, set this property. /// To reteive the value input by the user, access the property. /// Primitive types and structs should/will be boxed in NSValue containers. /// Concrete subclasses should override both the setter and getter for this property. /// Subclasses can call super.inputValue to access a backing store for the value. @property (nonatomic) id inputValue; /// Setting this value to large will make some argument input views increase the size of their input field(s). /// Useful to increase the use of space if there is only one input view on screen (i.e. for property and ivar editing). @property (nonatomic) FLEXArgumentInputViewSize targetSize; /// Users of the input view can get delegate callbacks for incremental changes in user input. @property (nonatomic, weak) id delegate; // Subclasses can override /// If the input view has one or more text views, returns YES when one of them is focused. @property (nonatomic, readonly) BOOL inputViewIsFirstResponder; /// For subclasses to indicate that they can handle editing a field the give type and value. /// Used by FLEXArgumentInputViewFactory to create appropriate input views. + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value; // For subclass eyes only @property (nonatomic, readonly) UILabel *titleLabel; @property (nonatomic, readonly) NSString *typeEncoding; @property (nonatomic, readonly) CGFloat topInputFieldVerticalLayoutGuide; @end @protocol FLEXArgumentInputViewDelegate - (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView; @end ================================================ FILE: Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.m ================================================ // // FLEXArgumentInputView.m // Flipboard // // Created by Ryan Olson on 5/30/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXArgumentInputView.h" #import "FLEXUtility.h" #import "FLEXColor.h" @interface FLEXArgumentInputView () @property (nonatomic) UILabel *titleLabel; @property (nonatomic) NSString *typeEncoding; @end @implementation FLEXArgumentInputView - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding { self = [super initWithFrame:CGRectZero]; if (self) { self.typeEncoding = typeEncoding != NULL ? @(typeEncoding) : nil; } return self; } - (void)layoutSubviews { [super layoutSubviews]; if (self.showsTitle) { CGSize constrainSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX); CGSize labelSize = [self.titleLabel sizeThatFits:constrainSize]; self.titleLabel.frame = CGRectMake(0, 0, labelSize.width, labelSize.height); } } - (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; self.titleLabel.backgroundColor = backgroundColor; } - (void)setTitle:(NSString *)title { if (![_title isEqual:title]) { _title = title; self.titleLabel.text = title; [self setNeedsLayout]; } } - (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [UILabel new]; _titleLabel.font = [[self class] titleFont]; _titleLabel.textColor = FLEXColor.primaryTextColor; _titleLabel.numberOfLines = 0; [self addSubview:_titleLabel]; } return _titleLabel; } - (BOOL)showsTitle { return self.title.length > 0; } - (CGFloat)topInputFieldVerticalLayoutGuide { CGFloat verticalLayoutGuide = 0; if (self.showsTitle) { CGFloat titleHeight = [self.titleLabel sizeThatFits:self.bounds.size].height; verticalLayoutGuide = titleHeight + [[self class] titleBottomPadding]; } return verticalLayoutGuide; } #pragma mark - Subclasses Can Override - (BOOL)inputViewIsFirstResponder { return NO; } + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value { return NO; } #pragma mark - Class Helpers + (UIFont *)titleFont { return [UIFont systemFontOfSize:12.0]; } + (CGFloat)titleBottomPadding { return 4.0; } #pragma mark - Sizing - (CGSize)sizeThatFits:(CGSize)size { CGFloat height = 0; if (self.title.length > 0) { CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX); height += ceil([self.titleLabel sizeThatFits:constrainSize].height); height += [[self class] titleBottomPadding]; } return CGSizeMake(size.width, height); } @end ================================================ FILE: Classes/Editing/FLEXArgumentInputViewFactory.h ================================================ // // FLEXArgumentInputViewFactory.h // FLEXInjected // // Created by Ryan Olson on 6/15/14. // // #import #import "FLEXArgumentInputSwitchView.h" @interface FLEXArgumentInputViewFactory : NSObject /// Forwards to argumentInputViewForTypeEncoding:currentValue: with a nil currentValue. + (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding; /// The main factory method for making argument input view subclasses that are the best fit for the type. + (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue; /// A way to check if we should try editing a filed given its type encoding and value. /// Useful when deciding whether to edit or explore a property, ivar, or NSUserDefaults value. + (BOOL)canEditFieldWithTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue; /// Enable displaying ivar names for custom struct types + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding; @end ================================================ FILE: Classes/Editing/FLEXArgumentInputViewFactory.m ================================================ // // FLEXArgumentInputViewFactory.m // FLEXInjected // // Created by Ryan Olson on 6/15/14. // // #import "FLEXArgumentInputViewFactory.h" #import "FLEXArgumentInputView.h" #import "FLEXArgumentInputObjectView.h" #import "FLEXArgumentInputNumberView.h" #import "FLEXArgumentInputSwitchView.h" #import "FLEXArgumentInputStructView.h" #import "FLEXArgumentInputNotSupportedView.h" #import "FLEXArgumentInputStringView.h" #import "FLEXArgumentInputFontView.h" #import "FLEXArgumentInputColorView.h" #import "FLEXArgumentInputDateView.h" #import "FLEXRuntimeUtility.h" @implementation FLEXArgumentInputViewFactory + (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding { return [self argumentInputViewForTypeEncoding:typeEncoding currentValue:nil]; } + (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue { Class subclass = [self argumentInputViewSubclassForTypeEncoding:typeEncoding currentValue:currentValue]; if (!subclass) { // Fall back to a FLEXArgumentInputNotSupportedView if we can't find a subclass that fits the type encoding. // The unsupported view shows "nil" and does not allow user input. subclass = [FLEXArgumentInputNotSupportedView class]; } // Remove the field name if there is any (e.g. \"width\"d -> d) const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:typeEncoding]; return [[subclass alloc] initWithArgumentTypeEncoding:typeEncoding + fieldNameOffset]; } + (Class)argumentInputViewSubclassForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue { // Remove the field name if there is any (e.g. \"width\"d -> d) const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:typeEncoding]; Class argumentInputViewSubclass = nil; NSArray *inputViewClasses = @[[FLEXArgumentInputColorView class], [FLEXArgumentInputFontView class], [FLEXArgumentInputStringView class], [FLEXArgumentInputStructView class], [FLEXArgumentInputSwitchView class], [FLEXArgumentInputDateView class], [FLEXArgumentInputNumberView class], [FLEXArgumentInputObjectView class]]; // Note that order is important here since multiple subclasses may support the same type. // An example is the number subclass and the bool subclass for the type @encode(BOOL). // Both work, but we'd prefer to use the bool subclass. for (Class inputViewClass in inputViewClasses) { if ([inputViewClass supportsObjCType:typeEncoding + fieldNameOffset withCurrentValue:currentValue]) { argumentInputViewSubclass = inputViewClass; break; } } return argumentInputViewSubclass; } + (BOOL)canEditFieldWithTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue { return [self argumentInputViewSubclassForTypeEncoding:typeEncoding currentValue:currentValue] != nil; } /// Enable displaying ivar names for custom struct types + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding { [FLEXArgumentInputStructView registerFieldNames:names forTypeEncoding:typeEncoding]; } @end ================================================ FILE: Classes/Editing/FLEXDefaultEditorViewController.h ================================================ // // FLEXDefaultEditorViewController.h // Flipboard // // Created by Ryan Olson on 5/23/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXFieldEditorViewController.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXDefaultEditorViewController : FLEXVariableEditorViewController + (instancetype)target:(NSUserDefaults *)defaults key:(NSString *)key commitHandler:(void(^_Nullable)(void))onCommit; + (BOOL)canEditDefaultWithValue:(nullable id)currentValue; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Editing/FLEXDefaultEditorViewController.m ================================================ // // FLEXDefaultEditorViewController.m // Flipboard // // Created by Ryan Olson on 5/23/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXDefaultEditorViewController.h" #import "FLEXFieldEditorView.h" #import "FLEXRuntimeUtility.h" #import "FLEXArgumentInputView.h" #import "FLEXArgumentInputViewFactory.h" @interface FLEXDefaultEditorViewController () @property (nonatomic, readonly) NSUserDefaults *defaults; @property (nonatomic, readonly) NSString *key; @end @implementation FLEXDefaultEditorViewController + (instancetype)target:(NSUserDefaults *)defaults key:(NSString *)key commitHandler:(void(^_Nullable)(void))onCommit { FLEXDefaultEditorViewController *editor = [self target:defaults data:key commitHandler:onCommit]; editor.title = @"Edit Default"; return editor; } - (NSUserDefaults *)defaults { return [_target isKindOfClass:[NSUserDefaults class]] ? _target : nil; } - (NSString *)key { return _data; } - (void)viewDidLoad { [super viewDidLoad]; self.fieldEditorView.fieldDescription = self.key; id currentValue = [self.defaults objectForKey:self.key]; FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:FLEXEncodeObject(currentValue) currentValue:currentValue ]; inputView.backgroundColor = self.view.backgroundColor; inputView.inputValue = currentValue; self.fieldEditorView.argumentInputViews = @[inputView]; } - (void)actionButtonPressed:(id)sender { id value = self.firstInputView.inputValue; if (value) { [self.defaults setObject:value forKey:self.key]; } else { [self.defaults removeObjectForKey:self.key]; } [self.defaults synchronize]; // Dismiss keyboard and handle committed changes [super actionButtonPressed:sender]; // Go back after setting, but not for switches. if (sender) { [self.navigationController popViewControllerAnimated:YES]; } else { self.firstInputView.inputValue = [self.defaults objectForKey:self.key]; } } + (BOOL)canEditDefaultWithValue:(id)currentValue { return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:FLEXEncodeObject(currentValue) currentValue:currentValue ]; } @end ================================================ FILE: Classes/Editing/FLEXFieldEditorView.h ================================================ // // FLEXFieldEditorView.h // Flipboard // // Created by Ryan Olson on 5/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXArgumentInputView; @interface FLEXFieldEditorView : UIView @property (nonatomic, copy) NSString *targetDescription; @property (nonatomic, copy) NSString *fieldDescription; @property (nonatomic, copy) NSArray *argumentInputViews; @end ================================================ FILE: Classes/Editing/FLEXFieldEditorView.m ================================================ // // FLEXFieldEditorView.m // Flipboard // // Created by Ryan Olson on 5/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXFieldEditorView.h" #import "FLEXArgumentInputView.h" #import "FLEXUtility.h" #import "FLEXColor.h" @interface FLEXFieldEditorView () @property (nonatomic) UILabel *targetDescriptionLabel; @property (nonatomic) UIView *targetDescriptionDivider; @property (nonatomic) UILabel *fieldDescriptionLabel; @property (nonatomic) UIView *fieldDescriptionDivider; @end @implementation FLEXFieldEditorView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.targetDescriptionLabel = [UILabel new]; self.targetDescriptionLabel.numberOfLines = 0; self.targetDescriptionLabel.font = [[self class] labelFont]; [self addSubview:self.targetDescriptionLabel]; self.targetDescriptionDivider = [[self class] dividerView]; [self addSubview:self.targetDescriptionDivider]; self.fieldDescriptionLabel = [UILabel new]; self.fieldDescriptionLabel.numberOfLines = 0; self.fieldDescriptionLabel.font = [[self class] labelFont]; [self addSubview:self.fieldDescriptionLabel]; self.fieldDescriptionDivider = [[self class] dividerView]; [self addSubview:self.fieldDescriptionDivider]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat horizontalPadding = [[self class] horizontalPadding]; CGFloat verticalPadding = [[self class] verticalPadding]; CGFloat dividerLineHeight = [[self class] dividerLineHeight]; CGFloat originY = verticalPadding; CGFloat originX = horizontalPadding; CGFloat contentWidth = self.bounds.size.width - 2.0 * horizontalPadding; CGSize constrainSize = CGSizeMake(contentWidth, CGFLOAT_MAX); CGSize instanceDescriptionSize = [self.targetDescriptionLabel sizeThatFits:constrainSize]; self.targetDescriptionLabel.frame = CGRectMake(originX, originY, instanceDescriptionSize.width, instanceDescriptionSize.height); originY = CGRectGetMaxY(self.targetDescriptionLabel.frame) + verticalPadding; self.targetDescriptionDivider.frame = CGRectMake(originX, originY, contentWidth, dividerLineHeight); originY = CGRectGetMaxY(self.targetDescriptionDivider.frame) + verticalPadding; CGSize fieldDescriptionSize = [self.fieldDescriptionLabel sizeThatFits:constrainSize]; self.fieldDescriptionLabel.frame = CGRectMake(originX, originY, fieldDescriptionSize.width, fieldDescriptionSize.height); originY = CGRectGetMaxY(self.fieldDescriptionLabel.frame) + verticalPadding; self.fieldDescriptionDivider.frame = CGRectMake(originX, originY, contentWidth, dividerLineHeight); originY = CGRectGetMaxY(self.fieldDescriptionDivider.frame) + verticalPadding; for (UIView *argumentInputView in self.argumentInputViews) { CGSize inputViewSize = [argumentInputView sizeThatFits:constrainSize]; argumentInputView.frame = CGRectMake(originX, originY, inputViewSize.width, inputViewSize.height); originY = CGRectGetMaxY(argumentInputView.frame) + verticalPadding; } } - (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; self.targetDescriptionLabel.backgroundColor = backgroundColor; self.fieldDescriptionLabel.backgroundColor = backgroundColor; } - (void)setTargetDescription:(NSString *)targetDescription { if (![_targetDescription isEqual:targetDescription]) { _targetDescription = targetDescription; self.targetDescriptionLabel.text = targetDescription; [self setNeedsLayout]; } } - (void)setFieldDescription:(NSString *)fieldDescription { if (![_fieldDescription isEqual:fieldDescription]) { _fieldDescription = fieldDescription; self.fieldDescriptionLabel.text = fieldDescription; [self setNeedsLayout]; } } - (void)setArgumentInputViews:(NSArray *)argumentInputViews { if (![_argumentInputViews isEqual:argumentInputViews]) { for (FLEXArgumentInputView *inputView in _argumentInputViews) { [inputView removeFromSuperview]; } _argumentInputViews = argumentInputViews; for (FLEXArgumentInputView *newInputView in argumentInputViews) { [self addSubview:newInputView]; } [self setNeedsLayout]; } } + (UIView *)dividerView { UIView *dividerView = [UIView new]; dividerView.backgroundColor = [self dividerColor]; return dividerView; } + (UIColor *)dividerColor { return FLEXColor.tertiaryBackgroundColor; } + (CGFloat)horizontalPadding { return 10.0; } + (CGFloat)verticalPadding { return 20.0; } + (UIFont *)labelFont { return [UIFont systemFontOfSize:14.0]; } + (CGFloat)dividerLineHeight { return 1.0; } - (CGSize)sizeThatFits:(CGSize)size { CGFloat horizontalPadding = [[self class] horizontalPadding]; CGFloat verticalPadding = [[self class] verticalPadding]; CGFloat dividerLineHeight = [[self class] dividerLineHeight]; CGFloat height = 0; CGFloat availableWidth = size.width - 2.0 * horizontalPadding; CGSize constrainSize = CGSizeMake(availableWidth, CGFLOAT_MAX); height += verticalPadding; height += ceil([self.targetDescriptionLabel sizeThatFits:constrainSize].height); height += verticalPadding; height += dividerLineHeight; height += verticalPadding; height += ceil([self.fieldDescriptionLabel sizeThatFits:constrainSize].height); height += verticalPadding; height += dividerLineHeight; height += verticalPadding; for (FLEXArgumentInputView *inputView in self.argumentInputViews) { height += [inputView sizeThatFits:constrainSize].height; height += verticalPadding; } return CGSizeMake(size.width, height); } @end ================================================ FILE: Classes/Editing/FLEXFieldEditorViewController.h ================================================ // // FLEXFieldEditorViewController.h // FLEX // // Created by Tanner on 11/22/18. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXVariableEditorViewController.h" #import "FLEXProperty.h" #import "FLEXIvar.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXFieldEditorViewController : FLEXVariableEditorViewController /// @return nil if the property is readonly or if the type is unsupported + (nullable instancetype)target:(id)target property:(FLEXProperty *)property commitHandler:(void(^_Nullable)(void))onCommit; /// @return nil if the ivar type is unsupported + (nullable instancetype)target:(id)target ivar:(FLEXIvar *)ivar commitHandler:(void(^_Nullable)(void))onCommit; /// Subclasses can change the button title via the \c title property @property (nonatomic, readonly) UIBarButtonItem *getterButton; - (void)getterButtonPressed:(id)sender; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Editing/FLEXFieldEditorViewController.m ================================================ // // FLEXFieldEditorViewController.m // FLEX // // Created by Tanner on 11/22/18. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXFieldEditorViewController.h" #import "FLEXFieldEditorView.h" #import "FLEXArgumentInputViewFactory.h" #import "FLEXPropertyAttributes.h" #import "FLEXRuntimeUtility.h" #import "FLEXMetadataExtras.h" #import "FLEXUtility.h" #import "FLEXColor.h" #import "UIBarButtonItem+FLEX.h" @interface FLEXFieldEditorViewController () @property (nonatomic, readonly) id auxiliaryInfoProvider; @property (nonatomic) FLEXProperty *property; @property (nonatomic) FLEXIvar *ivar; @property (nonatomic, readonly) id currentValue; @property (nonatomic, readonly) const FLEXTypeEncoding *typeEncoding; @property (nonatomic, readonly) NSString *fieldDescription; @end @implementation FLEXFieldEditorViewController #pragma mark - Initialization + (instancetype)target:(id)target property:(nonnull FLEXProperty *)property commitHandler:(void(^)(void))onCommit { FLEXFieldEditorViewController *editor = [self target:target data:property commitHandler:onCommit]; editor.title = [@"Property: " stringByAppendingString:property.name]; editor.property = property; return editor; } + (instancetype)target:(id)target ivar:(nonnull FLEXIvar *)ivar commitHandler:(void(^)(void))onCommit { FLEXFieldEditorViewController *editor = [self target:target data:ivar commitHandler:onCommit]; editor.title = [@"Ivar: " stringByAppendingString:ivar.name]; editor.ivar = ivar; return editor; } #pragma mark - Overrides - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = FLEXColor.groupedBackgroundColor; // Create getter button _getterButton = [[UIBarButtonItem alloc] initWithTitle:@"Get" style:UIBarButtonItemStyleDone target:self action:@selector(getterButtonPressed:) ]; self.toolbarItems = @[ UIBarButtonItem.flex_flexibleSpace, self.getterButton, self.actionButton ]; [self registerAuxiliaryInfo]; // Configure input view self.fieldEditorView.fieldDescription = self.fieldDescription; FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:self.typeEncoding]; inputView.inputValue = self.currentValue; inputView.delegate = self; self.fieldEditorView.argumentInputViews = @[inputView]; // Don't show a "set" button for switches; we mutate when the switch is flipped if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) { self.actionButton.enabled = NO; self.actionButton.title = @"Flip the switch to call the setter"; // Put getter button before setter button self.toolbarItems = @[ UIBarButtonItem.flex_flexibleSpace, self.actionButton, self.getterButton ]; } } - (void)actionButtonPressed:(id)sender { if (self.property) { id userInputObject = self.firstInputView.inputValue; NSArray *arguments = userInputObject ? @[userInputObject] : nil; SEL setterSelector = self.property.likelySetter; NSError *error = nil; [FLEXRuntimeUtility performSelector:setterSelector onObject:self.target withArguments:arguments error:&error]; if (error) { [FLEXAlert showAlert:@"Property Setter Failed" message:error.localizedDescription from:self]; sender = nil; // Don't pop back } } else { // TODO: check mutability and use mutableCopy if necessary; // this currently could and would assign NSArray to NSMutableArray [self.ivar setValue:self.firstInputView.inputValue onObject:self.target]; } // Dismiss keyboard and handle committed changes [super actionButtonPressed:sender]; // Go back after setting, but not for switches. if (sender) { [self.navigationController popViewControllerAnimated:YES]; } else { self.firstInputView.inputValue = self.currentValue; } } - (void)getterButtonPressed:(id)sender { [self.fieldEditorView endEditing:YES]; [self exploreObjectOrPopViewController:self.currentValue]; } - (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView { if ([argumentInputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) { [self actionButtonPressed:nil]; } } #pragma mark - Private - (void)registerAuxiliaryInfo { // This is how Reflex will get Swift struct field names into the editor at runtime NSDictionary *labels = [self.auxiliaryInfoProvider auxiliaryInfoForKey:FLEXAuxiliarynfoKeyFieldLabels ]; for (NSString *type in labels) { [FLEXArgumentInputViewFactory registerFieldNames:labels[type] forTypeEncoding:type]; } } - (id)currentValue { if (self.property) { return [self.property getValue:self.target]; } else { return [self.ivar getValue:self.target]; } } - (id)auxiliaryInfoProvider { return self.ivar ?: self.property; } - (const FLEXTypeEncoding *)typeEncoding { if (self.property) { return self.property.attributes.typeEncoding.UTF8String; } else { return self.ivar.typeEncoding.UTF8String; } } - (NSString *)fieldDescription { if (self.property) { return self.property.fullDescription; } else { return self.ivar.description; } } @end ================================================ FILE: Classes/Editing/FLEXMethodCallingViewController.h ================================================ // // FLEXMethodCallingViewController.h // Flipboard // // Created by Ryan Olson on 5/23/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXVariableEditorViewController.h" #import "FLEXMethod.h" @interface FLEXMethodCallingViewController : FLEXVariableEditorViewController + (instancetype)target:(id)target method:(FLEXMethod *)method; @end ================================================ FILE: Classes/Editing/FLEXMethodCallingViewController.m ================================================ // // FLEXMethodCallingViewController.m // Flipboard // // Created by Ryan Olson on 5/23/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXMethodCallingViewController.h" #import "FLEXRuntimeUtility.h" #import "FLEXFieldEditorView.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXArgumentInputView.h" #import "FLEXArgumentInputViewFactory.h" #import "FLEXUtility.h" @interface FLEXMethodCallingViewController () @property (nonatomic, readonly) FLEXMethod *method; @end @implementation FLEXMethodCallingViewController + (instancetype)target:(id)target method:(FLEXMethod *)method { return [[self alloc] initWithTarget:target method:method]; } - (id)initWithTarget:(id)target method:(FLEXMethod *)method { NSParameterAssert(method.isInstanceMethod == !object_isClass(target)); self = [super initWithTarget:target data:method commitHandler:nil]; if (self) { self.title = method.isInstanceMethod ? @"Method: " : @"Class Method: "; self.title = [self.title stringByAppendingString:method.selectorString]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.actionButton.title = @"Call"; // Configure field editor view self.fieldEditorView.argumentInputViews = [self argumentInputViews]; self.fieldEditorView.fieldDescription = [NSString stringWithFormat: @"Signature:\n%@\n\nReturn Type:\n%s", self.method.description, (char *)self.method.returnType ]; } - (NSArray *)argumentInputViews { Method method = self.method.objc_method; NSArray *methodComponents = [FLEXRuntimeUtility prettyArgumentComponentsForMethod:method]; NSMutableArray *argumentInputViews = [NSMutableArray new]; unsigned int argumentIndex = kFLEXNumberOfImplicitArgs; for (NSString *methodComponent in methodComponents) { char *argumentTypeEncoding = method_copyArgumentType(method, argumentIndex); FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:argumentTypeEncoding]; free(argumentTypeEncoding); inputView.backgroundColor = self.view.backgroundColor; inputView.title = methodComponent; [argumentInputViews addObject:inputView]; argumentIndex++; } return argumentInputViews; } - (void)actionButtonPressed:(id)sender { // Gather arguments NSMutableArray *arguments = [NSMutableArray new]; for (FLEXArgumentInputView *inputView in self.fieldEditorView.argumentInputViews) { // Use NSNull as a nil placeholder; it will be interpreted as nil [arguments addObject:inputView.inputValue ?: NSNull.null]; } // Call method NSError *error = nil; id returnValue = [FLEXRuntimeUtility performSelector:self.method.selector onObject:self.target withArguments:arguments error:&error ]; // Dismiss keyboard and handle committed changes [super actionButtonPressed:sender]; // Display return value or error if (error) { [FLEXAlert showAlert:@"Method Call Failed" message:error.localizedDescription from:self]; } else if (returnValue) { // For non-nil (or void) return types, push an explorer view controller to display the returned object returnValue = [FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:returnValue type:self.method.returnType]; FLEXObjectExplorerViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:returnValue]; [self.navigationController pushViewController:explorer animated:YES]; } else { [self exploreObjectOrPopViewController:returnValue]; } } - (FLEXMethod *)method { return _data; } @end ================================================ FILE: Classes/Editing/FLEXVariableEditorViewController.h ================================================ // // FLEXVariableEditorViewController.h // Flipboard // // Created by Ryan Olson on 5/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXFieldEditorView; @class FLEXArgumentInputView; NS_ASSUME_NONNULL_BEGIN /// An abstract screen for editing or configuring one or more variables. /// "Target" is the target of the edit operation, and "data" is the data /// you want to mutate or pass to the target when the action is performed. /// The action may be something like calling a method, setting an ivar, etc. @interface FLEXVariableEditorViewController : UIViewController { @protected id _target; _Nullable id _data; void (^_Nullable _commitHandler)(void); } /// @param target The target of the operation /// @param data The data associated with the operation /// @param onCommit An action to perform when the data changes + (instancetype)target:(id)target data:(nullable id)data commitHandler:(void(^_Nullable)(void))onCommit; /// @param target The target of the operation /// @param data The data associated with the operation /// @param onCommit An action to perform when the data changes - (id)initWithTarget:(id)target data:(nullable id)data commitHandler:(void(^_Nullable)(void))onCommit; @property (nonatomic, readonly) id target; /// Convenience accessor since many subclasses only use one input view @property (nonatomic, readonly, nullable) FLEXArgumentInputView *firstInputView; @property (nonatomic, readonly) FLEXFieldEditorView *fieldEditorView; /// Subclasses can change the button title via the button's \c title property @property (nonatomic, readonly) UIBarButtonItem *actionButton; /// Subclasses should override to provide "set" functionality. /// The commit handler--if present--is called here. - (void)actionButtonPressed:(nullable id)sender; /// Pushes an explorer view controller for the given object /// or pops the current view controller. - (void)exploreObjectOrPopViewController:(nullable id)objectOrNil; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Editing/FLEXVariableEditorViewController.m ================================================ // // FLEXVariableEditorViewController.m // Flipboard // // Created by Ryan Olson on 5/16/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXVariableEditorViewController.h" #import "FLEXFieldEditorView.h" #import "FLEXRuntimeUtility.h" #import "FLEXUtility.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXArgumentInputView.h" #import "FLEXArgumentInputViewFactory.h" #import "FLEXObjectExplorerViewController.h" #import "UIBarButtonItem+FLEX.h" @interface FLEXVariableEditorViewController () @property (nonatomic) UIScrollView *scrollView; @end @implementation FLEXVariableEditorViewController #pragma mark - Initialization + (instancetype)target:(id)target data:(nullable id)data commitHandler:(void(^_Nullable)(void))onCommit { return [[self alloc] initWithTarget:target data:data commitHandler:onCommit]; } - (id)initWithTarget:(id)target data:(nullable id)data commitHandler:(void(^_Nullable)(void))onCommit { self = [super init]; if (self) { _target = target; _data = data; _commitHandler = onCommit; [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil ]; [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil ]; } return self; } - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; } #pragma mark - UIViewController methods - (void)keyboardDidShow:(NSNotification *)notification { CGRect keyboardRectInWindow = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGSize keyboardSize = [self.view convertRect:keyboardRectInWindow fromView:nil].size; UIEdgeInsets scrollInsets = self.scrollView.contentInset; scrollInsets.bottom = keyboardSize.height; self.scrollView.contentInset = scrollInsets; self.scrollView.scrollIndicatorInsets = scrollInsets; // Find the active input view and scroll to make sure it's visible. for (FLEXArgumentInputView *argumentInputView in self.fieldEditorView.argumentInputViews) { if (argumentInputView.inputViewIsFirstResponder) { CGRect scrollToVisibleRect = [self.scrollView convertRect:argumentInputView.bounds fromView:argumentInputView]; [self.scrollView scrollRectToVisible:scrollToVisibleRect animated:YES]; break; } } } - (void)keyboardWillHide:(NSNotification *)notification { UIEdgeInsets scrollInsets = self.scrollView.contentInset; scrollInsets.bottom = 0.0; self.scrollView.contentInset = scrollInsets; self.scrollView.scrollIndicatorInsets = scrollInsets; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = FLEXColor.scrollViewBackgroundColor; self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; self.scrollView.backgroundColor = self.view.backgroundColor; self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.scrollView.delegate = self; [self.view addSubview:self.scrollView]; _fieldEditorView = [FLEXFieldEditorView new]; self.fieldEditorView.targetDescription = [NSString stringWithFormat:@"%@ %p", [self.target class], self.target]; [self.scrollView addSubview:self.fieldEditorView]; _actionButton = [[UIBarButtonItem alloc] initWithTitle:@"Set" style:UIBarButtonItemStyleDone target:self action:@selector(actionButtonPressed:) ]; self.navigationController.toolbarHidden = NO; self.toolbarItems = @[UIBarButtonItem.flex_flexibleSpace, self.actionButton]; } - (void)viewWillLayoutSubviews { CGSize constrainSize = CGSizeMake(self.scrollView.bounds.size.width, CGFLOAT_MAX); CGSize fieldEditorSize = [self.fieldEditorView sizeThatFits:constrainSize]; self.fieldEditorView.frame = CGRectMake(0, 0, fieldEditorSize.width, fieldEditorSize.height); self.scrollView.contentSize = fieldEditorSize; } #pragma mark - Public - (FLEXArgumentInputView *)firstInputView { return [self.fieldEditorView argumentInputViews].firstObject; } - (void)actionButtonPressed:(id)sender { // Subclasses can override [self.fieldEditorView endEditing:YES]; if (_commitHandler) { _commitHandler(); } } - (void)exploreObjectOrPopViewController:(id)objectOrNil { if (objectOrNil) { // For non-nil (or void) return types, push an explorer view controller to display the object FLEXObjectExplorerViewController *explorerViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:objectOrNil]; [self.navigationController pushViewController:explorerViewController animated:YES]; } else { // If we didn't get a returned object but the method call succeeded, // pop this view controller off the stack to indicate that the call went through. [self.navigationController popViewControllerAnimated:YES]; } } @end ================================================ FILE: Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.h ================================================ // // FLEXBookmarkManager.h // FLEX // // Created by Tanner on 2/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface FLEXBookmarkManager : NSObject @property (nonatomic, readonly, class) NSMutableArray *bookmarks; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.m ================================================ // // FLEXBookmarkManager.m // FLEX // // Created by Tanner on 2/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXBookmarkManager.h" static NSMutableArray *kFLEXBookmarkManagerBookmarks = nil; @implementation FLEXBookmarkManager + (void)initialize { if (self == [FLEXBookmarkManager class]) { kFLEXBookmarkManagerBookmarks = [NSMutableArray new]; } } + (NSMutableArray *)bookmarks { return kFLEXBookmarkManagerBookmarks; } @end ================================================ FILE: Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.h ================================================ // // FLEXBookmarksViewController.h // FLEX // // Created by Tanner on 2/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXBookmarksViewController : FLEXTableViewController @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.m ================================================ // // FLEXBookmarksViewController.m // FLEX // // Created by Tanner on 2/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXBookmarksViewController.h" #import "FLEXExplorerViewController.h" #import "FLEXNavigationController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXBookmarkManager.h" #import "UIBarButtonItem+FLEX.h" #import "FLEXColor.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "FLEXTableView.h" @interface FLEXBookmarksViewController () @property (nonatomic, copy) NSArray *bookmarks; @property (nonatomic, readonly) FLEXExplorerViewController *corePresenter; @end @implementation FLEXBookmarksViewController #pragma mark - Initialization - (id)init { return [self initWithStyle:UITableViewStylePlain]; } - (void)viewDidLoad { [super viewDidLoad]; self.navigationController.hidesBarsOnSwipe = NO; self.tableView.allowsMultipleSelectionDuringEditing = YES; [self reloadData]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self setupDefaultBarItems]; } #pragma mark - Private - (void)reloadData { // We assume the bookmarks aren't going to change out from under us, since // presenting any other tool via keyboard shortcuts should dismiss us first self.bookmarks = FLEXBookmarkManager.bookmarks; self.title = [NSString stringWithFormat:@"Bookmarks (%@)", @(self.bookmarks.count)]; } - (void)setupDefaultBarItems { self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated)); self.toolbarItems = @[ UIBarButtonItem.flex_flexibleSpace, FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)), ]; // Disable editing if no bookmarks available self.toolbarItems.lastObject.enabled = self.bookmarks.count > 0; } - (void)setupEditingBarItems { self.navigationItem.rightBarButtonItem = nil; self.toolbarItems = @[ [UIBarButtonItem flex_itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)], UIBarButtonItem.flex_flexibleSpace, // We use a non-system done item because we change its title dynamically [UIBarButtonItem flex_doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)] ]; self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor; } - (FLEXExplorerViewController *)corePresenter { // We must be presented by a FLEXExplorerViewController, or presented // by another view controller that was presented by FLEXExplorerViewController FLEXExplorerViewController *presenter = (id)self.presentingViewController; presenter = (id)presenter.presentingViewController ?: presenter; presenter = (id)presenter.presentingViewController ?: presenter; NSAssert( [presenter isKindOfClass:[FLEXExplorerViewController class]], @"The bookmarks view controller expects to be presented by the explorer controller" ); return presenter; } #pragma mark Button Actions - (void)dismissAnimated { [self dismissAnimated:nil]; } - (void)dismissAnimated:(id)selectedObject { if (selectedObject) { UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:selectedObject ]; if ([self.presentingViewController isKindOfClass:[FLEXNavigationController class]]) { // I am presented on an existing navigation stack, so // dismiss myself and push the bookmark there UINavigationController *presenter = (id)self.presentingViewController; [presenter dismissViewControllerAnimated:YES completion:^{ [presenter pushViewController:explorer animated:YES]; }]; } else { // Dismiss myself and present explorer UIViewController *presenter = self.corePresenter; [presenter dismissViewControllerAnimated:YES completion:^{ [presenter presentViewController:[FLEXNavigationController withRootViewController:explorer ] animated:YES completion:nil]; }]; } } else { // Just dismiss myself [self dismissViewControllerAnimated:YES completion:nil]; } } - (void)toggleEditing { NSArray *selected = self.tableView.indexPathsForSelectedRows; self.editing = !self.editing; if (self.isEditing) { [self setupEditingBarItems]; } else { [self setupDefaultBarItems]; // Get index set of bookmarks to close NSMutableIndexSet *indexes = [NSMutableIndexSet new]; for (NSIndexPath *ip in selected) { [indexes addIndex:ip.row]; } if (selected.count) { // Close bookmarks and update data source [FLEXBookmarkManager.bookmarks removeObjectsAtIndexes:indexes]; [self reloadData]; // Remove deleted rows [self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic]; } } } - (void)closeAllButtonPressed:(UIBarButtonItem *)sender { [FLEXAlert makeSheet:^(FLEXAlert *make) { NSInteger count = self.bookmarks.count; NSString *title = FLEXPluralFormatString(count, @"Remove %@ bookmarks", @"Remove %@ bookmark"); make.button(title).destructiveStyle().handler(^(NSArray *strings) { [self closeAll]; [self toggleEditing]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } - (void)closeAll { NSInteger rowCount = self.bookmarks.count; // Close bookmarks and update data source [FLEXBookmarkManager.bookmarks removeAllObjects]; [self reloadData]; // Delete rows from table view NSArray *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) { return [NSIndexPath indexPathForRow:row inSection:0]; }]; [self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic]; } #pragma mark - Table View Data Source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.bookmarks.count; } - (UITableViewCell *)tableView:(FLEXTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath]; id object = self.bookmarks[indexPath.row]; cell.textLabel.text = [FLEXRuntimeUtility safeDescriptionForObject:object]; cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ — %p", [object class], object]; return cell; } #pragma mark - Table View Delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (self.editing) { // Case: editing with multi-select self.toolbarItems.lastObject.title = @"Remove Selected"; self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor; } else { // Case: selected a bookmark [self dismissAnimated:self.bookmarks[indexPath.row]]; } } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { NSParameterAssert(self.editing); if (tableView.indexPathsForSelectedRows.count == 0) { self.toolbarItems.lastObject.title = @"Done"; self.toolbarItems.lastObject.tintColor = self.view.tintColor; } } - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (void)tableView:(UITableView *)table commitEditingStyle:(UITableViewCellEditingStyle)edit forRowAtIndexPath:(NSIndexPath *)indexPath { NSParameterAssert(edit == UITableViewCellEditingStyleDelete); // Remove bookmark and update data source [FLEXBookmarkManager.bookmarks removeObjectAtIndex:indexPath.row]; [self reloadData]; // Delete row from table view [table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } @end ================================================ FILE: Classes/ExplorerInterface/FLEXExplorerViewController.h ================================================ // // FLEXExplorerViewController.h // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXExplorerToolbar.h" @class FLEXWindow; @protocol FLEXExplorerViewControllerDelegate; /// A view controller that manages the FLEX toolbar. @interface FLEXExplorerViewController : UIViewController @property (nonatomic, weak) id delegate; @property (nonatomic, readonly) BOOL wantsWindowToBecomeKey; @property (nonatomic, readonly) FLEXExplorerToolbar *explorerToolbar; - (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates; /// @brief Used to present (or dismiss) a modal view controller ("tool"), /// typically triggered by pressing a button in the toolbar. /// /// If a tool is already presented, this method simply dismisses it and calls the completion block. /// If no tool is presented, @code future() @endcode is presented and the completion block is called. - (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future completion:(void (^)(void))completion; /// @brief Used to present (or dismiss) a modal view controller ("tool"), /// typically triggered by pressing a button in the toolbar. /// /// If a tool is already presented, this method dismisses it and presents the given tool. /// The completion block is called once the tool has been presented. - (void)presentTool:(UINavigationController *(^)(void))future completion:(void (^)(void))completion; // Keyboard shortcut helpers - (void)toggleSelectTool; - (void)toggleMoveTool; - (void)toggleViewsTool; - (void)toggleMenuTool; /// @return YES if the explorer used the key press to perform an action, NO otherwise - (BOOL)handleDownArrowKeyPressed; /// @return YES if the explorer used the key press to perform an action, NO otherwise - (BOOL)handleUpArrowKeyPressed; /// @return YES if the explorer used the key press to perform an action, NO otherwise - (BOOL)handleRightArrowKeyPressed; /// @return YES if the explorer used the key press to perform an action, NO otherwise - (BOOL)handleLeftArrowKeyPressed; @end #pragma mark - @protocol FLEXExplorerViewControllerDelegate - (void)explorerViewControllerDidFinish:(FLEXExplorerViewController *)explorerViewController; @end ================================================ FILE: Classes/ExplorerInterface/FLEXExplorerViewController.m ================================================ // // FLEXExplorerViewController.m // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXExplorerViewController.h" #import "FLEXExplorerToolbarItem.h" #import "FLEXUtility.h" #import "FLEXWindow.h" #import "FLEXTabList.h" #import "FLEXNavigationController.h" #import "FLEXHierarchyViewController.h" #import "FLEXGlobalsViewController.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXNetworkMITMViewController.h" #import "FLEXTabsViewController.h" #import "FLEXWindowManagerController.h" #import "FLEXViewControllersViewController.h" #import "NSUserDefaults+FLEX.h" typedef NS_ENUM(NSUInteger, FLEXExplorerMode) { FLEXExplorerModeDefault, FLEXExplorerModeSelect, FLEXExplorerModeMove }; @interface FLEXExplorerViewController () /// Tracks the currently active tool/mode @property (nonatomic) FLEXExplorerMode currentMode; /// Gesture recognizer for dragging a view in move mode @property (nonatomic) UIPanGestureRecognizer *movePanGR; /// Gesture recognizer for showing additional details on the selected view @property (nonatomic) UITapGestureRecognizer *detailsTapGR; /// Only valid while a move pan gesture is in progress. @property (nonatomic) CGRect selectedViewFrameBeforeDragging; /// Only valid while a toolbar drag pan gesture is in progress. @property (nonatomic) CGRect toolbarFrameBeforeDragging; /// Only valid while a selected view pan gesture is in progress. @property (nonatomic) CGFloat selectedViewLastPanX; /// Borders of all the visible views in the hierarchy at the selection point. /// The keys are NSValues with the corresponding view (nonretained). @property (nonatomic) NSDictionary *outlineViewsForVisibleViews; /// The actual views at the selection point with the deepest view last. @property (nonatomic) NSArray *viewsAtTapPoint; /// The view that we're currently highlighting with an overlay and displaying details for. @property (nonatomic) UIView *selectedView; /// A colored transparent overlay to indicate that the view is selected. @property (nonatomic) UIView *selectedViewOverlay; /// Used to actuate changes in view selection on iOS 10+ @property (nonatomic, readonly) UISelectionFeedbackGenerator *selectionFBG API_AVAILABLE(ios(10.0)); /// self.view.window as a \c FLEXWindow @property (nonatomic, readonly) FLEXWindow *window; /// All views that we're KVOing. Used to help us clean up properly. @property (nonatomic) NSMutableSet *observedViews; /// Used to preserve the target app's UIMenuController items. @property (nonatomic) NSArray *appMenuItems; @end @implementation FLEXExplorerViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { self.observedViews = [NSMutableSet new]; } return self; } - (void)dealloc { for (UIView *view in _observedViews) { [self stopObservingView:view]; } } - (void)viewDidLoad { [super viewDidLoad]; // Toolbar _explorerToolbar = [FLEXExplorerToolbar new]; // Start the toolbar off below any bars that may be at the top of the view. CGFloat toolbarOriginY = NSUserDefaults.standardUserDefaults.flex_toolbarTopMargin; CGRect safeArea = [self viewSafeArea]; CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake( CGRectGetWidth(self.view.bounds), CGRectGetHeight(safeArea) )]; [self updateToolbarPositionWithUnconstrainedFrame:CGRectMake( CGRectGetMinX(safeArea), toolbarOriginY, toolbarSize.width, toolbarSize.height )]; self.explorerToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin; [self.view addSubview:self.explorerToolbar]; [self setupToolbarActions]; [self setupToolbarGestures]; // View selection UITapGestureRecognizer *selectionTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSelectionTap:) ]; [self.view addGestureRecognizer:selectionTapGR]; // View moving self.movePanGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleMovePan:)]; self.movePanGR.enabled = self.currentMode == FLEXExplorerModeMove; [self.view addGestureRecognizer:self.movePanGR]; // Feedback if (@available(iOS 10.0, *)) { _selectionFBG = [UISelectionFeedbackGenerator new]; } // Observe keyboard to move self out of the way [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardShown:) name:UIKeyboardWillShowNotification object:nil ]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self updateButtonStates]; } #pragma mark - Rotation - (UIViewController *)viewControllerForRotationAndOrientation { UIViewController *viewController = FLEXUtility.appKeyWindow.rootViewController; // Obfuscating selector _viewControllerForSupportedInterfaceOrientations NSString *viewControllerSelectorString = [@[ @"_vie", @"wContro", @"llerFor", @"Supported", @"Interface", @"Orientations" ] componentsJoinedByString:@""]; SEL viewControllerSelector = NSSelectorFromString(viewControllerSelectorString); if ([viewController respondsToSelector:viewControllerSelector]) { viewController = [viewController valueForKey:viewControllerSelectorString]; } return viewController; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { // Commenting this out until I can figure out a better way to solve this // if (self.window.isKeyWindow) { // [self.window resignKeyWindow]; // } UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation]; UIInterfaceOrientationMask supportedOrientations = FLEXUtility.infoPlistSupportedInterfaceOrientationsMask; // We check its class by name because using isKindOfClass will fail for the same class defined // twice in the runtime; and the goal here is to avoid calling -supportedInterfaceOrientations // recursively when I'm inspecting FLEX with itself from a tweak dylib if (viewControllerToAsk && ![NSStringFromClass([viewControllerToAsk class]) hasPrefix:@"FLEX"]) { supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations]; } // The UIViewController docs state that this method must not return zero. // If we weren't able to get a valid value for the supported interface // orientations, default to all supported. if (supportedOrientations == 0) { supportedOrientations = UIInterfaceOrientationMaskAll; } return supportedOrientations; } - (BOOL)shouldAutorotate { UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation]; BOOL shouldAutorotate = YES; if (viewControllerToAsk && viewControllerToAsk != self) { shouldAutorotate = [viewControllerToAsk shouldAutorotate]; } return shouldAutorotate; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:^(id context) { for (UIView *outlineView in self.outlineViewsForVisibleViews.allValues) { outlineView.hidden = YES; } self.selectedViewOverlay.hidden = YES; } completion:^(id context) { for (UIView *view in self.viewsAtTapPoint) { NSValue *key = [NSValue valueWithNonretainedObject:view]; UIView *outlineView = self.outlineViewsForVisibleViews[key]; outlineView.frame = [self frameInLocalCoordinatesForView:view]; if (self.currentMode == FLEXExplorerModeSelect) { outlineView.hidden = NO; } } if (self.selectedView) { self.selectedViewOverlay.frame = [self frameInLocalCoordinatesForView:self.selectedView]; self.selectedViewOverlay.hidden = NO; } }]; } #pragma mark - Setter Overrides - (void)setSelectedView:(UIView *)selectedView { if (![_selectedView isEqual:selectedView]) { if (![self.viewsAtTapPoint containsObject:_selectedView]) { [self stopObservingView:_selectedView]; } _selectedView = selectedView; [self beginObservingView:selectedView]; // Update the toolbar and selected overlay self.explorerToolbar.selectedViewDescription = [FLEXUtility descriptionForView:selectedView includingFrame:YES ]; self.explorerToolbar.selectedViewOverlayColor = [FLEXUtility consistentRandomColorForObject:selectedView ]; if (selectedView) { if (!self.selectedViewOverlay) { self.selectedViewOverlay = [UIView new]; [self.view addSubview:self.selectedViewOverlay]; self.selectedViewOverlay.layer.borderWidth = 1.0; } UIColor *outlineColor = [FLEXUtility consistentRandomColorForObject:selectedView]; self.selectedViewOverlay.backgroundColor = [outlineColor colorWithAlphaComponent:0.2]; self.selectedViewOverlay.layer.borderColor = outlineColor.CGColor; self.selectedViewOverlay.frame = [self.view convertRect:selectedView.bounds fromView:selectedView]; // Make sure the selected overlay is in front of all the other subviews // except the toolbar, which should always stay on top. [self.view bringSubviewToFront:self.selectedViewOverlay]; [self.view bringSubviewToFront:self.explorerToolbar]; } else { [self.selectedViewOverlay removeFromSuperview]; self.selectedViewOverlay = nil; } // Some of the button states depend on whether we have a selected view. [self updateButtonStates]; } } - (void)setViewsAtTapPoint:(NSArray *)viewsAtTapPoint { if (![_viewsAtTapPoint isEqual:viewsAtTapPoint]) { for (UIView *view in _viewsAtTapPoint) { if (view != self.selectedView) { [self stopObservingView:view]; } } _viewsAtTapPoint = viewsAtTapPoint; for (UIView *view in viewsAtTapPoint) { [self beginObservingView:view]; } } } - (void)setCurrentMode:(FLEXExplorerMode)currentMode { if (_currentMode != currentMode) { _currentMode = currentMode; switch (currentMode) { case FLEXExplorerModeDefault: [self removeAndClearOutlineViews]; self.viewsAtTapPoint = nil; self.selectedView = nil; break; case FLEXExplorerModeSelect: // Make sure the outline views are unhidden in case we came from the move mode. for (NSValue *key in self.outlineViewsForVisibleViews) { UIView *outlineView = self.outlineViewsForVisibleViews[key]; outlineView.hidden = NO; } break; case FLEXExplorerModeMove: // Hide all the outline views to focus on the selected view, // which is the only one that will move. for (NSValue *key in self.outlineViewsForVisibleViews) { UIView *outlineView = self.outlineViewsForVisibleViews[key]; outlineView.hidden = YES; } break; } self.movePanGR.enabled = currentMode == FLEXExplorerModeMove; [self updateButtonStates]; } } #pragma mark - View Tracking - (void)beginObservingView:(UIView *)view { // Bail if we're already observing this view or if there's nothing to observe. if (!view || [self.observedViews containsObject:view]) { return; } for (NSString *keyPath in self.viewKeyPathsToTrack) { [view addObserver:self forKeyPath:keyPath options:0 context:NULL]; } [self.observedViews addObject:view]; } - (void)stopObservingView:(UIView *)view { if (!view) { return; } for (NSString *keyPath in self.viewKeyPathsToTrack) { [view removeObserver:self forKeyPath:keyPath]; } [self.observedViews removeObject:view]; } - (NSArray *)viewKeyPathsToTrack { static NSArray *trackedViewKeyPaths = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *frameKeyPath = NSStringFromSelector(@selector(frame)); trackedViewKeyPaths = @[frameKeyPath]; }); return trackedViewKeyPaths; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { [self updateOverlayAndDescriptionForObjectIfNeeded:object]; } - (void)updateOverlayAndDescriptionForObjectIfNeeded:(id)object { NSUInteger indexOfView = [self.viewsAtTapPoint indexOfObject:object]; if (indexOfView != NSNotFound) { UIView *view = self.viewsAtTapPoint[indexOfView]; NSValue *key = [NSValue valueWithNonretainedObject:view]; UIView *outline = self.outlineViewsForVisibleViews[key]; if (outline) { outline.frame = [self frameInLocalCoordinatesForView:view]; } } if (object == self.selectedView) { // Update the selected view description since we show the frame value there. self.explorerToolbar.selectedViewDescription = [FLEXUtility descriptionForView:self.selectedView includingFrame:YES ]; CGRect selectedViewOutlineFrame = [self frameInLocalCoordinatesForView:self.selectedView]; self.selectedViewOverlay.frame = selectedViewOutlineFrame; } } - (CGRect)frameInLocalCoordinatesForView:(UIView *)view { // Convert to window coordinates since the view may be in a different window than our view CGRect frameInWindow = [view convertRect:view.bounds toView:nil]; // Convert from the window to our view's coordinate space return [self.view convertRect:frameInWindow fromView:nil]; } - (void)keyboardShown:(NSNotification *)notif { CGRect keyboardFrame = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGRect toolbarFrame = self.explorerToolbar.frame; if (CGRectGetMinY(keyboardFrame) < CGRectGetMaxY(toolbarFrame)) { toolbarFrame.origin.y = keyboardFrame.origin.y - toolbarFrame.size.height; // Subtract a little more, to ignore accessory input views toolbarFrame.origin.y -= 50; [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{ [self updateToolbarPositionWithUnconstrainedFrame:toolbarFrame]; } completion:nil]; } } #pragma mark - Toolbar Buttons - (void)setupToolbarActions { FLEXExplorerToolbar *toolbar = self.explorerToolbar; NSDictionary *actionsToItems = @{ NSStringFromSelector(@selector(selectButtonTapped:)): toolbar.selectItem, NSStringFromSelector(@selector(hierarchyButtonTapped:)): toolbar.hierarchyItem, NSStringFromSelector(@selector(recentButtonTapped:)): toolbar.recentItem, NSStringFromSelector(@selector(moveButtonTapped:)): toolbar.moveItem, NSStringFromSelector(@selector(globalsButtonTapped:)): toolbar.globalsItem, NSStringFromSelector(@selector(closeButtonTapped:)): toolbar.closeItem, }; [actionsToItems enumerateKeysAndObjectsUsingBlock:^(NSString *sel, FLEXExplorerToolbarItem *item, BOOL *stop) { [item addTarget:self action:NSSelectorFromString(sel) forControlEvents:UIControlEventTouchUpInside]; }]; } - (void)selectButtonTapped:(FLEXExplorerToolbarItem *)sender { [self toggleSelectTool]; } - (void)hierarchyButtonTapped:(FLEXExplorerToolbarItem *)sender { [self toggleViewsTool]; } - (UIWindow *)statusWindow { if (!@available(iOS 16, *)) { NSString *statusBarString = [NSString stringWithFormat:@"%@arWindow", @"_statusB"]; return [UIApplication.sharedApplication valueForKey:statusBarString]; } return nil; } - (void)recentButtonTapped:(FLEXExplorerToolbarItem *)sender { NSAssert(FLEXTabList.sharedList.activeTab, @"Must have active tab"); [self presentViewController:FLEXTabList.sharedList.activeTab animated:YES completion:nil]; } - (void)moveButtonTapped:(FLEXExplorerToolbarItem *)sender { [self toggleMoveTool]; } - (void)globalsButtonTapped:(FLEXExplorerToolbarItem *)sender { [self toggleMenuTool]; } - (void)closeButtonTapped:(FLEXExplorerToolbarItem *)sender { self.currentMode = FLEXExplorerModeDefault; [self.delegate explorerViewControllerDidFinish:self]; } - (void)updateButtonStates { FLEXExplorerToolbar *toolbar = self.explorerToolbar; toolbar.selectItem.selected = self.currentMode == FLEXExplorerModeSelect; // Move only enabled when an object is selected. BOOL hasSelectedObject = self.selectedView != nil; toolbar.moveItem.enabled = hasSelectedObject; toolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove; // Recent only enabled when we have a last active tab if (!self.presentedViewController) { toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil; } else { toolbar.recentItem.enabled = NO; } } #pragma mark - Toolbar Dragging - (void)setupToolbarGestures { FLEXExplorerToolbar *toolbar = self.explorerToolbar; // Pan gesture for dragging. [toolbar.dragHandle addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarPanGesture:) ]]; // Tap gesture for hinting. [toolbar.dragHandle addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarHintTapGesture:) ]]; // Tap gesture for showing additional details self.detailsTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarDetailsTapGesture:) ]; [toolbar.selectedViewDescriptionContainer addGestureRecognizer:self.detailsTapGR]; // Swipe gestures for selecting deeper / higher views at a point UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleChangeViewAtPointGesture:) ]; [toolbar.selectedViewDescriptionContainer addGestureRecognizer:panGesture]; // Long press gesture to present tabs manager [toolbar.globalsItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarShowTabsGesture:) ]]; // Long press gesture to present window manager [toolbar.selectItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarWindowManagerGesture:) ]]; // Long press gesture to present view controllers at tap [toolbar.hierarchyItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarShowViewControllersGesture:) ]]; } - (void)handleToolbarPanGesture:(UIPanGestureRecognizer *)panGR { switch (panGR.state) { case UIGestureRecognizerStateBegan: self.toolbarFrameBeforeDragging = self.explorerToolbar.frame; [self updateToolbarPositionWithDragGesture:panGR]; break; case UIGestureRecognizerStateChanged: case UIGestureRecognizerStateEnded: [self updateToolbarPositionWithDragGesture:panGR]; break; default: break; } } - (void)updateToolbarPositionWithDragGesture:(UIPanGestureRecognizer *)panGR { CGPoint translation = [panGR translationInView:self.view]; CGRect newToolbarFrame = self.toolbarFrameBeforeDragging; newToolbarFrame.origin.y += translation.y; [self updateToolbarPositionWithUnconstrainedFrame:newToolbarFrame]; } - (void)updateToolbarPositionWithUnconstrainedFrame:(CGRect)unconstrainedFrame { CGRect safeArea = [self viewSafeArea]; // We only constrain the Y-axis because we want the toolbar // to handle the X-axis safeArea layout by itself CGFloat minY = CGRectGetMinY(safeArea); CGFloat maxY = CGRectGetMaxY(safeArea) - unconstrainedFrame.size.height; if (unconstrainedFrame.origin.y < minY) { unconstrainedFrame.origin.y = minY; } else if (unconstrainedFrame.origin.y > maxY) { unconstrainedFrame.origin.y = maxY; } self.explorerToolbar.frame = unconstrainedFrame; NSUserDefaults.standardUserDefaults.flex_toolbarTopMargin = unconstrainedFrame.origin.y; } - (void)handleToolbarHintTapGesture:(UITapGestureRecognizer *)tapGR { // Bounce the toolbar to indicate that it is draggable. // TODO: make it bouncier. if (tapGR.state == UIGestureRecognizerStateRecognized) { CGRect originalToolbarFrame = self.explorerToolbar.frame; const NSTimeInterval kHalfwayDuration = 0.2; const CGFloat kVerticalOffset = 30.0; [UIView animateWithDuration:kHalfwayDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ CGRect newToolbarFrame = self.explorerToolbar.frame; newToolbarFrame.origin.y += kVerticalOffset; self.explorerToolbar.frame = newToolbarFrame; } completion:^(BOOL finished) { [UIView animateWithDuration:kHalfwayDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ self.explorerToolbar.frame = originalToolbarFrame; } completion:nil]; }]; } } - (void)handleToolbarDetailsTapGesture:(UITapGestureRecognizer *)tapGR { if (tapGR.state == UIGestureRecognizerStateRecognized && self.selectedView) { UIViewController *topStackVC = [FLEXObjectExplorerFactory explorerViewControllerForObject:self.selectedView]; [self presentViewController: [FLEXNavigationController withRootViewController:topStackVC] animated:YES completion:nil]; } } - (void)handleToolbarShowTabsGesture:(UILongPressGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateBegan) { // Back up the UIMenuController items since dismissViewController: will attempt to replace them self.appMenuItems = UIMenuController.sharedMenuController.menuItems; // Don't use FLEXNavigationController because the tab viewer itself is not a tab [super presentViewController:[[UINavigationController alloc] initWithRootViewController:[FLEXTabsViewController new] ] animated:YES completion:nil]; } } - (void)handleToolbarWindowManagerGesture:(UILongPressGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateBegan) { // Back up the UIMenuController items since dismissViewController: will attempt to replace them self.appMenuItems = UIMenuController.sharedMenuController.menuItems; [super presentViewController:[FLEXNavigationController withRootViewController:[FLEXWindowManagerController new] ] animated:YES completion:nil]; } } - (void)handleToolbarShowViewControllersGesture:(UILongPressGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateBegan && self.viewsAtTapPoint.count) { // Back up the UIMenuController items since dismissViewController: will attempt to replace them self.appMenuItems = UIMenuController.sharedMenuController.menuItems; UIViewController *list = [FLEXViewControllersViewController controllersForViews:self.viewsAtTapPoint ]; [self presentViewController: [FLEXNavigationController withRootViewController:list ] animated:YES completion:nil]; } } #pragma mark - View Selection - (void)handleSelectionTap:(UITapGestureRecognizer *)tapGR { // Only if we're in selection mode if (self.currentMode == FLEXExplorerModeSelect && tapGR.state == UIGestureRecognizerStateRecognized) { // Note that [tapGR locationInView:nil] is broken in iOS 8, // so we have to do a two step conversion to window coordinates. // Thanks to @lascorbe for finding this: https://github.com/Flipboard/FLEX/pull/31 CGPoint tapPointInView = [tapGR locationInView:self.view]; CGPoint tapPointInWindow = [self.view convertPoint:tapPointInView toView:nil]; [self updateOutlineViewsForSelectionPoint:tapPointInWindow]; } } - (void)handleChangeViewAtPointGesture:(UIPanGestureRecognizer *)sender { NSInteger max = self.viewsAtTapPoint.count - 1; NSInteger currentIdx = [self.viewsAtTapPoint indexOfObject:self.selectedView]; CGFloat locationX = [sender locationInView:self.view].x; // Track the pan gesture: every N points we move along the X axis, // actuate some haptic feedback and move up or down the hierarchy. // We only store the "last" location when we've met the threshold. // We only change the view and actuate feedback if the view selection // changes; that is, as long as we don't go outside or under the array. switch (sender.state) { case UIGestureRecognizerStateBegan: { self.selectedViewLastPanX = locationX; break; } case UIGestureRecognizerStateChanged: { static CGFloat kNextLevelThreshold = 20.f; CGFloat lastX = self.selectedViewLastPanX; NSInteger newSelection = currentIdx; // Left, go down the hierarchy if (locationX < lastX && (lastX - locationX) >= kNextLevelThreshold) { // Choose a new view index up to the max index newSelection = MIN(max, currentIdx + 1); self.selectedViewLastPanX = locationX; } // Right, go up the hierarchy else if (lastX < locationX && (locationX - lastX) >= kNextLevelThreshold) { // Choose a new view index down to the min index newSelection = MAX(0, currentIdx - 1); self.selectedViewLastPanX = locationX; } if (currentIdx != newSelection) { self.selectedView = self.viewsAtTapPoint[newSelection]; [self actuateSelectionChangedFeedback]; } break; } default: break; } } - (void)actuateSelectionChangedFeedback { if (@available(iOS 10.0, *)) { [self.selectionFBG selectionChanged]; } } - (void)updateOutlineViewsForSelectionPoint:(CGPoint)selectionPointInWindow { [self removeAndClearOutlineViews]; // Include hidden views in the "viewsAtTapPoint" array so we can show them in the hierarchy list. self.viewsAtTapPoint = [self viewsAtPoint:selectionPointInWindow skipHiddenViews:NO]; // For outlined views and the selected view, only use visible views. // Outlining hidden views adds clutter and makes the selection behavior confusing. NSArray *visibleViewsAtTapPoint = [self viewsAtPoint:selectionPointInWindow skipHiddenViews:YES]; NSMutableDictionary *newOutlineViewsForVisibleViews = [NSMutableDictionary new]; for (UIView *view in visibleViewsAtTapPoint) { UIView *outlineView = [self outlineViewForView:view]; [self.view addSubview:outlineView]; NSValue *key = [NSValue valueWithNonretainedObject:view]; [newOutlineViewsForVisibleViews setObject:outlineView forKey:key]; } self.outlineViewsForVisibleViews = newOutlineViewsForVisibleViews; self.selectedView = [self viewForSelectionAtPoint:selectionPointInWindow]; // Make sure the explorer toolbar doesn't end up behind the newly added outline views. [self.view bringSubviewToFront:self.explorerToolbar]; [self updateButtonStates]; } - (UIView *)outlineViewForView:(UIView *)view { CGRect outlineFrame = [self frameInLocalCoordinatesForView:view]; UIView *outlineView = [[UIView alloc] initWithFrame:outlineFrame]; outlineView.backgroundColor = UIColor.clearColor; outlineView.layer.borderColor = [FLEXUtility consistentRandomColorForObject:view].CGColor; outlineView.layer.borderWidth = 1.0; return outlineView; } - (void)removeAndClearOutlineViews { for (NSValue *key in self.outlineViewsForVisibleViews) { UIView *outlineView = self.outlineViewsForVisibleViews[key]; [outlineView removeFromSuperview]; } self.outlineViewsForVisibleViews = nil; } - (NSArray *)viewsAtPoint:(CGPoint)tapPointInWindow skipHiddenViews:(BOOL)skipHidden { NSMutableArray *views = [NSMutableArray new]; for (UIWindow *window in FLEXUtility.allWindows) { // Don't include the explorer's own window or subviews. if (window != self.view.window && [window pointInside:tapPointInWindow withEvent:nil]) { [views addObject:window]; [views addObjectsFromArray:[self recursiveSubviewsAtPoint:tapPointInWindow inView:window skipHiddenViews:skipHidden ]]; } } return views; } - (UIView *)viewForSelectionAtPoint:(CGPoint)tapPointInWindow { // Select in the window that would handle the touch, but don't just use the result of // hitTest:withEvent: so we can still select views with interaction disabled. // Default to the the application's key window if none of the windows want the touch. UIWindow *windowForSelection = UIApplication.sharedApplication.keyWindow; for (UIWindow *window in FLEXUtility.allWindows.reverseObjectEnumerator) { // Ignore the explorer's own window. if (window != self.view.window) { if ([window hitTest:tapPointInWindow withEvent:nil]) { windowForSelection = window; break; } } } // Select the deepest visible view at the tap point. This generally corresponds to what the user wants to select. return [self recursiveSubviewsAtPoint:tapPointInWindow inView:windowForSelection skipHiddenViews:YES].lastObject; } - (NSArray *)recursiveSubviewsAtPoint:(CGPoint)pointInView inView:(UIView *)view skipHiddenViews:(BOOL)skipHidden { NSMutableArray *subviewsAtPoint = [NSMutableArray new]; for (UIView *subview in view.subviews) { BOOL isHidden = subview.hidden || subview.alpha < 0.01; if (skipHidden && isHidden) { continue; } BOOL subviewContainsPoint = CGRectContainsPoint(subview.frame, pointInView); if (subviewContainsPoint) { [subviewsAtPoint addObject:subview]; } // If this view doesn't clip to its bounds, we need to check its subviews even if it // doesn't contain the selection point. They may be visible and contain the selection point. if (subviewContainsPoint || !subview.clipsToBounds) { CGPoint pointInSubview = [view convertPoint:pointInView toView:subview]; [subviewsAtPoint addObjectsFromArray:[self recursiveSubviewsAtPoint:pointInSubview inView:subview skipHiddenViews:skipHidden ]]; } } return subviewsAtPoint; } #pragma mark - Selected View Moving - (void)handleMovePan:(UIPanGestureRecognizer *)movePanGR { switch (movePanGR.state) { case UIGestureRecognizerStateBegan: self.selectedViewFrameBeforeDragging = self.selectedView.frame; [self updateSelectedViewPositionWithDragGesture:movePanGR]; break; case UIGestureRecognizerStateChanged: case UIGestureRecognizerStateEnded: [self updateSelectedViewPositionWithDragGesture:movePanGR]; break; default: break; } } - (void)updateSelectedViewPositionWithDragGesture:(UIPanGestureRecognizer *)movePanGR { CGPoint translation = [movePanGR translationInView:self.selectedView.superview]; CGRect newSelectedViewFrame = self.selectedViewFrameBeforeDragging; newSelectedViewFrame.origin.x = FLEXFloor(newSelectedViewFrame.origin.x + translation.x); newSelectedViewFrame.origin.y = FLEXFloor(newSelectedViewFrame.origin.y + translation.y); self.selectedView.frame = newSelectedViewFrame; } #pragma mark - Safe Area Handling - (CGRect)viewSafeArea { CGRect safeArea = self.view.bounds; if (@available(iOS 11.0, *)) { safeArea = UIEdgeInsetsInsetRect(self.view.bounds, self.view.safeAreaInsets); } return safeArea; } - (void)viewSafeAreaInsetsDidChange { if (@available(iOS 11.0, *)) { [super viewSafeAreaInsetsDidChange]; CGRect safeArea = [self viewSafeArea]; CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake( CGRectGetWidth(self.view.bounds), CGRectGetHeight(safeArea) )]; [self updateToolbarPositionWithUnconstrainedFrame:CGRectMake( CGRectGetMinX(self.explorerToolbar.frame), CGRectGetMinY(self.explorerToolbar.frame), toolbarSize.width, toolbarSize.height) ]; } } #pragma mark - Touch Handling - (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates { CGPoint pointInLocalCoordinates = [self.view convertPoint:pointInWindowCoordinates fromView:nil]; // If we have a modal presented, is it in the modal? if (self.presentedViewController) { UIView *presentedView = self.presentedViewController.view; CGPoint pipvc = [presentedView convertPoint:pointInLocalCoordinates fromView:self.view]; UIView *hit = [presentedView hitTest:pipvc withEvent:nil]; if (hit != nil) { return YES; } } // Always if we're in selection mode if (self.currentMode == FLEXExplorerModeSelect) { return YES; } // Always in move mode too if (self.currentMode == FLEXExplorerModeMove) { return YES; } // Always if it's on the toolbar if (CGRectContainsPoint(self.explorerToolbar.frame, pointInLocalCoordinates)) { return YES; } return NO; } #pragma mark - FLEXHierarchyDelegate - (void)viewHierarchyDidDismiss:(UIView *)selectedView { // Note that we need to wait until the view controller is dismissed to calculate the frame // of the outline view, otherwise the coordinate conversion doesn't give the correct result. [self toggleViewsToolWithCompletion:^{ // If the selected view is outside of the tap point array (selected from "Full Hierarchy"), // then clear out the tap point array and remove all the outline views. if (![self.viewsAtTapPoint containsObject:selectedView]) { self.viewsAtTapPoint = nil; [self removeAndClearOutlineViews]; } // If we now have a selected view and we didn't have one previously, go to "select" mode. if (self.currentMode == FLEXExplorerModeDefault && selectedView) { self.currentMode = FLEXExplorerModeSelect; } // The selected view setter will also update the selected view overlay appropriately. self.selectedView = selectedView; }]; } #pragma mark - Modal Presentation and Window Management - (void)presentViewController:(UIViewController *)toPresent animated:(BOOL)animated completion:(void (^)(void))completion { // Make our window key to correctly handle input. [self.view.window makeKeyWindow]; // Move the status bar on top of FLEX so we can get scroll to top behavior for taps. if (!@available(iOS 13, *)) { [self statusWindow].windowLevel = self.view.window.windowLevel + 1.0; } // Back up and replace the UIMenuController items // Edit: no longer replacing the items, but still backing them // up in case we start replacing them again in the future self.appMenuItems = UIMenuController.sharedMenuController.menuItems; [self updateButtonStates]; // Show the view controller [super presentViewController:toPresent animated:animated completion:^{ [self updateButtonStates]; if (completion) completion(); }]; } - (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion { UIWindow *appWindow = self.window.previousKeyWindow; [appWindow makeKeyWindow]; [appWindow.rootViewController setNeedsStatusBarAppearanceUpdate]; // Restore previous UIMenuController items // Back up and replace the UIMenuController items UIMenuController.sharedMenuController.menuItems = self.appMenuItems; [UIMenuController.sharedMenuController update]; self.appMenuItems = nil; // Restore the status bar window's normal window level. // We want it above FLEX while a modal is presented for // scroll to top, but below FLEX otherwise for exploration. [self statusWindow].windowLevel = UIWindowLevelStatusBar; [self updateButtonStates]; [super dismissViewControllerAnimated:animated completion:^{ [self updateButtonStates]; if (completion) completion(); }]; } - (BOOL)wantsWindowToBecomeKey { return self.window.previousKeyWindow != nil; } - (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future completion:(void (^)(void))completion { if (self.presentedViewController) { // We do NOT want to present the future; this is // a convenience method for toggling the SAME TOOL [self dismissViewControllerAnimated:YES completion:completion]; } else if (future) { [self presentViewController:future() animated:YES completion:completion]; } } - (void)presentTool:(UINavigationController *(^)(void))future completion:(void (^)(void))completion { if (self.presentedViewController) { // If a tool is already presented, dismiss it first [self dismissViewControllerAnimated:YES completion:^{ [self presentViewController:future() animated:YES completion:completion]; }]; } else if (future) { [self presentViewController:future() animated:YES completion:completion]; } } - (FLEXWindow *)window { return (id)self.view.window; } #pragma mark - Keyboard Shortcut Helpers - (void)toggleSelectTool { if (self.currentMode == FLEXExplorerModeSelect) { self.currentMode = FLEXExplorerModeDefault; } else { self.currentMode = FLEXExplorerModeSelect; } } - (void)toggleMoveTool { if (self.currentMode == FLEXExplorerModeMove) { self.currentMode = FLEXExplorerModeSelect; } else if (self.currentMode == FLEXExplorerModeSelect && self.selectedView) { self.currentMode = FLEXExplorerModeMove; } } - (void)toggleViewsTool { [self toggleViewsToolWithCompletion:nil]; } - (void)toggleViewsToolWithCompletion:(void(^)(void))completion { [self toggleToolWithViewControllerProvider:^UINavigationController *{ if (self.selectedView) { return [FLEXHierarchyViewController delegate:self viewsAtTap:self.viewsAtTapPoint selectedView:self.selectedView ]; } else { return [FLEXHierarchyViewController delegate:self]; } } completion:completion]; } - (void)toggleMenuTool { [self toggleToolWithViewControllerProvider:^UINavigationController *{ return [FLEXNavigationController withRootViewController:[FLEXGlobalsViewController new]]; } completion:nil]; } - (BOOL)handleDownArrowKeyPressed { if (self.currentMode == FLEXExplorerModeMove) { CGRect frame = self.selectedView.frame; frame.origin.y += 1.0 / UIScreen.mainScreen.scale; self.selectedView.frame = frame; } else if (self.currentMode == FLEXExplorerModeSelect && self.viewsAtTapPoint.count > 0) { NSInteger selectedViewIndex = [self.viewsAtTapPoint indexOfObject:self.selectedView]; if (selectedViewIndex > 0) { self.selectedView = [self.viewsAtTapPoint objectAtIndex:selectedViewIndex - 1]; } } else { return NO; } return YES; } - (BOOL)handleUpArrowKeyPressed { if (self.currentMode == FLEXExplorerModeMove) { CGRect frame = self.selectedView.frame; frame.origin.y -= 1.0 / UIScreen.mainScreen.scale; self.selectedView.frame = frame; } else if (self.currentMode == FLEXExplorerModeSelect && self.viewsAtTapPoint.count > 0) { NSInteger selectedViewIndex = [self.viewsAtTapPoint indexOfObject:self.selectedView]; if (selectedViewIndex < self.viewsAtTapPoint.count - 1) { self.selectedView = [self.viewsAtTapPoint objectAtIndex:selectedViewIndex + 1]; } } else { return NO; } return YES; } - (BOOL)handleRightArrowKeyPressed { if (self.currentMode == FLEXExplorerModeMove) { CGRect frame = self.selectedView.frame; frame.origin.x += 1.0 / UIScreen.mainScreen.scale; self.selectedView.frame = frame; return YES; } return NO; } - (BOOL)handleLeftArrowKeyPressed { if (self.currentMode == FLEXExplorerModeMove) { CGRect frame = self.selectedView.frame; frame.origin.x -= 1.0 / UIScreen.mainScreen.scale; self.selectedView.frame = frame; return YES; } return NO; } @end ================================================ FILE: Classes/ExplorerInterface/FLEXViewControllersViewController.h ================================================ // // FLEXViewControllersViewController.h // FLEX // // Created by Tanner Bennett on 2/13/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXFilteringTableViewController.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXViewControllersViewController : FLEXFilteringTableViewController + (instancetype)controllersForViews:(NSArray *)views; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ExplorerInterface/FLEXViewControllersViewController.m ================================================ // // FLEXViewControllersViewController.m // FLEX // // Created by Tanner Bennett on 2/13/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXViewControllersViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXMutableListSection.h" #import "FLEXUtility.h" @interface FLEXViewControllersViewController () @property (nonatomic, readonly) FLEXMutableListSection *section; @property (nonatomic, readonly) NSArray *controllers; @end @implementation FLEXViewControllersViewController @dynamic sections, allSections; #pragma mark - Initialization + (instancetype)controllersForViews:(NSArray *)views { return [[self alloc] initWithViews:views]; } - (id)initWithViews:(NSArray *)views { NSParameterAssert(views.count); self = [self initWithStyle:UITableViewStylePlain]; if (self) { _controllers = [views flex_mapped:^id(UIView *view, NSUInteger idx) { return [FLEXUtility viewControllerForView:view]; }]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.title = @"View Controllers at Tap"; self.showsSearchBar = YES; [self disableToolbar]; } - (NSArray *)makeSections { _section = [FLEXMutableListSection list:self.controllers cellConfiguration:^(UITableViewCell *cell, UIViewController *controller, NSInteger row) { cell.textLabel.text = [NSString stringWithFormat:@"%@ — %p", NSStringFromClass(controller.class), controller ]; cell.detailTextLabel.text = controller.view.description; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; } filterMatcher:^BOOL(NSString *filterText, UIViewController *controller) { return [NSStringFromClass(controller.class) localizedCaseInsensitiveContainsString:filterText]; }]; self.section.selectionHandler = ^(UIViewController *host, UIViewController *controller) { [host.navigationController pushViewController: [FLEXObjectExplorerFactory explorerViewControllerForObject:controller] animated:YES]; }; self.section.customTitle = @"View Controllers"; return @[self.section]; } #pragma mark - Private - (void)dismissAnimated { [self dismissViewControllerAnimated:YES completion:nil]; } @end ================================================ FILE: Classes/ExplorerInterface/FLEXWindow.h ================================================ // // FLEXWindow.h // Flipboard // // Created by Ryan Olson on 4/13/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @protocol FLEXWindowEventDelegate - (BOOL)shouldHandleTouchAtPoint:(CGPoint)pointInWindow; - (BOOL)canBecomeKeyWindow; @end #pragma mark - @interface FLEXWindow : UIWindow @property (nonatomic, weak) id eventDelegate; /// Tracked so we can restore the key window after dismissing a modal. /// We need to become key after modal presentation so we can correctly capture input. /// If we're just showing the toolbar, we want the main app's window to remain key /// so that we don't interfere with input, status bar, etc. @property (nonatomic, readonly) UIWindow *previousKeyWindow; @end ================================================ FILE: Classes/ExplorerInterface/FLEXWindow.m ================================================ // // FLEXWindow.m // Flipboard // // Created by Ryan Olson on 4/13/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXWindow.h" #import "FLEXUtility.h" #import @implementation FLEXWindow - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Some apps have windows at UIWindowLevelStatusBar + n. // If we make the window level too high, we block out UIAlertViews. // There's a balance between staying above the app's windows and staying below alerts. self.windowLevel = UIWindowLevelAlert - 1; } return self; } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return [self.eventDelegate shouldHandleTouchAtPoint:point]; } - (BOOL)shouldAffectStatusBarAppearance { return [self isKeyWindow]; } - (BOOL)canBecomeKeyWindow { return [self.eventDelegate canBecomeKeyWindow]; } - (void)makeKeyWindow { _previousKeyWindow = FLEXUtility.appKeyWindow; [super makeKeyWindow]; } - (void)resignKeyWindow { [super resignKeyWindow]; _previousKeyWindow = nil; } + (void)initialize { // This adds a method (superclass override) at runtime which gives us the status bar behavior we want. // The FLEX window is intended to be an overlay that generally doesn't affect the app underneath. // Most of the time, we want the app's main window(s) to be in control of status bar behavior. // Done at runtime with an obfuscated selector because it is private API. But you shouldn't ship this to the App Store anyways... NSString *canAffectSelectorString = [@[@"_can", @"Affect", @"Status", @"Bar", @"Appearance"] componentsJoinedByString:@""]; SEL canAffectSelector = NSSelectorFromString(canAffectSelectorString); Method shouldAffectMethod = class_getInstanceMethod(self, @selector(shouldAffectStatusBarAppearance)); IMP canAffectImplementation = method_getImplementation(shouldAffectMethod); class_addMethod(self, canAffectSelector, canAffectImplementation, method_getTypeEncoding(shouldAffectMethod)); // One more... NSString *canBecomeKeySelectorString = [NSString stringWithFormat:@"_%@", NSStringFromSelector(@selector(canBecomeKeyWindow))]; SEL canBecomeKeySelector = NSSelectorFromString(canBecomeKeySelectorString); Method canBecomeKeyMethod = class_getInstanceMethod(self, @selector(canBecomeKeyWindow)); IMP canBecomeKeyImplementation = method_getImplementation(canBecomeKeyMethod); class_addMethod(self, canBecomeKeySelector, canBecomeKeyImplementation, method_getTypeEncoding(canBecomeKeyMethod)); } @end ================================================ FILE: Classes/ExplorerInterface/FLEXWindowManagerController.h ================================================ // // FLEXWindowManagerController.h // FLEX // // Created by Tanner on 2/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXWindowManagerController : FLEXTableViewController @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ExplorerInterface/FLEXWindowManagerController.m ================================================ // // FLEXWindowManagerController.m // FLEX // // Created by Tanner on 2/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXWindowManagerController.h" #import "FLEXManager+Private.h" #import "FLEXUtility.h" #import "FLEXObjectExplorerFactory.h" @interface FLEXWindowManagerController () @property (nonatomic) UIWindow *keyWindow; @property (nonatomic, copy) NSString *keyWindowSubtitle; @property (nonatomic, copy) NSArray *windows; @property (nonatomic, copy) NSArray *windowSubtitles; @property (nonatomic, copy) NSArray *scenes API_AVAILABLE(ios(13)); @property (nonatomic, copy) NSArray *sceneSubtitles; @property (nonatomic, copy) NSArray *sections; @end @implementation FLEXWindowManagerController #pragma mark - Initialization - (id)init { return [self initWithStyle:UITableViewStylePlain]; } - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Windows"; if (@available(iOS 13, *)) { self.title = @"Windows and Scenes"; } [self disableToolbar]; [self reloadData]; } #pragma mark - Private - (void)reloadData { self.keyWindow = UIApplication.sharedApplication.keyWindow; self.windows = UIApplication.sharedApplication.windows; self.keyWindowSubtitle = self.windowSubtitles[[self.windows indexOfObject:self.keyWindow]]; self.windowSubtitles = [self.windows flex_mapped:^id(UIWindow *window, NSUInteger idx) { return [NSString stringWithFormat:@"Level: %@ — Root: %@", @(window.windowLevel), window.rootViewController ]; }]; if (@available(iOS 13, *)) { self.scenes = UIApplication.sharedApplication.connectedScenes.allObjects; self.sceneSubtitles = [self.scenes flex_mapped:^id(UIScene *scene, NSUInteger idx) { return [self sceneDescription:scene]; }]; self.sections = @[@[self.keyWindow], self.windows, self.scenes]; } else { self.sections = @[@[self.keyWindow], self.windows]; } [self.tableView reloadData]; } - (void)dismissAnimated { [self dismissViewControllerAnimated:YES completion:nil]; } - (void)showRevertOrDismissAlert:(void(^)(void))revertBlock { [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES]; [self reloadData]; [self.tableView reloadData]; UIWindow *highestWindow = UIApplication.sharedApplication.keyWindow; UIWindowLevel maxLevel = 0; for (UIWindow *window in UIApplication.sharedApplication.windows) { if (window.windowLevel > maxLevel) { maxLevel = window.windowLevel; highestWindow = window; } } [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Keep Changes?"); make.message(@"If you do not wish to keep these settings, choose 'Revert Changes' below."); make.button(@"Keep Changes").destructiveStyle(); make.button(@"Keep Changes and Dismiss").destructiveStyle().handler(^(NSArray *strings) { [self dismissAnimated]; }); make.button(@"Revert Changes").cancelStyle().handler(^(NSArray *strings) { revertBlock(); [self reloadData]; [self.tableView reloadData]; }); } showFrom:[FLEXUtility topViewControllerInWindow:highestWindow]]; } - (NSString *)sceneDescription:(UIScene *)scene API_AVAILABLE(ios(13)) { NSString *state = [self stringFromSceneState:scene.activationState]; NSString *title = scene.title.length ? scene.title : nil; NSString *suffix = nil; if ([scene isKindOfClass:[UIWindowScene class]]) { UIWindowScene *windowScene = (id)scene; suffix = FLEXPluralString(windowScene.windows.count, @"windows", @"window"); } NSMutableString *description = state.mutableCopy; if (title) { [description appendFormat:@" — %@", title]; } if (suffix) { [description appendFormat:@" — %@", suffix]; } return description.copy; } - (NSString *)stringFromSceneState:(UISceneActivationState)state API_AVAILABLE(ios(13)) { switch (state) { case UISceneActivationStateUnattached: return @"Unattached"; case UISceneActivationStateForegroundActive: return @"Active"; case UISceneActivationStateForegroundInactive: return @"Inactive"; case UISceneActivationStateBackground: return @"Backgrounded"; } return [NSString stringWithFormat:@"Unknown state: %@", @(state)]; } #pragma mark - Table View Data Source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return self.sections.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.sections[section].count; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { switch (section) { case 0: return @"Key Window"; case 1: return @"Windows"; case 2: return @"Connected Scenes"; } return nil; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath]; cell.accessoryType = UITableViewCellAccessoryDetailButton; cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; UIWindow *window = nil; NSString *subtitle = nil; switch (indexPath.section) { case 0: window = self.keyWindow; subtitle = self.keyWindowSubtitle; break; case 1: window = self.windows[indexPath.row]; subtitle = self.windowSubtitles[indexPath.row]; break; case 2: if (@available(iOS 13, *)) { UIScene *scene = self.scenes[indexPath.row]; cell.textLabel.text = scene.description; cell.detailTextLabel.text = self.sceneSubtitles[indexPath.row]; return cell; } } cell.textLabel.text = window.description; cell.detailTextLabel.text = [NSString stringWithFormat:@"Level: %@ — Root: %@", @((NSInteger)window.windowLevel), window.rootViewController.class ]; return cell; } #pragma mark - Table View Delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIWindow *window = nil; NSString *subtitle = nil; FLEXWindow *flex = FLEXManager.sharedManager.explorerWindow; id cancelHandler = ^{ [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES]; }; switch (indexPath.section) { case 0: window = self.keyWindow; subtitle = self.keyWindowSubtitle; break; case 1: window = self.windows[indexPath.row]; subtitle = self.windowSubtitles[indexPath.row]; break; case 2: if (@available(iOS 13, *)) { UIScene *scene = self.scenes[indexPath.row]; UIWindowScene *oldScene = flex.windowScene; BOOL isWindowScene = [scene isKindOfClass:[UIWindowScene class]]; BOOL isFLEXScene = isWindowScene ? flex.windowScene == scene : NO; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(NSStringFromClass(scene.class)); if (isWindowScene) { if (isFLEXScene) { make.message(@"Already the FLEX window scene"); } make.button(@"Set as FLEX Window Scene") .handler(^(NSArray *strings) { flex.windowScene = (id)scene; [self showRevertOrDismissAlert:^{ flex.windowScene = oldScene; }]; }).enabled(!isFLEXScene); make.button(@"Cancel").cancelStyle(); } else { make.message(@"Not a UIWindowScene"); make.button(@"Dismiss").cancelStyle().handler(cancelHandler); } } showFrom:self]; } } __block UIWindow *targetWindow = nil, *oldKeyWindow = nil; __block UIWindowLevel oldLevel; __block BOOL wasVisible; subtitle = [subtitle stringByAppendingString: @"\n\n1) Adjust the FLEX window level relative to this window,\n" "2) adjust this window's level relative to the FLEX window,\n" "3) set this window's level to a specific value, or\n" "4) make this window the key window if it isn't already." ]; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(NSStringFromClass(window.class)).message(subtitle); make.button(@"Adjust FLEX Window Level").handler(^(NSArray *strings) { targetWindow = flex; oldLevel = flex.windowLevel; flex.windowLevel = window.windowLevel + strings.firstObject.integerValue; [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }]; }); make.button(@"Adjust This Window's Level").handler(^(NSArray *strings) { targetWindow = window; oldLevel = window.windowLevel; window.windowLevel = flex.windowLevel + strings.firstObject.integerValue; [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }]; }); make.button(@"Set This Window's Level").handler(^(NSArray *strings) { targetWindow = window; oldLevel = window.windowLevel; window.windowLevel = strings.firstObject.integerValue; [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }]; }); make.button(@"Make Key And Visible").handler(^(NSArray *strings) { oldKeyWindow = UIApplication.sharedApplication.keyWindow; wasVisible = window.hidden; [window makeKeyAndVisible]; [self showRevertOrDismissAlert:^{ window.hidden = wasVisible; [oldKeyWindow makeKeyWindow]; }]; }).enabled(!window.isKeyWindow && !window.hidden); make.button(@"Cancel").cancelStyle().handler(cancelHandler); make.textField(@"+/- window level, i.e. 5 or -10"); } showFrom:self]; } - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)ip { [self.navigationController pushViewController: [FLEXObjectExplorerFactory explorerViewControllerForObject:self.sections[ip.section][ip.row]] animated:YES]; } @end ================================================ FILE: Classes/ExplorerInterface/Tabs/FLEXTabList.h ================================================ // // FLEXTabList.h // FLEX // // Created by Tanner on 2/1/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface FLEXTabList : NSObject @property (nonatomic, readonly, class) FLEXTabList *sharedList; @property (nonatomic, readonly, nullable) UINavigationController *activeTab; @property (nonatomic, readonly) NSArray *openTabs; /// Snapshots of each tab when they were last active. @property (nonatomic, readonly) NSArray *openTabSnapshots; /// \c NSNotFound if no tabs are present. /// Setting this property changes the active tab to one of the already open tabs. @property (nonatomic) NSInteger activeTabIndex; /// Adds a new tab and sets the new tab as the active tab. - (void)addTab:(UINavigationController *)newTab; /// Closes the given tab. If this tab was the active tab, /// the most recent tab before that becomes the active tab. - (void)closeTab:(UINavigationController *)tab; /// Closes a tab at the given index. If this tab was the active tab, /// the most recent tab before that becomes the active tab. - (void)closeTabAtIndex:(NSInteger)idx; /// Closes all of the tabs at the given indexes. If the active tab /// is included, the most recent still-open tab becomes the active tab. - (void)closeTabsAtIndexes:(NSIndexSet *)indexes; /// A shortcut to close the active tab. - (void)closeActiveTab; /// A shortcut to close \e every tab. - (void)closeAllTabs; - (void)updateSnapshotForActiveTab; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ExplorerInterface/Tabs/FLEXTabList.m ================================================ // // FLEXTabList.m // FLEX // // Created by Tanner on 2/1/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTabList.h" #import "FLEXUtility.h" @interface FLEXTabList () { NSMutableArray *_openTabs; NSMutableArray *_openTabSnapshots; } @end #pragma mark - @implementation FLEXTabList #pragma mark Initialization + (FLEXTabList *)sharedList { static FLEXTabList *sharedList = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedList = [self new]; }); return sharedList; } - (id)init { self = [super init]; if (self) { _openTabs = [NSMutableArray new]; _openTabSnapshots = [NSMutableArray new]; _activeTabIndex = NSNotFound; } return self; } #pragma mark Private - (void)chooseNewActiveTab { if (self.openTabs.count) { self.activeTabIndex = self.openTabs.count - 1; } else { self.activeTabIndex = NSNotFound; } } #pragma mark Public - (void)setActiveTabIndex:(NSInteger)idx { NSParameterAssert(idx < self.openTabs.count || idx == NSNotFound); if (_activeTabIndex == idx) return; _activeTabIndex = idx; _activeTab = (idx == NSNotFound) ? nil : self.openTabs[idx]; } - (void)addTab:(UINavigationController *)newTab { NSParameterAssert(newTab); // Update snapshot of the last active tab if (self.activeTab) { [self updateSnapshotForActiveTab]; } // Add new tab and snapshot, // update active tab and index [_openTabs addObject:newTab]; [_openTabSnapshots addObject:[FLEXUtility previewImageForView:newTab.view]]; _activeTab = newTab; _activeTabIndex = self.openTabs.count - 1; } - (void)closeTab:(UINavigationController *)tab { NSParameterAssert(tab); NSInteger idx = [self.openTabs indexOfObject:tab]; if (idx != NSNotFound) { [self closeTabAtIndex:idx]; } // Not sure how this is possible, but it happens sometimes if (self.activeTab == tab) { [self chooseNewActiveTab]; } // It is possible for an object explorer to form a retain cycle // with its own navigation controller; clearing the view controllers // manually when closing a tab breaks the cycle tab.viewControllers = @[]; } - (void)closeTabAtIndex:(NSInteger)idx { NSParameterAssert(idx < self.openTabs.count); // Remove old tab and snapshot [_openTabs removeObjectAtIndex:idx]; [_openTabSnapshots removeObjectAtIndex:idx]; // Update active tab and index if needed if (self.activeTabIndex == idx) { [self chooseNewActiveTab]; } } - (void)closeTabsAtIndexes:(NSIndexSet *)indexes { // Remove old tabs and snapshot [_openTabs removeObjectsAtIndexes:indexes]; [_openTabSnapshots removeObjectsAtIndexes:indexes]; // Update active tab and index if needed if ([indexes containsIndex:self.activeTabIndex]) { [self chooseNewActiveTab]; } } - (void)closeActiveTab { [self closeTab:self.activeTab]; } - (void)closeAllTabs { // Remove tabs and snapshots [_openTabs removeAllObjects]; [_openTabSnapshots removeAllObjects]; // Update active tab index self.activeTabIndex = NSNotFound; } - (void)updateSnapshotForActiveTab { if (self.activeTabIndex != NSNotFound) { UIImage *newSnapshot = [FLEXUtility previewImageForView:self.activeTab.view]; [_openTabSnapshots replaceObjectAtIndex:self.activeTabIndex withObject:newSnapshot]; } } @end ================================================ FILE: Classes/ExplorerInterface/Tabs/FLEXTabsViewController.h ================================================ // // FLEXTabsViewController.h // FLEX // // Created by Tanner on 2/4/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" @interface FLEXTabsViewController : FLEXTableViewController @end ================================================ FILE: Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m ================================================ // // FLEXTabsViewController.m // FLEX // // Created by Tanner on 2/4/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTabsViewController.h" #import "FLEXNavigationController.h" #import "FLEXTabList.h" #import "FLEXBookmarkManager.h" #import "FLEXTableView.h" #import "FLEXUtility.h" #import "FLEXColor.h" #import "UIBarButtonItem+FLEX.h" #import "FLEXExplorerViewController.h" #import "FLEXGlobalsViewController.h" #import "FLEXBookmarksViewController.h" @interface FLEXTabsViewController () @property (nonatomic, copy) NSArray *openTabs; @property (nonatomic, copy) NSArray *tabSnapshots; @property (nonatomic) NSInteger activeIndex; @property (nonatomic) BOOL presentNewActiveTabOnDismiss; @property (nonatomic, readonly) FLEXExplorerViewController *corePresenter; @end @implementation FLEXTabsViewController #pragma mark - Initialization - (id)init { return [self initWithStyle:UITableViewStylePlain]; } - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Open Tabs"; self.navigationController.hidesBarsOnSwipe = NO; self.tableView.allowsMultipleSelectionDuringEditing = YES; [self reloadData:NO]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self setupDefaultBarItems]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Instead of updating the active snapshot before we present, // we update it after we present to avoid pre-presenation latency dispatch_async(dispatch_get_main_queue(), ^{ [FLEXTabList.sharedList updateSnapshotForActiveTab]; [self reloadData:NO]; [self.tableView reloadData]; }); } #pragma mark - Private /// @param trackActiveTabDelta whether to check if the active /// tab changed and needs to be presented upon "Done" dismissal. /// @return whether the active tab changed or not (if there are any tabs left) - (BOOL)reloadData:(BOOL)trackActiveTabDelta { BOOL activeTabDidChange = NO; FLEXTabList *list = FLEXTabList.sharedList; // Flag to enable check to determine whether if (trackActiveTabDelta) { NSInteger oldActiveIndex = self.activeIndex; if (oldActiveIndex != list.activeTabIndex && list.activeTabIndex != NSNotFound) { self.presentNewActiveTabOnDismiss = YES; activeTabDidChange = YES; } else if (self.presentNewActiveTabOnDismiss) { // If we had something to present before, now we don't // (i.e. activeTabIndex == NSNotFound) self.presentNewActiveTabOnDismiss = NO; } } // We assume the tabs aren't going to change out from under us, since // presenting any other tool via keyboard shortcuts should dismiss us first self.openTabs = list.openTabs; self.tabSnapshots = list.openTabSnapshots; self.activeIndex = list.activeTabIndex; return activeTabDidChange; } - (void)reloadActiveTabRowIfChanged:(BOOL)activeTabChanged { // Refresh the newly active tab row if needed if (activeTabChanged) { NSIndexPath *active = [NSIndexPath indexPathForRow:self.activeIndex inSection:0 ]; [self.tableView reloadRowsAtIndexPaths:@[active] withRowAnimation:UITableViewRowAnimationNone]; } } - (void)setupDefaultBarItems { self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated)); self.toolbarItems = @[ UIBarButtonItem.flex_fixedSpace, UIBarButtonItem.flex_flexibleSpace, FLEXBarButtonItemSystem(Add, self, @selector(addTabButtonPressed:)), UIBarButtonItem.flex_flexibleSpace, FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)), ]; // Disable editing if no tabs available self.toolbarItems.lastObject.enabled = self.openTabs.count > 0; } - (void)setupEditingBarItems { self.navigationItem.rightBarButtonItem = nil; self.toolbarItems = @[ [UIBarButtonItem flex_itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)], UIBarButtonItem.flex_flexibleSpace, [UIBarButtonItem flex_disabledSystemItem:UIBarButtonSystemItemAdd], UIBarButtonItem.flex_flexibleSpace, // We use a non-system done item because we change its title dynamically [UIBarButtonItem flex_doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)] ]; self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor; } - (FLEXExplorerViewController *)corePresenter { // We must be presented by a FLEXExplorerViewController, or presented // by another view controller that was presented by FLEXExplorerViewController FLEXExplorerViewController *presenter = (id)self.presentingViewController; presenter = (id)presenter.presentingViewController ?: presenter; NSAssert( [presenter isKindOfClass:[FLEXExplorerViewController class]], @"The tabs view controller expects to be presented by the explorer controller" ); return presenter; } #pragma mark Button Actions - (void)dismissAnimated { if (self.presentNewActiveTabOnDismiss) { // The active tab was closed so we need to present the new one UIViewController *activeTab = FLEXTabList.sharedList.activeTab; FLEXExplorerViewController *presenter = self.corePresenter; [presenter dismissViewControllerAnimated:YES completion:^{ [presenter presentViewController:activeTab animated:YES completion:nil]; }]; } else if (self.activeIndex == NSNotFound) { // The only tab was closed, so dismiss everything [self.corePresenter dismissViewControllerAnimated:YES completion:nil]; } else { // Simple dismiss with the same active tab, only dismiss myself [self dismissViewControllerAnimated:YES completion:nil]; } } - (void)toggleEditing { NSArray *selected = self.tableView.indexPathsForSelectedRows; self.editing = !self.editing; if (self.isEditing) { [self setupEditingBarItems]; } else { [self setupDefaultBarItems]; // Get index set of tabs to close NSMutableIndexSet *indexes = [NSMutableIndexSet new]; for (NSIndexPath *ip in selected) { [indexes addIndex:ip.row]; } if (selected.count) { // Close tabs and update data source [FLEXTabList.sharedList closeTabsAtIndexes:indexes]; BOOL activeTabChanged = [self reloadData:YES]; // Remove deleted rows [self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic]; // Refresh the newly active tab row if needed [self reloadActiveTabRowIfChanged:activeTabChanged]; } } } - (void)addTabButtonPressed:(UIBarButtonItem *)sender { if (FLEXBookmarkManager.bookmarks.count) { [FLEXAlert makeSheet:^(FLEXAlert *make) { make.title(@"New Tab"); make.button(@"Main Menu").handler(^(NSArray *strings) { [self addTabAndDismiss:[FLEXNavigationController withRootViewController:[FLEXGlobalsViewController new] ]]; }); make.button(@"Choose from Bookmarks").handler(^(NSArray *strings) { [self presentViewController:[FLEXNavigationController withRootViewController:[FLEXBookmarksViewController new] ] animated:YES completion:nil]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } else { // No bookmarks, just open the main menu [self addTabAndDismiss:[FLEXNavigationController withRootViewController:[FLEXGlobalsViewController new] ]]; } } - (void)addTabAndDismiss:(UINavigationController *)newTab { FLEXExplorerViewController *presenter = self.corePresenter; [presenter dismissViewControllerAnimated:YES completion:^{ [presenter presentViewController:newTab animated:YES completion:nil]; }]; } - (void)closeAllButtonPressed:(UIBarButtonItem *)sender { [FLEXAlert makeSheet:^(FLEXAlert *make) { NSInteger count = self.openTabs.count; NSString *title = FLEXPluralFormatString(count, @"Close %@ tabs", @"Close %@ tab"); make.button(title).destructiveStyle().handler(^(NSArray *strings) { [self closeAll]; [self toggleEditing]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } - (void)closeAll { NSInteger rowCount = self.openTabs.count; // Close tabs and update data source [FLEXTabList.sharedList closeAllTabs]; [self reloadData:YES]; // Delete rows from table view NSArray *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) { return [NSIndexPath indexPathForRow:row inSection:0]; }]; [self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic]; } #pragma mark - Table View Data Source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.openTabs.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath]; UINavigationController *tab = self.openTabs[indexPath.row]; cell.imageView.image = self.tabSnapshots[indexPath.row]; cell.textLabel.text = tab.topViewController.title; cell.detailTextLabel.text = FLEXPluralString(tab.viewControllers.count, @"pages", @"page"); if (!cell.tag) { cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; cell.detailTextLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]; cell.tag = 1; } if (indexPath.row == self.activeIndex) { cell.backgroundColor = FLEXColor.secondaryBackgroundColor; } else { cell.backgroundColor = FLEXColor.primaryBackgroundColor; } return cell; } #pragma mark - Table View Delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (self.editing) { // Case: editing with multi-select self.toolbarItems.lastObject.title = @"Close Selected"; self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor; } else { if (self.activeIndex == indexPath.row && self.corePresenter != self.presentingViewController) { // Case: selected the already active tab [self dismissAnimated]; } else { // Case: selected a different tab, // or selected a tab when presented from the FLEX toolbar FLEXTabList.sharedList.activeTabIndex = indexPath.row; self.presentNewActiveTabOnDismiss = YES; [self dismissAnimated]; } } } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { NSParameterAssert(self.editing); if (tableView.indexPathsForSelectedRows.count == 0) { self.toolbarItems.lastObject.title = @"Done"; self.toolbarItems.lastObject.tintColor = self.view.tintColor; } } - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (void)tableView:(UITableView *)table commitEditingStyle:(UITableViewCellEditingStyle)edit forRowAtIndexPath:(NSIndexPath *)indexPath { NSParameterAssert(edit == UITableViewCellEditingStyleDelete); // Close tab and update data source [FLEXTabList.sharedList closeTab:self.openTabs[indexPath.row]]; BOOL activeTabChanged = [self reloadData:YES]; // Delete row from table view [table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; // Refresh the newly active tab row if needed [self reloadActiveTabRowIfChanged:activeTabChanged]; } @end ================================================ FILE: Classes/FLEX-Categories.h ================================================ // // FLEX-Categories.h // FLEX // // Created by Tanner on 3/12/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIBarButtonItem+FLEX.h" #import "CALayer+FLEX.h" #import "UIFont+FLEX.h" #import "UIGestureRecognizer+Blocks.h" #import "UIPasteboard+FLEX.h" #import "UIMenu+FLEX.h" #import "UITextField+Range.h" #import "NSObject+FLEX_Reflection.h" #import "NSArray+FLEX.h" #import "NSUserDefaults+FLEX.h" #import "NSTimer+FLEX.h" #import "NSDateFormatter+FLEX.h" ================================================ FILE: Classes/FLEX-Core.h ================================================ // // FLEX-Core.h // FLEX // // Created by Tanner on 3/11/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXFilteringTableViewController.h" #import "FLEXNavigationController.h" #import "FLEXTableViewController.h" #import "FLEXTableView.h" #import "FLEXSingleRowSection.h" #import "FLEXTableViewSection.h" #import "FLEXCodeFontCell.h" #import "FLEXSubtitleTableViewCell.h" #import "FLEXTableViewCell.h" #import "FLEXMultilineTableViewCell.h" #import "FLEXKeyValueTableViewCell.h" ================================================ FILE: Classes/FLEX-ObjectExploring.h ================================================ // // FLEX-ObjectExploring.h // FLEX // // Created by Tanner on 3/11/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXObjectExplorerFactory.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXObjectExplorer.h" #import "FLEXShortcut.h" #import "FLEXShortcutsSection.h" #import "FLEXCollectionContentSection.h" #import "FLEXColorPreviewSection.h" #import "FLEXDefaultsContentSection.h" #import "FLEXMetadataSection.h" #import "FLEXMutableListSection.h" #import "FLEXObjectInfoSection.h" ================================================ FILE: Classes/FLEX-Runtime.h ================================================ // // FLEX-Runtime.h // FLEX // // Created by Tanner on 3/11/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXObjcInternal.h" #import "FLEXSwiftInternal.h" #import "FLEXRuntimeSafety.h" #import "FLEXBlockDescription.h" #import "FLEXTypeEncodingParser.h" #import "FLEXMirror.h" #import "FLEXProtocol.h" #import "FLEXProperty.h" #import "FLEXIvar.h" #import "FLEXMethodBase.h" #import "FLEXMethod.h" #import "FLEXPropertyAttributes.h" #import "FLEXRuntime+Compare.h" #import "FLEXRuntime+UIKitHelpers.h" #import "FLEXMetadataExtras.h" #import "FLEXProtocolBuilder.h" #import "FLEXClassBuilder.h" ================================================ FILE: Classes/FLEX.h ================================================ // // FLEX.h // FLEX // // Created by Eric Horacek on 7/18/15. // Modified by Tanner Bennett on 3/12/20. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXManager.h" #import "FLEXManager+Extensibility.h" #import "FLEXManager+Networking.h" #import "FLEXExplorerToolbar.h" #import "FLEXExplorerToolbarItem.h" #import "FLEXGlobalsEntry.h" #import "FLEX-Core.h" #import "FLEX-Runtime.h" #import "FLEX-Categories.h" #import "FLEX-ObjectExploring.h" #import "FLEXMacros.h" #import "FLEXAlert.h" #import "FLEXResources.h" ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.h ================================================ // // FLEXDBQueryRowCell.h // FLEX // // Created by Peng Tao on 15/11/24. // Copyright © 2015年 f. All rights reserved. // #import @class FLEXDBQueryRowCell; extern NSString * const kFLEXDBQueryRowCellReuse; @protocol FLEXDBQueryRowCellLayoutSource - (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell minXForColumn:(NSUInteger)column; - (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell widthForColumn:(NSUInteger)column; @end @interface FLEXDBQueryRowCell : UITableViewCell /// An array of NSString, NSNumber, or NSData objects @property (nonatomic) NSArray *data; @property (nonatomic, weak) id layoutSource; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.m ================================================ // // FLEXDBQueryRowCell.m // FLEX // // Created by Peng Tao on 15/11/24. // Copyright © 2015年 f. All rights reserved. // #import "FLEXDBQueryRowCell.h" #import "FLEXMultiColumnTableView.h" #import "NSArray+FLEX.h" #import "UIFont+FLEX.h" #import "FLEXColor.h" NSString * const kFLEXDBQueryRowCellReuse = @"kFLEXDBQueryRowCellReuse"; @interface FLEXDBQueryRowCell () @property (nonatomic) NSInteger columnCount; @property (nonatomic) NSArray *labels; @end @implementation FLEXDBQueryRowCell - (void)setData:(NSArray *)data { _data = data; self.columnCount = data.count; [self.labels flex_forEach:^(UILabel *label, NSUInteger idx) { id content = self.data[idx]; if ([content isKindOfClass:[NSString class]]) { label.text = content; } else if (content == NSNull.null) { label.text = @""; label.textColor = FLEXColor.deemphasizedTextColor; } else { label.text = [content description]; } }]; } - (void)setColumnCount:(NSInteger)columnCount { if (columnCount != _columnCount) { _columnCount = columnCount; // Remove existing labels for (UILabel *l in self.labels) { [l removeFromSuperview]; } // Create new labels self.labels = [NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) { UILabel *label = [UILabel new]; label.font = UIFont.flex_defaultTableCellFont; label.textAlignment = NSTextAlignmentLeft; [self.contentView addSubview:label]; return label; }]; } } - (void)layoutSubviews { [super layoutSubviews]; CGFloat height = self.contentView.frame.size.height; [self.labels flex_forEach:^(UILabel *label, NSUInteger i) { CGFloat width = [self.layoutSource dbQueryRowCell:self widthForColumn:i]; CGFloat minX = [self.layoutSource dbQueryRowCell:self minXForColumn:i]; label.frame = CGRectMake(minX + 5, 0, (width - 10), height); }]; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h ================================================ // // PTDatabaseManager.h // Derived from: // // FMDatabase.h // FMDB( https://github.com/ccgus/fmdb ) // // Created by Peng Tao on 15/11/23. // // Licensed to Flying Meat Inc. under one or more contributor license agreements. // See the LICENSE file distributed with this work for the terms under // which Flying Meat Inc. licenses this file to you. #import #import "FLEXSQLResult.h" /// Conformers should automatically open and close the database @protocol FLEXDatabaseManager @required /// @return \c nil if the database couldn't be opened + (instancetype)managerForDatabase:(NSString *)path; /// @return a list of all table names - (NSArray *)queryAllTables; - (NSArray *)queryAllColumnsOfTable:(NSString *)tableName; - (NSArray *)queryAllDataInTable:(NSString *)tableName; @optional - (NSArray *)queryRowIDsInTable:(NSString *)tableName; - (FLEXSQLResult *)executeStatement:(NSString *)SQLStatement; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXMultiColumnTableView.h ================================================ // // PTMultiColumnTableView.h // PTMultiColumnTableViewDemo // // Created by Peng Tao on 15/11/16. // Copyright © 2015年 Peng Tao. All rights reserved. // #import #import "FLEXTableColumnHeader.h" @class FLEXMultiColumnTableView; @protocol FLEXMultiColumnTableViewDelegate @required - (void)multiColumnTableView:(FLEXMultiColumnTableView *)tableView didSelectRow:(NSInteger)row; - (void)multiColumnTableView:(FLEXMultiColumnTableView *)tableView didSelectHeaderForColumn:(NSInteger)column sortType:(FLEXTableColumnHeaderSortType)sortType; @end @protocol FLEXMultiColumnTableViewDataSource @required - (NSInteger)numberOfColumnsInTableView:(FLEXMultiColumnTableView *)tableView; - (NSInteger)numberOfRowsInTableView:(FLEXMultiColumnTableView *)tableView; - (NSString *)columnTitle:(NSInteger)column; - (NSString *)rowTitle:(NSInteger)row; - (NSArray *)contentForRow:(NSInteger)row; - (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView minWidthForContentCellInColumn:(NSInteger)column; - (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView heightForContentCellInRow:(NSInteger)row; - (CGFloat)heightForTopHeaderInTableView:(FLEXMultiColumnTableView *)tableView; - (CGFloat)widthForLeftHeaderInTableView:(FLEXMultiColumnTableView *)tableView; @end @interface FLEXMultiColumnTableView : UIView @property (nonatomic, weak) id dataSource; @property (nonatomic, weak) id delegate; - (void)reloadData; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXMultiColumnTableView.m ================================================ // // PTMultiColumnTableView.m // PTMultiColumnTableViewDemo // // Created by Peng Tao on 15/11/16. // Copyright © 2015年 Peng Tao. All rights reserved. // #import "FLEXMultiColumnTableView.h" #import "FLEXDBQueryRowCell.h" #import "FLEXTableLeftCell.h" #import "NSArray+FLEX.h" #import "FLEXColor.h" @interface FLEXMultiColumnTableView () < UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, FLEXDBQueryRowCellLayoutSource > @property (nonatomic) UIScrollView *contentScrollView; @property (nonatomic) UIScrollView *headerScrollView; @property (nonatomic) UITableView *leftTableView; @property (nonatomic) UITableView *contentTableView; @property (nonatomic) UIView *leftHeader; @property (nonatomic) NSArray *headerViews; /// \c NSNotFound if no column selected @property (nonatomic) NSInteger sortColumn; @property (nonatomic) FLEXTableColumnHeaderSortType sortType; @property (nonatomic, readonly) NSInteger numberOfColumns; @property (nonatomic, readonly) NSInteger numberOfRows; @property (nonatomic, readonly) CGFloat topHeaderHeight; @property (nonatomic, readonly) CGFloat leftHeaderWidth; @property (nonatomic, readonly) CGFloat columnMargin; @end static const CGFloat kColumnMargin = 1; @implementation FLEXMultiColumnTableView #pragma mark - Initialization - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.autoresizingMask |= UIViewAutoresizingFlexibleWidth; self.autoresizingMask |= UIViewAutoresizingFlexibleHeight; self.autoresizingMask |= UIViewAutoresizingFlexibleTopMargin; self.backgroundColor = FLEXColor.groupedBackgroundColor; [self loadHeaderScrollView]; [self loadContentScrollView]; [self loadLeftView]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat width = self.frame.size.width; CGFloat height = self.frame.size.height; CGFloat topheaderHeight = self.topHeaderHeight; CGFloat leftHeaderWidth = self.leftHeaderWidth; CGFloat topInsets = 0.f; if (@available (iOS 11.0, *)) { topInsets = self.safeAreaInsets.top; } CGFloat contentWidth = 0.0; NSInteger columnsCount = self.numberOfColumns; for (int i = 0; i < columnsCount; i++) { contentWidth += CGRectGetWidth(self.headerViews[i].bounds); } CGFloat contentHeight = height - topheaderHeight - topInsets; self.leftHeader.frame = CGRectMake(0, topInsets, self.leftHeaderWidth, self.topHeaderHeight); self.leftTableView.frame = CGRectMake( 0, topheaderHeight + topInsets, leftHeaderWidth, contentHeight ); self.headerScrollView.frame = CGRectMake( leftHeaderWidth, topInsets, width - leftHeaderWidth, topheaderHeight ); self.headerScrollView.contentSize = CGSizeMake( self.contentTableView.frame.size.width, self.headerScrollView.frame.size.height ); self.contentTableView.frame = CGRectMake( 0, 0, contentWidth + self.numberOfColumns * self.columnMargin , contentHeight ); self.contentScrollView.frame = CGRectMake( leftHeaderWidth, topheaderHeight + topInsets, width - leftHeaderWidth, contentHeight ); self.contentScrollView.contentSize = self.contentTableView.frame.size; } #pragma mark - UI - (void)loadHeaderScrollView { UIScrollView *headerScrollView = [UIScrollView new]; headerScrollView.delegate = self; headerScrollView.backgroundColor = FLEXColor.secondaryGroupedBackgroundColor; self.headerScrollView = headerScrollView; [self addSubview:headerScrollView]; } - (void)loadContentScrollView { UIScrollView *scrollView = [UIScrollView new]; scrollView.bounces = NO; scrollView.delegate = self; UITableView *tableView = [UITableView new]; tableView.delegate = self; tableView.dataSource = self; tableView.separatorStyle = UITableViewCellSeparatorStyleNone; [tableView registerClass:[FLEXDBQueryRowCell class] forCellReuseIdentifier:kFLEXDBQueryRowCellReuse ]; [scrollView addSubview:tableView]; [self addSubview:scrollView]; self.contentScrollView = scrollView; self.contentTableView = tableView; } - (void)loadLeftView { UITableView *leftTableView = [UITableView new]; leftTableView.delegate = self; leftTableView.dataSource = self; leftTableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.leftTableView = leftTableView; [self addSubview:leftTableView]; UIView *leftHeader = [UIView new]; leftHeader.backgroundColor = FLEXColor.secondaryBackgroundColor; self.leftHeader = leftHeader; [self addSubview:leftHeader]; } #pragma mark - Data - (void)reloadData { [self loadHeaderData]; [self loadLeftViewData]; [self loadContentData]; } - (void)loadHeaderData { // Remove existing headers, if any for (UIView *subview in self.headerViews) { [subview removeFromSuperview]; } __block CGFloat xOffset = 0; self.headerViews = [NSArray flex_forEachUpTo:self.numberOfColumns map:^id(NSUInteger column) { FLEXTableColumnHeader *header = [FLEXTableColumnHeader new]; header.titleLabel.text = [self columnTitle:column]; CGSize fittingSize = CGSizeMake(CGFLOAT_MAX, self.topHeaderHeight - 1); CGFloat width = self.columnMargin + MAX( [self minContentWidthForColumn:column], [header sizeThatFits:fittingSize].width ); header.frame = CGRectMake(xOffset, 0, width, self.topHeaderHeight - 1); if (column == self.sortColumn) { header.sortType = self.sortType; } // Header tap gesture UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(contentHeaderTap:) ]; [header addGestureRecognizer:gesture]; header.userInteractionEnabled = YES; xOffset += width; [self.headerScrollView addSubview:header]; return header; }]; } - (void)contentHeaderTap:(UIGestureRecognizer *)gesture { NSInteger newSortColumn = [self.headerViews indexOfObject:gesture.view]; FLEXTableColumnHeaderSortType newType = FLEXNextTableColumnHeaderSortType(self.sortType); // Reset old header FLEXTableColumnHeader *oldHeader = (id)self.headerViews[self.sortColumn]; oldHeader.sortType = FLEXTableColumnHeaderSortTypeNone; // Update new header FLEXTableColumnHeader *newHeader = (id)self.headerViews[newSortColumn]; newHeader.sortType = newType; // Update self self.sortColumn = newSortColumn; self.sortType = newType; // Notify delegate [self.delegate multiColumnTableView:self didSelectHeaderForColumn:newSortColumn sortType:newType]; } - (void)loadContentData { [self.contentTableView reloadData]; } - (void)loadLeftViewData { [self.leftTableView reloadData]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Alternating background color UIColor *backgroundColor = FLEXColor.primaryBackgroundColor; if (indexPath.row % 2 != 0) { backgroundColor = FLEXColor.secondaryBackgroundColor; } // Left side table view for row numbers if (tableView == self.leftTableView) { FLEXTableLeftCell *cell = [FLEXTableLeftCell cellWithTableView:tableView]; cell.contentView.backgroundColor = backgroundColor; cell.titlelabel.text = [self rowTitle:indexPath.row]; return cell; } // Right side table view for data else { FLEXDBQueryRowCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDBQueryRowCellReuse forIndexPath:indexPath ]; cell.contentView.backgroundColor = backgroundColor; cell.data = [self.dataSource contentForRow:indexPath.row]; cell.layoutSource = self; NSAssert(cell.data.count == self.numberOfColumns, @"Count of data provided was incorrect"); return cell; } } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.dataSource numberOfRowsInTableView:self]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [self.dataSource multiColumnTableView:self heightForContentCellInRow:indexPath.row]; } // Scroll all scroll views in sync - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.contentScrollView) { self.headerScrollView.contentOffset = scrollView.contentOffset; } else if (scrollView == self.headerScrollView) { self.contentScrollView.contentOffset = scrollView.contentOffset; } else if (scrollView == self.leftTableView) { self.contentTableView.contentOffset = scrollView.contentOffset; } else if (scrollView == self.contentTableView) { self.leftTableView.contentOffset = scrollView.contentOffset; } } #pragma mark UITableView Delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.leftTableView) { [self.contentTableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone ]; } else if (tableView == self.contentTableView) { [self.delegate multiColumnTableView:self didSelectRow:indexPath.row]; } } #pragma mark FLEXDBQueryRowCellLayoutSource - (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell minXForColumn:(NSUInteger)column { return CGRectGetMinX(self.headerViews[column].frame); } - (CGFloat)dbQueryRowCell:(FLEXDBQueryRowCell *)dbQueryRowCell widthForColumn:(NSUInteger)column { return CGRectGetWidth(self.headerViews[column].bounds); } #pragma mark DataSource Accessor - (NSInteger)numberOfRows { return [self.dataSource numberOfRowsInTableView:self]; } - (NSInteger)numberOfColumns { return [self.dataSource numberOfColumnsInTableView:self]; } - (NSString *)columnTitle:(NSInteger)column { return [self.dataSource columnTitle:column]; } - (NSString *)rowTitle:(NSInteger)row { return [self.dataSource rowTitle:row]; } - (CGFloat)minContentWidthForColumn:(NSInteger)column { return [self.dataSource multiColumnTableView:self minWidthForContentCellInColumn:column]; } - (CGFloat)contentHeightForRow:(NSInteger)row { return [self.dataSource multiColumnTableView:self heightForContentCellInRow:row]; } - (CGFloat)topHeaderHeight { return [self.dataSource heightForTopHeaderInTableView:self]; } - (CGFloat)leftHeaderWidth { return [self.dataSource widthForLeftHeaderInTableView:self]; } - (CGFloat)columnMargin { return kColumnMargin; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.h ================================================ // // FLEXRealmDatabaseManager.h // FLEX // // Created by Tim Oliver on 28/01/2016. // Copyright © 2016 Realm. All rights reserved. // #import #import "FLEXDatabaseManager.h" @interface FLEXRealmDatabaseManager : NSObject @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.m ================================================ // // FLEXRealmDatabaseManager.m // FLEX // // Created by Tim Oliver on 28/01/2016. // Copyright © 2016 Realm. All rights reserved. // #import "FLEXRealmDatabaseManager.h" #import "NSArray+FLEX.h" #import "FLEXSQLResult.h" #if __has_include() #import #import #else #import "FLEXRealmDefines.h" #endif @interface FLEXRealmDatabaseManager () @property (nonatomic, copy) NSString *path; @property (nonatomic) RLMRealm *realm; @end @implementation FLEXRealmDatabaseManager static Class RLMRealmClass = nil; + (void)load { RLMRealmClass = NSClassFromString(@"RLMRealm"); } + (instancetype)managerForDatabase:(NSString *)path { return [[self alloc] initWithPath:path]; } - (instancetype)initWithPath:(NSString *)path { if (!RLMRealmClass) { return nil; } self = [super init]; if (self) { _path = path; if (![self open]) { return nil; } } return self; } - (BOOL)open { Class configurationClass = NSClassFromString(@"RLMRealmConfiguration"); if (!RLMRealmClass || !configurationClass) { return NO; } NSError *error = nil; id configuration = [configurationClass new]; [(RLMRealmConfiguration *)configuration setFileURL:[NSURL fileURLWithPath:self.path]]; self.realm = [RLMRealmClass realmWithConfiguration:configuration error:&error]; return (error == nil); } - (NSArray *)queryAllTables { // Map each schema to its name NSArray *tableNames = [self.realm.schema.objectSchema flex_mapped:^id(RLMObjectSchema *schema, NSUInteger idx) { return schema.className ?: nil; }]; return [tableNames sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; } - (NSArray *)queryAllColumnsOfTable:(NSString *)tableName { RLMObjectSchema *objectSchema = [self.realm.schema schemaForClassName:tableName]; // Map each column to its name return [objectSchema.properties flex_mapped:^id(RLMProperty *property, NSUInteger idx) { return property.name; }]; } - (NSArray *)queryAllDataInTable:(NSString *)tableName { RLMObjectSchema *objectSchema = [self.realm.schema schemaForClassName:tableName]; RLMResults *results = [self.realm allObjects:tableName]; if (results.count == 0 || !objectSchema) { return nil; } // Map results to an array of rows return [NSArray flex_mapped:results block:^id(RLMObject *result, NSUInteger idx) { // Map each row to an array of the values of its properties return [objectSchema.properties flex_mapped:^id(RLMProperty *property, NSUInteger idx) { return [result valueForKey:property.name] ?: NSNull.null; }]; }]; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDefines.h ================================================ // // Realm.h // FLEX // // Created by Tim Oliver on 16/02/2016. // Copyright © 2016 Realm. All rights reserved. // #if __has_include() #else @class RLMObject, RLMResults, RLMRealm, RLMRealmConfiguration, RLMSchema, RLMObjectSchema, RLMProperty; @interface RLMRealmConfiguration : NSObject @property (nonatomic, copy) NSURL *fileURL; @end @interface RLMRealm : NSObject @property (nonatomic, readonly) RLMSchema *schema; + (RLMRealm *)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; - (RLMResults *)allObjects:(NSString *)className; @end @interface RLMSchema : NSObject @property (nonatomic, readonly) NSArray *objectSchema; - (RLMObjectSchema *)schemaForClassName:(NSString *)className; @end @interface RLMObjectSchema : NSObject @property (nonatomic, readonly) NSString *className; @property (nonatomic, readonly) NSArray *properties; @end @interface RLMProperty : NSString @property (nonatomic, readonly) NSString *name; @end @interface RLMResults : NSObject @property (nonatomic, readonly) NSInteger count; @end @interface RLMObject : NSObject @end #endif ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.h ================================================ // // FLEXSQLResult.h // FLEX // // Created by Tanner on 3/3/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface FLEXSQLResult : NSObject /// Describes the result of a non-select query, or an error of any kind of query + (instancetype)message:(NSString *)message; /// Describes the result of a known failed execution + (instancetype)error:(NSString *)message; /// @param rowData A list of rows, where each element in the row /// corresponds to the column given in /c columnNames + (instancetype)columns:(NSArray *)columnNames rows:(NSArray *> *)rowData; @property (nonatomic, readonly, nullable) NSString *message; /// A value of YES means this is surely an error, /// but it still might be an error even with a value of NO @property (nonatomic, readonly) BOOL isError; /// A list of column names @property (nonatomic, readonly, nullable) NSArray *columns; /// A list of rows, where each element in the row corresponds /// to the value of the column at the same index in \c columns. /// /// That is, given a row, looping over the contents of the row and /// the contents of \c columns will give you key-value pairs of /// column names to column values for that row. @property (nonatomic, readonly, nullable) NSArray *> *rows; /// A list of rows where the fields are paired to column names. /// /// This property is lazily constructed by looping over /// the rows and columns present in the other two properties. @property (nonatomic, readonly, nullable) NSArray *> *keyedRows; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.m ================================================ // // FLEXSQLResult.m // FLEX // // Created by Tanner on 3/3/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXSQLResult.h" #import "NSArray+FLEX.h" @implementation FLEXSQLResult @synthesize keyedRows = _keyedRows; + (instancetype)message:(NSString *)message { return [[self alloc] initWithMessage:message columns:nil rows:nil]; } + (instancetype)error:(NSString *)message { FLEXSQLResult *result = [self message:message]; result->_isError = YES; return result; } + (instancetype)columns:(NSArray *)columnNames rows:(NSArray *> *)rowData { return [[self alloc] initWithMessage:nil columns:columnNames rows:rowData]; } - (instancetype)initWithMessage:(NSString *)message columns:(NSArray *)columns rows:(NSArray *> *)rows { NSParameterAssert(message || (columns && rows)); NSParameterAssert(rows.count == 0 || columns.count == rows.firstObject.count); self = [super init]; if (self) { _message = message; _columns = columns; _rows = rows; } return self; } - (NSArray *> *)keyedRows { if (!_keyedRows) { _keyedRows = [self.rows flex_mapped:^id(NSArray *row, NSUInteger idx) { return [NSDictionary dictionaryWithObjects:row forKeys:self.columns]; }]; } return _keyedRows; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.h ================================================ // // PTDatabaseManager.h // Derived from: // // FMDatabase.h // FMDB( https://github.com/ccgus/fmdb ) // // Created by Peng Tao on 15/11/23. // // Licensed to Flying Meat Inc. under one or more contributor license agreements. // See the LICENSE file distributed with this work for the terms under // which Flying Meat Inc. licenses this file to you. #import #import "FLEXDatabaseManager.h" #import "FLEXSQLResult.h" @interface FLEXSQLiteDatabaseManager : NSObject /// Contains the result of the last operation, which may be an error @property (nonatomic, readonly) FLEXSQLResult *lastResult; /// Calls into \c sqlite3_last_insert_rowid() @property (nonatomic, readonly) NSInteger lastRowID; /// Given a statement like 'SELECT * from @table where @col = @val' and arguments /// like { @"table": @"Album", @"col": @"year", @"val" @1 } this method will /// invoke the statement and properly bind the given arguments to the statement. /// /// You may pass NSStrings, NSData, NSNumbers, or NSNulls as values. - (FLEXSQLResult *)executeStatement:(NSString *)statement arguments:(NSDictionary *)args; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.m ================================================ // // PTDatabaseManager.m // PTDatabaseReader // // Created by Peng Tao on 15/11/23. // Copyright © 2015年 Peng Tao. All rights reserved. // #import "FLEXSQLiteDatabaseManager.h" #import "FLEXManager.h" #import "NSArray+FLEX.h" #import "FLEXRuntimeConstants.h" #import #define kQuery(name, str) static NSString * const QUERY_##name = str kQuery(TABLENAMES, @"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"); kQuery(ROWIDS, @"SELECT rowid FROM \"%@\" ORDER BY rowid ASC"); @interface FLEXSQLiteDatabaseManager () @property (nonatomic) sqlite3 *db; @property (nonatomic, copy) NSString *path; @end @implementation FLEXSQLiteDatabaseManager #pragma mark - FLEXDatabaseManager + (instancetype)managerForDatabase:(NSString *)path { return [[self alloc] initWithPath:path]; } - (instancetype)initWithPath:(NSString *)path { self = [super init]; if (self) { self.path = path; } return self; } - (void)dealloc { [self close]; } - (BOOL)open { if (self.db) { return YES; } int err = sqlite3_open(self.path.UTF8String, &_db); #if SQLITE_HAS_CODEC NSString *defaultSqliteDatabasePassword = FLEXManager.sharedManager.defaultSqliteDatabasePassword; if (defaultSqliteDatabasePassword) { const char *key = defaultSqliteDatabasePassword.UTF8String; sqlite3_key(_db, key, (int)strlen(key)); } #endif if (err != SQLITE_OK) { return [self storeErrorForLastTask:@"Open"]; } return YES; } - (BOOL)close { if (!self.db) { return YES; } int rc; BOOL retry, triedFinalizingOpenStatements = NO; do { retry = NO; rc = sqlite3_close(_db); if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { if (!triedFinalizingOpenStatements) { triedFinalizingOpenStatements = YES; sqlite3_stmt *pStmt; while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) { NSLog(@"Closing leaked statement"); sqlite3_finalize(pStmt); retry = YES; } } } else if (SQLITE_OK != rc) { [self storeErrorForLastTask:@"Close"]; self.db = nil; return NO; } } while (retry); self.db = nil; return YES; } - (NSInteger)lastRowID { return (NSInteger)sqlite3_last_insert_rowid(self.db); } - (NSArray *)queryAllTables { return [[self executeStatement:QUERY_TABLENAMES].rows flex_mapped:^id(NSArray *table, NSUInteger idx) { return table.firstObject; }] ?: @[]; } - (NSArray *)queryAllColumnsOfTable:(NSString *)tableName { NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')",tableName]; FLEXSQLResult *results = [self executeStatement:sql]; // https://github.com/FLEXTool/FLEX/issues/554 if (!results.keyedRows.count) { sql = [NSString stringWithFormat:@"SELECT * FROM pragma_table_info('%@')", tableName]; results = [self executeStatement:sql]; // Fallback to empty query if (!results.keyedRows.count) { sql = [NSString stringWithFormat:@"SELECT * FROM \"%@\" where 0=1", tableName]; return [self executeStatement:sql].columns ?: @[]; } } return [results.keyedRows flex_mapped:^id(NSDictionary *column, NSUInteger idx) { return column[@"name"]; }] ?: @[]; } - (NSArray *)queryAllDataInTable:(NSString *)tableName { NSString *command = [NSString stringWithFormat:@"SELECT * FROM \"%@\"", tableName]; return [self executeStatement:command].rows ?: @[]; } - (NSArray *)queryRowIDsInTable:(NSString *)tableName { NSString *command = [NSString stringWithFormat:QUERY_ROWIDS, tableName]; NSArray *> *data = [self executeStatement:command].rows ?: @[]; return [data flex_mapped:^id(NSArray *obj, NSUInteger idx) { return obj.firstObject; }]; } - (FLEXSQLResult *)executeStatement:(NSString *)sql { return [self executeStatement:sql arguments:nil]; } - (FLEXSQLResult *)executeStatement:(NSString *)sql arguments:(NSDictionary *)args { [self open]; FLEXSQLResult *result = nil; sqlite3_stmt *pstmt; int status; if ((status = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &pstmt, 0)) == SQLITE_OK) { NSMutableArray *rows = [NSMutableArray new]; // Bind parameters, if any if (![self bindParameters:args toStatement:pstmt]) { return self.lastResult; } // Grab columns (columnCount will be 0 for insert/update/delete) int columnCount = sqlite3_column_count(pstmt); NSArray *columns = [NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) { return @(sqlite3_column_name(pstmt, (int)i)); }]; // Execute statement while ((status = sqlite3_step(pstmt)) == SQLITE_ROW) { // Grab rows if this is a selection query int dataCount = sqlite3_data_count(pstmt); if (dataCount > 0) { [rows addObject:[NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) { return [self objectForColumnIndex:(int)i stmt:pstmt]; }]]; } } if (status == SQLITE_DONE) { // columnCount will be 0 for insert/update/delete if (rows.count || columnCount > 0) { // We executed a SELECT query result = _lastResult = [FLEXSQLResult columns:columns rows:rows]; } else { // We executed a query like INSERT, UDPATE, or DELETE int rowsAffected = sqlite3_changes(_db); NSString *message = [NSString stringWithFormat:@"%d row(s) affected", rowsAffected]; result = _lastResult = [FLEXSQLResult message:message]; } } else { // An error occured executing the query result = _lastResult = [self errorResult:@"Execution"]; } } else { // An error occurred creating the prepared statement result = _lastResult = [self errorResult:@"Prepared statement"]; } sqlite3_finalize(pstmt); return result; } #pragma mark - Private /// @return YES on success, NO if an error was encountered and stored in \c lastResult - (BOOL)bindParameters:(NSDictionary *)args toStatement:(sqlite3_stmt *)pstmt { for (NSString *param in args.allKeys) { int status = SQLITE_OK, idx = sqlite3_bind_parameter_index(pstmt, param.UTF8String); id value = args[param]; if (idx == 0) { // No parameter matching that arg @throw NSInternalInconsistencyException; } // Null if ([value isKindOfClass:[NSNull class]]) { status = sqlite3_bind_null(pstmt, idx); } // String params else if ([value isKindOfClass:[NSString class]]) { const char *str = [value UTF8String]; status = sqlite3_bind_text(pstmt, idx, str, (int)strlen(str), SQLITE_TRANSIENT); } // Data params else if ([value isKindOfClass:[NSData class]]) { const void *blob = [value bytes]; status = sqlite3_bind_blob64(pstmt, idx, blob, [value length], SQLITE_TRANSIENT); } // Primitive params else if ([value isKindOfClass:[NSNumber class]]) { FLEXTypeEncoding type = [value objCType][0]; switch (type) { case FLEXTypeEncodingCBool: case FLEXTypeEncodingChar: case FLEXTypeEncodingUnsignedChar: case FLEXTypeEncodingShort: case FLEXTypeEncodingUnsignedShort: case FLEXTypeEncodingInt: case FLEXTypeEncodingUnsignedInt: case FLEXTypeEncodingLong: case FLEXTypeEncodingUnsignedLong: case FLEXTypeEncodingLongLong: case FLEXTypeEncodingUnsignedLongLong: status = sqlite3_bind_int64(pstmt, idx, (sqlite3_int64)[value longValue]); break; case FLEXTypeEncodingFloat: case FLEXTypeEncodingDouble: status = sqlite3_bind_double(pstmt, idx, [value doubleValue]); break; default: @throw NSInternalInconsistencyException; break; } } // Unsupported type else { @throw NSInternalInconsistencyException; } if (status != SQLITE_OK) { return [self storeErrorForLastTask: [NSString stringWithFormat:@"Binding param named '%@'", param] ]; } } return YES; } - (BOOL)storeErrorForLastTask:(NSString *)action { _lastResult = [self errorResult:action]; return NO; } - (FLEXSQLResult *)errorResult:(NSString *)description { const char *error = sqlite3_errmsg(_db); NSString *message = error ? @(error) : [NSString stringWithFormat:@"(%@: empty error)", description ]; return [FLEXSQLResult error:message]; } - (id)objectForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt*)stmt { int columnType = sqlite3_column_type(stmt, columnIdx); switch (columnType) { case SQLITE_INTEGER: return @(sqlite3_column_int64(stmt, columnIdx)).stringValue; case SQLITE_FLOAT: return @(sqlite3_column_double(stmt, columnIdx)).stringValue; case SQLITE_BLOB: return [NSString stringWithFormat:@"Data (%@ bytes)", @([self dataForColumnIndex:columnIdx stmt:stmt].length) ]; default: // Default to a string for everything else return [self stringForColumnIndex:columnIdx stmt:stmt] ?: NSNull.null; } } - (NSString *)stringForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt { if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || columnIdx < 0) { return nil; } const char *text = (const char *)sqlite3_column_text(stmt, columnIdx); return text ? @(text) : nil; } - (NSData *)dataForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt { if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { return nil; } const void *blob = sqlite3_column_blob(stmt, columnIdx); NSInteger size = (NSInteger)sqlite3_column_bytes(stmt, columnIdx); return blob ? [NSData dataWithBytes:blob length:size] : nil; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableColumnHeader.h ================================================ // // FLEXTableContentHeaderCell.h // FLEX // // Created by Peng Tao on 15/11/26. // Copyright © 2015年 f. All rights reserved. // #import typedef NS_ENUM(NSUInteger, FLEXTableColumnHeaderSortType) { FLEXTableColumnHeaderSortTypeNone = 0, FLEXTableColumnHeaderSortTypeAsc, FLEXTableColumnHeaderSortTypeDesc, }; NS_INLINE FLEXTableColumnHeaderSortType FLEXNextTableColumnHeaderSortType( FLEXTableColumnHeaderSortType current) { switch (current) { case FLEXTableColumnHeaderSortTypeAsc: return FLEXTableColumnHeaderSortTypeDesc; case FLEXTableColumnHeaderSortTypeNone: case FLEXTableColumnHeaderSortTypeDesc: return FLEXTableColumnHeaderSortTypeAsc; } return FLEXTableColumnHeaderSortTypeNone; } @interface FLEXTableColumnHeader : UIView @property (nonatomic) NSInteger index; @property (nonatomic, readonly) UILabel *titleLabel; @property (nonatomic) FLEXTableColumnHeaderSortType sortType; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableColumnHeader.m ================================================ // // FLEXTableContentHeaderCell.m // FLEX // // Created by Peng Tao on 15/11/26. // Copyright © 2015年 f. All rights reserved. // #import "FLEXTableColumnHeader.h" #import "FLEXColor.h" #import "UIFont+FLEX.h" #import "FLEXUtility.h" static const CGFloat kMargin = 5; static const CGFloat kArrowWidth = 20; @interface FLEXTableColumnHeader () @property (nonatomic, readonly) UILabel *arrowLabel; @property (nonatomic, readonly) UIView *lineView; @end @implementation FLEXTableColumnHeader - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = FLEXColor.secondaryBackgroundColor; _titleLabel = [UILabel new]; _titleLabel.font = UIFont.flex_defaultTableCellFont; [self addSubview:_titleLabel]; _arrowLabel = [UILabel new]; _arrowLabel.font = UIFont.flex_defaultTableCellFont; [self addSubview:_arrowLabel]; _lineView = [UIView new]; _lineView.backgroundColor = FLEXColor.hairlineColor; [self addSubview:_lineView]; } return self; } - (void)setSortType:(FLEXTableColumnHeaderSortType)type { _sortType = type; switch (type) { case FLEXTableColumnHeaderSortTypeNone: _arrowLabel.text = @""; break; case FLEXTableColumnHeaderSortTypeAsc: _arrowLabel.text = @"⬆️"; break; case FLEXTableColumnHeaderSortTypeDesc: _arrowLabel.text = @"⬇️"; break; } } - (void)layoutSubviews { [super layoutSubviews]; CGSize size = self.frame.size; self.titleLabel.frame = CGRectMake(kMargin, 0, size.width - kArrowWidth - kMargin, size.height); self.arrowLabel.frame = CGRectMake(size.width - kArrowWidth, 0, kArrowWidth, size.height); self.lineView.frame = CGRectMake(size.width - 1, 2, FLEXPointsToPixels(1), size.height - 4); } - (CGSize)sizeThatFits:(CGSize)size { CGFloat margins = kArrowWidth - 2 * kMargin; size = CGSizeMake(size.width - margins, size.height); CGFloat width = [_titleLabel sizeThatFits:size].width + margins; return CGSizeMake(width, size.height); } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.h ================================================ // // PTTableContentViewController.h // PTDatabaseReader // // Created by Peng Tao on 15/11/23. // Copyright © 2015年 Peng Tao. All rights reserved. // #import #import "FLEXDatabaseManager.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXTableContentViewController : UIViewController /// Display a mutable table with the given columns, rows, and name. /// /// @param columnNames self explanatory. /// @param rowData an array of rows, where each row is an array of column data. /// @param rowIDs an array of stringy row IDs. Required for deleting rows. /// @param tableName an optional name of the table being viewed, if any. Enables adding rows. /// @param databaseManager an optional manager to allow modifying the table. /// Required for deleting rows. Required for adding rows if \c tableName is supplied. + (instancetype)columns:(NSArray *)columnNames rows:(NSArray *> *)rowData rowIDs:(NSArray *)rowIDs tableName:(NSString *)tableName database:(id)databaseManager; /// Display an immutable table with the given columns and rows. + (instancetype)columns:(NSArray *)columnNames rows:(NSArray *> *)rowData; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m ================================================ // // PTTableContentViewController.m // PTDatabaseReader // // Created by Peng Tao on 15/11/23. // Copyright © 2015年 Peng Tao. All rights reserved. // #import "FLEXTableContentViewController.h" #import "FLEXTableRowDataViewController.h" #import "FLEXMultiColumnTableView.h" #import "FLEXWebViewController.h" #import "FLEXUtility.h" #import "UIBarButtonItem+FLEX.h" @interface FLEXTableContentViewController () < FLEXMultiColumnTableViewDataSource, FLEXMultiColumnTableViewDelegate > @property (nonatomic, readonly) NSArray *columns; @property (nonatomic) NSMutableArray *rows; @property (nonatomic, readonly) NSString *tableName; @property (nonatomic, nullable) NSMutableArray *rowIDs; @property (nonatomic, readonly, nullable) id databaseManager; @property (nonatomic, readonly) BOOL canRefresh; @property (nonatomic) FLEXMultiColumnTableView *multiColumnView; @end @implementation FLEXTableContentViewController + (instancetype)columns:(NSArray *)columnNames rows:(NSArray *> *)rowData rowIDs:(NSArray *)rowIDs tableName:(NSString *)tableName database:(id)databaseManager { return [[self alloc] initWithColumns:columnNames rows:rowData rowIDs:rowIDs tableName:tableName database:databaseManager ]; } + (instancetype)columns:(NSArray *)cols rows:(NSArray *> *)rowData { return [[self alloc] initWithColumns:cols rows:rowData rowIDs:nil tableName:nil database:nil]; } - (instancetype)initWithColumns:(NSArray *)columnNames rows:(NSArray *> *)rowData rowIDs:(nullable NSArray *)rowIDs tableName:(nullable NSString *)tableName database:(nullable id)databaseManager { // Must supply all optional parameters as one, or none BOOL all = rowIDs && tableName && databaseManager; BOOL none = !rowIDs && !tableName && !databaseManager; NSParameterAssert(all || none); self = [super init]; if (self) { self->_columns = columnNames.copy; self->_rows = rowData.mutableCopy; self->_rowIDs = rowIDs.mutableCopy; self->_tableName = tableName.copy; self->_databaseManager = databaseManager; } return self; } - (void)loadView { [super loadView]; [self.view addSubview:self.multiColumnView]; } - (void)viewDidLoad { [super viewDidLoad]; self.title = self.tableName; [self.multiColumnView reloadData]; [self setupToolbarItems]; } - (FLEXMultiColumnTableView *)multiColumnView { if (!_multiColumnView) { _multiColumnView = [[FLEXMultiColumnTableView alloc] initWithFrame:FLEXRectSetSize(CGRectZero, self.view.frame.size) ]; _multiColumnView.dataSource = self; _multiColumnView.delegate = self; } return _multiColumnView; } - (BOOL)canRefresh { return self.databaseManager && self.tableName; } #pragma mark MultiColumnTableView DataSource - (NSInteger)numberOfColumnsInTableView:(FLEXMultiColumnTableView *)tableView { return self.columns.count; } - (NSInteger)numberOfRowsInTableView:(FLEXMultiColumnTableView *)tableView { return self.rows.count; } - (NSString *)columnTitle:(NSInteger)column { return self.columns[column]; } - (NSString *)rowTitle:(NSInteger)row { return @(row).stringValue; } - (NSArray *)contentForRow:(NSInteger)row { return self.rows[row]; } - (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView heightForContentCellInRow:(NSInteger)row { return 40; } - (CGFloat)multiColumnTableView:(FLEXMultiColumnTableView *)tableView minWidthForContentCellInColumn:(NSInteger)column { return 100; } - (CGFloat)heightForTopHeaderInTableView:(FLEXMultiColumnTableView *)tableView { return 40; } - (CGFloat)widthForLeftHeaderInTableView:(FLEXMultiColumnTableView *)tableView { NSString *str = [NSString stringWithFormat:@"%lu",(unsigned long)self.rows.count]; NSDictionary *attrs = @{ NSFontAttributeName : [UIFont systemFontOfSize:17.0] }; CGSize size = [str boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 14) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil ].size; return size.width + 20; } #pragma mark MultiColumnTableView Delegate - (void)multiColumnTableView:(FLEXMultiColumnTableView *)tableView didSelectRow:(NSInteger)row { NSArray *fields = [self.rows[row] flex_mapped:^id(NSString *field, NSUInteger idx) { return [NSString stringWithFormat:@"%@:\n%@", self.columns[idx], field]; }]; NSArray *values = [self.rows[row] flex_mapped:^id(NSString *value, NSUInteger idx) { return [NSString stringWithFormat:@"'%@'", value]; }]; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title([@"Row " stringByAppendingString:@(row).stringValue]); NSString *message = [fields componentsJoinedByString:@"\n\n"]; make.message(message); make.button(@"Copy").handler(^(NSArray *strings) { UIPasteboard.generalPasteboard.string = message; }); make.button(@"Copy as CSV").handler(^(NSArray *strings) { UIPasteboard.generalPasteboard.string = [values componentsJoinedByString:@", "]; }); make.button(@"Focus on Row").handler(^(NSArray *strings) { UIViewController *focusedRow = [FLEXTableRowDataViewController rows:[NSDictionary dictionaryWithObjects:self.rows[row] forKeys:self.columns] ]; [self.navigationController pushViewController:focusedRow animated:YES]; }); // Option to delete row BOOL hasRowID = self.rows.count && row < self.rows.count; if (hasRowID && self.canRefresh) { make.button(@"Delete").destructiveStyle().handler(^(NSArray *strings) { NSString *deleteRow = [NSString stringWithFormat: @"DELETE FROM %@ WHERE rowid = %@", self.tableName, self.rowIDs[row] ]; [self executeStatementAndShowResult:deleteRow completion:^(BOOL success) { // Remove deleted row and reload view if (success) { [self reloadTableDataFromDB]; } }]; }); } make.button(@"Dismiss").cancelStyle(); } showFrom:self]; } - (void)multiColumnTableView:(FLEXMultiColumnTableView *)tableView didSelectHeaderForColumn:(NSInteger)column sortType:(FLEXTableColumnHeaderSortType)sortType { NSArray *sortContentData = [self.rows sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(NSArray *obj1, NSArray *obj2) { id a = obj1[column], b = obj2[column]; if (a == NSNull.null) { return NSOrderedAscending; } if (b == NSNull.null) { return NSOrderedDescending; } if ([a respondsToSelector:@selector(compare:options:)] && [b respondsToSelector:@selector(compare:options:)]) { return [a compare:b options:NSNumericSearch]; } if ([a respondsToSelector:@selector(compare:)] && [b respondsToSelector:@selector(compare:)]) { return [a compare:b]; } return NSOrderedSame; } ]; if (sortType == FLEXTableColumnHeaderSortTypeDesc) { sortContentData = sortContentData.reverseObjectEnumerator.allObjects.copy; } self.rows = sortContentData.mutableCopy; [self.multiColumnView reloadData]; } #pragma mark - About Transition - (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id )coordinator { [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:^(id context) { if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) { self.multiColumnView.frame = CGRectMake(0, 32, self.view.frame.size.width, self.view.frame.size.height - 32); } else { self.multiColumnView.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64); } [self.view setNeedsLayout]; } completion:nil]; } #pragma mark - Toolbar - (void)setupToolbarItems { // We do not support modifying realm databases if (![self.databaseManager respondsToSelector:@selector(executeStatement:)]) { return; } UIBarButtonItem *trashButton = FLEXBarButtonItemSystem(Trash, self, @selector(trashPressed)); UIBarButtonItem *addButton = FLEXBarButtonItemSystem(Add, self, @selector(addPressed)); // Only allow adding rows or deleting rows if we have a table name trashButton.enabled = self.canRefresh; addButton.enabled = self.canRefresh; self.toolbarItems = @[ UIBarButtonItem.flex_flexibleSpace, addButton, UIBarButtonItem.flex_flexibleSpace, [trashButton flex_withTintColor:UIColor.redColor], ]; } - (void)trashPressed { NSParameterAssert(self.tableName); [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Delete All Rows"); make.message(@"All rows in this table will be permanently deleted.\nDo you want to proceed?"); make.button(@"Yes, I'm sure").destructiveStyle().handler(^(NSArray *strings) { NSString *deleteAll = [NSString stringWithFormat:@"DELETE FROM %@", self.tableName]; [self executeStatementAndShowResult:deleteAll completion:^(BOOL success) { // Only dismiss on success if (success) { [self.navigationController popViewControllerAnimated:YES]; } }]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } - (void)addPressed { NSParameterAssert(self.tableName); [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Add a New Row"); make.message(@"Comma separate values to use in an INSERT statement.\n\n"); make.message(@"INSERT INTO [table] VALUES (your_input)"); make.textField(@"5, 'John Smith', 14,..."); make.button(@"Insert").handler(^(NSArray *strings) { NSString *statement = [NSString stringWithFormat: @"INSERT INTO %@ VALUES (%@)", self.tableName, strings[0] ]; [self executeStatementAndShowResult:statement completion:^(BOOL success) { if (success) { [self reloadTableDataFromDB]; } }]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } #pragma mark - Helpers - (void)executeStatementAndShowResult:(NSString *)statement completion:(void (^_Nullable)(BOOL success))completion { NSParameterAssert(self.databaseManager); FLEXSQLResult *result = [self.databaseManager executeStatement:statement]; [FLEXAlert makeAlert:^(FLEXAlert *make) { if (result.isError) { make.title(@"Error"); } make.message(result.message ?: @""); make.button(@"Dismiss").cancelStyle().handler(^(NSArray *_) { if (completion) { completion(!result.isError); } }); } showFrom:self]; } - (void)reloadTableDataFromDB { if (!self.canRefresh) { return; } NSArray *rows = [self.databaseManager queryAllDataInTable:self.tableName]; NSArray *rowIDs = nil; if ([self.databaseManager respondsToSelector:@selector(queryRowIDsInTable:)]) { rowIDs = [self.databaseManager queryRowIDsInTable:self.tableName]; } self.rows = rows.mutableCopy; self.rowIDs = rowIDs.mutableCopy; [self.multiColumnView reloadData]; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableLeftCell.h ================================================ // // FLEXTableLeftCell.h // FLEX // // Created by Peng Tao on 15/11/24. // Copyright © 2015年 f. All rights reserved. // #import @interface FLEXTableLeftCell : UITableViewCell @property (nonatomic) UILabel *titlelabel; + (instancetype)cellWithTableView:(UITableView *)tableView; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableLeftCell.m ================================================ // // FLEXTableLeftCell.m // FLEX // // Created by Peng Tao on 15/11/24. // Copyright © 2015年 f. All rights reserved. // #import "FLEXTableLeftCell.h" @implementation FLEXTableLeftCell + (instancetype)cellWithTableView:(UITableView *)tableView { static NSString *identifier = @"FLEXTableLeftCell"; FLEXTableLeftCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { cell = [[FLEXTableLeftCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; UILabel *textLabel = [UILabel new]; textLabel.textAlignment = NSTextAlignmentCenter; textLabel.font = [UIFont systemFontOfSize:13.0]; [cell.contentView addSubview:textLabel]; cell.titlelabel = textLabel; } return cell; } - (void)layoutSubviews { [super layoutSubviews]; self.titlelabel.frame = self.contentView.frame; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableListViewController.h ================================================ // // PTTableListViewController.h // PTDatabaseReader // // Created by Peng Tao on 15/11/23. // Copyright © 2015年 Peng Tao. All rights reserved. // #import "FLEXFilteringTableViewController.h" @interface FLEXTableListViewController : FLEXFilteringTableViewController + (BOOL)supportsExtension:(NSString *)extension; - (instancetype)initWithPath:(NSString *)path; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableListViewController.m ================================================ // // PTTableListViewController.m // PTDatabaseReader // // Created by Peng Tao on 15/11/23. // Copyright © 2015年 Peng Tao. All rights reserved. // #import "FLEXTableListViewController.h" #import "FLEXDatabaseManager.h" #import "FLEXSQLiteDatabaseManager.h" #import "FLEXRealmDatabaseManager.h" #import "FLEXTableContentViewController.h" #import "FLEXMutableListSection.h" #import "NSArray+FLEX.h" #import "FLEXAlert.h" #import "FLEXMacros.h" @interface FLEXTableListViewController () @property (nonatomic, readonly) id dbm; @property (nonatomic, readonly) NSString *path; @property (nonatomic, readonly) FLEXMutableListSection *tables; + (NSArray *)supportedSQLiteExtensions; + (NSArray *)supportedRealmExtensions; @end @implementation FLEXTableListViewController - (instancetype)initWithPath:(NSString *)path { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { _path = path.copy; _dbm = [self databaseManagerForFileAtPath:path]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.showsSearchBar = YES; // Compose query button // UIBarButtonItem *composeQuery = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:@selector(queryButtonPressed) ]; // Cannot run custom queries on realm databases composeQuery.enabled = [self.dbm respondsToSelector:@selector(executeStatement:) ]; [self addToolbarItems:@[composeQuery]]; } - (NSArray *)makeSections { _tables = [FLEXMutableListSection list:[self.dbm queryAllTables] cellConfiguration:^(__kindof UITableViewCell *cell, NSString *tableName, NSInteger row) { cell.textLabel.text = tableName; } filterMatcher:^BOOL(NSString *filterText, NSString *tableName) { return [tableName localizedCaseInsensitiveContainsString:filterText]; } ]; self.tables.selectionHandler = ^(FLEXTableListViewController *host, NSString *tableName) { NSArray *rows = [host.dbm queryAllDataInTable:tableName]; NSArray *columns = [host.dbm queryAllColumnsOfTable:tableName]; NSArray *rowIDs = nil; if ([host.dbm respondsToSelector:@selector(queryRowIDsInTable:)]) { rowIDs = [host.dbm queryRowIDsInTable:tableName]; } UIViewController *resultsScreen = [FLEXTableContentViewController columns:columns rows:rows rowIDs:rowIDs tableName:tableName database:host.dbm ]; [host.navigationController pushViewController:resultsScreen animated:YES]; }; return @[self.tables]; } - (void)reloadData { self.tables.customTitle = [NSString stringWithFormat:@"Tables (%@)", @(self.tables.filteredList.count) ]; [super reloadData]; } - (void)queryButtonPressed { [self showQueryInput:nil]; } - (void)showQueryInput:(NSString *)prefillQuery { FLEXSQLiteDatabaseManager *database = self.dbm; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Execute an SQL query"); make.configuredTextField(^(UITextField *textField) { textField.text = prefillQuery; }); make.button(@"Run").handler(^(NSArray *strings) { NSString *query = strings[0]; FLEXSQLResult *result = [database executeStatement:query]; if (result.message) { // Allow users to edit their last query if it had an error if ([result.message containsString:@"error"]) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Error").message(result.message); make.button(@"Edit Query").preferred().handler(^(NSArray *_) { // Show query editor again with our last input [self showQueryInput:query]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } else { [FLEXAlert showAlert:@"Message" message:result.message from:self]; } } else { UIViewController *resultsScreen = [FLEXTableContentViewController columns:result.columns rows:result.rows ]; [self.navigationController pushViewController:resultsScreen animated:YES]; } }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } - (id)databaseManagerForFileAtPath:(NSString *)path { NSString *pathExtension = path.pathExtension.lowercaseString; NSArray *sqliteExtensions = FLEXTableListViewController.supportedSQLiteExtensions; if ([sqliteExtensions indexOfObject:pathExtension] != NSNotFound) { return [FLEXSQLiteDatabaseManager managerForDatabase:path]; } NSArray *realmExtensions = FLEXTableListViewController.supportedRealmExtensions; if (realmExtensions != nil && [realmExtensions indexOfObject:pathExtension] != NSNotFound) { return [FLEXRealmDatabaseManager managerForDatabase:path]; } return nil; } #pragma mark - FLEXTableListViewController + (BOOL)supportsExtension:(NSString *)extension { extension = extension.lowercaseString; NSArray *sqliteExtensions = FLEXTableListViewController.supportedSQLiteExtensions; if (sqliteExtensions.count > 0 && [sqliteExtensions indexOfObject:extension] != NSNotFound) { return YES; } NSArray *realmExtensions = FLEXTableListViewController.supportedRealmExtensions; if (realmExtensions.count > 0 && [realmExtensions indexOfObject:extension] != NSNotFound) { return YES; } return NO; } + (NSArray *)supportedSQLiteExtensions { return @[@"db", @"sqlite", @"sqlite3"]; } + (NSArray *)supportedRealmExtensions { if (NSClassFromString(@"RLMRealm") == nil) { return nil; } return @[@"realm"]; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableRowDataViewController.h ================================================ // // FLEXTableRowDataViewController.h // FLEX // // Created by Chaoshuai Lu on 7/8/20. // #import "FLEXFilteringTableViewController.h" @interface FLEXTableRowDataViewController : FLEXFilteringTableViewController + (instancetype)rows:(NSDictionary *)rowData; @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableRowDataViewController.m ================================================ // // FLEXTableRowDataViewController.m // FLEX // // Created by Chaoshuai Lu on 7/8/20. // #import "FLEXTableRowDataViewController.h" #import "FLEXMutableListSection.h" #import "FLEXAlert.h" @interface FLEXTableRowDataViewController () @property (nonatomic) NSDictionary *rowsByColumn; @end @implementation FLEXTableRowDataViewController #pragma mark - Initialization + (instancetype)rows:(NSDictionary *)rowData { FLEXTableRowDataViewController *controller = [self new]; controller.rowsByColumn = rowData; return controller; } #pragma mark - Overrides - (NSArray *)makeSections { NSDictionary *rowsByColumn = self.rowsByColumn; FLEXMutableListSection *section = [FLEXMutableListSection list:self.rowsByColumn.allKeys cellConfiguration:^(UITableViewCell *cell, NSString *column, NSInteger row) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = column; cell.detailTextLabel.text = rowsByColumn[column].description; } filterMatcher:^BOOL(NSString *filterText, NSString *column) { return [column localizedCaseInsensitiveContainsString:filterText] || [rowsByColumn[column] localizedCaseInsensitiveContainsString:filterText]; } ]; section.selectionHandler = ^(UIViewController *host, NSString *column) { UIPasteboard.generalPasteboard.string = rowsByColumn[column].description; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Column Copied to Clipboard"); make.message(rowsByColumn[column].description); make.button(@"Dismiss").cancelStyle(); } showFrom:host]; }; return @[section]; } @end ================================================ FILE: Classes/GlobalStateExplorers/DatabaseBrowser/LICENSE ================================================ FMDB Copyright (c) 2008-2014 Flying Meat Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Classes/GlobalStateExplorers/FLEXAPNSViewController.h ================================================ // // FLEXAPNSViewController.h // FLEX // // Created by Tanner Bennett on 6/28/22. // Copyright © 2022 FLEX Team. All rights reserved. // #import "FLEXGlobalsEntry.h" #import "FLEXFilteringTableViewController.h" @interface FLEXAPNSViewController : FLEXFilteringTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXAPNSViewController.m ================================================ // // FLEXAPNSViewController.m // FLEX // // Created by Tanner Bennett on 6/28/22. // Copyright © 2022 FLEX Team. All rights reserved. // #import "FLEXAPNSViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXMutableListSection.h" #import "FLEXSingleRowSection.h" #import "NSUserDefaults+FLEX.h" #import "UIBarButtonItem+FLEX.h" #import "NSDateFormatter+FLEX.h" #import "FLEXResources.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "flex_fishhook.h" #import #import #define orig(method, ...) if (orig_##method) { orig_##method(__VA_ARGS__); } #define method_lookup(__selector, __cls, __return, ...) \ ([__cls instancesRespondToSelector:__selector] ? \ (__return(*)(__VA_ARGS__))class_getMethodImplementation(__cls, __selector) : nil) @interface FLEXAPNSViewController () @property (nonatomic, readonly, class) Class appDelegateClass; @property (nonatomic, class) NSData *deviceToken; @property (nonatomic, class) NSError *registrationError; @property (nonatomic, readonly, class) NSString *deviceTokenString; @property (nonatomic, readonly, class) NSMutableArray *remoteNotifications; @property (nonatomic, readonly, class) NSMutableArray *userNotifications API_AVAILABLE(ios(10.0)); @property (nonatomic) FLEXSingleRowSection *deviceToken; @property (nonatomic) FLEXMutableListSection *remoteNotifications; @property (nonatomic) FLEXMutableListSection *userNotifications API_AVAILABLE(ios(10.0)); @end @implementation FLEXAPNSViewController #pragma mark Swizzles /// Hook User Notifications related methods on the app delegate /// and UNUserNotificationCenter delegate classes + (void)load { FLEX_EXIT_IF_NO_CTORS() if (!NSUserDefaults.standardUserDefaults.flex_enableAPNSCapture) { return; } //──────────────────────// // App Delegate // //──────────────────────// // Hook UIApplication to intercept app delegate Class uiapp = UIApplication.self; auto orig_uiapp_setDelegate = (void(*)(id, SEL, id))class_getMethodImplementation( uiapp, @selector(setDelegate:) ); IMP uiapp_setDelegate = imp_implementationWithBlock(^(id _, id delegate) { [self hookAppDelegateClass:[delegate class]]; orig_uiapp_setDelegate(_, @selector(setDelegate:), delegate); }); class_replaceMethod( uiapp, @selector(setDelegate:), uiapp_setDelegate, "v@:@" ); //───────────────────────────────────────────// // UNUserNotificationCenter Delegate // //───────────────────────────────────────────// if (@available(iOS 10.0, *)) { Class unusernc = UNUserNotificationCenter.self; auto orig_unusernc_setDelegate = (void(*)(id, SEL, id))class_getMethodImplementation( unusernc, @selector(setDelegate:) ); IMP unusernc_setDelegate = imp_implementationWithBlock(^(id _, id delegate) { [self hookUNUserNotificationCenterDelegateClass:[delegate class]]; orig_unusernc_setDelegate(_, @selector(setDelegate:), delegate); }); class_replaceMethod( unusernc, @selector(setDelegate:), unusernc_setDelegate, "v@:@" ); } } + (void)hookAppDelegateClass:(Class)appDelegate { // Abort if we already hooked something if (_appDelegateClass) { return; } _appDelegateClass = appDelegate; // Better documentation for what's happening is in hookUNUserNotificationCenterDelegateClass: below auto types_didRegisterForRemoteNotificationsWithDeviceToken = "v@:@@"; auto types_didFailToRegisterForRemoteNotificationsWithError = "v@:@@"; auto types_didReceiveRemoteNotification = "v@:@@@?"; auto sel_didRegisterForRemoteNotifications = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); auto sel_didFailToRegisterForRemoteNotifs = @selector(application:didFailToRegisterForRemoteNotificationsWithError:); auto sel_didReceiveRemoteNotification = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); auto orig_didRegisterForRemoteNotificationsWithDeviceToken = method_lookup( sel_didRegisterForRemoteNotifications, appDelegate, void, id, SEL, id, id); auto orig_didFailToRegisterForRemoteNotificationsWithError = method_lookup( sel_didFailToRegisterForRemoteNotifs, appDelegate, void, id, SEL, id, id); auto orig_didReceiveRemoteNotification = method_lookup( sel_didReceiveRemoteNotification, appDelegate, void, id, SEL, id, id, id); IMP didRegisterForRemoteNotificationsWithDeviceToken = imp_implementationWithBlock(^(id _, id app, NSData *token) { self.deviceToken = token; orig(didRegisterForRemoteNotificationsWithDeviceToken, _, nil, app, token); }); IMP didFailToRegisterForRemoteNotificationsWithError = imp_implementationWithBlock(^(id _, id app, NSError *error) { self.registrationError = error; orig(didFailToRegisterForRemoteNotificationsWithError, _, nil, app, error); }); IMP didReceiveRemoteNotification = imp_implementationWithBlock(^(id _, id app, NSDictionary *payload, id handler) { // TODO: notify when new notifications are added [self.remoteNotifications addObject:payload]; orig(didReceiveRemoteNotification, _, nil, app, payload, handler); }); class_replaceMethod( appDelegate, sel_didRegisterForRemoteNotifications, didRegisterForRemoteNotificationsWithDeviceToken, types_didRegisterForRemoteNotificationsWithDeviceToken ); class_replaceMethod( appDelegate, sel_didFailToRegisterForRemoteNotifs, didFailToRegisterForRemoteNotificationsWithError, types_didFailToRegisterForRemoteNotificationsWithError ); class_replaceMethod( appDelegate, sel_didReceiveRemoteNotification, didReceiveRemoteNotification, types_didReceiveRemoteNotification ); } + (void)hookUNUserNotificationCenterDelegateClass:(Class)delegate API_AVAILABLE(ios(10.0)) { // Selector auto sel_didReceiveNotification = @selector(userNotificationCenter:willPresentNotification:withCompletionHandler:); // Original implementation (or nil if unimplemented) auto orig_didReceiveNotification = method_lookup( sel_didReceiveNotification, delegate, void, id, SEL, id, id, id); // Our hook (ignores self and other unneeded parameters) IMP didReceiveNotification = imp_implementationWithBlock(^(id _, id __, UNNotification *notification, id ___) { [self.userNotifications addObject:notification]; // This macro is a no-op if there is no original implementation orig(didReceiveNotification, _, nil, __, notification, ___); }); // Set the hook class_replaceMethod( delegate, sel_didReceiveNotification, didReceiveNotification, "v@:@@@?" ); } #pragma mark Class Properties static Class _appDelegateClass = nil; + (Class)appDelegateClass { return _appDelegateClass; } static NSData *_apnsDeviceToken = nil; + (NSData *)deviceToken { return _apnsDeviceToken; } + (void)setDeviceToken:(NSData *)deviceToken { _apnsDeviceToken = deviceToken; } + (NSString *)deviceTokenString { static NSString *_deviceTokenString = nil; if (!_deviceTokenString && self.deviceToken) { NSData *token = self.deviceToken; NSUInteger capacity = token.length * 2; NSMutableString *tokenString = [NSMutableString stringWithCapacity:capacity]; const UInt8 *tokenData = token.bytes; for (NSUInteger idx = 0; idx < token.length; ++idx) { [tokenString appendFormat:@"%02X", (int)tokenData[idx]]; } _deviceTokenString = tokenString; } return _deviceTokenString; } static NSError *_apnsRegistrationError = nil; + (NSError *)registrationError { return _apnsRegistrationError; } + (void)setRegistrationError:(NSError *)error { _apnsRegistrationError = error; } + (NSMutableArray *)userNotifications { static NSMutableArray *_userNotifications = nil; if (!_userNotifications) { _userNotifications = [NSMutableArray new]; } return _userNotifications; } + (NSMutableArray *)remoteNotifications { static NSMutableArray *_remoteNotifications = nil; if (!_remoteNotifications) { _remoteNotifications = [NSMutableArray new]; } return _remoteNotifications; } #pragma mark Instance stuff - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Push Notifications"; self.refreshControl = [UIRefreshControl new]; [self.refreshControl addTarget:self action:@selector(reloadData) forControlEvents:UIControlEventValueChanged]; [self addToolbarItems:@[ [UIBarButtonItem flex_itemWithImage:FLEXResources.gearIcon target:self action:@selector(settingsButtonTapped) ], ]]; } - (NSArray *)makeSections { self.deviceToken = [FLEXSingleRowSection title:@"APNS Device Token" reuse:nil cell:^(UITableViewCell *cell) { NSString *tokenString = FLEXAPNSViewController.deviceTokenString; if (tokenString) { cell.textLabel.text = tokenString; cell.textLabel.numberOfLines = 0; } else if (!NSUserDefaults.standardUserDefaults.flex_enableAPNSCapture) { cell.textLabel.text = @"APNS capture disabled"; } else { cell.textLabel.text = @"Not yet registered"; } }]; self.deviceToken.selectionAction = ^(UIViewController *host) { UIPasteboard.generalPasteboard.string = FLEXAPNSViewController.deviceTokenString; [FLEXAlert showQuickAlert:@"Copied to Clipboard" from:host]; }; // Remote Notifications // self.remoteNotifications = [FLEXMutableListSection list:FLEXAPNSViewController.remoteNotifications cellConfiguration:^(UITableViewCell *cell, NSDictionary *notif, NSInteger row) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // TODO: date received cell.detailTextLabel.text = [FLEXRuntimeUtility summaryForObject:notif]; } filterMatcher:^BOOL(NSString *filterText, NSDictionary *notif) { return [notif.description localizedCaseInsensitiveContainsString:filterText]; } ]; self.remoteNotifications.customTitle = @"Remote Notifications"; self.remoteNotifications.selectionHandler = ^(UIViewController *host, NSDictionary *notif) { [host.navigationController pushViewController:[ FLEXObjectExplorerFactory explorerViewControllerForObject:notif ] animated:YES]; }; // User Notifications // if (@available(iOS 10.0, *)) { self.userNotifications = [FLEXMutableListSection list:FLEXAPNSViewController.userNotifications cellConfiguration:^(UITableViewCell *cell, UNNotification *notif, NSInteger row) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // Subtitle is 'subtitle \n date' NSString *dateString = [NSDateFormatter flex_stringFrom:notif.date format:FLEXDateFormatPreciseClock]; NSString *subtitle = notif.request.content.subtitle; subtitle = subtitle ? [NSString stringWithFormat:@"%@\n%@", subtitle, dateString] : dateString; cell.textLabel.text = notif.request.content.title; cell.detailTextLabel.text = subtitle; } filterMatcher:^BOOL(NSString *filterText, NSDictionary *notif) { return [notif.description localizedCaseInsensitiveContainsString:filterText]; } ]; self.userNotifications.customTitle = @"Push Notifications"; self.userNotifications.selectionHandler = ^(UIViewController *host, UNNotification *notif) { [host.navigationController pushViewController:[ FLEXObjectExplorerFactory explorerViewControllerForObject:notif.request ] animated:YES]; }; return @[self.deviceToken, self.remoteNotifications, self.userNotifications]; } else { return @[self.deviceToken, self.remoteNotifications]; } } - (void)reloadData { [self.refreshControl endRefreshing]; self.remoteNotifications.customTitle = [NSString stringWithFormat: @"%@ notifications", @(self.remoteNotifications.filteredList.count) ]; [super reloadData]; } - (void)settingsButtonTapped { NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; BOOL enabled = defaults.flex_enableAPNSCapture; NSString *apnsToggle = enabled ? @"Disable Capture" : @"Enable Capture"; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Settings") .message(@"Enable or disable the capture of push notifications.\n\n") .message(@"This will hook UIApplicationMain on launch until it is disabled, ") .message(@"and swizzle some app delegate methods. Restart the app for changes to take effect."); make.button(apnsToggle).destructiveStyle().handler(^(NSArray *strings) { [defaults flex_toggleBoolForKey:kFLEXDefaultsAPNSCaptureEnabledKey]; }); make.button(@"Dismiss").cancelStyle(); } showFrom:self]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"📌 Push Notifications"; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { return [self new]; } @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXAddressExplorerCoordinator.h ================================================ // // FLEXAddressExplorerCoordinator.h // FLEX // // Created by Tanner Bennett on 7/10/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsEntry.h" @interface FLEXAddressExplorerCoordinator : NSObject @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXAddressExplorerCoordinator.m ================================================ // // FLEXAddressExplorerCoordinator.m // FLEX // // Created by Tanner Bennett on 7/10/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXAddressExplorerCoordinator.h" #import "FLEXGlobalsViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXRuntimeUtility.h" #import "FLEXUtility.h" @interface UITableViewController (FLEXAddressExploration) - (void)deselectSelectedRow; - (void)tryExploreAddress:(NSString *)addressString safely:(BOOL)safely; @end @implementation FLEXAddressExplorerCoordinator #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"🔎 Address Explorer"; } + (FLEXGlobalsEntryRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row { return ^(UITableViewController *host) { NSString *title = @"Explore Object at Address"; NSString *message = @"Paste a hexadecimal address below, starting with '0x'. " "Use the unsafe option if you need to bypass pointer validation, " "but know that it may crash the app if the address is invalid."; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(title).message(message); make.configuredTextField(^(UITextField *textField) { NSString *copied = UIPasteboard.generalPasteboard.string; textField.placeholder = @"0x00000070deadbeef"; // Go ahead and paste our clipboard if we have an address copied if ([copied hasPrefix:@"0x"]) { textField.text = copied; [textField selectAll:nil]; } }); make.button(@"Explore").handler(^(NSArray *strings) { [host tryExploreAddress:strings.firstObject safely:YES]; }); make.button(@"Unsafe Explore").destructiveStyle().handler(^(NSArray *strings) { [host tryExploreAddress:strings.firstObject safely:NO]; }); make.button(@"Cancel").cancelStyle(); } showFrom:host]; }; } @end @implementation UITableViewController (FLEXAddressExploration) - (void)deselectSelectedRow { NSIndexPath *selected = self.tableView.indexPathForSelectedRow; [self.tableView deselectRowAtIndexPath:selected animated:YES]; } - (void)tryExploreAddress:(NSString *)addressString safely:(BOOL)safely { NSScanner *scanner = [NSScanner scannerWithString:addressString]; unsigned long long hexValue = 0; BOOL didParseAddress = [scanner scanHexLongLong:&hexValue]; const void *pointerValue = (void *)hexValue; NSString *error = nil; if (didParseAddress) { if (safely && ![FLEXRuntimeUtility pointerIsValidObjcObject:pointerValue]) { error = @"The given address is unlikely to be a valid object."; } } else { error = @"Malformed address. Make sure it's not too long and starts with '0x'."; } if (!error) { id object = (__bridge id)pointerValue; FLEXObjectExplorerViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object]; [self.navigationController pushViewController:explorer animated:YES]; } else { [FLEXAlert showAlert:@"Uh-oh" message:error from:self]; [self deselectSelectedRow]; } } @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXCookiesViewController.h ================================================ // // FLEXCookiesViewController.h // FLEX // // Created by Rich Robinson on 19/10/2015. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsEntry.h" #import "FLEXFilteringTableViewController.h" @interface FLEXCookiesViewController : FLEXFilteringTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXCookiesViewController.m ================================================ // // FLEXCookiesViewController.m // FLEX // // Created by Rich Robinson on 19/10/2015. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXCookiesViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXMutableListSection.h" #import "FLEXUtility.h" @interface FLEXCookiesViewController () @property (nonatomic, readonly) FLEXMutableListSection *cookies; @property (nonatomic) NSString *headerTitle; @end @implementation FLEXCookiesViewController #pragma mark - Overrides - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Cookies"; } - (NSString *)headerTitle { return self.cookies.title; } - (void)setHeaderTitle:(NSString *)headerTitle { self.cookies.customTitle = headerTitle; } - (NSArray *)makeSections { NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:) ]; NSArray *cookies = [NSHTTPCookieStorage.sharedHTTPCookieStorage.cookies sortedArrayUsingDescriptors:@[nameSortDescriptor] ]; _cookies = [FLEXMutableListSection list:cookies cellConfiguration:^(UITableViewCell *cell, NSHTTPCookie *cookie, NSInteger row) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = [cookie.name stringByAppendingFormat:@" (%@)", cookie.value]; cell.detailTextLabel.text = [cookie.domain stringByAppendingFormat:@" — %@", cookie.path]; } filterMatcher:^BOOL(NSString *filterText, NSHTTPCookie *cookie) { return [cookie.name localizedCaseInsensitiveContainsString:filterText] || [cookie.value localizedCaseInsensitiveContainsString:filterText] || [cookie.domain localizedCaseInsensitiveContainsString:filterText] || [cookie.path localizedCaseInsensitiveContainsString:filterText]; } ]; self.cookies.selectionHandler = ^(UIViewController *host, NSHTTPCookie *cookie) { [host.navigationController pushViewController:[ FLEXObjectExplorerFactory explorerViewControllerForObject:cookie ] animated:YES]; }; return @[self.cookies]; } - (void)reloadData { self.headerTitle = [NSString stringWithFormat: @"%@ cookies", @(self.cookies.filteredList.count) ]; [super reloadData]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"🍪 Cookies"; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { return [self new]; } @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXLiveObjectsController.h ================================================ // // FLEXLiveObjectsController.h // Flipboard // // Created by Ryan Olson on 5/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" #import "FLEXGlobalsEntry.h" @interface FLEXLiveObjectsController : FLEXTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXLiveObjectsController.m ================================================ // // FLEXLiveObjectsController.m // Flipboard // // Created by Ryan Olson on 5/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXLiveObjectsController.h" #import "FLEXHeapEnumerator.h" #import "FLEXObjectListViewController.h" #import "FLEXUtility.h" #import "FLEXScopeCarousel.h" #import "FLEXTableView.h" #import static const NSInteger kFLEXLiveObjectsSortAlphabeticallyIndex = 0; static const NSInteger kFLEXLiveObjectsSortByCountIndex = 1; static const NSInteger kFLEXLiveObjectsSortBySizeIndex = 2; @interface FLEXLiveObjectsController () @property (nonatomic) NSDictionary *instanceCountsForClassNames; @property (nonatomic) NSDictionary *instanceSizesForClassNames; @property (nonatomic, readonly) NSArray *allClassNames; @property (nonatomic) NSArray *filteredClassNames; @property (nonatomic) NSString *headerTitle; @end @implementation FLEXLiveObjectsController - (void)viewDidLoad { [super viewDidLoad]; self.showsSearchBar = YES; self.showSearchBarInitially = YES; self.activatesSearchBarAutomatically = YES; self.searchBarDebounceInterval = kFLEXDebounceInstant; self.showsCarousel = YES; self.carousel.items = @[@"A→Z", @"Count", @"Size"]; self.refreshControl = [UIRefreshControl new]; [self.refreshControl addTarget:self action:@selector(refreshControlDidRefresh:) forControlEvents:UIControlEventValueChanged]; [self reloadTableData]; } - (NSArray *)allClassNames { return self.instanceCountsForClassNames.allKeys; } - (void)reloadTableData { // Set up a CFMutableDictionary with class pointer keys and NSUInteger values. // We abuse CFMutableDictionary a little to have primitive keys through judicious casting, but it gets the job done. // The dictionary is intialized with a 0 count for each class so that it doesn't have to expand during enumeration. // While it might be a little cleaner to populate an NSMutableDictionary with class name string keys to NSNumber counts, // we choose the CF/primitives approach because it lets us enumerate the objects in the heap without allocating any memory during enumeration. // The alternative of creating one NSString/NSNumber per object on the heap ends up polluting the count of live objects quite a bit. unsigned int classCount = 0; Class *classes = objc_copyClassList(&classCount); CFMutableDictionaryRef mutableCountsForClasses = CFDictionaryCreateMutable(NULL, classCount, NULL, NULL); for (unsigned int i = 0; i < classCount; i++) { CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)classes[i], (const void *)0); } // Enumerate all objects on the heap to build the counts of instances for each class. [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class actualClass) { NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)actualClass); instanceCount++; CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)actualClass, (const void *)instanceCount); }]; // Convert our CF primitive dictionary into a nicer mapping of class name strings to counts that we will use as the table's model. NSMutableDictionary *mutableCountsForClassNames = [NSMutableDictionary new]; NSMutableDictionary *mutableSizesForClassNames = [NSMutableDictionary new]; for (unsigned int i = 0; i < classCount; i++) { Class class = classes[i]; NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)(class)); NSString *className = @(class_getName(class)); if (instanceCount > 0) { [mutableCountsForClassNames setObject:@(instanceCount) forKey:className]; } [mutableSizesForClassNames setObject:@(class_getInstanceSize(class)) forKey:className]; } free(classes); self.instanceCountsForClassNames = mutableCountsForClassNames; self.instanceSizesForClassNames = mutableSizesForClassNames; [self updateSearchResults:nil]; } - (void)refreshControlDidRefresh:(id)sender { [self reloadTableData]; [self.refreshControl endRefreshing]; } - (void)updateHeaderTitle { NSUInteger totalCount = 0; NSUInteger totalSize = 0; for (NSString *className in self.allClassNames) { NSUInteger count = self.instanceCountsForClassNames[className].unsignedIntegerValue; totalCount += count; totalSize += count * self.instanceSizesForClassNames[className].unsignedIntegerValue; } NSUInteger filteredCount = 0; NSUInteger filteredSize = 0; for (NSString *className in self.filteredClassNames) { NSUInteger count = self.instanceCountsForClassNames[className].unsignedIntegerValue; filteredCount += count; filteredSize += count * self.instanceSizesForClassNames[className].unsignedIntegerValue; } if (filteredCount == totalCount) { // Unfiltered self.headerTitle = [NSString stringWithFormat:@"%@ objects, %@", @(totalCount), [NSByteCountFormatter stringFromByteCount:totalSize countStyle:NSByteCountFormatterCountStyleFile ] ]; } else { self.headerTitle = [NSString stringWithFormat:@"%@ of %@ objects, %@", @(filteredCount), @(totalCount), [NSByteCountFormatter stringFromByteCount:filteredSize countStyle:NSByteCountFormatterCountStyleFile ] ]; } } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"💩 Heap Objects"; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { FLEXLiveObjectsController *liveObjectsViewController = [self new]; liveObjectsViewController.title = [self globalsEntryTitle:row]; return liveObjectsViewController; } #pragma mark - Search bar - (void)updateSearchResults:(NSString *)filter { NSInteger selectedScope = self.selectedScope; if (filter.length) { NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] %@", filter]; self.filteredClassNames = [self.allClassNames filteredArrayUsingPredicate:searchPredicate]; } else { self.filteredClassNames = self.allClassNames; } if (selectedScope == kFLEXLiveObjectsSortAlphabeticallyIndex) { self.filteredClassNames = [self.filteredClassNames sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; } else if (selectedScope == kFLEXLiveObjectsSortByCountIndex) { self.filteredClassNames = [self.filteredClassNames sortedArrayUsingComparator:^NSComparisonResult(NSString *className1, NSString *className2) { NSNumber *count1 = self.instanceCountsForClassNames[className1]; NSNumber *count2 = self.instanceCountsForClassNames[className2]; // Reversed for descending counts. return [count2 compare:count1]; }]; } else if (selectedScope == kFLEXLiveObjectsSortBySizeIndex) { self.filteredClassNames = [self.filteredClassNames sortedArrayUsingComparator:^NSComparisonResult(NSString *className1, NSString *className2) { NSNumber *count1 = self.instanceCountsForClassNames[className1]; NSNumber *count2 = self.instanceCountsForClassNames[className2]; NSNumber *size1 = self.instanceSizesForClassNames[className1]; NSNumber *size2 = self.instanceSizesForClassNames[className2]; // Reversed for descending sizes. return [@(count2.integerValue * size2.integerValue) compare:@(count1.integerValue * size1.integerValue)]; }]; } [self updateHeaderTitle]; [self.tableView reloadData]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.filteredClassNames.count; } - (UITableViewCell *)tableView:(__kindof UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDefaultCell forIndexPath:indexPath ]; NSString *className = self.filteredClassNames[indexPath.row]; NSNumber *count = self.instanceCountsForClassNames[className]; NSNumber *size = self.instanceSizesForClassNames[className]; unsigned long totalSize = count.unsignedIntegerValue * size.unsignedIntegerValue; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = [NSString stringWithFormat:@"%@ (%ld, %@)", className, (long)[count integerValue], [NSByteCountFormatter stringFromByteCount:totalSize countStyle:NSByteCountFormatterCountStyleFile ] ]; return cell; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return self.headerTitle; } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSString *className = self.filteredClassNames[indexPath.row]; UIViewController *instances = [FLEXObjectListViewController instancesOfClassWithName:className retained:YES ]; [self.navigationController pushViewController:instances animated:YES]; } @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXObjectListViewController.h ================================================ // // FLEXObjectListViewController.h // Flipboard // // Created by Ryan Olson on 5/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXFilteringTableViewController.h" @interface FLEXObjectListViewController : FLEXFilteringTableViewController /// This will either return a list of the instances, or take you straight /// to the explorer itself if there is only one instance. + (UIViewController *)instancesOfClassWithName:(NSString *)className retained:(BOOL)retain; + (instancetype)subclassesOfClassWithName:(NSString *)className; + (instancetype)objectsWithReferencesToObject:(id)object retained:(BOOL)retain; @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXObjectListViewController.m ================================================ // // FLEXObjectListViewController.m // Flipboard // // Created by Ryan Olson on 5/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXObjectListViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXMutableListSection.h" #import "FLEXRuntimeUtility.h" #import "FLEXUtility.h" #import "FLEXHeapEnumerator.h" #import "FLEXObjectRef.h" #import "NSString+FLEX.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXTableViewCell.h" #import typedef NS_ENUM(NSUInteger, FLEXObjectReferenceSection) { FLEXObjectReferenceSectionMain, FLEXObjectReferenceSectionAutoLayout, FLEXObjectReferenceSectionKVO, FLEXObjectReferenceSectionFLEX, FLEXObjectReferenceSectionCount }; @interface FLEXObjectListViewController () @property (nonatomic, readonly, class) NSArray *defaultPredicates; @property (nonatomic, readonly, class) NSArray *defaultSectionTitles; @property (nonatomic, copy) NSArray *sections; @property (nonatomic, copy) NSArray *allSections; @property (nonatomic, readonly, nullable) NSArray *references; @property (nonatomic, readonly) NSArray *predicates; @property (nonatomic, readonly) NSArray *sectionTitles; @end @implementation FLEXObjectListViewController @dynamic sections, allSections; #pragma mark - Reference Grouping + (NSPredicate *)defaultPredicateForSection:(NSInteger)section { // These are the types of references that we typically don't care about. // We want this list of "object-ivar pairs" split into two sections. BOOL(^isKVORelated)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) { NSString *row = ref.reference; return [row isEqualToString:@"__NSObserver object"] || [row isEqualToString:@"_CFXNotificationObjcObserverRegistration _object"]; }; /// These are common AutoLayout related references we also rarely care about. BOOL(^isConstraintRelated)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) { static NSSet *ignored = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ ignored = [NSSet setWithArray:@[ @"NSLayoutConstraint _container", @"NSContentSizeLayoutConstraint _container", @"NSAutoresizingMaskLayoutConstraint _container", @"MASViewConstraint _installedView", @"MASLayoutConstraint _container", @"MASViewAttribute _view" ]]; }); NSString *row = ref.reference; return ([row hasPrefix:@"NSLayout"] && [row hasSuffix:@" _referenceItem"]) || ([row hasPrefix:@"NSIS"] && [row hasSuffix:@" _delegate"]) || ([row hasPrefix:@"_NSAutoresizingMask"] && [row hasSuffix:@" _referenceItem"]) || [ignored containsObject:row]; }; /// These are FLEX classes and usually you aren't looking for FLEX references inside FLEX itself BOOL(^isFLEXClass)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) { return [ref.reference hasPrefix:@"FLEX"]; }; BOOL(^isEssential)(FLEXObjectRef *, NSDictionary *) = ^BOOL(FLEXObjectRef *ref, NSDictionary *bindings) { return !( isKVORelated(ref, bindings) || isConstraintRelated(ref, bindings) || isFLEXClass(ref, bindings) ); }; switch (section) { case FLEXObjectReferenceSectionMain: return [NSPredicate predicateWithBlock:isEssential]; case FLEXObjectReferenceSectionAutoLayout: return [NSPredicate predicateWithBlock:isConstraintRelated]; case FLEXObjectReferenceSectionKVO: return [NSPredicate predicateWithBlock:isKVORelated]; case FLEXObjectReferenceSectionFLEX: return [NSPredicate predicateWithBlock:isFLEXClass]; default: return nil; } } + (NSArray *)defaultPredicates { return [NSArray flex_forEachUpTo:FLEXObjectReferenceSectionCount map:^id(NSUInteger i) { return [self defaultPredicateForSection:i]; }]; } + (NSArray *)defaultSectionTitles { return @[ @"", @"AutoLayout", @"Key-Value Observing", @"FLEX" ]; } #pragma mark - Initialization - (id)initWithReferences:(nullable NSArray *)references { return [self initWithReferences:references predicates:nil sectionTitles:nil]; } - (id)initWithReferences:(NSArray *)references predicates:(NSArray *)predicates sectionTitles:(NSArray *)sectionTitles { NSParameterAssert(predicates.count == sectionTitles.count); self = [super initWithStyle:UITableViewStylePlain]; if (self) { _references = references; _predicates = predicates; _sectionTitles = sectionTitles; } return self; } + (UIViewController *)instancesOfClassWithName:(NSString *)className retained:(BOOL)retain { NSArray *references = [FLEXHeapEnumerator instancesOfClassWithName:className retained:retain ]; if (references.count == 1) { return [FLEXObjectExplorerFactory explorerViewControllerForObject:references.firstObject.object ]; } FLEXObjectListViewController *controller = [[self alloc] initWithReferences:references]; controller.title = [NSString stringWithFormat:@"%@ (%@)", className, @(references.count)]; return controller; } + (instancetype)subclassesOfClassWithName:(NSString *)className { NSArray *references = [FLEXRuntimeUtility subclassesOfClassWithName:className]; FLEXObjectListViewController *controller = [[self alloc] initWithReferences:references]; controller.title = [NSString stringWithFormat:@"Subclasses of %@ (%@)", className, @(references.count) ]; return controller; } + (instancetype)objectsWithReferencesToObject:(id)object retained:(BOOL)retain { NSArray *instances = [FLEXHeapEnumerator objectsWithReferencesToObject:object retained:retain ]; FLEXObjectListViewController *viewController = [[self alloc] initWithReferences:instances predicates:self.defaultPredicates sectionTitles:self.defaultSectionTitles ]; viewController.title = [NSString stringWithFormat:@"Referencing %@ %p", [FLEXRuntimeUtility safeClassNameForObject:object], object ]; return viewController; } #pragma mark - Overrides - (void)viewDidLoad { [super viewDidLoad]; self.showsSearchBar = YES; } - (NSArray *)makeSections { if (self.predicates.count) { return [self buildSections:self.sectionTitles predicates:self.predicates]; } else { return @[[self makeSection:self.references title:nil]]; } } #pragma mark - Private - (NSArray *)buildSections:(NSArray *)titles predicates:(NSArray *)predicates { NSParameterAssert(titles.count == predicates.count); NSParameterAssert(titles); NSParameterAssert(predicates); return [NSArray flex_forEachUpTo:titles.count map:^id(NSUInteger i) { NSArray *rows = [self.references filteredArrayUsingPredicate:predicates[i]]; return [self makeSection:rows title:titles[i]]; }]; } - (FLEXMutableListSection *)makeSection:(NSArray *)rows title:(NSString *)title { FLEXMutableListSection *section = [FLEXMutableListSection list:rows cellConfiguration:^(FLEXTableViewCell *cell, FLEXObjectRef *ref, NSInteger row) { cell.textLabel.text = ref.reference; cell.detailTextLabel.text = ref.summary; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } filterMatcher:^BOOL(NSString *filterText, FLEXObjectRef *ref) { if (ref.summary && [ref.summary localizedCaseInsensitiveContainsString:filterText]) { return YES; } return [ref.reference localizedCaseInsensitiveContainsString:filterText]; } ]; section.selectionHandler = ^(UIViewController *host, FLEXObjectRef *ref) { [host.navigationController pushViewController:[ FLEXObjectExplorerFactory explorerViewControllerForObject:ref.object ] animated:YES]; }; section.customTitle = title; return section; } @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXObjectRef.h ================================================ // // FLEXObjectRef.h // FLEX // // Created by Tanner Bennett on 7/24/18. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @interface FLEXObjectRef : NSObject /// Reference an object without affecting its lifespan or or emitting reference-counting operations. + (instancetype)unretained:(__unsafe_unretained id)object; + (instancetype)unretained:(__unsafe_unretained id)object ivar:(NSString *)ivarName; /// Reference an object and control its lifespan. + (instancetype)retained:(id)object; + (instancetype)retained:(id)object ivar:(NSString *)ivarName; /// Reference an object and conditionally choose to retain it or not. + (instancetype)referencing:(__unsafe_unretained id)object retained:(BOOL)retain; + (instancetype)referencing:(__unsafe_unretained id)object ivar:(NSString *)ivarName retained:(BOOL)retain; + (NSArray *)referencingAll:(NSArray *)objects retained:(BOOL)retain; /// Classes do not have a summary, and the reference is just the class name. + (NSArray *)referencingClasses:(NSArray *)classes; /// For example, "NSString 0x1d4085d0" or "NSLayoutConstraint _object" @property (nonatomic, readonly) NSString *reference; /// For instances, this is the result of -[FLEXRuntimeUtility summaryForObject:] /// For classes, there is no summary. @property (nonatomic, readonly) NSString *summary; @property (nonatomic, readonly, unsafe_unretained) id object; /// Retains the referenced object if it is not already retained - (void)retainObject; /// Releases the referenced object if it is already retained - (void)releaseObject; @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXObjectRef.m ================================================ // // FLEXObjectRef.m // FLEX // // Created by Tanner Bennett on 7/24/18. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXObjectRef.h" #import "FLEXRuntimeUtility.h" #import "NSArray+FLEX.h" @interface FLEXObjectRef () { /// Used to retain the object if desired id _retainer; } @property (nonatomic, readonly) BOOL wantsSummary; @end @implementation FLEXObjectRef @synthesize summary = _summary; + (instancetype)unretained:(__unsafe_unretained id)object { return [self referencing:object showSummary:YES retained:NO]; } + (instancetype)unretained:(__unsafe_unretained id)object ivar:(NSString *)ivarName { return [[self alloc] initWithObject:object ivarName:ivarName showSummary:YES retained:NO]; } + (instancetype)retained:(id)object { return [self referencing:object showSummary:YES retained:YES]; } + (instancetype)retained:(id)object ivar:(NSString *)ivarName { return [[self alloc] initWithObject:object ivarName:ivarName showSummary:YES retained:YES]; } + (instancetype)referencing:(__unsafe_unretained id)object retained:(BOOL)retain { return retain ? [self retained:object] : [self unretained:object]; } + (instancetype)referencing:(__unsafe_unretained id)object ivar:(NSString *)ivarName retained:(BOOL)retain { return retain ? [self retained:object ivar:ivarName] : [self unretained:object ivar:ivarName]; } + (instancetype)referencing:(__unsafe_unretained id)object showSummary:(BOOL)showSummary retained:(BOOL)retain { return [[self alloc] initWithObject:object ivarName:nil showSummary:showSummary retained:retain]; } + (NSArray *)referencingAll:(NSArray *)objects retained:(BOOL)retain { return [objects flex_mapped:^id(id obj, NSUInteger idx) { return [self referencing:obj showSummary:YES retained:retain]; }]; } + (NSArray *)referencingClasses:(NSArray *)classes { return [classes flex_mapped:^id(id obj, NSUInteger idx) { return [self referencing:obj showSummary:NO retained:NO]; }]; } - (id)initWithObject:(__unsafe_unretained id)object ivarName:(NSString *)ivar showSummary:(BOOL)showSummary retained:(BOOL)retain { self = [super init]; if (self) { _object = object; _wantsSummary = showSummary; if (retain) { _retainer = object; } NSString *class = [FLEXRuntimeUtility safeClassNameForObject:object]; if (ivar) { _reference = [NSString stringWithFormat:@"%@ %@", class, ivar]; } else if (showSummary) { _reference = [NSString stringWithFormat:@"%@ %p", class, object]; } else { _reference = class; } } return self; } - (NSString *)summary { if (self.wantsSummary) { if (!_summary) { _summary = [FLEXRuntimeUtility summaryForObject:self.object]; } return _summary; } else { return nil; } } - (void)retainObject { if (!_retainer) { _retainer = _object; } } - (void)releaseObject { _retainer = nil; } - (NSString *)debugDescription { return [NSString stringWithFormat:@"<%@: %@>", [self class], self.reference ]; } @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXWebViewController.h ================================================ // // FLEXWebViewController.m // Flipboard // // Created by Ryan Olson on 6/10/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @interface FLEXWebViewController : UIViewController - (id)initWithURL:(NSURL *)url; - (id)initWithText:(NSString *)text; + (BOOL)supportsPathExtension:(NSString *)extension; @end ================================================ FILE: Classes/GlobalStateExplorers/FLEXWebViewController.m ================================================ // // FLEXWebViewController.m // Flipboard // // Created by Ryan Olson on 6/10/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXWebViewController.h" #import "FLEXUtility.h" #import @interface FLEXWebViewController () @property (nonatomic) WKWebView *webView; @property (nonatomic) NSString *originalText; @end @implementation FLEXWebViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { WKWebViewConfiguration *configuration = [WKWebViewConfiguration new]; if (@available(iOS 10.0, *)) { configuration.dataDetectorTypes = WKDataDetectorTypeLink; } self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration]; self.webView.navigationDelegate = self; } return self; } - (id)initWithText:(NSString *)text { self = [self initWithNibName:nil bundle:nil]; if (self) { self.originalText = text; NSString *html = @"" "
%@
"; // Loading message for when input text takes a long time to escape NSString *loadingMessage = [NSString stringWithFormat:html, @"Loading..."]; [self.webView loadHTMLString:loadingMessage baseURL:nil]; // Escape HTML on a background thread dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *escapedText = [FLEXUtility stringByEscapingHTMLEntitiesInString:text]; NSString *htmlString = [NSString stringWithFormat:html, escapedText]; // Update webview on the main thread dispatch_async(dispatch_get_main_queue(), ^{ [self.webView loadHTMLString:htmlString baseURL:nil]; }); }); } return self; } - (id)initWithURL:(NSURL *)url { self = [self initWithNibName:nil bundle:nil]; if (self) { NSURLRequest *request = [NSURLRequest requestWithURL:url]; [self.webView loadRequest:request]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.webView]; self.webView.frame = self.view.bounds; self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (self.originalText.length > 0) { self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Copy" style:UIBarButtonItemStylePlain target:self action:@selector(copyButtonTapped:) ]; } } - (void)copyButtonTapped:(id)sender { [UIPasteboard.generalPasteboard setString:self.originalText]; } #pragma mark - WKWebView Delegate - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))handler { WKNavigationActionPolicy policy = WKNavigationActionPolicyCancel; if (navigationAction.navigationType == WKNavigationTypeOther) { // Allow the initial load policy = WKNavigationActionPolicyAllow; } else { // For clicked links, push another web view controller onto the navigation stack // so that hitting the back button works as expected. // Don't allow the current web view to handle the navigation. NSURLRequest *request = navigationAction.request; FLEXWebViewController *webVC = [[[self class] alloc] initWithURL:request.URL]; webVC.title = request.URL.absoluteString; [self.navigationController pushViewController:webVC animated:YES]; } handler(policy); } #pragma mark - Class Helpers + (BOOL)supportsPathExtension:(NSString *)extension { BOOL supported = NO; NSSet *supportedExtensions = [self webViewSupportedPathExtensions]; if ([supportedExtensions containsObject:extension.lowercaseString]) { supported = YES; } return supported; } + (NSSet *)webViewSupportedPathExtensions { static NSSet *pathExtensions = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // Note that this is not exhaustive, but all these extensions should work well in the web view. // See https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingContentforSafarioniPhone/CreatingContentforSafarioniPhone.html#//apple_ref/doc/uid/TP40006482-SW7 pathExtensions = [NSSet setWithArray:@[ @"jpg", @"jpeg", @"png", @"gif", @"pdf", @"svg", @"tiff", @"3gp", @"3gpp", @"3g2", @"3gp2", @"aiff", @"aif", @"aifc", @"cdda", @"amr", @"mp3", @"swa", @"mp4", @"mpeg", @"mpg", @"mp3", @"wav", @"bwf", @"m4a", @"m4b", @"m4p", @"mov", @"qt", @"mqv", @"m4v" ]]; }); return pathExtensions; } @end ================================================ FILE: Classes/GlobalStateExplorers/FileBrowser/FLEXActivityViewController.h ================================================ // // FLEXActivityViewController.h // FLEX // // Created by Tanner Bennett on 5/26/22. // #import NS_ASSUME_NONNULL_BEGIN /// Wraps UIActivityViewController so that it can't dismiss other view controllers @interface FLEXActivityViewController : UIActivityViewController /// @param source A \c UIVIew, \c UIBarButtonItem, or \c NSValue representing a source rect. + (id)sharing:(NSArray *)items source:(nullable id)source; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/FileBrowser/FLEXActivityViewController.m ================================================ // // FLEXActivityViewController.m // FLEX // // Created by Tanner Bennett on 5/26/22. // #import "FLEXActivityViewController.h" #import "FLEXMacros.h" @interface FLEXActivityViewController () @end @implementation FLEXActivityViewController + (id)sharing:(NSArray *)items source:(id)sender { UIViewController *shareSheet = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil ]; if (sender && UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { UIPopoverPresentationController *popover = shareSheet.popoverPresentationController; // Source view if ([sender isKindOfClass:UIView.self]) { popover.sourceView = sender; } // Source bar item if ([sender isKindOfClass:UIBarButtonItem.self]) { popover.barButtonItem = sender; } // Source rect if ([sender isKindOfClass:NSValue.self]) { CGRect rect = [sender CGRectValue]; popover.sourceRect = rect; } } return shareSheet; } @end ================================================ FILE: Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h ================================================ // // FLEXFileBrowserController.h // Flipboard // // Created by Ryan Olson on 6/9/14. // Based on previous work by Evan Doll // #import "FLEXTableViewController.h" #import "FLEXGlobalsEntry.h" @interface FLEXFileBrowserController : FLEXTableViewController + (instancetype)path:(NSString *)path; - (id)initWithPath:(NSString *)path; @end ================================================ FILE: Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.m ================================================ // // FLEXFileBrowserController.m // Flipboard // // Created by Ryan Olson on 6/9/14. // // #import "FLEXFileBrowserController.h" #import "FLEXUtility.h" #import "FLEXWebViewController.h" #import "FLEXActivityViewController.h" #import "FLEXImagePreviewViewController.h" #import "FLEXTableListViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXObjectExplorerViewController.h" #import #import "FLEXFileBrowserSearchOperation.h" @interface FLEXFileBrowserTableViewCell : UITableViewCell @end typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) { FLEXFileBrowserSortAttributeNone = 0, FLEXFileBrowserSortAttributeName, FLEXFileBrowserSortAttributeCreationDate, }; @interface FLEXFileBrowserController () @property (nonatomic, copy) NSString *path; @property (nonatomic, copy) NSArray *childPaths; @property (nonatomic) NSArray *searchPaths; @property (nonatomic) NSNumber *recursiveSize; @property (nonatomic) NSNumber *searchPathsSize; @property (nonatomic) NSOperationQueue *operationQueue; @property (nonatomic) UIDocumentInteractionController *documentController; @property (nonatomic) FLEXFileBrowserSortAttribute sortAttribute; @end @implementation FLEXFileBrowserController + (instancetype)path:(NSString *)path { return [[self alloc] initWithPath:path]; } - (id)init { return [self initWithPath:NSHomeDirectory()]; } - (id)initWithPath:(NSString *)path { self = [super init]; if (self) { self.path = path; self.title = [path lastPathComponent]; self.operationQueue = [NSOperationQueue new]; // Compute path size weakify(self) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSFileManager *fileManager = NSFileManager.defaultManager; NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:NULL]; uint64_t totalSize = [attributes fileSize]; for (NSString *fileName in [fileManager enumeratorAtPath:path]) { attributes = [fileManager attributesOfItemAtPath:[path stringByAppendingPathComponent:fileName] error:NULL]; totalSize += [attributes fileSize]; // Bail if the interested view controller has gone away if (!self) { return; } } dispatch_async(dispatch_get_main_queue(), ^{ strongify(self) self.recursiveSize = @(totalSize); [self.tableView reloadData]; }); }); [self reloadCurrentPath]; } return self; } #pragma mark - UIViewController - (void)viewDidLoad { [super viewDidLoad]; self.showsSearchBar = YES; self.searchBarDebounceInterval = kFLEXDebounceForAsyncSearch; [self addToolbarItems:@[ [[UIBarButtonItem alloc] initWithTitle:@"Sort" style:UIBarButtonItemStylePlain target:self action:@selector(sortDidTouchUpInside:)] ]]; } - (void)sortDidTouchUpInside:(UIBarButtonItem *)sortButton { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Sort" message:nil preferredStyle:UIAlertControllerStyleActionSheet]; [alertController addAction:[UIAlertAction actionWithTitle:@"None" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { [self sortWithAttribute:FLEXFileBrowserSortAttributeNone]; }]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Name" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self sortWithAttribute:FLEXFileBrowserSortAttributeName]; }]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Creation Date" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self sortWithAttribute:FLEXFileBrowserSortAttributeCreationDate]; }]]; [self presentViewController:alertController animated:YES completion:nil]; } - (void)sortWithAttribute:(FLEXFileBrowserSortAttribute)attribute { self.sortAttribute = attribute; [self reloadDisplayedPaths]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { switch (row) { case FLEXGlobalsRowBrowseBundle: return @"📁 Browse Bundle Directory"; case FLEXGlobalsRowBrowseContainer: return @"📁 Browse Container Directory"; default: return nil; } } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { switch (row) { case FLEXGlobalsRowBrowseBundle: return [[self alloc] initWithPath:NSBundle.mainBundle.bundlePath]; case FLEXGlobalsRowBrowseContainer: return [[self alloc] initWithPath:NSHomeDirectory()]; default: return [self new]; } } #pragma mark - FLEXFileBrowserSearchOperationDelegate - (void)fileBrowserSearchOperationResult:(NSArray *)searchResult size:(uint64_t)size { self.searchPaths = searchResult; self.searchPathsSize = @(size); [self.tableView reloadData]; } #pragma mark - Search bar - (void)updateSearchResults:(NSString *)newText { [self reloadDisplayedPaths]; } #pragma mark UISearchControllerDelegate - (void)willDismissSearchController:(UISearchController *)searchController { [self.operationQueue cancelAllOperations]; [self reloadCurrentPath]; [self.tableView reloadData]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.searchController.isActive ? self.searchPaths.count : self.childPaths.count; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { BOOL isSearchActive = self.searchController.isActive; NSNumber *currentSize = isSearchActive ? self.searchPathsSize : self.recursiveSize; NSArray *currentPaths = isSearchActive ? self.searchPaths : self.childPaths; NSString *sizeString = nil; if (!currentSize) { sizeString = @"Computing size…"; } else { sizeString = [NSByteCountFormatter stringFromByteCount:[currentSize longLongValue] countStyle:NSByteCountFormatterCountStyleFile]; } return [NSString stringWithFormat:@"%lu files (%@)", (unsigned long)currentPaths.count, sizeString]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *fullPath = [self filePathAtIndexPath:indexPath]; NSDictionary *attributes = [NSFileManager.defaultManager attributesOfItemAtPath:fullPath error:NULL]; BOOL isDirectory = [attributes.fileType isEqual:NSFileTypeDirectory]; NSString *subtitle = nil; if (isDirectory) { NSUInteger count = [NSFileManager.defaultManager contentsOfDirectoryAtPath:fullPath error:NULL].count; subtitle = [NSString stringWithFormat:@"%lu item%@", (unsigned long)count, (count == 1 ? @"" : @"s")]; } else { NSString *sizeString = [NSByteCountFormatter stringFromByteCount:attributes.fileSize countStyle:NSByteCountFormatterCountStyleFile]; subtitle = [NSString stringWithFormat:@"%@ - %@", sizeString, attributes.fileModificationDate ?: @"Never modified"]; } static NSString *textCellIdentifier = @"textCell"; static NSString *imageCellIdentifier = @"imageCell"; UITableViewCell *cell = nil; // Separate image and text only cells because otherwise the separator lines get out-of-whack on image cells reused with text only. UIImage *image = [UIImage imageWithContentsOfFile:fullPath]; NSString *cellIdentifier = image ? imageCellIdentifier : textCellIdentifier; if (!cell) { cell = [[FLEXFileBrowserTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier]; cell.textLabel.font = UIFont.flex_defaultTableCellFont; cell.detailTextLabel.font = UIFont.flex_defaultTableCellFont; cell.detailTextLabel.textColor = UIColor.grayColor; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } NSString *cellTitle = [fullPath lastPathComponent]; cell.textLabel.text = cellTitle; cell.detailTextLabel.text = subtitle; if (image) { cell.imageView.contentMode = UIViewContentModeScaleAspectFit; cell.imageView.image = image; } return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; NSString *fullPath = [self filePathAtIndexPath:indexPath]; NSString *subpath = fullPath.lastPathComponent; NSString *pathExtension = subpath.pathExtension; BOOL isDirectory = NO; BOOL stillExists = [NSFileManager.defaultManager fileExistsAtPath:fullPath isDirectory:&isDirectory]; UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; UIImage *image = cell.imageView.image; if (!stillExists) { [FLEXAlert showAlert:@"File Not Found" message:@"The file at the specified path no longer exists." from:self]; [self reloadDisplayedPaths]; return; } UIViewController *drillInViewController = nil; if (isDirectory) { drillInViewController = [[[self class] alloc] initWithPath:fullPath]; } else if (image) { drillInViewController = [FLEXImagePreviewViewController forImage:image]; } else { NSData *fileData = [NSData dataWithContentsOfFile:fullPath]; if (!fileData.length) { [FLEXAlert showAlert:@"Empty File" message:@"No data returned from the file." from:self]; return; } // Special case keyed archives, json, and plists to get more readable data. NSString *prettyString = nil; if ([pathExtension isEqualToString:@"json"]) { prettyString = [FLEXUtility prettyJSONStringFromData:fileData]; } else { // Try to decode an archived object, regardless of file extension NSKeyedUnarchiver *unarchiver = ({ NSKeyedUnarchiver *obj = nil; if (@available(iOS 12.0, *)) { obj = [[NSKeyedUnarchiver alloc] initForReadingFromData:fileData error:nil]; } else { obj = [[NSKeyedUnarchiver alloc] initForReadingWithData:fileData]; } obj.requiresSecureCoding = NO; obj; }); id object = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey]; // Try to decode other things instead object = object ?: [NSPropertyListSerialization propertyListWithData:fileData options:0 format:NULL error:NULL ] ?: [NSDictionary dictionaryWithContentsOfFile:fullPath] ?: [NSArray arrayWithContentsOfFile:fullPath]; if (object) { drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:object]; } else { // Is it possibly a mach-O file? if (fileData.length > sizeof(struct mach_header_64)) { struct mach_header_64 header; [fileData getBytes:&header length:sizeof(struct mach_header_64)]; // Does it have the mach header magic number? if (header.magic == MH_MAGIC_64) { // See if we can get some classes out of it... unsigned int count = 0; const char **classList = objc_copyClassNamesForImage( fullPath.UTF8String, &count ); if (count > 0) { NSArray *classNames = [NSArray flex_forEachUpTo:count map:^id(NSUInteger i) { return objc_getClass(classList[i]); }]; drillInViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:classNames]; } } } } } if (prettyString.length) { drillInViewController = [[FLEXWebViewController alloc] initWithText:prettyString]; } else if ([FLEXWebViewController supportsPathExtension:pathExtension]) { drillInViewController = [[FLEXWebViewController alloc] initWithURL:[NSURL fileURLWithPath:fullPath]]; } else if ([FLEXTableListViewController supportsExtension:pathExtension]) { drillInViewController = [[FLEXTableListViewController alloc] initWithPath:fullPath]; } else if (!drillInViewController) { NSString *fileString = [NSString stringWithUTF8String:fileData.bytes]; if (fileString.length) { drillInViewController = [[FLEXWebViewController alloc] initWithText:fileString]; } } } if (drillInViewController) { drillInViewController.title = subpath.lastPathComponent; [self.navigationController pushViewController:drillInViewController animated:YES]; } else { // Share the file otherwise [self openFileController:fullPath]; } } - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { UIMenuItem *rename = [[UIMenuItem alloc] initWithTitle:@"Rename" action:@selector(fileBrowserRename:)]; UIMenuItem *delete = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(fileBrowserDelete:)]; UIMenuItem *copyPath = [[UIMenuItem alloc] initWithTitle:@"Copy Path" action:@selector(fileBrowserCopyPath:)]; UIMenuItem *share = [[UIMenuItem alloc] initWithTitle:@"Share" action:@selector(fileBrowserShare:)]; UIMenuController.sharedMenuController.menuItems = @[rename, delete, copyPath, share]; return YES; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { return action == @selector(fileBrowserDelete:) || action == @selector(fileBrowserRename:) || action == @selector(fileBrowserCopyPath:) || action == @selector(fileBrowserShare:); } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { // Empty, but has to exist for the menu to show // The table view only calls this method for actions in the UIResponderStandardEditActions informal protocol. // Since our actions are outside of that protocol, we need to manually handle the action forwarding from the cells. } - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) { weakify(self) return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu *(NSArray *suggestedActions) { UITableViewCell * const cell = [tableView cellForRowAtIndexPath:indexPath]; UIAction *rename = [UIAction actionWithTitle:@"Rename" image:nil identifier:@"Rename" handler:^(UIAction *action) { strongify(self) [self fileBrowserRename:cell]; } ]; UIAction *delete = [UIAction actionWithTitle:@"Delete" image:nil identifier:@"Delete" handler:^(UIAction *action) { strongify(self) [self fileBrowserDelete:cell]; } ]; UIAction *copyPath = [UIAction actionWithTitle:@"Copy Path" image:nil identifier:@"Copy Path" handler:^(UIAction *action) { strongify(self) [self fileBrowserCopyPath:cell]; } ]; UIAction *share = [UIAction actionWithTitle:@"Share" image:nil identifier:@"Share" handler:^(UIAction *action) { strongify(self) [self fileBrowserShare:cell]; } ]; return [UIMenu menuWithTitle:@"Manage File" image:nil identifier:@"Manage File" options:UIMenuOptionsDisplayInline children:@[rename, delete, copyPath, share] ]; } ]; } - (void)openFileController:(NSString *)fullPath { UIDocumentInteractionController *controller = [UIDocumentInteractionController new]; controller.URL = [NSURL fileURLWithPath:fullPath]; [controller presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES]; self.documentController = controller; } - (void)fileBrowserRename:(UITableViewCell *)sender { NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; NSString *fullPath = [self filePathAtIndexPath:indexPath]; BOOL stillExists = [NSFileManager.defaultManager fileExistsAtPath:self.path isDirectory:NULL]; if (stillExists) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title([NSString stringWithFormat:@"Rename %@?", fullPath.lastPathComponent]); make.configuredTextField(^(UITextField *textField) { textField.placeholder = @"New file name"; textField.text = fullPath.lastPathComponent; }); make.button(@"Rename").handler(^(NSArray *strings) { NSString *newFileName = strings.firstObject; NSString *newPath = [fullPath.stringByDeletingLastPathComponent stringByAppendingPathComponent:newFileName]; [NSFileManager.defaultManager moveItemAtPath:fullPath toPath:newPath error:NULL]; [self reloadDisplayedPaths]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } else { [FLEXAlert showAlert:@"File Removed" message:@"The file at the specified path no longer exists." from:self]; } } - (void)fileBrowserDelete:(UITableViewCell *)sender { NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; NSString *fullPath = [self filePathAtIndexPath:indexPath]; BOOL isDirectory = NO; BOOL stillExists = [NSFileManager.defaultManager fileExistsAtPath:fullPath isDirectory:&isDirectory]; if (stillExists) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Confirm Deletion"); make.message([NSString stringWithFormat: @"The %@ '%@' will be deleted. This operation cannot be undone", (isDirectory ? @"directory" : @"file"), fullPath.lastPathComponent ]); make.button(@"Delete").destructiveStyle().handler(^(NSArray *strings) { [NSFileManager.defaultManager removeItemAtPath:fullPath error:NULL]; [self reloadDisplayedPaths]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } else { [FLEXAlert showAlert:@"File Removed" message:@"The file at the specified path no longer exists." from:self]; } } - (void)fileBrowserCopyPath:(UITableViewCell *)sender { NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; NSString *fullPath = [self filePathAtIndexPath:indexPath]; UIPasteboard.generalPasteboard.string = fullPath; } - (void)fileBrowserShare:(UITableViewCell *)sender { NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; NSString *pathString = [self filePathAtIndexPath:indexPath]; NSURL *filePath = [NSURL fileURLWithPath:pathString]; BOOL isDirectory = NO; [NSFileManager.defaultManager fileExistsAtPath:pathString isDirectory:&isDirectory]; if (isDirectory) { // UIDocumentInteractionController for folders [self openFileController:pathString]; } else { // Share sheet for files UIViewController *shareSheet = [FLEXActivityViewController sharing:@[filePath] source:sender]; [self presentViewController:shareSheet animated:true completion:nil]; } } - (void)reloadDisplayedPaths { if (self.searchController.isActive) { [self updateSearchPaths]; } else { [self reloadCurrentPath]; [self.tableView reloadData]; } } - (void)reloadCurrentPath { NSMutableArray *childPaths = [NSMutableArray new]; NSArray *subpaths = [NSFileManager.defaultManager contentsOfDirectoryAtPath:self.path error:NULL]; for (NSString *subpath in subpaths) { [childPaths addObject:[self.path stringByAppendingPathComponent:subpath]]; } if (self.sortAttribute != FLEXFileBrowserSortAttributeNone) { [childPaths sortUsingComparator:^NSComparisonResult(NSString *path1, NSString *path2) { switch (self.sortAttribute) { case FLEXFileBrowserSortAttributeNone: // invalid state return NSOrderedSame; case FLEXFileBrowserSortAttributeName: return [path1 compare:path2]; case FLEXFileBrowserSortAttributeCreationDate: { NSDictionary *path1Attributes = [NSFileManager.defaultManager attributesOfItemAtPath:path1 error:NULL]; NSDictionary *path2Attributes = [NSFileManager.defaultManager attributesOfItemAtPath:path2 error:NULL]; NSDate *path1Date = path1Attributes[NSFileCreationDate]; NSDate *path2Date = path2Attributes[NSFileCreationDate]; return [path1Date compare:path2Date]; } } }]; } self.childPaths = childPaths; } - (void)updateSearchPaths { self.searchPaths = nil; self.searchPathsSize = nil; //clear pre search request and start a new one [self.operationQueue cancelAllOperations]; FLEXFileBrowserSearchOperation *newOperation = [[FLEXFileBrowserSearchOperation alloc] initWithPath:self.path searchString:self.searchText]; newOperation.delegate = self; [self.operationQueue addOperation:newOperation]; } - (NSString *)filePathAtIndexPath:(NSIndexPath *)indexPath { return self.searchController.isActive ? self.searchPaths[indexPath.row] : self.childPaths[indexPath.row]; } @end @implementation FLEXFileBrowserTableViewCell - (void)forwardAction:(SEL)action withSender:(id)sender { id target = [self.nextResponder targetForAction:action withSender:sender]; [UIApplication.sharedApplication sendAction:action to:target from:self forEvent:nil]; } - (void)fileBrowserRename:(UIMenuController *)sender { [self forwardAction:_cmd withSender:sender]; } - (void)fileBrowserDelete:(UIMenuController *)sender { [self forwardAction:_cmd withSender:sender]; } - (void)fileBrowserCopyPath:(UIMenuController *)sender { [self forwardAction:_cmd withSender:sender]; } - (void)fileBrowserShare:(UIMenuController *)sender { [self forwardAction:_cmd withSender:sender]; } @end ================================================ FILE: Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserSearchOperation.h ================================================ // // FLEXFileBrowserSearchOperation.h // FLEX // // Created by 啟倫 陳 on 2014/8/4. // Copyright (c) 2014年 f. All rights reserved. // #import @protocol FLEXFileBrowserSearchOperationDelegate; @interface FLEXFileBrowserSearchOperation : NSOperation @property (nonatomic, weak) id delegate; - (id)initWithPath:(NSString *)currentPath searchString:(NSString *)searchString; @end @protocol FLEXFileBrowserSearchOperationDelegate - (void)fileBrowserSearchOperationResult:(NSArray *)searchResult size:(uint64_t)size; @end ================================================ FILE: Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserSearchOperation.m ================================================ // // FLEXFileBrowserSearchOperation.m // FLEX // // Created by 啟倫 陳 on 2014/8/4. // Copyright (c) 2014年 f. All rights reserved. // #import "FLEXFileBrowserSearchOperation.h" @implementation NSMutableArray (FLEXStack) - (void)flex_push:(id)anObject { [self addObject:anObject]; } - (id)flex_pop { id anObject = self.lastObject; [self removeLastObject]; return anObject; } @end @interface FLEXFileBrowserSearchOperation () @property (nonatomic) NSString *path; @property (nonatomic) NSString *searchString; @end @implementation FLEXFileBrowserSearchOperation #pragma mark - private - (uint64_t)totalSizeAtPath:(NSString *)path { NSFileManager *fileManager = NSFileManager.defaultManager; NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:NULL]; uint64_t totalSize = [attributes fileSize]; for (NSString *fileName in [fileManager enumeratorAtPath:path]) { attributes = [fileManager attributesOfItemAtPath:[path stringByAppendingPathComponent:fileName] error:NULL]; totalSize += [attributes fileSize]; } return totalSize; } #pragma mark - instance method - (id)initWithPath:(NSString *)currentPath searchString:(NSString *)searchString { self = [super init]; if (self) { self.path = currentPath; self.searchString = searchString; } return self; } #pragma mark - methods to override - (void)main { NSFileManager *fileManager = NSFileManager.defaultManager; NSMutableArray *searchPaths = [NSMutableArray new]; NSMutableDictionary *sizeMapping = [NSMutableDictionary new]; uint64_t totalSize = 0; NSMutableArray *stack = [NSMutableArray new]; [stack flex_push:self.path]; //recursive found all match searchString paths, and precomputing there size while (stack.count) { NSString *currentPath = [stack flex_pop]; NSArray *directoryPath = [fileManager contentsOfDirectoryAtPath:currentPath error:nil]; for (NSString *subPath in directoryPath) { NSString *fullPath = [currentPath stringByAppendingPathComponent:subPath]; if ([[subPath lowercaseString] rangeOfString:[self.searchString lowercaseString]].location != NSNotFound) { [searchPaths addObject:fullPath]; if (!sizeMapping[fullPath]) { uint64_t fullPathSize = [self totalSizeAtPath:fullPath]; totalSize += fullPathSize; [sizeMapping setObject:@(fullPathSize) forKey:fullPath]; } } BOOL isDirectory; if ([fileManager fileExistsAtPath:fullPath isDirectory:&isDirectory] && isDirectory) { [stack flex_push:fullPath]; } if ([self isCancelled]) { return; } } } //sort NSArray *sortedArray = [searchPaths sortedArrayUsingComparator:^NSComparisonResult(NSString *path1, NSString *path2) { uint64_t pathSize1 = [sizeMapping[path1] unsignedLongLongValue]; uint64_t pathSize2 = [sizeMapping[path2] unsignedLongLongValue]; if (pathSize1 < pathSize2) { return NSOrderedAscending; } else if (pathSize1 > pathSize2) { return NSOrderedDescending; } else { return NSOrderedSame; } }]; if ([self isCancelled]) { return; } dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate fileBrowserSearchOperationResult:sortedArray size:totalSize]; }); } @end ================================================ FILE: Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h ================================================ // // FLEXGlobalsEntry.h // FLEX // // Created by Javier Soto on 7/26/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) { FLEXGlobalsRowProcessInfo, FLEXGlobalsRowNetworkHistory, FLEXGlobalsRowSystemLog, FLEXGlobalsRowLiveObjects, FLEXGlobalsRowAddressInspector, FLEXGlobalsRowCookies, FLEXGlobalsRowBrowseRuntime, FLEXGlobalsRowAppKeychainItems, FLEXGlobalsRowPushNotifications, FLEXGlobalsRowAppDelegate, FLEXGlobalsRowRootViewController, FLEXGlobalsRowUserDefaults, FLEXGlobalsRowMainBundle, FLEXGlobalsRowBrowseBundle, FLEXGlobalsRowBrowseContainer, FLEXGlobalsRowApplication, FLEXGlobalsRowKeyWindow, FLEXGlobalsRowMainScreen, FLEXGlobalsRowCurrentDevice, FLEXGlobalsRowPasteboard, FLEXGlobalsRowURLSession, FLEXGlobalsRowURLCache, FLEXGlobalsRowNotificationCenter, FLEXGlobalsRowMenuController, FLEXGlobalsRowFileManager, FLEXGlobalsRowTimeZone, FLEXGlobalsRowLocale, FLEXGlobalsRowCalendar, FLEXGlobalsRowMainRunLoop, FLEXGlobalsRowMainThread, FLEXGlobalsRowOperationQueue, FLEXGlobalsRowCount }; typedef NSString * _Nonnull (^FLEXGlobalsEntryNameFuture)(void); /// Simply return a view controller to be pushed on the navigation stack typedef UIViewController * _Nullable (^FLEXGlobalsEntryViewControllerFuture)(void); /// Do something like present an alert, then use the host /// view controller to present or push another view controller. typedef void (^FLEXGlobalsEntryRowAction)(__kindof UITableViewController * _Nonnull host); /// For view controllers to conform to to indicate they support being used /// in the globals table view controller. These methods help create concrete entries. /// /// Previously, the concrete entries relied on "futures" for the view controller and title. /// With this protocol, the conforming class itself can act as a future, since the methods /// will not be invoked until the title and view controller / row action are needed. /// /// Entries can implement \c globalsEntryViewController: to unconditionally provide a /// view controller, or \c globalsEntryRowAction: to conditionally provide one and /// perform some action (such as present an alert) if no view controller is available, /// or both if there is a mix of rows where some are guaranteed to work and some are not. /// Where both are implemented, \c globalsEntryRowAction: takes precedence; if it returns /// an action for the requested row, that will be used instead of \c globalsEntryViewController: @protocol FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row; // Must respond to at least one of the below. // globalsEntryRowAction: takes precedence if both are implemented. @optional + (nullable UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row; + (nullable FLEXGlobalsEntryRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row; @end @interface FLEXGlobalsEntry : NSObject @property (nonatomic, readonly, nonnull) FLEXGlobalsEntryNameFuture entryNameFuture; @property (nonatomic, readonly, nullable) FLEXGlobalsEntryViewControllerFuture viewControllerFuture; @property (nonatomic, readonly, nullable) FLEXGlobalsEntryRowAction rowAction; + (instancetype)entryWithEntry:(Class)entry row:(FLEXGlobalsRow)row; + (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture viewControllerFuture:(FLEXGlobalsEntryViewControllerFuture)viewControllerFuture; + (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture action:(FLEXGlobalsEntryRowAction)rowSelectedAction; @end @interface NSObject (FLEXGlobalsEntry) /// @return The result of passing self to +[FLEXGlobalsEntry entryWithEntry:] /// if the class conforms to FLEXGlobalsEntry, else, nil. + (nullable FLEXGlobalsEntry *)flex_concreteGlobalsEntry:(FLEXGlobalsRow)row; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.m ================================================ // // FLEXGlobalsEntry.m // FLEX // // Created by Javier Soto on 7/26/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsEntry.h" @implementation FLEXGlobalsEntry + (instancetype)entryWithEntry:(Class)cls row:(FLEXGlobalsRow)row { BOOL providesVCs = [cls respondsToSelector:@selector(globalsEntryViewController:)]; BOOL providesActions = [cls respondsToSelector:@selector(globalsEntryRowAction:)]; NSParameterAssert(cls); NSParameterAssert(providesVCs || providesActions); FLEXGlobalsEntry *entry = [self new]; entry->_entryNameFuture = ^{ return [cls globalsEntryTitle:row]; }; if (providesVCs) { id action = providesActions ? [cls globalsEntryRowAction:row] : nil; if (action) { entry->_rowAction = action; } else { entry->_viewControllerFuture = ^{ return [cls globalsEntryViewController:row]; }; } } else { entry->_rowAction = [cls globalsEntryRowAction:row]; } return entry; } + (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture viewControllerFuture:(FLEXGlobalsEntryViewControllerFuture)viewControllerFuture { NSParameterAssert(nameFuture); NSParameterAssert(viewControllerFuture); FLEXGlobalsEntry *entry = [self new]; entry->_entryNameFuture = [nameFuture copy]; entry->_viewControllerFuture = [viewControllerFuture copy]; return entry; } + (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture action:(FLEXGlobalsEntryRowAction)rowSelectedAction { NSParameterAssert(nameFuture); NSParameterAssert(rowSelectedAction); FLEXGlobalsEntry *entry = [self new]; entry->_entryNameFuture = [nameFuture copy]; entry->_rowAction = [rowSelectedAction copy]; return entry; } @end @interface FLEXGlobalsEntry (Debugging) @property (nonatomic, readonly) NSString *name; @end @implementation FLEXGlobalsEntry (Debugging) - (NSString *)name { return self.entryNameFuture(); } @end #pragma mark - flex_concreteGlobalsEntry @implementation NSObject (FLEXGlobalsEntry) + (FLEXGlobalsEntry *)flex_concreteGlobalsEntry:(FLEXGlobalsRow)row { if ([self conformsToProtocol:@protocol(FLEXGlobalsEntry)]) { return [FLEXGlobalsEntry entryWithEntry:self row:row]; } return nil; } @end ================================================ FILE: Classes/GlobalStateExplorers/Globals/FLEXGlobalsSection.h ================================================ // // FLEXGlobalsSection.h // FLEX // // Created by Tanner Bennett on 7/11/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewSection.h" #import "FLEXGlobalsEntry.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXGlobalsSection : FLEXTableViewSection + (instancetype)title:(NSString *)title rows:(NSArray *)rows; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/Globals/FLEXGlobalsSection.m ================================================ // // FLEXGlobalsSection.m // FLEX // // Created by Tanner Bennett on 7/11/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsSection.h" #import "NSArray+FLEX.h" #import "UIFont+FLEX.h" @interface FLEXGlobalsSection () /// Filtered rows @property (nonatomic) NSArray *rows; /// Unfiltered rows @property (nonatomic) NSArray *allRows; @end @implementation FLEXGlobalsSection #pragma mark - Initialization + (instancetype)title:(NSString *)title rows:(NSArray *)rows { FLEXGlobalsSection *s = [self new]; s->_title = title; s.allRows = rows; return s; } - (void)setAllRows:(NSArray *)allRows { _allRows = allRows.copy; [self reloadData]; } #pragma mark - Overrides - (NSInteger)numberOfRows { return self.rows.count; } - (void)setFilterText:(NSString *)filterText { super.filterText = filterText; [self reloadData]; } - (void)reloadData { NSString *filterText = self.filterText; if (filterText.length) { self.rows = [self.allRows flex_filtered:^BOOL(FLEXGlobalsEntry *entry, NSUInteger idx) { return [entry.entryNameFuture() localizedCaseInsensitiveContainsString:filterText]; }]; } else { self.rows = self.allRows; } } - (BOOL)canSelectRow:(NSInteger)row { return YES; } - (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row { return (id)self.rows[row].rowAction; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { return self.rows[row].viewControllerFuture ? self.rows[row].viewControllerFuture() : nil; } - (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.font = UIFont.flex_defaultTableCellFont; cell.textLabel.text = self.rows[row].entryNameFuture(); } @end @implementation FLEXGlobalsSection (Subscripting) - (id)objectAtIndexedSubscript:(NSUInteger)idx { return self.rows[idx]; } @end ================================================ FILE: Classes/GlobalStateExplorers/Globals/FLEXGlobalsViewController.h ================================================ // // FLEXGlobalsViewController.h // Flipboard // // Created by Ryan Olson on 2014-05-03. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXFilteringTableViewController.h" @protocol FLEXGlobalsTableViewControllerDelegate; typedef NS_ENUM(NSUInteger, FLEXGlobalsSectionKind) { FLEXGlobalsSectionCustom, /// NSProcessInfo, Network history, system log, /// heap, address explorer, libraries, app classes FLEXGlobalsSectionProcessAndEvents, /// Browse container, browse bundle, NSBundle.main, /// NSUserDefaults.standard, UIApplication, /// app delegate, key window, root VC, cookies FLEXGlobalsSectionAppShortcuts, /// UIPasteBoard.general, UIScreen, UIDevice FLEXGlobalsSectionMisc, FLEXGlobalsSectionCount }; @interface FLEXGlobalsViewController : FLEXFilteringTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/Globals/FLEXGlobalsViewController.m ================================================ // // FLEXGlobalsViewController.m // Flipboard // // Created by Ryan Olson on 2014-05-03. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsViewController.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "FLEXObjcRuntimeViewController.h" #import "FLEXKeychainViewController.h" #import "FLEXAPNSViewController.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXLiveObjectsController.h" #import "FLEXFileBrowserController.h" #import "FLEXCookiesViewController.h" #import "FLEXGlobalsEntry.h" #import "FLEXManager+Private.h" #import "FLEXSystemLogViewController.h" #import "FLEXNetworkMITMViewController.h" #import "FLEXAddressExplorerCoordinator.h" #import "FLEXGlobalsSection.h" #import "UIBarButtonItem+FLEX.h" @interface FLEXGlobalsViewController () /// Only displayed sections of the table view; empty sections are purged from this array. @property (nonatomic) NSArray *sections; /// Every section in the table view, regardless of whether or not a section is empty. @property (nonatomic, readonly) NSArray *allSections; @property (nonatomic, readonly) BOOL manuallyDeselectOnAppear; @end @implementation FLEXGlobalsViewController @dynamic sections, allSections; #pragma mark - Initialization + (NSString *)globalsTitleForSection:(FLEXGlobalsSectionKind)section { switch (section) { case FLEXGlobalsSectionCustom: return @"Custom Additions"; case FLEXGlobalsSectionProcessAndEvents: return @"Process and Events"; case FLEXGlobalsSectionAppShortcuts: return @"App Shortcuts"; case FLEXGlobalsSectionMisc: return @"Miscellaneous"; default: @throw NSInternalInconsistencyException; } } + (FLEXGlobalsEntry *)globalsEntryForRow:(FLEXGlobalsRow)row { switch (row) { case FLEXGlobalsRowAppKeychainItems: return [FLEXKeychainViewController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowPushNotifications: return [FLEXAPNSViewController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowAddressInspector: return [FLEXAddressExplorerCoordinator flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowBrowseRuntime: return [FLEXObjcRuntimeViewController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowLiveObjects: return [FLEXLiveObjectsController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowCookies: return [FLEXCookiesViewController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowBrowseBundle: case FLEXGlobalsRowBrowseContainer: return [FLEXFileBrowserController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowSystemLog: return [FLEXSystemLogViewController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowNetworkHistory: return [FLEXNetworkMITMViewController flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowKeyWindow: case FLEXGlobalsRowRootViewController: case FLEXGlobalsRowProcessInfo: case FLEXGlobalsRowAppDelegate: case FLEXGlobalsRowUserDefaults: case FLEXGlobalsRowMainBundle: case FLEXGlobalsRowApplication: case FLEXGlobalsRowMainScreen: case FLEXGlobalsRowCurrentDevice: case FLEXGlobalsRowPasteboard: case FLEXGlobalsRowURLSession: case FLEXGlobalsRowURLCache: case FLEXGlobalsRowNotificationCenter: case FLEXGlobalsRowMenuController: case FLEXGlobalsRowFileManager: case FLEXGlobalsRowTimeZone: case FLEXGlobalsRowLocale: case FLEXGlobalsRowCalendar: case FLEXGlobalsRowMainRunLoop: case FLEXGlobalsRowMainThread: case FLEXGlobalsRowOperationQueue: return [FLEXObjectExplorerFactory flex_concreteGlobalsEntry:row]; case FLEXGlobalsRowCount: break; } @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Missing globals case in switch" userInfo:nil ]; } + (NSArray *)defaultGlobalSections { static NSMutableArray *sections = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSDictionary *> *rowsBySection = @{ @(FLEXGlobalsSectionProcessAndEvents) : @[ [self globalsEntryForRow:FLEXGlobalsRowNetworkHistory], [self globalsEntryForRow:FLEXGlobalsRowSystemLog], [self globalsEntryForRow:FLEXGlobalsRowProcessInfo], [self globalsEntryForRow:FLEXGlobalsRowLiveObjects], [self globalsEntryForRow:FLEXGlobalsRowAddressInspector], [self globalsEntryForRow:FLEXGlobalsRowBrowseRuntime], ], @(FLEXGlobalsSectionAppShortcuts) : @[ [self globalsEntryForRow:FLEXGlobalsRowBrowseBundle], [self globalsEntryForRow:FLEXGlobalsRowBrowseContainer], [self globalsEntryForRow:FLEXGlobalsRowMainBundle], [self globalsEntryForRow:FLEXGlobalsRowUserDefaults], [self globalsEntryForRow:FLEXGlobalsRowAppKeychainItems], [self globalsEntryForRow:FLEXGlobalsRowPushNotifications], [self globalsEntryForRow:FLEXGlobalsRowApplication], [self globalsEntryForRow:FLEXGlobalsRowAppDelegate], [self globalsEntryForRow:FLEXGlobalsRowKeyWindow], [self globalsEntryForRow:FLEXGlobalsRowRootViewController], [self globalsEntryForRow:FLEXGlobalsRowCookies], ], @(FLEXGlobalsSectionMisc) : @[ [self globalsEntryForRow:FLEXGlobalsRowPasteboard], [self globalsEntryForRow:FLEXGlobalsRowMainScreen], [self globalsEntryForRow:FLEXGlobalsRowCurrentDevice], [self globalsEntryForRow:FLEXGlobalsRowURLSession], [self globalsEntryForRow:FLEXGlobalsRowURLCache], [self globalsEntryForRow:FLEXGlobalsRowNotificationCenter], [self globalsEntryForRow:FLEXGlobalsRowMenuController], [self globalsEntryForRow:FLEXGlobalsRowFileManager], [self globalsEntryForRow:FLEXGlobalsRowTimeZone], [self globalsEntryForRow:FLEXGlobalsRowLocale], [self globalsEntryForRow:FLEXGlobalsRowCalendar], [self globalsEntryForRow:FLEXGlobalsRowMainRunLoop], [self globalsEntryForRow:FLEXGlobalsRowMainThread], [self globalsEntryForRow:FLEXGlobalsRowOperationQueue], ] }; sections = [NSMutableArray array]; for (FLEXGlobalsSectionKind i = FLEXGlobalsSectionCustom + 1; i < FLEXGlobalsSectionCount; ++i) { NSString *title = [self globalsTitleForSection:i]; [sections addObject:[FLEXGlobalsSection title:title rows:rowsBySection[@(i)]]]; } }); return sections; } #pragma mark - Overrides - (void)viewDidLoad { [super viewDidLoad]; self.title = @"💪 FLEX"; self.showsSearchBar = YES; self.searchBarDebounceInterval = kFLEXDebounceInstant; self.navigationItem.backBarButtonItem = [UIBarButtonItem flex_backItemWithTitle:@"Back"]; _manuallyDeselectOnAppear = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < 10; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self disableToolbar]; if (self.manuallyDeselectOnAppear) { [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES]; } } - (NSArray *)makeSections { NSMutableArray *sections = [NSMutableArray array]; // Do we have custom sections to add? if (FLEXManager.sharedManager.userGlobalEntries.count) { NSString *title = [[self class] globalsTitleForSection:FLEXGlobalsSectionCustom]; FLEXGlobalsSection *custom = [FLEXGlobalsSection title:title rows:FLEXManager.sharedManager.userGlobalEntries ]; [sections addObject:custom]; } [sections addObjectsFromArray:[self.class defaultGlobalSections]]; return sections; } @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/FLEXKeychain.h ================================================ // // FLEXKeychain.h // // Derived from: // SSKeychain.h in SSKeychain // Created by Sam Soffes on 5/19/10. // Copyright (c) 2010-2014 Sam Soffes. All rights reserved. // #import /// Error code specific to FLEXKeychain that can be returned in NSError objects. /// For codes returned by the operating system, refer to SecBase.h for your /// platform. typedef NS_ENUM(OSStatus, FLEXKeychainErrorCode) { /// Some of the arguments were invalid. FLEXKeychainErrorBadArguments = -1001, }; /// FLEXKeychain error domain extern NSString *const kFLEXKeychainErrorDomain; /// Account name. extern NSString *const kFLEXKeychainAccountKey; /// Time the item was created. /// /// The value will be a string. extern NSString *const kFLEXKeychainCreatedAtKey; /// Item class. extern NSString *const kFLEXKeychainClassKey; /// Item description. extern NSString *const kFLEXKeychainDescriptionKey; /// Item group. extern NSString *const kFLEXKeychainGroupKey; /// Item label. extern NSString *const kFLEXKeychainLabelKey; /// Time the item was last modified. /// /// The value will be a string. extern NSString *const kFLEXKeychainLastModifiedKey; /// Where the item was created. extern NSString *const kFLEXKeychainWhereKey; /// A simple wrapper for accessing accounts, getting passwords, /// setting passwords, and deleting passwords using the system Keychain. @interface FLEXKeychain : NSObject #pragma mark - Classic methods /// @param serviceName The service for which to return the corresponding password. /// @param account The account for which to return the corresponding password. /// @return Returns a string containing the password for a given account and service, /// or `nil` if the Keychain doesn't have a password for the given parameters. + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account; + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /// Returns a nsdata containing the password for a given account and service, /// or `nil` if the Keychain doesn't have a password for the given parameters. /// /// @param serviceName The service for which to return the corresponding password. /// @param account The account for which to return the corresponding password. /// @return Returns a nsdata containing the password for a given account and service, /// or `nil` if the Keychain doesn't have a password for the given parameters. + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account; + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /// Deletes a password from the Keychain. /// /// @param serviceName The service for which to delete the corresponding password. /// @param account The account for which to delete the corresponding password. /// @return Returns `YES` on success, or `NO` on failure. + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account; + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /// Sets a password in the Keychain. /// /// @param password The password to store in the Keychain. /// @param serviceName The service for which to set the corresponding password. /// @param account The account for which to set the corresponding password. /// @return Returns `YES` on success, or `NO` on failure. + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account; + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /// Sets a password in the Keychain. /// /// @param password The password to store in the Keychain. /// @param serviceName The service for which to set the corresponding password. /// @param account The account for which to set the corresponding password. /// @return Returns `YES` on success, or `NO` on failure. + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account; + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; /// @return An array of dictionaries containing the Keychain's accounts, or `nil` if /// the Keychain doesn't have any accounts. The order of the objects in the array isn't defined. /// /// @note See the `NSString` constants declared in FLEXKeychain.h for a list of keys that /// can be used when accessing the dictionaries returned by this method. + (NSArray *> *)allAccounts; + (NSArray *> *)allAccounts:(NSError *__autoreleasing *)error; /// @param serviceName The service for which to return the corresponding accounts. /// @return An array of dictionaries containing the Keychain's accounts for a given `serviceName`, /// or `nil` if the Keychain doesn't have any accounts for the given `serviceName`. /// The order of the objects in the array isn't defined. /// /// @note See the `NSString` constants declared in FLEXKeychain.h for a list of keys that /// can be used when accessing the dictionaries returned by this method. + (NSArray *> *)accountsForService:(NSString *)serviceName; + (NSArray *> *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error; #pragma mark - Configuration #if __IPHONE_4_0 && TARGET_OS_IPHONE /// Returns the accessibility type for all future passwords saved to the Keychain. /// /// @return `NULL` or one of the "Keychain Item Accessibility /// Constants" used for determining when a keychain item should be readable. + (CFTypeRef)accessibilityType; /// Sets the accessibility type for all future passwords saved to the Keychain. /// /// @param accessibilityType One of the "Keychain Item Accessibility Constants" /// used for determining when a keychain item should be readable. /// If the value is `NULL` (the default), the Keychain default will be used which /// is highly insecure. You really should use at least `kSecAttrAccessibleAfterFirstUnlock` /// for background applications or `kSecAttrAccessibleWhenUnlocked` for all /// other applications. /// /// @note See Security/SecItem.h + (void)setAccessibilityType:(CFTypeRef)accessibilityType; #endif @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/FLEXKeychain.m ================================================ // // FLEXKeychain.m // // Forked from: // SSKeychain.m in SSKeychain // Created by Sam Soffes on 5/19/10. // Copyright (c) 2010-2014 Sam Soffes. All rights reserved. // #import "FLEXKeychain.h" #import "FLEXKeychainQuery.h" NSString * const kFLEXKeychainErrorDomain = @"com.flipboard.flex"; NSString * const kFLEXKeychainAccountKey = @"acct"; NSString * const kFLEXKeychainCreatedAtKey = @"cdat"; NSString * const kFLEXKeychainClassKey = @"labl"; NSString * const kFLEXKeychainDescriptionKey = @"desc"; NSString * const kFLEXKeychainGroupKey = @"agrp"; NSString * const kFLEXKeychainLabelKey = @"labl"; NSString * const kFLEXKeychainLastModifiedKey = @"mdat"; NSString * const kFLEXKeychainWhereKey = @"svce"; #if __IPHONE_4_0 && TARGET_OS_IPHONE static CFTypeRef FLEXKeychainAccessibilityType = NULL; #endif @implementation FLEXKeychain + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account { return [self passwordForService:serviceName account:account error:nil]; } + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error { FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = serviceName; query.account = account; [query fetch:error]; return query.password; } + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account { return [self passwordDataForService:serviceName account:account error:nil]; } + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error { FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = serviceName; query.account = account; [query fetch:error]; return query.passwordData; } + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account { return [self deletePasswordForService:serviceName account:account error:nil]; } + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error { FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = serviceName; query.account = account; return [query deleteItem:error]; } + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account { return [self setPassword:password forService:serviceName account:account error:nil]; } + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error { FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = serviceName; query.account = account; query.password = password; return [query save:error]; } + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account { return [self setPasswordData:password forService:serviceName account:account error:nil]; } + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error { FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = serviceName; query.account = account; query.passwordData = password; return [query save:error]; } + (NSArray *)allAccounts { return [self allAccounts:nil] ?: @[]; } + (NSArray *)allAccounts:(NSError *__autoreleasing *)error { return [self accountsForService:nil error:error]; } + (NSArray *)accountsForService:(NSString *)serviceName { return [self accountsForService:serviceName error:nil]; } + (NSArray *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error { FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = serviceName; return [query fetchAll:error]; } #if __IPHONE_4_0 && TARGET_OS_IPHONE + (CFTypeRef)accessibilityType { return FLEXKeychainAccessibilityType; } + (void)setAccessibilityType:(CFTypeRef)accessibilityType { CFRetain(accessibilityType); if (FLEXKeychainAccessibilityType) { CFRelease(FLEXKeychainAccessibilityType); } FLEXKeychainAccessibilityType = accessibilityType; } #endif @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/FLEXKeychainQuery.h ================================================ // // FLEXKeychainQuery.h // // Derived from: // SSKeychainQuery.h in SSKeychain // Created by Caleb Davenport on 3/19/13. // Copyright (c) 2010-2014 Sam Soffes. All rights reserved. // #import #import #if __IPHONE_7_0 || __MAC_10_9 // Keychain synchronization available at compile time #define FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE 1 #endif #if __IPHONE_3_0 || __MAC_10_9 // Keychain access group available at compile time #define FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE 1 #endif #ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE typedef NS_ENUM(NSUInteger, FLEXKeychainQuerySynchronizationMode) { FLEXKeychainQuerySynchronizationModeAny, FLEXKeychainQuerySynchronizationModeNo, FLEXKeychainQuerySynchronizationModeYes }; #endif /// Simple interface for querying or modifying keychain items. @interface FLEXKeychainQuery : NSObject /// kSecAttrAccount @property (nonatomic, copy) NSString *account; /// kSecAttrService @property (nonatomic, copy) NSString *service; /// kSecAttrLabel @property (nonatomic, copy) NSString *label; #ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE /// kSecAttrAccessGroup (only used on iOS) @property (nonatomic, copy) NSString *accessGroup; #endif #ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE /// kSecAttrSynchronizable @property (nonatomic) FLEXKeychainQuerySynchronizationMode synchronizationMode; #endif /// Root storage for password information @property (nonatomic, copy) NSData *passwordData; /// This property automatically transitions between an object and the value of /// `passwordData` using NSKeyedArchiver and NSKeyedUnarchiver. @property (nonatomic, copy) id passwordObject; /// Convenience accessor for setting and getting a password string. Passes through /// to `passwordData` using UTF-8 string encoding. @property (nonatomic, copy) NSString *password; #pragma mark Saving & Deleting /// Save the receiver's attributes as a keychain item. Existing items with the /// given account, service, and access group will first be deleted. /// /// @param error Populated should an error occur. /// @return `YES` if saving was successful, `NO` otherwise. - (BOOL)save:(NSError **)error; /// Delete keychain items that match the given account, service, and access group. /// /// @param error Populated should an error occur. /// @return `YES` if saving was successful, `NO` otherwise. - (BOOL)deleteItem:(NSError **)error; #pragma mark Fetching /// Fetch all keychain items that match the given account, service, and access /// group. The values of `password` and `passwordData` are ignored when fetching. /// /// @param error Populated should an error occur. /// @return An array of dictionaries that represent all matching keychain items, /// or `nil` should an error occur. The order of the items is not determined. - (NSArray *> *)fetchAll:(NSError **)error; /// Fetch the keychain item that matches the given account, service, and access /// group. The `password` and `passwordData` properties will be populated unless /// an error occurs. The values of `password` and `passwordData` are ignored when /// fetching. /// /// @param error Populated should an error occur. /// @return `YES` if fetching was successful, `NO` otherwise. - (BOOL)fetch:(NSError **)error; #pragma mark Synchronization Status #ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE /// Returns a boolean indicating if keychain synchronization is available on the device at runtime. /// The #define FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time. /// If you are checking for the presence of synchronization, you should use this method. /// /// @return A value indicating if keychain synchronization is available + (BOOL)isSynchronizationAvailable; #endif @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/FLEXKeychainQuery.m ================================================ // // FLEXKeychainQuery.m // FLEXKeychain // // Created by Caleb Davenport on 3/19/13. // Copyright (c) 2013-2014 Sam Soffes. All rights reserved. // #import "FLEXKeychainQuery.h" #import "FLEXKeychain.h" @implementation FLEXKeychainQuery #pragma mark - Public - (BOOL)save:(NSError *__autoreleasing *)error { OSStatus status = FLEXKeychainErrorBadArguments; if (!self.service || !self.account || !self.passwordData) { if (error) { *error = [self errorWithCode:status]; } return NO; } NSMutableDictionary *query = nil; NSMutableDictionary * searchQuery = [self query]; status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil); if (status == errSecSuccess) {//item already exists, update it! query = [[NSMutableDictionary alloc]init]; query[(__bridge id)kSecValueData] = self.passwordData; #if __IPHONE_4_0 && TARGET_OS_IPHONE CFTypeRef accessibilityType = FLEXKeychain.accessibilityType; if (accessibilityType) { query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType; } #endif status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query)); } else if (status == errSecItemNotFound){//item not found, create it! query = [self query]; if (self.label) { query[(__bridge id)kSecAttrLabel] = self.label; } query[(__bridge id)kSecValueData] = self.passwordData; #if __IPHONE_4_0 && TARGET_OS_IPHONE CFTypeRef accessibilityType = FLEXKeychain.accessibilityType; if (accessibilityType) { query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType; } #endif status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); } if (status != errSecSuccess && error != NULL) { *error = [self errorWithCode:status]; } return (status == errSecSuccess); } - (BOOL)deleteItem:(NSError *__autoreleasing *)error { OSStatus status = FLEXKeychainErrorBadArguments; if (!self.service || !self.account) { if (error) { *error = [self errorWithCode:status]; } return NO; } NSMutableDictionary *query = [self query]; #if TARGET_OS_IPHONE status = SecItemDelete((__bridge CFDictionaryRef)query); #else // On Mac OS, SecItemDelete will not delete a key created in a different // app, nor in a different version of the same app. // // To replicate the issue, save a password, change to the code and // rebuild the app, and then attempt to delete that password. // // This was true in OS X 10.6 and probably later versions as well. // // Work around it by using SecItemCopyMatching and SecKeychainItemDelete. CFTypeRef result = NULL; query[(__bridge id)kSecReturnRef] = @YES; status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status == errSecSuccess) { status = SecKeychainItemDelete((SecKeychainItemRef)result); CFRelease(result); } #endif if (status != errSecSuccess && error != NULL) { *error = [self errorWithCode:status]; } return (status == errSecSuccess); } - (NSArray *)fetchAll:(NSError *__autoreleasing *)error { NSMutableDictionary *query = [self query]; query[(__bridge id)kSecReturnAttributes] = @YES; query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; #if __IPHONE_4_0 && TARGET_OS_IPHONE CFTypeRef accessibilityType = FLEXKeychain.accessibilityType; if (accessibilityType) { query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType; } #endif CFTypeRef result = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status != errSecSuccess && error != NULL) { *error = [self errorWithCode:status]; return nil; } return (__bridge_transfer NSArray *)result ?: @[]; } - (BOOL)fetch:(NSError *__autoreleasing *)error { OSStatus status = FLEXKeychainErrorBadArguments; if (!self.service || !self.account) { if (error) { *error = [self errorWithCode:status]; } return NO; } CFTypeRef result = NULL; NSMutableDictionary *query = [self query]; query[(__bridge id)kSecReturnData] = @YES; query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); if (status != errSecSuccess) { if (error) { *error = [self errorWithCode:status]; } return NO; } self.passwordData = (__bridge_transfer NSData *)result; return YES; } #pragma mark - Accessors - (void)setPassword:(NSString *)password { self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; } - (NSString *)password { if (self.passwordData.length) { return [[NSString alloc] initWithData:self.passwordData encoding:NSUTF8StringEncoding]; } return nil; } #pragma mark - Synchronization Status #ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE + (BOOL)isSynchronizationAvailable { #if TARGET_OS_IPHONE return YES; #else return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4; #endif } #endif #pragma mark - Private - (NSMutableDictionary *)query { NSMutableDictionary *dictionary = [NSMutableDictionary new]; dictionary[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; if (self.service) { dictionary[(__bridge id)kSecAttrService] = self.service; } if (self.account) { dictionary[(__bridge id)kSecAttrAccount] = self.account; } #ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE #if !TARGET_IPHONE_SIMULATOR if (self.accessGroup) { dictionary[(__bridge id)kSecAttrAccessGroup] = self.accessGroup; } #endif #endif #ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE if ([[self class] isSynchronizationAvailable]) { id value; switch (self.synchronizationMode) { case FLEXKeychainQuerySynchronizationModeNo: { value = @NO; break; } case FLEXKeychainQuerySynchronizationModeYes: { value = @YES; break; } case FLEXKeychainQuerySynchronizationModeAny: { value = (__bridge id)(kSecAttrSynchronizableAny); break; } } dictionary[(__bridge id)(kSecAttrSynchronizable)] = value; } #endif return dictionary; } - (NSError *)errorWithCode:(OSStatus)code { static dispatch_once_t onceToken; static NSBundle *resourcesBundle = nil; dispatch_once(&onceToken, ^{ NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:@"FLEXKeychain" withExtension:@"bundle"]; resourcesBundle = [NSBundle bundleWithURL:url]; }); NSString *message = nil; switch (code) { case errSecSuccess: return nil; case FLEXKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"FLEXKeychainErrorBadArguments", @"FLEXKeychain", resourcesBundle, nil); break; #if TARGET_OS_IPHONE case errSecUnimplemented: { message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecParam: { message = NSLocalizedStringFromTableInBundle(@"errSecParam", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecAllocate: { message = NSLocalizedStringFromTableInBundle(@"errSecAllocate", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecNotAvailable: { message = NSLocalizedStringFromTableInBundle(@"errSecNotAvailable", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecDuplicateItem: { message = NSLocalizedStringFromTableInBundle(@"errSecDuplicateItem", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecItemNotFound: { message = NSLocalizedStringFromTableInBundle(@"errSecItemNotFound", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecInteractionNotAllowed: { message = NSLocalizedStringFromTableInBundle(@"errSecInteractionNotAllowed", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecDecode: { message = NSLocalizedStringFromTableInBundle(@"errSecDecode", @"FLEXKeychain", resourcesBundle, nil); break; } case errSecAuthFailed: { message = NSLocalizedStringFromTableInBundle(@"errSecAuthFailed", @"FLEXKeychain", resourcesBundle, nil); break; } default: { message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"FLEXKeychain", resourcesBundle, nil); } #else default: message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL); #endif } NSDictionary *userInfo = message ? @{ NSLocalizedDescriptionKey : message } : nil; return [NSError errorWithDomain:kFLEXKeychainErrorDomain code:code userInfo:userInfo]; } @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/FLEXKeychainViewController.h ================================================ // // FLEXKeychainViewController.h // FLEX // // Created by ray on 2019/8/17. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsEntry.h" #import "FLEXFilteringTableViewController.h" @interface FLEXKeychainViewController : FLEXFilteringTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/FLEXKeychainViewController.m ================================================ // // FLEXKeychainViewController.m // FLEX // // Created by ray on 2019/8/17. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXKeychain.h" #import "FLEXKeychainQuery.h" #import "FLEXKeychainViewController.h" #import "FLEXTableViewCell.h" #import "FLEXMutableListSection.h" #import "FLEXUtility.h" #import "UIPasteboard+FLEX.h" #import "UIBarButtonItem+FLEX.h" @interface FLEXKeychainViewController () @property (nonatomic, readonly) FLEXMutableListSection *section; @end @implementation FLEXKeychainViewController - (id)init { return [self initWithStyle:UITableViewStyleGrouped]; } #pragma mark - Overrides - (void)viewDidLoad { [super viewDidLoad]; [self addToolbarItems:@[ FLEXBarButtonItemSystem(Add, self, @selector(addPressed)), [FLEXBarButtonItemSystem(Trash, self, @selector(trashPressed:)) flex_withTintColor:UIColor.redColor], ]]; [self reloadData]; } - (NSArray *)makeSections { _section = [FLEXMutableListSection list:FLEXKeychain.allAccounts.mutableCopy cellConfiguration:^(__kindof FLEXTableViewCell *cell, NSDictionary *item, NSInteger row) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; id service = item[kFLEXKeychainWhereKey]; if ([service isKindOfClass:[NSString class]]) { cell.textLabel.text = service; cell.detailTextLabel.text = [item[kFLEXKeychainAccountKey] description]; } else { cell.textLabel.text = [NSString stringWithFormat: @"[%@]\n\n%@", NSStringFromClass([service class]), [service description] ]; } } filterMatcher:^BOOL(NSString *filterText, NSDictionary *item) { // Loop over contents of the keychain item looking for a match for (NSString *field in item.allValues) { if ([field isKindOfClass:[NSString class]]) { if ([field localizedCaseInsensitiveContainsString:filterText]) { return YES; } } } return NO; } ]; return @[self.section]; } /// We always want to show this section - (NSArray *)nonemptySections { return @[self.section]; } - (void)reloadSections { self.section.list = FLEXKeychain.allAccounts.mutableCopy; } - (void)refreshSectionTitle { self.section.customTitle = FLEXPluralString( self.section.filteredList.count, @"items", @"item" ); } - (void)reloadData { [self reloadSections]; [self refreshSectionTitle]; [super reloadData]; } #pragma mark - Private - (FLEXKeychainQuery *)queryForItemAtIndex:(NSInteger)idx { NSDictionary *item = self.section.filteredList[idx]; FLEXKeychainQuery *query = [FLEXKeychainQuery new]; query.service = [item[kFLEXKeychainWhereKey] description]; query.account = [item[kFLEXKeychainAccountKey] description]; query.accessGroup = [item[kFLEXKeychainGroupKey] description]; [query fetch:nil]; return query; } - (void)deleteItem:(NSDictionary *)item { NSError *error = nil; BOOL success = [FLEXKeychain deletePasswordForService:item[kFLEXKeychainWhereKey] account:item[kFLEXKeychainAccountKey] error:&error ]; if (!success) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Error Deleting Item"); make.message(error.localizedDescription); } showFrom:self]; } } #pragma mark Buttons - (void)trashPressed:(UIBarButtonItem *)sender { [FLEXAlert makeSheet:^(FLEXAlert *make) { make.title(@"Clear Keychain"); make.message(@"This will remove all keychain items for this app.\n"); make.message(@"This action cannot be undone. Are you sure?"); make.button(@"Yes, clear the keychain").destructiveStyle().handler(^(NSArray *strings) { [self confirmClearKeychain]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } - (void)confirmClearKeychain { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"ARE YOU SURE?"); make.message(@"This action CANNOT BE UNDONE.\nAre you sure you want to continue?\n"); make.message(@"If you're sure, scroll to confirm."); make.button(@"Yes, clear the keychain").destructiveStyle().handler(^(NSArray *strings) { for (id account in self.section.list) { [self deleteItem:account]; } [self reloadData]; }); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel"); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } - (void)addPressed { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Add Keychain Item"); make.textField(@"Service name, i.e. Instagram"); make.textField(@"Account"); make.textField(@"Password"); make.button(@"Cancel").cancelStyle(); make.button(@"Save").handler(^(NSArray *strings) { // Display errors NSError *error = nil; if (![FLEXKeychain setPassword:strings[2] forService:strings[0] account:strings[1] error:&error]) { [FLEXAlert showAlert:@"Error" message:error.localizedDescription from:self]; } [self reloadData]; }); } showFrom:self]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"🔑 Keychain"; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { FLEXKeychainViewController *viewController = [self new]; viewController.title = [self globalsEntryTitle:row]; return viewController; } #pragma mark - Table View Data Source - (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)style forRowAtIndexPath:(NSIndexPath *)ip { if (style == UITableViewCellEditingStyleDelete) { // Update the model NSDictionary *toRemove = self.section.filteredList[ip.row]; [self deleteItem:toRemove]; [self.section mutate:^(NSMutableArray *list) { [list removeObject:toRemove]; }]; // Delete the row [tv deleteRowsAtIndexPaths:@[ip] withRowAnimation:UITableViewRowAnimationAutomatic]; // Update the title by refreshing the section without disturbing the delete animation // // This is an ugly hack, but literally nothing else works, save for manually getting // the header and setting its title, which I personally think is worse since it // would need to make assumptions about the default style of the header (CAPS) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self refreshSectionTitle]; [tv reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone]; }); } } #pragma mark - Table View Delegate - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { FLEXKeychainQuery *query = [self queryForItemAtIndex:indexPath.row]; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(query.service); make.message(@"Service: ").message(query.service); make.message(@"\nAccount: ").message(query.account); make.message(@"\nPassword: ").message(query.password); make.message(@"\nGroup: ").message(query.accessGroup); make.button(@"Copy Service").handler(^(NSArray *strings) { [UIPasteboard.generalPasteboard flex_copy:query.service]; }); make.button(@"Copy Account").handler(^(NSArray *strings) { [UIPasteboard.generalPasteboard flex_copy:query.account]; }); make.button(@"Copy Password").handler(^(NSArray *strings) { [UIPasteboard.generalPasteboard flex_copy:query.password]; }); make.button(@"Dismiss").cancelStyle(); } showFrom:self]; [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @end ================================================ FILE: Classes/GlobalStateExplorers/Keychain/SSKeychain_LICENSE ================================================ Copyright (c) 2010-2012 Sam Soffes. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/DataSources/FLEXRuntimeClient.h ================================================ // // FLEXRuntimeClient.h // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXSearchToken.h" @class FLEXMethod; /// Accepts runtime queries given a token. @interface FLEXRuntimeClient : NSObject @property (nonatomic, readonly, class) FLEXRuntimeClient *runtime; /// Called automatically when \c FLEXRuntime is first used. /// You may call it again when you think a library has /// been loaded since this method was first called. - (void)reloadLibrariesList; /// You must call this method on the main thread /// before you attempt to call \c copySafeClassList. + (void)initializeWebKitLegacy; /// Do not call unless you absolutely need all classes. This will cause /// every class in the runtime to initialize itself, which is not common. /// Before you call this method, call \c initializeWebKitLegacy on the main thread. - (NSArray *)copySafeClassList; - (NSArray *)copyProtocolList; /// An array of strings representing the currently loaded libraries. @property (nonatomic, readonly) NSArray *imageDisplayNames; /// "Image name" is the path of the bundle - (NSString *)shortNameForImageName:(NSString *)imageName; /// "Image name" is the path of the bundle - (NSString *)imageNameForShortName:(NSString *)imageName; /// @return Bundle names for the UI - (NSMutableArray *)bundleNamesForToken:(FLEXSearchToken *)token; /// @return Bundle paths for more queries - (NSMutableArray *)bundlePathsForToken:(FLEXSearchToken *)token; /// @return Class names - (NSMutableArray *)classesForToken:(FLEXSearchToken *)token inBundles:(NSMutableArray *)bundlePaths; /// @return A list of lists of \c FLEXMethods where /// each list corresponds to one of the given classes - (NSArray *> *)methodsForToken:(FLEXSearchToken *)token instance:(NSNumber *)onlyInstanceMethods inClasses:(NSArray *)classes; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/DataSources/FLEXRuntimeClient.m ================================================ // // FLEXRuntimeClient.m // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeClient.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXMethod.h" #import "NSArray+FLEX.h" #import "FLEXRuntimeSafety.h" #include #define Equals(a, b) ([a compare:b options:NSCaseInsensitiveSearch] == NSOrderedSame) #define Contains(a, b) ([a rangeOfString:b options:NSCaseInsensitiveSearch].location != NSNotFound) #define HasPrefix(a, b) ([a rangeOfString:b options:NSCaseInsensitiveSearch].location == 0) #define HasSuffix(a, b) ([a rangeOfString:b options:NSCaseInsensitiveSearch].location == (a.length - b.length)) @interface FLEXRuntimeClient () { NSMutableArray *_imageDisplayNames; } @property (nonatomic) NSMutableDictionary *bundles_pathToShort; @property (nonatomic) NSMutableDictionary *bundles_shortToPath; @property (nonatomic) NSCache *bundles_pathToClassNames; @property (nonatomic) NSMutableArray *imagePaths; @end /// @return success if the map passes. static inline NSString * TBWildcardMap_(NSString *token, NSString *candidate, NSString *success, TBWildcardOptions options) { switch (options) { case TBWildcardOptionsNone: // Only "if equals" if (Equals(candidate, token)) { return success; } default: { // Only "if contains" if (options & TBWildcardOptionsPrefix && options & TBWildcardOptionsSuffix) { if (Contains(candidate, token)) { return success; } } // Only "if candidate ends with with token" else if (options & TBWildcardOptionsPrefix) { if (HasSuffix(candidate, token)) { return success; } } // Only "if candidate starts with with token" else if (options & TBWildcardOptionsSuffix) { // Case like "Bundle." where we want "" to match anything if (!token.length) { return success; } if (HasPrefix(candidate, token)) { return success; } } } } return nil; } /// @return candidate if the map passes. static inline NSString * TBWildcardMap(NSString *token, NSString *candidate, TBWildcardOptions options) { return TBWildcardMap_(token, candidate, candidate, options); } @implementation FLEXRuntimeClient #pragma mark - Initialization + (instancetype)runtime { static FLEXRuntimeClient *runtime; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ runtime = [self new]; [runtime reloadLibrariesList]; }); return runtime; } - (id)init { self = [super init]; if (self) { _imagePaths = [NSMutableArray new]; _bundles_pathToShort = [NSMutableDictionary new]; _bundles_shortToPath = [NSMutableDictionary new]; _bundles_pathToClassNames = [NSCache new]; } return self; } #pragma mark - Private - (void)reloadLibrariesList { unsigned int imageCount = 0; const char **imageNames = objc_copyImageNames(&imageCount); if (imageNames) { NSMutableArray *imageNameStrings = [NSMutableArray flex_forEachUpTo:imageCount map:^NSString *(NSUInteger i) { return @(imageNames[i]); }]; self.imagePaths = imageNameStrings; free(imageNames); // Sort alphabetically [imageNameStrings sortUsingComparator:^NSComparisonResult(NSString *name1, NSString *name2) { NSString *shortName1 = [self shortNameForImageName:name1]; NSString *shortName2 = [self shortNameForImageName:name2]; return [shortName1 caseInsensitiveCompare:shortName2]; }]; // Cache image display names _imageDisplayNames = [imageNameStrings flex_mapped:^id(NSString *path, NSUInteger idx) { return [self shortNameForImageName:path]; }]; } } - (NSString *)shortNameForImageName:(NSString *)imageName { // Cache NSString *shortName = _bundles_pathToShort[imageName]; if (shortName) { return shortName; } NSArray *components = [imageName componentsSeparatedByString:@"/"]; if (components.count >= 2) { NSString *parentDir = components[components.count - 2]; if ([parentDir hasSuffix:@".framework"] || [parentDir hasSuffix:@".axbundle"]) { if ([imageName hasSuffix:@".dylib"]) { shortName = imageName.lastPathComponent; } else { shortName = parentDir; } } } if (!shortName) { shortName = imageName.lastPathComponent; } _bundles_pathToShort[imageName] = shortName; _bundles_shortToPath[shortName] = imageName; return shortName; } - (NSString *)imageNameForShortName:(NSString *)imageName { return _bundles_shortToPath[imageName]; } - (NSMutableArray *)classNamesInImageAtPath:(NSString *)path { // Check cache NSMutableArray *classNameStrings = [_bundles_pathToClassNames objectForKey:path]; if (classNameStrings) { return classNameStrings.mutableCopy; } unsigned int classCount = 0; const char **classNames = objc_copyClassNamesForImage(path.UTF8String, &classCount); if (classNames) { classNameStrings = [NSMutableArray flex_forEachUpTo:classCount map:^id(NSUInteger i) { return @(classNames[i]); }]; free(classNames); [classNameStrings sortUsingSelector:@selector(caseInsensitiveCompare:)]; [_bundles_pathToClassNames setObject:classNameStrings forKey:path]; return classNameStrings.mutableCopy; } return [NSMutableArray new]; } #pragma mark - Public + (void)initializeWebKitLegacy { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ void *handle = dlopen( "/System/Library/PrivateFrameworks/WebKitLegacy.framework/WebKitLegacy", RTLD_LAZY ); void (*WebKitInitialize)(void) = dlsym(handle, "WebKitInitialize"); if (WebKitInitialize) { NSAssert(NSThread.isMainThread, @"WebKitInitialize can only be called on the main thread" ); WebKitInitialize(); } }); } - (NSArray *)copySafeClassList { unsigned int count = 0; Class *classes = objc_copyClassList(&count); return [NSArray flex_forEachUpTo:count map:^id(NSUInteger i) { Class cls = classes[i]; return FLEXClassIsSafe(cls) ? cls : nil; }]; } - (NSArray *)copyProtocolList { unsigned int count = 0; Protocol *__unsafe_unretained *protocols = objc_copyProtocolList(&count); return [NSArray arrayWithObjects:protocols count:count]; } - (NSMutableArray *)bundleNamesForToken:(FLEXSearchToken *)token { if (self.imagePaths.count) { TBWildcardOptions options = token.options; NSString *query = token.string; // Optimization, avoid a loop if (options == TBWildcardOptionsAny) { return _imageDisplayNames; } // No dot syntax because imageDisplayNames is only mutable internally return [_imageDisplayNames flex_mapped:^id(NSString *binary, NSUInteger idx) { // NSString *UIName = [self shortNameForImageName:binary]; return TBWildcardMap(query, binary, options); }]; } return [NSMutableArray new]; } - (NSMutableArray *)bundlePathsForToken:(FLEXSearchToken *)token { if (self.imagePaths.count) { TBWildcardOptions options = token.options; NSString *query = token.string; // Optimization, avoid a loop if (options == TBWildcardOptionsAny) { return self.imagePaths; } return [self.imagePaths flex_mapped:^id(NSString *binary, NSUInteger idx) { NSString *UIName = [self shortNameForImageName:binary]; // If query == UIName, -> binary return TBWildcardMap_(query, UIName, binary, options); }]; } return [NSMutableArray new]; } - (NSMutableArray *)classesForToken:(FLEXSearchToken *)token inBundles:(NSMutableArray *)bundles { // Edge case where token is the class we want already; return superclasses if (token.isAbsolute) { if (FLEXClassIsSafe(NSClassFromString(token.string))) { return [NSMutableArray arrayWithObject:token.string]; } return [NSMutableArray new]; } if (bundles.count) { // Get class names, remove unsafe classes NSMutableArray *names = [self _classesForToken:token inBundles:bundles]; return [names flex_mapped:^NSString *(NSString *name, NSUInteger idx) { Class cls = NSClassFromString(name); BOOL safe = FLEXClassIsSafe(cls); return safe ? name : nil; }]; } return [NSMutableArray new]; } - (NSMutableArray *)_classesForToken:(FLEXSearchToken *)token inBundles:(NSMutableArray *)bundles { TBWildcardOptions options = token.options; NSString *query = token.string; // Optimization, avoid unnecessary sorting if (bundles.count == 1) { // Optimization, avoid a loop if (options == TBWildcardOptionsAny) { return [self classNamesInImageAtPath:bundles.firstObject]; } return [[self classNamesInImageAtPath:bundles.firstObject] flex_mapped:^id(NSString *className, NSUInteger idx) { return TBWildcardMap(query, className, options); }]; } else { // Optimization, avoid a loop if (options == TBWildcardOptionsAny) { return [[bundles flex_flatmapped:^NSArray *(NSString *bundlePath, NSUInteger idx) { return [self classNamesInImageAtPath:bundlePath]; }] flex_sortedUsingSelector:@selector(caseInsensitiveCompare:)]; } return [[bundles flex_flatmapped:^NSArray *(NSString *bundlePath, NSUInteger idx) { return [[self classNamesInImageAtPath:bundlePath] flex_mapped:^id(NSString *className, NSUInteger idx) { return TBWildcardMap(query, className, options); }]; }] flex_sortedUsingSelector:@selector(caseInsensitiveCompare:)]; } } - (NSArray *> *)methodsForToken:(FLEXSearchToken *)token instance:(NSNumber *)checkInstance inClasses:(NSArray *)classes { if (classes.count) { TBWildcardOptions options = token.options; BOOL instance = checkInstance.boolValue; NSString *selector = token.string; switch (options) { // In practice I don't think this case is ever used with methods, // since they will always have a suffix wildcard at the end case TBWildcardOptionsNone: { SEL sel = (SEL)selector.UTF8String; return @[[classes flex_mapped:^id(NSString *name, NSUInteger idx) { Class cls = NSClassFromString(name); // Use metaclass if not instance if (!instance) { cls = object_getClass(cls); } // Method is absolute return [FLEXMethod selector:sel class:cls]; }]]; } case TBWildcardOptionsAny: { return [classes flex_mapped:^NSArray *(NSString *name, NSUInteger idx) { // Any means `instance` was not specified Class cls = NSClassFromString(name); return [cls flex_allMethods]; }]; } default: { // Only "if contains" if (options & TBWildcardOptionsPrefix && options & TBWildcardOptionsSuffix) { return [classes flex_mapped:^NSArray *(NSString *name, NSUInteger idx) { Class cls = NSClassFromString(name); return [[cls flex_allMethods] flex_mapped:^id(FLEXMethod *method, NSUInteger idx) { // Method is a prefix-suffix wildcard if (Contains(method.selectorString, selector)) { return method; } return nil; }]; }]; } // Only "if method ends with with selector" else if (options & TBWildcardOptionsPrefix) { return [classes flex_mapped:^NSArray *(NSString *name, NSUInteger idx) { Class cls = NSClassFromString(name); return [[cls flex_allMethods] flex_mapped:^id(FLEXMethod *method, NSUInteger idx) { // Method is a prefix wildcard if (HasSuffix(method.selectorString, selector)) { return method; } return nil; }]; }]; } // Only "if method starts with with selector" else if (options & TBWildcardOptionsSuffix) { assert(checkInstance); return [classes flex_mapped:^NSArray *(NSString *name, NSUInteger idx) { Class cls = NSClassFromString(name); // Case like "Bundle.class.-" where we want "-" to match anything if (!selector.length) { if (instance) { return [cls flex_allInstanceMethods]; } else { return [cls flex_allClassMethods]; } } id mapping = ^id(FLEXMethod *method) { // Method is a suffix wildcard if (HasPrefix(method.selectorString, selector)) { return method; } return nil; }; if (instance) { return [[cls flex_allInstanceMethods] flex_mapped:mapping]; } else { return [[cls flex_allClassMethods] flex_mapped:mapping]; } }]; } } } } return [NSMutableArray new]; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/DataSources/FLEXRuntimeController.h ================================================ // // FLEXRuntimeController.h // FLEX // // Created by Tanner on 3/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeKeyPath.h" /// Wraps FLEXRuntimeClient and provides extra caching mechanisms @interface FLEXRuntimeController : NSObject /// @return An array of strings if the key path only evaluates /// to a class or bundle; otherwise, a list of lists of FLEXMethods. + (NSArray *)dataForKeyPath:(FLEXRuntimeKeyPath *)keyPath; /// Useful when you need to specify which classes to search in. /// \c dataForKeyPath: will only search classes matching the class key. /// We use this elsewhere when we need to search a class hierarchy. + (NSArray *> *)methodsForToken:(FLEXSearchToken *)token instance:(NSNumber *)onlyInstanceMethods inClasses:(NSArray *)classes; /// Useful when you need the classes that are associated with the /// double list of methods returned from \c dataForKeyPath + (NSMutableArray *)classesForKeyPath:(FLEXRuntimeKeyPath *)keyPath; + (NSString *)shortBundleNameForClass:(NSString *)name; + (NSString *)imagePathWithShortName:(NSString *)suffix; /// Gives back short names. For example, "Foundation.framework" + (NSArray *)allBundleNames; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/DataSources/FLEXRuntimeController.m ================================================ // // FLEXRuntimeController.m // FLEX // // Created by Tanner on 3/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeController.h" #import "FLEXRuntimeClient.h" #import "FLEXMethod.h" @interface FLEXRuntimeController () @property (nonatomic, readonly) NSCache *bundlePathsCache; @property (nonatomic, readonly) NSCache *bundleNamesCache; @property (nonatomic, readonly) NSCache *classNamesCache; @property (nonatomic, readonly) NSCache *methodsCache; @end @implementation FLEXRuntimeController #pragma mark Initialization static FLEXRuntimeController *controller = nil; + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ controller = [self new]; }); return controller; } - (id)init { self = [super init]; if (self) { _bundlePathsCache = [NSCache new]; _bundleNamesCache = [NSCache new]; _classNamesCache = [NSCache new]; _methodsCache = [NSCache new]; } return self; } #pragma mark Public + (NSArray *)dataForKeyPath:(FLEXRuntimeKeyPath *)keyPath { if (keyPath.bundleKey) { if (keyPath.classKey) { if (keyPath.methodKey) { return [[self shared] methodsForKeyPath:keyPath]; } else { return [[self shared] classesForKeyPath:keyPath]; } } else { return [[self shared] bundleNamesForToken:keyPath.bundleKey]; } } else { return @[]; } } + (NSArray *> *)methodsForToken:(FLEXSearchToken *)token instance:(NSNumber *)inst inClasses:(NSArray *)classes { return [FLEXRuntimeClient.runtime methodsForToken:token instance:inst inClasses:classes ]; } + (NSMutableArray *)classesForKeyPath:(FLEXRuntimeKeyPath *)keyPath { return [[self shared] classesForKeyPath:keyPath]; } + (NSString *)shortBundleNameForClass:(NSString *)name { const char *imageName = class_getImageName(NSClassFromString(name)); if (!imageName) { return @"(unspecified)"; } return [FLEXRuntimeClient.runtime shortNameForImageName:@(imageName)]; } + (NSString *)imagePathWithShortName:(NSString *)suffix { return [FLEXRuntimeClient.runtime imageNameForShortName:suffix]; } + (NSArray *)allBundleNames { return FLEXRuntimeClient.runtime.imageDisplayNames; } #pragma mark Private - (NSMutableArray *)bundlePathsForToken:(FLEXSearchToken *)token { // Only cache if no wildcard BOOL shouldCache = token == TBWildcardOptionsNone; if (shouldCache) { NSMutableArray *cached = [self.bundlePathsCache objectForKey:token]; if (cached) { return cached; } NSMutableArray *bundles = [FLEXRuntimeClient.runtime bundlePathsForToken:token]; [self.bundlePathsCache setObject:bundles forKey:token]; return bundles; } else { return [FLEXRuntimeClient.runtime bundlePathsForToken:token]; } } - (NSMutableArray *)bundleNamesForToken:(FLEXSearchToken *)token { // Only cache if no wildcard BOOL shouldCache = token == TBWildcardOptionsNone; if (shouldCache) { NSMutableArray *cached = [self.bundleNamesCache objectForKey:token]; if (cached) { return cached; } NSMutableArray *bundles = [FLEXRuntimeClient.runtime bundleNamesForToken:token]; [self.bundleNamesCache setObject:bundles forKey:token]; return bundles; } else { return [FLEXRuntimeClient.runtime bundleNamesForToken:token]; } } - (NSMutableArray *)classesForKeyPath:(FLEXRuntimeKeyPath *)keyPath { FLEXSearchToken *classToken = keyPath.classKey; FLEXSearchToken *bundleToken = keyPath.bundleKey; // Only cache if no wildcard BOOL shouldCache = bundleToken.options == 0 && classToken.options == 0; NSString *key = nil; if (shouldCache) { key = [@[bundleToken.description, classToken.description] componentsJoinedByString:@"+"]; NSMutableArray *cached = [self.classNamesCache objectForKey:key]; if (cached) { return cached; } } NSMutableArray *bundles = [self bundlePathsForToken:bundleToken]; NSMutableArray *classes = [FLEXRuntimeClient.runtime classesForToken:classToken inBundles:bundles ]; if (shouldCache) { [self.classNamesCache setObject:classes forKey:key]; } return classes; } - (NSArray *> *)methodsForKeyPath:(FLEXRuntimeKeyPath *)keyPath { // Only cache if no wildcard, but check cache anyway bc I'm lazy NSArray *cached = [self.methodsCache objectForKey:keyPath]; if (cached) { return cached; } NSArray *classes = [self classesForKeyPath:keyPath]; NSArray *> *methodLists = [FLEXRuntimeClient.runtime methodsForToken:keyPath.methodKey instance:keyPath.instanceMethods inClasses:classes ]; for (NSMutableArray *methods in methodLists) { [methods sortUsingComparator:^NSComparisonResult(FLEXMethod *m1, FLEXMethod *m2) { return [m1.description caseInsensitiveCompare:m2.description]; }]; } // Only cache if no wildcard, otherwise the cache could grow very large if (keyPath.bundleKey.isAbsolute && keyPath.classKey.isAbsolute) { [self.methodsCache setObject:methodLists forKey:keyPath]; } return methodLists; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/DataSources/FLEXRuntimeExporter.h ================================================ // // FLEXRuntimeExporter.h // FLEX // // Created by Tanner Bennett on 3/26/20. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN /// A class for exporting all runtime metadata to an SQLite database. //API_AVAILABLE(ios(10.0)) @interface FLEXRuntimeExporter : NSObject + (void)createRuntimeDatabaseAtPath:(NSString *)path progressHandler:(void(^)(NSString *status))progress completion:(void(^)(NSString *_Nullable error))completion; + (void)createRuntimeDatabaseAtPath:(NSString *)path forImages:(nullable NSArray *)images progressHandler:(void(^)(NSString *status))progress completion:(void(^)(NSString *_Nullable error))completion; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/DataSources/FLEXRuntimeExporter.m ================================================ // // FLEXRuntimeExporter.m // FLEX // // Created by Tanner Bennett on 3/26/20. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeExporter.h" #import "FLEXSQLiteDatabaseManager.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXRuntimeController.h" #import "FLEXRuntimeClient.h" #import "NSArray+FLEX.h" #import "FLEXTypeEncodingParser.h" #import #import "FLEXProtocol.h" #import "FLEXProperty.h" #import "FLEXIvar.h" #import "FLEXMethodBase.h" #import "FLEXMethod.h" #import "FLEXPropertyAttributes.h" NSString * const kFREEnableForeignKeys = @"PRAGMA foreign_keys = ON;"; /// Loaded images NSString * const kFRECreateTableMachOCommand = @"CREATE TABLE MachO( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "shortName TEXT, " "imagePath TEXT, " "bundleID TEXT " ");"; NSString * const kFREInsertImage = @"INSERT INTO MachO ( " "shortName, imagePath, bundleID " ") VALUES ( " "$shortName, $imagePath, $bundleID " ");"; /// Objc classes NSString * const kFRECreateTableClassCommand = @"CREATE TABLE Class( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "className TEXT, " "superclass INTEGER, " "instanceSize INTEGER, " "version INTEGER, " "image INTEGER, " "FOREIGN KEY(superclass) REFERENCES Class(id), " "FOREIGN KEY(image) REFERENCES MachO(id) " ");"; NSString * const kFREInsertClass = @"INSERT INTO Class ( " "className, instanceSize, version, image " ") VALUES ( " "$className, $instanceSize, $version, $image " ");"; NSString * const kFREUpdateClassSetSuper = @"UPDATE Class SET superclass = $super WHERE id = $id;"; /// Unique objc selectors NSString * const kFRECreateTableSelectorCommand = @"CREATE TABLE Selector( " "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "name text NOT NULL UNIQUE " ");"; NSString * const kFREInsertSelector = @"INSERT OR IGNORE INTO Selector (name) VALUES ($name);"; /// Unique objc type encodings NSString * const kFRECreateTableTypeEncodingCommand = @"CREATE TABLE TypeEncoding( " "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "string text NOT NULL UNIQUE, " "size integer " ");"; NSString * const kFREInsertTypeEncoding = @"INSERT OR IGNORE INTO TypeEncoding " "(string, size) VALUES ($type, $size);"; /// Unique objc type signatures NSString * const kFRECreateTableTypeSignatureCommand = @"CREATE TABLE TypeSignature( " "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "string text NOT NULL UNIQUE " ");"; NSString * const kFREInsertTypeSignature = @"INSERT OR IGNORE INTO TypeSignature " "(string) VALUES ($type);"; NSString * const kFRECreateTableMethodSignatureCommand = @"CREATE TABLE MethodSignature( " "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " "typeEncoding TEXT, " "argc INTEGER, " "returnType INTEGER, " "frameLength INTEGER, " "FOREIGN KEY(returnType) REFERENCES TypeEncoding(id) " ");"; NSString * const kFREInsertMethodSignature = @"INSERT INTO MethodSignature ( " "typeEncoding, argc, returnType, frameLength " ") VALUES ( " "$typeEncoding, $argc, $returnType, $frameLength " ");"; NSString * const kFRECreateTableMethodCommand = @"CREATE TABLE Method( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "sel INTEGER, " "class INTEGER, " "instance INTEGER, " // 0 if class method, 1 if instance method "signature INTEGER, " "image INTEGER, " "FOREIGN KEY(sel) REFERENCES Selector(id), " "FOREIGN KEY(class) REFERENCES Class(id), " "FOREIGN KEY(signature) REFERENCES MethodSignature(id), " "FOREIGN KEY(image) REFERENCES MachO(id) " ");"; NSString * const kFREInsertMethod = @"INSERT INTO Method ( " "sel, class, instance, signature, image " ") VALUES ( " "$sel, $class, $instance, $signature, $image " ");"; NSString * const kFRECreateTablePropertyCommand = @"CREATE TABLE Property( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name TEXT, " "class INTEGER, " "instance INTEGER, " // 0 if class prop, 1 if instance prop "image INTEGER, " "attributes TEXT, " "customGetter INTEGER, " "customSetter INTEGER, " "type INTEGER, " "ivar TEXT, " "readonly INTEGER, " "copy INTEGER, " "retained INTEGER, " "nonatomic INTEGER, " "dynamic INTEGER, " "weak INTEGER, " "canGC INTEGER, " "FOREIGN KEY(class) REFERENCES Class(id), " "FOREIGN KEY(customGetter) REFERENCES Selector(id), " "FOREIGN KEY(customSetter) REFERENCES Selector(id), " "FOREIGN KEY(image) REFERENCES MachO(id) " ");"; NSString * const kFREInsertProperty = @"INSERT INTO Property ( " "name, class, instance, attributes, image, " "customGetter, customSetter, type, ivar, readonly, " "copy, retained, nonatomic, dynamic, weak, canGC " ") VALUES ( " "$name, $class, $instance, $attributes, $image, " "$customGetter, $customSetter, $type, $ivar, $readonly, " "$copy, $retained, $nonatomic, $dynamic, $weak, $canGC " ");"; NSString * const kFRECreateTableIvarCommand = @"CREATE TABLE Ivar( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name TEXT, " "offset INTEGER, " "type INTEGER, " "class INTEGER, " "image INTEGER, " "FOREIGN KEY(type) REFERENCES TypeEncoding(id), " "FOREIGN KEY(class) REFERENCES Class(id), " "FOREIGN KEY(image) REFERENCES MachO(id) " ");"; NSString * const kFREInsertIvar = @"INSERT INTO Ivar ( " "name, offset, type, class, image " ") VALUES ( " "$name, $offset, $type, $class, $image " ");"; NSString * const kFRECreateTableProtocolCommand = @"CREATE TABLE Protocol( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name TEXT, " "image INTEGER, " "FOREIGN KEY(image) REFERENCES MachO(id) " ");"; NSString * const kFREInsertProtocol = @"INSERT INTO Protocol " "(name, image) VALUES ($name, $image);"; NSString * const kFRECreateTableProtocolPropertyCommand = @"CREATE TABLE ProtocolMember( " "id INTEGER PRIMARY KEY AUTOINCREMENT, " "protocol INTEGER, " "required INTEGER, " "instance INTEGER, " // 0 if class member, 1 if instance member // Only of the two below is used "property TEXT, " "method TEXT, " "image INTEGER, " "FOREIGN KEY(protocol) REFERENCES Protocol(id), " "FOREIGN KEY(image) REFERENCES MachO(id) " ");"; NSString * const kFREInsertProtocolMember = @"INSERT INTO ProtocolMember ( " "protocol, required, instance, property, method, image " ") VALUES ( " "$protocol, $required, $instance, $property, $method, $image " ");"; /// For protocols conforming to other protocols NSString * const kFRECreateTableProtocolConformanceCommand = @"CREATE TABLE ProtocolConformance( " "protocol INTEGER, " "conformance INTEGER, " "FOREIGN KEY(protocol) REFERENCES Protocol(id), " "FOREIGN KEY(conformance) REFERENCES Protocol(id) " ");"; NSString * const kFREInsertProtocolConformance = @"INSERT INTO ProtocolConformance " "(protocol, conformance) VALUES ($protocol, $conformance);"; /// For classes conforming to protocols NSString * const kFRECreateTableClassConformanceCommand = @"CREATE TABLE ClassConformance( " "class INTEGER, " "conformance INTEGER, " "FOREIGN KEY(class) REFERENCES Class(id), " "FOREIGN KEY(conformance) REFERENCES Protocol(id) " ");"; NSString * const kFREInsertClassConformance = @"INSERT INTO ClassConformance " "(class, conformance) VALUES ($class, $conformance);"; @interface FLEXRuntimeExporter () @property (nonatomic, readonly) FLEXSQLiteDatabaseManager *db; @property (nonatomic, copy) NSArray *loadedShortBundleNames; @property (nonatomic, copy) NSArray *loadedBundlePaths; @property (nonatomic, copy) NSArray *protocols; @property (nonatomic, copy) NSArray *classes; @property (nonatomic) NSMutableDictionary *bundlePathsToIDs; @property (nonatomic) NSMutableDictionary *protocolsToIDs; @property (nonatomic) NSMutableDictionary *classesToIDs; @property (nonatomic) NSMutableDictionary *typeEncodingsToIDs; @property (nonatomic) NSMutableDictionary *methodSignaturesToIDs; @property (nonatomic) NSMutableDictionary *selectorsToIDs; @end @implementation FLEXRuntimeExporter + (NSString *)tempFilename { NSString *temp = NSTemporaryDirectory(); NSString *uuid = [NSUUID.UUID.UUIDString substringToIndex:8]; NSString *filename = [NSString stringWithFormat:@"FLEXRuntimeDatabase-%@.db", uuid]; return [temp stringByAppendingPathComponent:filename]; } + (void)createRuntimeDatabaseAtPath:(NSString *)path progressHandler:(void(^)(NSString *status))progress completion:(void (^)(NSString *))completion { [self createRuntimeDatabaseAtPath:path forImages:nil progressHandler:progress completion:completion]; } + (void)createRuntimeDatabaseAtPath:(NSString *)path forImages:(NSArray *)images progressHandler:(void(^)(NSString *status))progress completion:(void(^)(NSString *_Nullable error))completion { __typeof(completion) callback = ^(NSString *error) { dispatch_async(dispatch_get_main_queue(), ^{ completion(error); }); }; // This must be called on the main thread first if (NSThread.isMainThread) { [FLEXRuntimeClient initializeWebKitLegacy]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ [FLEXRuntimeClient initializeWebKitLegacy]; }); } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSError *error = nil; NSString *errorMessage = nil; // Get unused temp filename, remove existing database if any NSString *tempPath = [self tempFilename]; if ([NSFileManager.defaultManager fileExistsAtPath:tempPath]) { [NSFileManager.defaultManager removeItemAtPath:tempPath error:&error]; if (error) { callback(error.localizedDescription); return; } } // Attempt to create and populate the database, abort if we fail FLEXRuntimeExporter *exporter = [self new]; exporter.loadedBundlePaths = images; if (![exporter createAndPopulateDatabaseAtPath:tempPath progressHandler:progress error:&errorMessage]) { // Remove temp database if it was not moved if ([NSFileManager.defaultManager fileExistsAtPath:tempPath]) { [NSFileManager.defaultManager removeItemAtPath:tempPath error:nil]; } callback(errorMessage); return; } // Remove old database at given path if ([NSFileManager.defaultManager fileExistsAtPath:path]) { [NSFileManager.defaultManager removeItemAtPath:path error:&error]; if (error) { callback(error.localizedDescription); return; } } // Move new database to desired path [NSFileManager.defaultManager moveItemAtPath:tempPath toPath:path error:&error]; if (error) { callback(error.localizedDescription); } // Remove temp database if it was not moved if ([NSFileManager.defaultManager fileExistsAtPath:tempPath]) { [NSFileManager.defaultManager removeItemAtPath:tempPath error:nil]; } callback(nil); }); } - (id)init { self = [super init]; if (self) { _bundlePathsToIDs = [NSMutableDictionary new]; _protocolsToIDs = [NSMutableDictionary new]; _classesToIDs = [NSMutableDictionary new]; _typeEncodingsToIDs = [NSMutableDictionary new]; _methodSignaturesToIDs = [NSMutableDictionary new]; _selectorsToIDs = [NSMutableDictionary new]; _bundlePathsToIDs[NSNull.null] = (id)NSNull.null; } return self; } - (BOOL)createAndPopulateDatabaseAtPath:(NSString *)path progressHandler:(void(^)(NSString *status))step error:(NSString **)error { _db = [FLEXSQLiteDatabaseManager managerForDatabase:path]; [self loadMetadata:step]; if ([self createTables] && [self addImages:step] && [self addProtocols:step] && [self addClasses:step] && [self setSuperclasses:step] && [self addProtocolConformances:step] && [self addClassConformances:step] && [self addIvars:step] && [self addMethods:step] && [self addProperties:step]) { _db = nil; // Close the database return YES; } *error = self.db.lastResult.message; return NO; } - (void)loadMetadata:(void(^)(NSString *status))progress { progress(@"Loading metadata…"); FLEXRuntimeClient *runtime = FLEXRuntimeClient.runtime; // Only load metadata for the existing paths if any if (self.loadedBundlePaths) { // Images self.loadedShortBundleNames = [self.loadedBundlePaths flex_mapped:^id(NSString *path, NSUInteger idx) { return [runtime shortNameForImageName:path]; }]; // Classes self.classes = [[runtime classesForToken:FLEXSearchToken.any inBundles:self.loadedBundlePaths.mutableCopy ] flex_mapped:^id(NSString *cls, NSUInteger idx) { return NSClassFromString(cls); }]; } else { // Images self.loadedShortBundleNames = runtime.imageDisplayNames; self.loadedBundlePaths = [self.loadedShortBundleNames flex_mapped:^id(NSString *name, NSUInteger idx) { return [runtime imageNameForShortName:name]; }]; // Classes self.classes = [runtime copySafeClassList]; } // ...except protocols, because there's not a lot of them // and there's no way load the protocols for a given image self.protocols = [[runtime copyProtocolList] flex_mapped:^id(Protocol *proto, NSUInteger idx) { return [FLEXProtocol protocol:proto]; }]; } - (BOOL)createTables { NSArray *commands = @[ kFREEnableForeignKeys, kFRECreateTableMachOCommand, kFRECreateTableClassCommand, kFRECreateTableSelectorCommand, kFRECreateTableTypeEncodingCommand, kFRECreateTableTypeSignatureCommand, kFRECreateTableMethodSignatureCommand, kFRECreateTableMethodCommand, kFRECreateTablePropertyCommand, kFRECreateTableIvarCommand, kFRECreateTableProtocolCommand, kFRECreateTableProtocolPropertyCommand, kFRECreateTableProtocolConformanceCommand, kFRECreateTableClassConformanceCommand ]; for (NSString *command in commands) { if (![self.db executeStatement:command]) { return NO; } } return YES; } - (BOOL)addImages:(void(^)(NSString *status))progress { progress(@"Adding loaded images…"); FLEXSQLiteDatabaseManager *database = self.db; NSArray *shortNames = self.loadedShortBundleNames; NSArray *fullPaths = self.loadedBundlePaths; NSParameterAssert(shortNames.count == fullPaths.count); NSInteger count = shortNames.count; for (NSInteger i = 0; i < count; i++) { // Grab bundle ID NSString *bundleID = [NSBundle bundleWithPath:fullPaths[i] ].bundleIdentifier; [database executeStatement:kFREInsertImage arguments:@{ @"$shortName": shortNames[i], @"$imagePath": fullPaths[i], @"$bundleID": bundleID ?: NSNull.null }]; if (database.lastResult.isError) { return NO; } else { self.bundlePathsToIDs[fullPaths[i]] = @(database.lastRowID); } } return YES; } NS_INLINE BOOL FREInsertProtocolMember(FLEXSQLiteDatabaseManager *db, id proto, id required, id instance, id prop, id methSel, id image) { return ![db executeStatement:kFREInsertProtocolMember arguments:@{ @"$protocol": proto, @"$required": required, @"$instance": instance ?: NSNull.null, @"$property": prop ?: NSNull.null, @"$method": methSel ?: NSNull.null, @"$image": image }].isError; } - (BOOL)addProtocols:(void(^)(NSString *status))progress { progress([NSString stringWithFormat:@"Adding %@ protocols…", @(self.protocols.count)]); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *imageIDs = self.bundlePathsToIDs; for (FLEXProtocol *proto in self.protocols) { id imagePath = proto.imagePath ?: NSNull.null; NSNumber *image = imageIDs[imagePath] ?: NSNull.null; NSNumber *pid = nil; // Insert protocol BOOL failed = [database executeStatement:kFREInsertProtocol arguments:@{ @"$name": proto.name, @"$image": image }].isError; // Cache rowid if (failed) { return NO; } else { self.protocolsToIDs[proto.name] = pid = @(database.lastRowID); } // Insert its members // // Required methods for (FLEXMethodDescription *method in proto.requiredMethods) { NSString *selector = NSStringFromSelector(method.selector); if (!FREInsertProtocolMember(database, pid, @YES, method.instance, nil, selector, image)) { return NO; } } // Optional methods for (FLEXMethodDescription *method in proto.optionalMethods) { NSString *selector = NSStringFromSelector(method.selector); if (!FREInsertProtocolMember(database, pid, @NO, method.instance, nil, selector, image)) { return NO; } } if (@available(iOS 10, *)) { // Required properties for (FLEXProperty *property in proto.requiredProperties) { BOOL success = FREInsertProtocolMember( database, pid, @YES, @(property.isClassProperty), property.name, NSNull.null, image ); if (!success) return NO; } // Optional properties for (FLEXProperty *property in proto.optionalProperties) { BOOL success = FREInsertProtocolMember( database, pid, @NO, @(property.isClassProperty), property.name, NSNull.null, image ); if (!success) return NO; } } else { // Just... properties. for (FLEXProperty *property in proto.properties) { BOOL success = FREInsertProtocolMember( database, pid, nil, @(property.isClassProperty), property.name, NSNull.null, image ); if (!success) return NO; } } } return YES; } - (BOOL)addProtocolConformances:(void(^)(NSString *status))progress { progress(@"Adding protocol-to-protocol conformances…"); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *protocolIDs = self.protocolsToIDs; for (FLEXProtocol *proto in self.protocols) { id protoID = protocolIDs[proto.name]; for (FLEXProtocol *conform in proto.protocols) { BOOL failed = [database executeStatement:kFREInsertProtocolConformance arguments:@{ @"$protocol": protoID, @"$conformance": protocolIDs[conform.name] }].isError; if (failed) { return NO; } } } return YES; } - (BOOL)addClasses:(void(^)(NSString *status))progress { progress([NSString stringWithFormat:@"Adding %@ classes…", @(self.classes.count)]); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *imageIDs = self.bundlePathsToIDs; for (Class cls in self.classes) { const char *imageName = class_getImageName(cls); id image = imageName ? imageIDs[@(imageName)] : NSNull.null; image = image ?: NSNull.null; BOOL failed = [database executeStatement:kFREInsertClass arguments:@{ @"$className": NSStringFromClass(cls), @"$instanceSize": @(class_getInstanceSize(cls)), @"$version": @(class_getVersion(cls)), @"$image": image }].isError; if (failed) { return NO; } else { self.classesToIDs[(id)cls] = @(database.lastRowID); } } return YES; } - (BOOL)setSuperclasses:(void(^)(NSString *status))progress { progress(@"Setting superclasses…"); FLEXSQLiteDatabaseManager *database = self.db; for (Class cls in self.classes) { // Grab superclass ID Class superclass = class_getSuperclass(cls); NSNumber *superclassID = _classesToIDs[class_getSuperclass(cls)]; // ... or add the superclass and cache its ID if the // superclass does not reside in the target image(s) if (!superclassID) { NSDictionary *args = @{ @"$className": NSStringFromClass(superclass) }; BOOL failed = [database executeStatement:kFREInsertClass arguments:args].isError; if (failed) { return NO; } _classesToIDs[(id)superclass] = superclassID = @(database.lastRowID); } if (superclass) { BOOL failed = [database executeStatement:kFREUpdateClassSetSuper arguments:@{ @"$super": superclassID, @"$id": _classesToIDs[cls] }].isError; if (failed) { return NO; } } } return YES; } - (BOOL)addClassConformances:(void(^)(NSString *status))progress { progress(@"Adding class-to-protocol conformances…"); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *protocolIDs = self.protocolsToIDs; NSDictionary *classIDs = self.classesToIDs; for (Class cls in self.classes) { id classID = classIDs[(id)cls]; for (FLEXProtocol *conform in FLEXGetConformedProtocols(cls)) { BOOL failed = [database executeStatement:kFREInsertClassConformance arguments:@{ @"$class": classID, @"$conformance": protocolIDs[conform.name] }].isError; if (failed) { return NO; } } } return YES; } - (BOOL)addIvars:(void(^)(NSString *status))progress { progress(@"Adding ivars…"); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *imageIDs = self.bundlePathsToIDs; for (Class cls in self.classes) { for (FLEXIvar *ivar in FLEXGetAllIvars(cls)) { // Insert type first if (![self addTypeEncoding:ivar.typeEncoding size:ivar.size]) { return NO; } id imagePath = ivar.imagePath ?: NSNull.null; NSNumber *image = imageIDs[imagePath] ?: NSNull.null; BOOL failed = [database executeStatement:kFREInsertIvar arguments:@{ @"$name": ivar.name, @"$offset": @(ivar.offset), @"$type": _typeEncodingsToIDs[ivar.typeEncoding], @"$class": _classesToIDs[cls], @"$image": image }].isError; if (failed) { return NO; } } } return YES; } - (BOOL)addMethods:(void(^)(NSString *status))progress { progress(@"Adding methods…"); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *imageIDs = self.bundlePathsToIDs; // Loop over all classes for (Class cls in self.classes) { NSNumber *classID = _classesToIDs[(id)cls]; const char *imageName = class_getImageName(cls); id image = imageName ? imageIDs[@(imageName)] : NSNull.null; image = image ?: NSNull.null; // Block used to process each message BOOL (^insert)(FLEXMethod *, NSNumber *) = ^BOOL(FLEXMethod *method, NSNumber *instance) { // Insert selector and signature first if (![self addSelector:method.selectorString]) { return NO; } if (![self addMethodSignature:method]) { return NO; } return ![database executeStatement:kFREInsertMethod arguments:@{ @"$sel": self->_selectorsToIDs[method.selectorString], @"$class": classID, @"$instance": instance, @"$signature": self->_methodSignaturesToIDs[method.signatureString], @"$image": image }].isError; }; // Loop over all instance and class methods of that class // for (FLEXMethod *method in FLEXGetAllMethods(cls, YES)) { if (!insert(method, @YES)) { return NO; } } for (FLEXMethod *method in FLEXGetAllMethods(object_getClass(cls), NO)) { if (!insert(method, @NO)) { return NO; } } } return YES; } - (BOOL)addProperties:(void(^)(NSString *status))progress { progress(@"Adding properties…"); FLEXSQLiteDatabaseManager *database = self.db; NSDictionary *imageIDs = self.bundlePathsToIDs; // Loop over all classes for (Class cls in self.classes) { NSNumber *classID = _classesToIDs[(id)cls]; // Block used to process each message BOOL (^insert)(FLEXProperty *, NSNumber *) = ^BOOL(FLEXProperty *property, NSNumber *instance) { FLEXPropertyAttributes *attrs = property.attributes; NSString *customGetter = attrs.customGetterString; NSString *customSetter = attrs.customSetterString; // Insert selectors first if (customGetter) { if (![self addSelector:customGetter]) { return NO; } } if (customSetter) { if (![self addSelector:customSetter]) { return NO; } } // Insert type encoding first NSInteger size = [FLEXTypeEncodingParser sizeForTypeEncoding:attrs.typeEncoding alignment:nil ]; if (![self addTypeEncoding:attrs.typeEncoding size:size]) { return NO; } id imagePath = property.imagePath ?: NSNull.null; id image = imageIDs[imagePath] ?: NSNull.null; return ![database executeStatement:kFREInsertProperty arguments:@{ @"$name": property.name, @"$class": classID, @"$instance": instance, @"$image": image, @"$attributes": attrs.string, @"$customGetter": self->_selectorsToIDs[customGetter] ?: NSNull.null, @"$customSetter": self->_selectorsToIDs[customSetter] ?: NSNull.null, @"$type": self->_typeEncodingsToIDs[attrs.typeEncoding] ?: NSNull.null, @"$ivar": attrs.backingIvar ?: NSNull.null, @"$readonly": @(attrs.isReadOnly), @"$copy": @(attrs.isCopy), @"$retained": @(attrs.isRetained), @"$nonatomic": @(attrs.isNonatomic), @"$dynamic": @(attrs.isDynamic), @"$weak": @(attrs.isWeak), @"$canGC": @(attrs.isGarbageCollectable), }].isError; }; // Loop over all instance and class methods of that class // for (FLEXProperty *property in FLEXGetAllProperties(cls)) { if (!insert(property, @YES)) { return NO; } } for (FLEXProperty *property in FLEXGetAllProperties(object_getClass(cls))) { if (!insert(property, @NO)) { return NO; } } } return YES; } - (BOOL)addSelector:(NSString *)sel { return [self executeInsert:kFREInsertSelector args:@{ @"$name": sel } key:sel cacheResult:_selectorsToIDs]; } - (BOOL)addTypeEncoding:(NSString *)type size:(NSInteger)size { return [self executeInsert:kFREInsertTypeEncoding args:@{ @"$type": type, @"$size": @(size) } key:type cacheResult:_typeEncodingsToIDs]; } - (BOOL)addMethodSignature:(FLEXMethod *)method { NSString *signature = method.signatureString; NSString *returnType = @((char *)method.returnType); // Insert return type first if (![self addTypeEncoding:returnType size:method.returnSize]) { return NO; } return [self executeInsert:kFREInsertMethodSignature args:@{ @"$typeEncoding": signature, @"$returnType": _typeEncodingsToIDs[returnType], @"$argc": @(method.numberOfArguments), @"$frameLength": @(method.signature.frameLength) } key:signature cacheResult:_methodSignaturesToIDs]; } - (BOOL)executeInsert:(NSString *)statement args:(NSDictionary *)args key:(NSString *)cacheKey cacheResult:(NSMutableDictionary *)rowids { // Check if already inserted if (rowids[cacheKey]) { return YES; } // Insert FLEXSQLiteDatabaseManager *database = _db; [database executeStatement:statement arguments:args]; if (database.lastResult.isError) { return NO; } // Cache rowid rowids[cacheKey] = @(database.lastRowID); return YES; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKBToolbarButton.h ================================================ // // FLEXKBToolbarButton.h // FLEX // // Created by Tanner on 6/11/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import typedef void (^FLEXKBToolbarAction)(NSString *buttonTitle, BOOL isSuggestion); @interface FLEXKBToolbarButton : UIButton /// Set to `default` to use the system appearance on iOS 13+ @property (nonatomic) UIKeyboardAppearance appearance; + (instancetype)buttonWithTitle:(NSString *)title; + (instancetype)buttonWithTitle:(NSString *)title action:(FLEXKBToolbarAction)eventHandler; + (instancetype)buttonWithTitle:(NSString *)title action:(FLEXKBToolbarAction)action forControlEvents:(UIControlEvents)controlEvents; /// Adds the event handler for the button. /// /// @param eventHandler The event handler block. /// @param controlEvents The type of event. - (void)addEventHandler:(FLEXKBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvents; @end @interface FLEXKBToolbarSuggestedButton : FLEXKBToolbarButton @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKBToolbarButton.m ================================================ // // FLEXKBToolbarButton.m // FLEX // // Created by Tanner on 6/11/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXKBToolbarButton.h" #import "UIFont+FLEX.h" #import "FLEXUtility.h" #import "CALayer+FLEX.h" @interface FLEXKBToolbarButton () @property (nonatomic ) NSString *title; @property (nonatomic, copy) FLEXKBToolbarAction buttonPressBlock; /// YES if appearance is set to `default` @property (nonatomic, readonly) BOOL useSystemAppearance; /// YES if the current trait collection is set to dark mode and \c useSystemAppearance is YES @property (nonatomic, readonly) BOOL usingDarkMode; @end @implementation FLEXKBToolbarButton + (instancetype)buttonWithTitle:(NSString *)title { return [[self alloc] initWithTitle:title]; } + (instancetype)buttonWithTitle:(NSString *)title action:(FLEXKBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvent { FLEXKBToolbarButton *newButton = [self buttonWithTitle:title]; [newButton addEventHandler:eventHandler forControlEvents:controlEvent]; return newButton; } + (instancetype)buttonWithTitle:(NSString *)title action:(FLEXKBToolbarAction)eventHandler { return [self buttonWithTitle:title action:eventHandler forControlEvents:UIControlEventTouchUpInside]; } - (id)initWithTitle:(NSString *)title { self = [super init]; if (self) { _title = title; self.layer.shadowOffset = CGSizeMake(0, 1); self.layer.shadowOpacity = 0.35; self.layer.shadowRadius = 0; self.layer.cornerRadius = 5; self.clipsToBounds = NO; self.titleLabel.font = [UIFont systemFontOfSize:18.0]; self.layer.flex_continuousCorners = YES; [self setTitle:self.title forState:UIControlStateNormal]; [self sizeToFit]; if (@available(iOS 13, *)) { self.appearance = UIKeyboardAppearanceDefault; } else { self.appearance = UIKeyboardAppearanceLight; } CGRect frame = self.frame; frame.size.width += title.length < 3 ? 30 : 15; frame.size.height += 10; self.frame = frame; } return self; } - (void)addEventHandler:(FLEXKBToolbarAction)eventHandler forControlEvents:(UIControlEvents)controlEvent { self.buttonPressBlock = eventHandler; [self addTarget:self action:@selector(buttonPressed) forControlEvents:controlEvent]; } - (void)buttonPressed { self.buttonPressBlock(self.title, NO); } - (void)setAppearance:(UIKeyboardAppearance)appearance { _appearance = appearance; UIColor *titleColor = nil, *backgroundColor = nil; UIColor *lightColor = [UIColor colorWithRed:253.0/255.0 green:253.0/255.0 blue:254.0/255.0 alpha:1]; UIColor *darkColor = [UIColor colorWithRed:101.0/255.0 green:102.0/255.0 blue:104.0/255.0 alpha:1]; switch (_appearance) { default: case UIKeyboardAppearanceDefault: if (@available(iOS 13, *)) { titleColor = UIColor.labelColor; if (self.usingDarkMode) { // style = UIBlurEffectStyleSystemUltraThinMaterialLight; backgroundColor = darkColor; } else { // style = UIBlurEffectStyleSystemMaterialLight; backgroundColor = lightColor; } break; } case UIKeyboardAppearanceLight: titleColor = UIColor.blackColor; backgroundColor = lightColor; // style = UIBlurEffectStyleExtraLight; break; case UIKeyboardAppearanceDark: titleColor = UIColor.whiteColor; backgroundColor = darkColor; // style = UIBlurEffectStyleDark; break; } self.backgroundColor = backgroundColor; [self setTitleColor:titleColor forState:UIControlStateNormal]; } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[FLEXKBToolbarButton class]]) { return [self.title isEqualToString:[object title]]; } return NO; } - (NSUInteger)hash { return self.title.hash; } - (BOOL)useSystemAppearance { return self.appearance == UIKeyboardAppearanceDefault; } - (BOOL)usingDarkMode { if (@available(iOS 12, *)) { return self.useSystemAppearance && self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark; } return self.appearance == UIKeyboardAppearanceDark; } - (void)traitCollectionDidChange:(UITraitCollection *)previous { if (@available(iOS 12, *)) { // Was darkmode toggled? if (previous.userInterfaceStyle != self.traitCollection.userInterfaceStyle) { if (self.useSystemAppearance) { // Recreate the background view with the proper colors self.appearance = self.appearance; } } } } @end @implementation FLEXKBToolbarSuggestedButton - (void)buttonPressed { self.buttonPressBlock(self.title, YES); } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyPathSearchController.h ================================================ // // FLEXKeyPathSearchController.h // FLEX // // Created by Tanner on 3/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import #import "FLEXRuntimeBrowserToolbar.h" #import "FLEXMethod.h" @protocol FLEXKeyPathSearchControllerDelegate @property (nonatomic, readonly) UITableView *tableView; @property (nonatomic, readonly) UISearchController *searchController; /// For loaded images which don't have an NSBundle - (void)didSelectImagePath:(NSString *)message shortName:(NSString *)shortName; - (void)didSelectBundle:(NSBundle *)bundle; - (void)didSelectClass:(Class)cls; @end @interface FLEXKeyPathSearchController : NSObject + (instancetype)delegate:(id)delegate; @property (nonatomic) FLEXRuntimeBrowserToolbar *toolbar; /// Suggestions for the toolbar @property (nonatomic, readonly) NSArray *suggestions; - (void)didSelectKeyPathOption:(NSString *)text; - (void)didPressButton:(NSString *)text insertInto:(UISearchBar *)searchBar; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyPathSearchController.m ================================================ // // FLEXKeyPathSearchController.m // FLEX // // Created by Tanner on 3/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXKeyPathSearchController.h" #import "FLEXRuntimeKeyPathTokenizer.h" #import "FLEXRuntimeController.h" #import "NSString+FLEX.h" #import "NSArray+FLEX.h" #import "UITextField+Range.h" #import "NSTimer+FLEX.h" #import "FLEXTableView.h" #import "FLEXUtility.h" #import "FLEXObjectExplorerFactory.h" @interface FLEXKeyPathSearchController () @property (nonatomic, readonly, weak) id delegate; @property (nonatomic) NSTimer *timer; /// If \c keyPath is \c nil or if it only has a \c bundleKey, this is /// a list of bundle key path components like \c UICatalog or \c UIKit\.framework /// If \c keyPath has more than a \c bundleKey then it is a list of class names. @property (nonatomic) NSArray *bundlesOrClasses; /// nil when search bar is empty @property (nonatomic) FLEXRuntimeKeyPath *keyPath; @property (nonatomic, readonly) NSString *emptySuggestion; /// Used to track which methods go with which classes. This is used in /// two scenarios: (1) when the target class is absolute and has classes, /// (this list will include the "leaf" class as well as parent classes in this case) /// or (2) when the class key is a wildcard and we're searching methods in many /// classes at once. Each list in \c classesToMethods correspnds to a class here. @property (nonatomic) NSArray *classes; /// A filtered version of \c classes used when searching for a specific attribute. /// Classes with no matching ivars/properties/methods are not shown. @property (nonatomic) NSArray *filteredClasses; // We use this regardless of whether the target class is absolute, just as above @property (nonatomic) NSArray *> *classesToMethods; @end @implementation FLEXKeyPathSearchController + (instancetype)delegate:(id)delegate { FLEXKeyPathSearchController *controller = [self new]; controller->_bundlesOrClasses = [FLEXRuntimeController allBundleNames]; controller->_delegate = delegate; controller->_emptySuggestion = NSBundle.mainBundle.executablePath.lastPathComponent; NSParameterAssert(delegate.tableView); NSParameterAssert(delegate.searchController); delegate.tableView.delegate = controller; delegate.tableView.dataSource = controller; UISearchBar *searchBar = delegate.searchController.searchBar; searchBar.delegate = controller; searchBar.keyboardType = UIKeyboardTypeWebSearch; searchBar.autocorrectionType = UITextAutocorrectionTypeNo; if (@available(iOS 11, *)) { searchBar.smartQuotesType = UITextSmartQuotesTypeNo; searchBar.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo; } return controller; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) { [self.delegate.searchController.searchBar resignFirstResponder]; } } - (void)setToolbar:(FLEXRuntimeBrowserToolbar *)toolbar { _toolbar = toolbar; self.delegate.searchController.searchBar.inputAccessoryView = toolbar; } - (NSArray *)classesOf:(NSString *)className { Class baseClass = NSClassFromString(className); if (!baseClass) { return @[]; } // Find classes NSMutableArray *classes = [NSMutableArray arrayWithObject:className]; while ([baseClass superclass]) { [classes addObject:NSStringFromClass([baseClass superclass])]; baseClass = [baseClass superclass]; } return classes; } #pragma mark Key path stuff - (void)didSelectKeyPathOption:(NSString *)text { [_timer invalidate]; // Still might be waiting to refresh when method is selected // Change "Bundle.fooba" to "Bundle.foobar." NSString *orig = self.delegate.searchController.searchBar.text; NSString *keyPath = [orig flex_stringByReplacingLastKeyPathComponent:text]; self.delegate.searchController.searchBar.text = keyPath; self.keyPath = [FLEXRuntimeKeyPathTokenizer tokenizeString:keyPath]; // Get classes if class was selected if (self.keyPath.classKey.isAbsolute && self.keyPath.methodKey.isAny) { [self didSelectAbsoluteClass:text]; } else { self.classes = nil; self.filteredClasses = nil; } [self updateTable]; } - (void)didSelectAbsoluteClass:(NSString *)name { self.classes = [self classesOf:name]; self.filteredClasses = self.classes; self.bundlesOrClasses = nil; self.classesToMethods = nil; } - (void)didPressButton:(NSString *)text insertInto:(UISearchBar *)searchBar { [self.toolbar setKeyPath:self.keyPath suggestions:nil]; // Available since at least iOS 9, still present in iOS 13 UITextField *field = [searchBar valueForKey:@"_searchBarTextField"]; if ([self searchBar:searchBar shouldChangeTextInRange:field.flex_selectedRange replacementText:text]) { [field replaceRange:field.selectedTextRange withText:text]; } } - (NSArray *)suggestions { if (self.bundlesOrClasses) { if (self.classes) { if (self.classesToMethods) { // We have selected a class and are searching metadata return nil; } // We are currently searching classes return [self.filteredClasses flex_subArrayUpto:10]; } if (!self.keyPath) { // Search bar is empty return @[self.emptySuggestion]; } // We are currently searching bundles return [self.bundlesOrClasses flex_subArrayUpto:10]; } // We have nothing at all to even search return nil; } #pragma mark - Filtering + UISearchBarDelegate - (void)updateTable { // Compute the method, class, or bundle lists on a background thread dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ if (self.classes) { // Here, our class key is 'absolute'; .classes is a list of superclasses // and we want to show the methods for those classes specifically // TODO: add caching to this somehow NSMutableArray *methods = [FLEXRuntimeController methodsForToken:self.keyPath.methodKey instance:self.keyPath.instanceMethods inClasses:self.classes ].mutableCopy; // Remove classes without results if we're searching for a method // // Note: this will remove classes without any methods or overrides // even if the query doesn't specify a method, like `*.*.` if (self.keyPath.methodKey) { [self setNonEmptyMethodLists:methods withClasses:self.classes.mutableCopy]; } else { self.filteredClasses = self.classes; } } else { FLEXRuntimeKeyPath *keyPath = self.keyPath; NSArray *models = [FLEXRuntimeController dataForKeyPath:keyPath]; if (keyPath.methodKey) { // We're looking at methods self.bundlesOrClasses = nil; NSMutableArray *methods = models.mutableCopy; NSMutableArray *classes = [ FLEXRuntimeController classesForKeyPath:keyPath ]; self.classes = classes; [self setNonEmptyMethodLists:methods withClasses:classes]; } else { // We're looking at bundles or classes self.bundlesOrClasses = models; self.classesToMethods = nil; } } // Finally, reload the table on the main thread dispatch_async(dispatch_get_main_queue(), ^{ [self updateToolbarButtons]; [self.delegate.tableView reloadData]; }); }); } - (void)updateToolbarButtons { // Update toolbar buttons [self.toolbar setKeyPath:self.keyPath suggestions:self.suggestions]; } /// Assign assign .filteredClasses and .classesToMethods after removing empty sections - (void)setNonEmptyMethodLists:(NSMutableArray *> *)methods withClasses:(NSMutableArray *)classes { // Remove sections with no methods NSIndexSet *allEmpty = [methods indexesOfObjectsPassingTest:^BOOL(NSArray *list, NSUInteger idx, BOOL *stop) { return list.count == 0; }]; [methods removeObjectsAtIndexes:allEmpty]; [classes removeObjectsAtIndexes:allEmpty]; self.filteredClasses = classes; self.classesToMethods = methods; } - (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { // Check if character is even legal if (![FLEXRuntimeKeyPathTokenizer allowedInKeyPath:text]) { return NO; } BOOL terminatedToken = NO; BOOL isAppending = range.length == 0 && range.location == searchBar.text.length; if (isAppending && [text isEqualToString:@"."]) { terminatedToken = YES; } // Actually parse input @try { text = [searchBar.text stringByReplacingCharactersInRange:range withString:text] ?: text; self.keyPath = [FLEXRuntimeKeyPathTokenizer tokenizeString:text]; if (self.keyPath.classKey.isAbsolute && terminatedToken) { [self didSelectAbsoluteClass:self.keyPath.classKey.string]; } } @catch (id e) { return NO; } return YES; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [_timer invalidate]; // Schedule update timer if (searchText.length) { if (!self.keyPath.methodKey) { self.classes = nil; self.filteredClasses = nil; } self.timer = [NSTimer flex_fireSecondsFromNow:0.15 block:^{ [self updateTable]; }]; } // ... or remove all rows else { _bundlesOrClasses = [FLEXRuntimeController allBundleNames]; _classesToMethods = nil; _classes = nil; _keyPath = nil; [self updateToolbarButtons]; [self.delegate.tableView reloadData]; } } - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { self.keyPath = FLEXRuntimeKeyPath.empty; [self updateTable]; } /// Restore key path when going "back" and activating search bar again - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { searchBar.text = self.keyPath.description; } - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [_timer invalidate]; [searchBar resignFirstResponder]; [self updateTable]; } #pragma mark UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.filteredClasses.count ?: self.bundlesOrClasses.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXMultilineDetailCell forIndexPath:indexPath ]; if (self.bundlesOrClasses.count) { cell.accessoryType = UITableViewCellAccessoryDetailButton; cell.textLabel.text = self.bundlesOrClasses[indexPath.row]; cell.detailTextLabel.text = nil; if (self.keyPath.classKey) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } } // One row per section else if (self.filteredClasses.count) { NSArray *methods = self.classesToMethods[indexPath.row]; NSMutableString *summary = [NSMutableString new]; [methods enumerateObjectsUsingBlock:^(FLEXMethod *method, NSUInteger idx, BOOL *stop) { NSString *format = nil; if (idx == methods.count-1) { format = @"%@%@"; *stop = YES; } else if (idx < 3) { format = @"%@%@\n"; } else { format = @"%@%@\n…"; *stop = YES; } [summary appendFormat:format, method.isInstanceMethod ? @"-" : @"+", method.selectorString]; }]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = self.filteredClasses[indexPath.row]; if (@available(iOS 10, *)) { cell.detailTextLabel.text = summary.length ? summary : nil; } } else { @throw NSInternalInconsistencyException; } return cell; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (self.filteredClasses || self.keyPath.methodKey) { return @" "; } else if (self.bundlesOrClasses) { NSInteger count = self.bundlesOrClasses.count; if (self.keyPath.classKey) { return FLEXPluralString(count, @"classes", @"class"); } else { return FLEXPluralString(count, @"bundles", @"bundle"); } } return [self.delegate tableView:tableView titleForHeaderInSection:section]; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { if (self.filteredClasses || self.keyPath.methodKey) { if (section == 0) { return 55; } return 0; } return 55; } #pragma mark UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (self.bundlesOrClasses) { NSString *bundleSuffixOrClass = self.bundlesOrClasses[indexPath.row]; if (self.keyPath.classKey) { NSParameterAssert(NSClassFromString(bundleSuffixOrClass)); [self.delegate didSelectClass:NSClassFromString(bundleSuffixOrClass)]; } else { // Selected a bundle [self didSelectKeyPathOption:bundleSuffixOrClass]; } } else { if (self.filteredClasses.count) { Class cls = NSClassFromString(self.filteredClasses[indexPath.row]); NSParameterAssert(cls); [self.delegate didSelectClass:cls]; } else { @throw NSInternalInconsistencyException; } } } - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { NSString *bundleSuffixOrClass = self.bundlesOrClasses[indexPath.row]; NSString *imagePath = [FLEXRuntimeController imagePathWithShortName:bundleSuffixOrClass]; NSBundle *bundle = [NSBundle bundleWithPath:imagePath.stringByDeletingLastPathComponent]; if (bundle) { [self.delegate didSelectBundle:bundle]; } else { [self.delegate didSelectImagePath:imagePath shortName:bundleSuffixOrClass]; } } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyboardToolbar.h ================================================ // // FLEXKeyboardToolbar.h // FLEX // // Created by Tanner on 6/11/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXKBToolbarButton.h" @interface FLEXKeyboardToolbar : UIView + (instancetype)toolbarWithButtons:(NSArray *)buttons; @property (nonatomic) NSArray *buttons; @property (nonatomic) UIKeyboardAppearance appearance; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyboardToolbar.m ================================================ // // FLEXKeyboardToolbar.m // FLEX // // Created by Tanner on 6/11/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXKeyboardToolbar.h" #import "FLEXUtility.h" #define kToolbarHeight 44 #define kButtonSpacing 6 #define kScrollViewHorizontalMargins 3 @interface FLEXKeyboardToolbar () /// The fake top border to replicate the toolbar. @property (nonatomic) CALayer *topBorder; @property (nonatomic) UIView *toolbarView; @property (nonatomic) UIScrollView *scrollView; @property (nonatomic) UIVisualEffectView *blurView; /// YES if appearance is set to `default` @property (nonatomic, readonly) BOOL useSystemAppearance; /// YES if the current trait collection is set to dark mode and \c useSystemAppearance is YES @property (nonatomic, readonly) BOOL usingDarkMode; @end @implementation FLEXKeyboardToolbar + (instancetype)toolbarWithButtons:(NSArray *)buttons { return [[self alloc] initWithButtons:buttons]; } - (id)initWithButtons:(NSArray *)buttons { self = [super initWithFrame:CGRectMake(0, 0, self.window.rootViewController.view.bounds.size.width, kToolbarHeight)]; if (self) { _buttons = [buttons copy]; self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (@available(iOS 13, *)) { self.appearance = UIKeyboardAppearanceDefault; } else { self.appearance = UIKeyboardAppearanceLight; } } return self; } - (void)setAppearance:(UIKeyboardAppearance)appearance { _appearance = appearance; // Remove toolbar if it exits because it will be recreated below if (self.toolbarView) { [self.toolbarView removeFromSuperview]; } [self addSubview:self.inputAccessoryView]; } - (void)layoutSubviews { [super layoutSubviews]; // Layout top border CGRect frame = _toolbarView.bounds; frame.size.height = 0.5; _topBorder.frame = frame; // Scroll view // frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight); CGSize contentSize = self.scrollView.contentSize; CGFloat scrollViewWidth = frame.size.width; // If our content size is smaller than the scroll view, // we want to right-align all the content if (contentSize.width < scrollViewWidth) { // Compute the content size to scroll view size difference UIEdgeInsets insets = self.scrollView.contentInset; CGFloat margin = insets.left + insets.right; CGFloat difference = scrollViewWidth - contentSize.width - margin; // Update the content size to be the full width of the scroll view contentSize.width += difference; self.scrollView.contentSize = contentSize; // Offset every button by the difference above // so that every button appears right-aligned for (UIView *button in self.scrollView.subviews) { CGRect f = button.frame; f.origin.x += difference; button.frame = f; } } } - (UIView *)inputAccessoryView { _topBorder = [CALayer new]; _topBorder.frame = CGRectMake(0.0, 0.0, self.bounds.size.width, 0.5); [self makeScrollView]; UIColor *borderColor = nil, *backgroundColor = nil; UIColor *lightColor = [UIColor colorWithHue:216.0/360.0 saturation:0.05 brightness:0.85 alpha:1]; UIColor *darkColor = [UIColor colorWithHue:220.0/360.0 saturation:0.07 brightness:0.16 alpha:1]; switch (_appearance) { case UIKeyboardAppearanceDefault: if (@available(iOS 13, *)) { borderColor = UIColor.systemBackgroundColor; if (self.usingDarkMode) { // style = UIBlurEffectStyleSystemThickMaterial; backgroundColor = darkColor; } else { // style = UIBlurEffectStyleSystemUltraThinMaterialLight; backgroundColor = lightColor; } break; } case UIKeyboardAppearanceLight: { borderColor = UIColor.clearColor; backgroundColor = lightColor; break; } case UIKeyboardAppearanceDark: { borderColor = [UIColor colorWithWhite:0.100 alpha:1.000]; backgroundColor = darkColor; break; } } self.toolbarView = [UIView new]; [self.toolbarView addSubview:self.scrollView]; [self.toolbarView.layer addSublayer:self.topBorder]; self.toolbarView.frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight); self.toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.backgroundColor = backgroundColor; self.topBorder.backgroundColor = borderColor.CGColor; return self.toolbarView; } - (UIScrollView *)makeScrollView { UIScrollView *scrollView = [UIScrollView new]; scrollView.backgroundColor = UIColor.clearColor; scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; scrollView.contentInset = UIEdgeInsetsMake( 8.f, kScrollViewHorizontalMargins, 4.f, kScrollViewHorizontalMargins ); scrollView.showsHorizontalScrollIndicator = NO; self.scrollView = scrollView; [self addButtons]; return scrollView; } - (void)addButtons { NSUInteger originX = 0.f; CGRect originFrame; CGFloat top = self.scrollView.contentInset.top; CGFloat bottom = self.scrollView.contentInset.bottom; for (FLEXKBToolbarButton *button in self.buttons) { button.appearance = self.appearance; originFrame = button.frame; originFrame.origin.x = originX; originFrame.origin.y = 0.f; originFrame.size.height = kToolbarHeight - (top + bottom); button.frame = originFrame; [self.scrollView addSubview:button]; // originX tracks the origin of the next button to be added, // so at the end of each iteration of this loop we increment // it by the size of the last button with some padding originX += button.bounds.size.width + kButtonSpacing; } // Update contentSize, // set to the max x value of the last button added CGSize contentSize = self.scrollView.contentSize; contentSize.width = originX - kButtonSpacing; self.scrollView.contentSize = contentSize; // Needed to potentially right-align buttons [self setNeedsLayout]; } - (void)setButtons:(NSArray *)buttons { [_buttons makeObjectsPerformSelector:@selector(removeFromSuperview)]; _buttons = buttons.copy; [self addButtons]; } - (BOOL)useSystemAppearance { return self.appearance == UIKeyboardAppearanceDefault; } - (BOOL)usingDarkMode { if (@available(iOS 12, *)) { return self.useSystemAppearance && self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark; } return self.appearance == UIKeyboardAppearanceDark; } - (void)traitCollectionDidChange:(UITraitCollection *)previous { if (@available(iOS 12, *)) { // Was darkmode toggled? if (previous.userInterfaceStyle != self.traitCollection.userInterfaceStyle) { if (self.useSystemAppearance) { // Recreate the background view with the proper colors self.appearance = self.appearance; } } } } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXObjcRuntimeViewController.h ================================================ // // FLEXObjcRuntimeViewController.h // FLEX // // Created by Tanner on 3/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXTableViewController.h" #import "FLEXGlobalsEntry.h" @interface FLEXObjcRuntimeViewController : FLEXTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXObjcRuntimeViewController.m ================================================ // // FLEXObjcRuntimeViewController.m // FLEX // // Created by Tanner on 3/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXObjcRuntimeViewController.h" #import "FLEXKeyPathSearchController.h" #import "FLEXRuntimeBrowserToolbar.h" #import "UIGestureRecognizer+Blocks.h" #import "UIBarButtonItem+FLEX.h" #import "FLEXTableView.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXAlert.h" #import "FLEXRuntimeClient.h" #import @interface FLEXObjcRuntimeViewController () @property (nonatomic, readonly ) FLEXKeyPathSearchController *keyPathController; @property (nonatomic, readonly ) UIView *promptView; @end @implementation FLEXObjcRuntimeViewController #pragma mark - Setup, view events - (void)viewDidLoad { [super viewDidLoad]; // Long press on navigation bar to initialize webkit legacy // // We call initializeWebKitLegacy automatically before you search // all bundles just to be safe (since touching some classes before // WebKit is initialized will initialize it on a thread other than // the main thread), but sometimes you can encounter this crash // without searching through all bundles, of course. [self.navigationController.navigationBar addGestureRecognizer:[ [UILongPressGestureRecognizer alloc] initWithTarget:[FLEXRuntimeClient class] action:@selector(initializeWebKitLegacy) ] ]; [self addToolbarItems:@[FLEXBarButtonItem(@"dlopen()", self, @selector(dlopenPressed:))]]; // Search bar stuff, must be first because this creates self.searchController self.showsSearchBar = YES; self.showSearchBarInitially = YES; self.activatesSearchBarAutomatically = YES; // Using pinSearchBar on this screen causes a weird visual // thing on the next view controller that gets pushed. // // self.pinSearchBar = YES; self.searchController.searchBar.placeholder = @"UIKit*.UIView.-setFrame:"; // Search controller stuff // key path controller automatically assigns itself as the delegate of the search bar // To avoid a retain cycle below, use local variables UISearchBar *searchBar = self.searchController.searchBar; FLEXKeyPathSearchController *keyPathController = [FLEXKeyPathSearchController delegate:self]; _keyPathController = keyPathController; _keyPathController.toolbar = [FLEXRuntimeBrowserToolbar toolbarWithHandler:^(NSString *text, BOOL suggestion) { if (suggestion) { [keyPathController didSelectKeyPathOption:text]; } else { [keyPathController didPressButton:text insertInto:searchBar]; } } suggestions:keyPathController.suggestions]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES]; } #pragma mark dlopen /// Prompt user for dlopen shortcuts to choose from - (void)dlopenPressed:(id)sender { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Dynamically Open Library"); make.message(@"Invoke dlopen() with the given path. Choose an option below."); make.button(@"System Framework").handler(^(NSArray *_) { [self dlopenWithFormat:@"/System/Library/Frameworks/%@.framework/%@"]; }); make.button(@"System Private Framework").handler(^(NSArray *_) { [self dlopenWithFormat:@"/System/Library/PrivateFrameworks/%@.framework/%@"]; }); make.button(@"Arbitrary Binary").handler(^(NSArray *_) { [self dlopenWithFormat:nil]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } /// Prompt user for input and dlopen - (void)dlopenWithFormat:(NSString *)format { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Dynamically Open Library"); if (format) { make.message(@"Pass in a framework name, such as CarKit or FrontBoard."); } else { make.message(@"Pass in an absolute path to a binary."); } make.textField(format ? @"ARKit" : @"/System/Library/Frameworks/ARKit.framework/ARKit"); make.button(@"Cancel").cancelStyle(); make.button(@"Open").destructiveStyle().handler(^(NSArray *strings) { NSString *path = strings[0]; if (path.length < 2) { [self dlopenInvalidPath]; } else if (format) { path = [NSString stringWithFormat:format, path, path]; } if (!dlopen(path.UTF8String, RTLD_NOW)) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Error").message(@(dlerror())); make.button(@"Dismiss").cancelStyle(); }]; } }); } showFrom:self]; } - (void)dlopenInvalidPath { [FLEXAlert makeAlert:^(FLEXAlert * _Nonnull make) { make.title(@"Path or Name Too Short"); make.button(@"Dismiss").cancelStyle(); } showFrom:self]; } #pragma mark Delegate stuff - (void)didSelectImagePath:(NSString *)path shortName:(NSString *)shortName { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(shortName); make.message(@"No NSBundle associated with this path:\n\n"); make.message(path); make.button(@"Copy Path").handler(^(NSArray *strings) { UIPasteboard.generalPasteboard.string = path; }); make.button(@"Dismiss").cancelStyle(); } showFrom:self]; } - (void)didSelectBundle:(NSBundle *)bundle { NSParameterAssert(bundle); FLEXObjectExplorerViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:bundle]; [self.navigationController pushViewController:explorer animated:YES]; } - (void)didSelectClass:(Class)cls { NSParameterAssert(cls); FLEXObjectExplorerViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:cls]; [self.navigationController pushViewController:explorer animated:YES]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"📚 Runtime Browser"; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { UIViewController *controller = [self new]; controller.title = [self globalsEntryTitle:row]; return controller; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXRuntimeBrowserToolbar.h ================================================ // // FLEXRuntimeBrowserToolbar.h // FLEX // // Created by Tanner on 6/11/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXKeyboardToolbar.h" #import "FLEXRuntimeKeyPath.h" @interface FLEXRuntimeBrowserToolbar : FLEXKeyboardToolbar + (instancetype)toolbarWithHandler:(FLEXKBToolbarAction)tapHandler suggestions:(NSArray *)suggestions; - (void)setKeyPath:(FLEXRuntimeKeyPath *)keyPath suggestions:(NSArray *)suggestions; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXRuntimeBrowserToolbar.m ================================================ // // FLEXRuntimeBrowserToolbar.m // FLEX // // Created by Tanner on 6/11/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeBrowserToolbar.h" #import "FLEXRuntimeKeyPathTokenizer.h" @interface FLEXRuntimeBrowserToolbar () @property (nonatomic, copy) FLEXKBToolbarAction tapHandler; @end @implementation FLEXRuntimeBrowserToolbar + (instancetype)toolbarWithHandler:(FLEXKBToolbarAction)tapHandler suggestions:(NSArray *)suggestions { NSArray *buttons = [self buttonsForKeyPath:FLEXRuntimeKeyPath.empty suggestions:suggestions handler:tapHandler ]; FLEXRuntimeBrowserToolbar *me = [self toolbarWithButtons:buttons]; me.tapHandler = tapHandler; return me; } + (NSArray *)buttonsForKeyPath:(FLEXRuntimeKeyPath *)keyPath suggestions:(NSArray *)suggestions handler:(FLEXKBToolbarAction)handler { NSMutableArray *buttons = [NSMutableArray new]; FLEXSearchToken *lastKey = nil; BOOL lastKeyIsMethod = NO; if (keyPath.methodKey) { lastKey = keyPath.methodKey; lastKeyIsMethod = YES; } else { lastKey = keyPath.classKey ?: keyPath.bundleKey; } switch (lastKey.options) { case TBWildcardOptionsNone: case TBWildcardOptionsAny: if (lastKeyIsMethod) { if (!keyPath.instanceMethods) { [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"-" action:handler]]; [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"+" action:handler]]; } [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*" action:handler]]; } else { [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*" action:handler]]; [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*." action:handler]]; } break; default: { if (lastKey.options & TBWildcardOptionsPrefix) { if (lastKeyIsMethod) { if (lastKey.string.length) { [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*" action:handler]]; } } else { if (lastKey.string.length) { [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*." action:handler]]; } } } else if (lastKey.options & TBWildcardOptionsSuffix) { if (!lastKeyIsMethod) { [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*" action:handler]]; [buttons addObject:[FLEXKBToolbarButton buttonWithTitle:@"*." action:handler]]; } } } } for (NSString *suggestion in suggestions) { [buttons addObject:[FLEXKBToolbarSuggestedButton buttonWithTitle:suggestion action:handler]]; } return buttons; } - (void)setKeyPath:(FLEXRuntimeKeyPath *)keyPath suggestions:(NSArray *)suggestions { self.buttons = [self.class buttonsForKeyPath:keyPath suggestions:suggestions handler:self.tapHandler ]; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXRuntimeKeyPath.h ================================================ // // FLEXRuntimeKeyPath.h // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXSearchToken.h" @class FLEXMethod; NS_ASSUME_NONNULL_BEGIN /// A key path represents a query into a set of bundles or classes /// for a set of one or more methods. It is composed of three tokens: /// bundle, class, and method. A key path may be incomplete if it /// is missing any of the tokens. A key path is considered "absolute" /// if all tokens have no options and if methodKey.string begins /// with a + or a -. /// /// The @code TBKeyPathTokenizer @endcode class is used to create /// a key path from a string. @interface FLEXRuntimeKeyPath : NSObject + (instancetype)empty; /// @param method must start with either a wildcard or a + or -. + (instancetype)bundle:(FLEXSearchToken *)bundle class:(FLEXSearchToken *)cls method:(FLEXSearchToken *)method isInstance:(NSNumber *)instance string:(NSString *)keyPathString; @property (nonatomic, nullable, readonly) FLEXSearchToken *bundleKey; @property (nonatomic, nullable, readonly) FLEXSearchToken *classKey; @property (nonatomic, nullable, readonly) FLEXSearchToken *methodKey; /// Indicates whether the method token specifies instance methods. /// Nil if not specified. @property (nonatomic, nullable, readonly) NSNumber *instanceMethods; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXRuntimeKeyPath.m ================================================ // // FLEXRuntimeKeyPath.m // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeKeyPath.h" #import "FLEXRuntimeClient.h" @interface FLEXRuntimeKeyPath () { NSString *flex_description; } @end @implementation FLEXRuntimeKeyPath + (instancetype)empty { static FLEXRuntimeKeyPath *empty = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ FLEXSearchToken *any = FLEXSearchToken.any; empty = [self new]; empty->_bundleKey = any; empty->flex_description = @""; }); return empty; } + (instancetype)bundle:(FLEXSearchToken *)bundle class:(FLEXSearchToken *)cls method:(FLEXSearchToken *)method isInstance:(NSNumber *)instance string:(NSString *)keyPathString { FLEXRuntimeKeyPath *keyPath = [self new]; keyPath->_bundleKey = bundle; keyPath->_classKey = cls; keyPath->_methodKey = method; keyPath->_instanceMethods = instance; // Remove irrelevant trailing '*' for equality purposes if ([keyPathString hasSuffix:@"*"]) { keyPathString = [keyPathString substringToIndex:keyPathString.length]; } keyPath->flex_description = keyPathString; if (bundle.isAny && cls.isAny && method.isAny) { [FLEXRuntimeClient initializeWebKitLegacy]; } return keyPath; } - (NSString *)description { return flex_description; } - (NSUInteger)hash { return flex_description.hash; } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[FLEXRuntimeKeyPath class]]) { FLEXRuntimeKeyPath *kp = object; return [flex_description isEqualToString:kp->flex_description]; } return NO; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXRuntimeKeyPathTokenizer.h ================================================ // // FLEXRuntimeKeyPathTokenizer.h // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeKeyPath.h" @interface FLEXRuntimeKeyPathTokenizer : NSObject + (NSUInteger)tokenCountOfString:(NSString *)userInput; + (FLEXRuntimeKeyPath *)tokenizeString:(NSString *)userInput; + (BOOL)allowedInKeyPath:(NSString *)text; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXRuntimeKeyPathTokenizer.m ================================================ // // FLEXRuntimeKeyPathTokenizer.m // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeKeyPathTokenizer.h" #define TBCountOfStringOccurence(target, str) ([target componentsSeparatedByString:str].count - 1) @implementation FLEXRuntimeKeyPathTokenizer #pragma mark Initialization static NSCharacterSet *firstAllowed = nil; static NSCharacterSet *identifierAllowed = nil; static NSCharacterSet *filenameAllowed = nil; static NSCharacterSet *keyPathDisallowed = nil; static NSCharacterSet *methodAllowed = nil; + (void)initialize { if (self == [self class]) { NSString *_methodFirstAllowed = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$"; NSString *_identifierAllowed = [_methodFirstAllowed stringByAppendingString:@"1234567890"]; NSString *_methodAllowedSansType = [_identifierAllowed stringByAppendingString:@":"]; NSString *_filenameNameAllowed = [_identifierAllowed stringByAppendingString:@"-+?!"]; firstAllowed = [NSCharacterSet characterSetWithCharactersInString:_methodFirstAllowed]; identifierAllowed = [NSCharacterSet characterSetWithCharactersInString:_identifierAllowed]; filenameAllowed = [NSCharacterSet characterSetWithCharactersInString:_filenameNameAllowed]; methodAllowed = [NSCharacterSet characterSetWithCharactersInString:_methodAllowedSansType]; NSString *_kpDisallowed = [_identifierAllowed stringByAppendingString:@"-+:\\.*"]; keyPathDisallowed = [NSCharacterSet characterSetWithCharactersInString:_kpDisallowed].invertedSet; } } #pragma mark Public + (FLEXRuntimeKeyPath *)tokenizeString:(NSString *)userInput { if (!userInput.length) { return nil; } NSUInteger tokens = [self tokenCountOfString:userInput]; if (tokens == 0) { return nil; } if ([userInput containsString:@"**"]) { @throw NSInternalInconsistencyException; } NSNumber *instance = nil; NSScanner *scanner = [NSScanner scannerWithString:userInput]; FLEXSearchToken *bundle = [self scanToken:scanner allowed:filenameAllowed first:filenameAllowed]; FLEXSearchToken *cls = [self scanToken:scanner allowed:identifierAllowed first:firstAllowed]; FLEXSearchToken *method = tokens > 2 ? [self scanMethodToken:scanner instance:&instance] : nil; return [FLEXRuntimeKeyPath bundle:bundle class:cls method:method isInstance:instance string:userInput]; } + (BOOL)allowedInKeyPath:(NSString *)text { if (!text.length) { return YES; } return [text rangeOfCharacterFromSet:keyPathDisallowed].location == NSNotFound; } #pragma mark Private + (NSUInteger)tokenCountOfString:(NSString *)userInput { NSUInteger escapedCount = TBCountOfStringOccurence(userInput, @"\\."); NSUInteger tokenCount = TBCountOfStringOccurence(userInput, @".") - escapedCount + 1; return tokenCount; } + (FLEXSearchToken *)scanToken:(NSScanner *)scanner allowed:(NSCharacterSet *)allowedChars first:(NSCharacterSet *)first { if (scanner.isAtEnd) { if ([scanner.string hasSuffix:@"."] && ![scanner.string hasSuffix:@"\\."]) { return [FLEXSearchToken string:nil options:TBWildcardOptionsAny]; } return nil; } TBWildcardOptions options = TBWildcardOptionsNone; NSMutableString *token = [NSMutableString new]; // Token cannot start with '.' if ([scanner scanString:@"." intoString:nil]) { @throw NSInternalInconsistencyException; } if ([scanner scanString:@"*." intoString:nil]) { return [FLEXSearchToken string:nil options:TBWildcardOptionsAny]; } else if ([scanner scanString:@"*" intoString:nil]) { if (scanner.isAtEnd) { return FLEXSearchToken.any; } options |= TBWildcardOptionsPrefix; } NSString *tmp = nil; BOOL stop = NO, didScanDelimiter = NO, didScanFirstAllowed = NO; NSCharacterSet *disallowed = allowedChars.invertedSet; while (!stop && ![scanner scanString:@"." intoString:&tmp] && !scanner.isAtEnd) { // Scan word chars // In this block, we have not scanned anything yet, except maybe leading '\' or '\.' if (!didScanFirstAllowed) { if ([scanner scanCharactersFromSet:first intoString:&tmp]) { [token appendString:tmp]; didScanFirstAllowed = YES; } else if ([scanner scanString:@"\\" intoString:nil]) { if (options == TBWildcardOptionsPrefix && [scanner scanString:@"." intoString:nil]) { [token appendString:@"."]; } else if (scanner.isAtEnd && options == TBWildcardOptionsPrefix) { // Only allow standalone '\' if prefixed by '*' return FLEXSearchToken.any; } else { // Token starts with a number, period, or something else not allowed, // or token is a standalone '\' with no '*' prefix @throw NSInternalInconsistencyException; } } else { // Token starts with a number, period, or something else not allowed @throw NSInternalInconsistencyException; } } else if ([scanner scanCharactersFromSet:allowedChars intoString:&tmp]) { [token appendString:tmp]; } // Scan '\.' or trailing '\' else if ([scanner scanString:@"\\" intoString:nil]) { if ([scanner scanString:@"." intoString:nil]) { [token appendString:@"."]; } else if (scanner.isAtEnd) { // Ignore forward slash not followed by period if at end return [FLEXSearchToken string:token options:options | TBWildcardOptionsSuffix]; } else { // Only periods can follow a forward slash @throw NSInternalInconsistencyException; } } // Scan '*.' else if ([scanner scanString:@"*." intoString:nil]) { options |= TBWildcardOptionsSuffix; stop = YES; didScanDelimiter = YES; } // Scan '*' not followed by . else if ([scanner scanString:@"*" intoString:nil]) { if (!scanner.isAtEnd) { // Invalid token, wildcard in middle of token @throw NSInternalInconsistencyException; } } else if ([scanner scanCharactersFromSet:disallowed intoString:nil]) { // Invalid token, invalid characters @throw NSInternalInconsistencyException; } } // Did we scan a trailing, un-escsaped '.'? if ([tmp isEqualToString:@"."]) { didScanDelimiter = YES; } if (!didScanDelimiter) { options |= TBWildcardOptionsSuffix; } return [FLEXSearchToken string:token options:options]; } + (FLEXSearchToken *)scanMethodToken:(NSScanner *)scanner instance:(NSNumber **)instance { if (scanner.isAtEnd) { if ([scanner.string hasSuffix:@"."]) { return [FLEXSearchToken string:nil options:TBWildcardOptionsAny]; } return nil; } if ([scanner.string hasSuffix:@"."] && ![scanner.string hasSuffix:@"\\."]) { // Methods cannot end with '.' except for '\.' @throw NSInternalInconsistencyException; } if ([scanner scanString:@"-" intoString:nil]) { *instance = @YES; } else if ([scanner scanString:@"+" intoString:nil]) { *instance = @NO; } else { if ([scanner scanString:@"*" intoString:nil]) { // Just checking... It has to start with one of these three! scanner.scanLocation--; } else { @throw NSInternalInconsistencyException; } } // -*foo not allowed if (*instance && [scanner scanString:@"*" intoString:nil]) { @throw NSInternalInconsistencyException; } if (scanner.isAtEnd) { return [FLEXSearchToken string:@"" options:TBWildcardOptionsSuffix]; } return [self scanToken:scanner allowed:methodAllowed first:firstAllowed]; } @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXSearchToken.h ================================================ // // FLEXSearchToken.h // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import typedef NS_OPTIONS(NSUInteger, TBWildcardOptions) { TBWildcardOptionsNone = 0, TBWildcardOptionsAny = 1, TBWildcardOptionsPrefix = 1 << 1, TBWildcardOptionsSuffix = 1 << 2, }; /// A token may contain wildcards at one or either end, /// but not in the middle of the token (as of now). @interface FLEXSearchToken : NSObject + (instancetype)any; + (instancetype)string:(NSString *)string options:(TBWildcardOptions)options; /// Will not contain the wildcard (*) symbol @property (nonatomic, readonly) NSString *string; @property (nonatomic, readonly) TBWildcardOptions options; /// Opposite of "is ambiguous" @property (nonatomic, readonly) BOOL isAbsolute; @property (nonatomic, readonly) BOOL isAny; /// Still \c isAny, but checks that the string is empty @property (nonatomic, readonly) BOOL isEmpty; @end ================================================ FILE: Classes/GlobalStateExplorers/RuntimeBrowser/FLEXSearchToken.m ================================================ // // FLEXSearchToken.m // FLEX // // Created by Tanner on 3/22/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXSearchToken.h" @interface FLEXSearchToken () { NSString *flex_description; } @end @implementation FLEXSearchToken + (instancetype)any { static FLEXSearchToken *any = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ any = [self string:nil options:TBWildcardOptionsAny]; }); return any; } + (instancetype)string:(NSString *)string options:(TBWildcardOptions)options { FLEXSearchToken *token = [self new]; token->_string = string; token->_options = options; return token; } - (BOOL)isAbsolute { return _options == TBWildcardOptionsNone; } - (BOOL)isAny { return _options == TBWildcardOptionsAny; } - (BOOL)isEmpty { return self.isAny && self.string.length == 0; } - (NSString *)description { if (flex_description) { return flex_description; } switch (_options) { case TBWildcardOptionsNone: flex_description = _string; break; case TBWildcardOptionsAny: flex_description = @"*"; break; default: { NSMutableString *desc = [NSMutableString new]; if (_options & TBWildcardOptionsPrefix) { [desc appendString:@"*"]; } [desc appendString:_string]; if (_options & TBWildcardOptionsSuffix) { [desc appendString:@"*"]; } flex_description = desc; } } return flex_description; } - (NSUInteger)hash { return self.description.hash; } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[FLEXSearchToken class]]) { FLEXSearchToken *token = object; return [_string isEqualToString:token->_string] && _options == token->_options; } return NO; } @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/ActivityStreamAPI.h ================================================ // // Taken from https://github.com/llvm-mirror/lldb/blob/master/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h // by Tanner Bennett on 03/03/2019 with minimal modifications. // //===-- ActivityStreamAPI.h -------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef ActivityStreamSPI_h #define ActivityStreamSPI_h #include #include // #include /* By default, XPC objects are declared as Objective-C types when building with * an Objective-C compiler. This allows them to participate in ARC, in RR * management by the Blocks runtime and in leaks checking by the static * analyzer, and enables them to be added to Cocoa collections. * * See for details. */ #if !TARGET_OS_MACCATALYST && !__has_include() #if OS_OBJECT_USE_OBJC OS_OBJECT_DECL(xpc_object); #else typedef void * xpc_object_t; #endif #endif #define OS_ACTIVITY_MAX_CALLSTACK 32 // Enums typedef NS_ENUM(uint32_t, os_activity_stream_flag_t) { OS_ACTIVITY_STREAM_PROCESS_ONLY = 0x00000001, OS_ACTIVITY_STREAM_SKIP_DECODE = 0x00000002, OS_ACTIVITY_STREAM_PAYLOAD = 0x00000004, OS_ACTIVITY_STREAM_HISTORICAL = 0x00000008, OS_ACTIVITY_STREAM_CALLSTACK = 0x00000010, OS_ACTIVITY_STREAM_DEBUG = 0x00000020, OS_ACTIVITY_STREAM_BUFFERED = 0x00000040, OS_ACTIVITY_STREAM_NO_SENSITIVE = 0x00000080, OS_ACTIVITY_STREAM_INFO = 0x00000100, OS_ACTIVITY_STREAM_PROMISCUOUS = 0x00000200, OS_ACTIVITY_STREAM_PRECISE_TIMESTAMPS = 0x00000200 }; typedef NS_ENUM(uint32_t, os_activity_stream_type_t) { OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE = 0x0201, OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION = 0x0202, OS_ACTIVITY_STREAM_TYPE_ACTIVITY_USERACTION = 0x0203, OS_ACTIVITY_STREAM_TYPE_TRACE_MESSAGE = 0x0300, OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE = 0x0400, OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE = 0x0480, OS_ACTIVITY_STREAM_TYPE_SIGNPOST_BEGIN = 0x0601, OS_ACTIVITY_STREAM_TYPE_SIGNPOST_END = 0x0602, OS_ACTIVITY_STREAM_TYPE_SIGNPOST_EVENT = 0x0603, OS_ACTIVITY_STREAM_TYPE_STATEDUMP_EVENT = 0x0A00, }; typedef NS_ENUM(uint32_t, os_activity_stream_event_t) { OS_ACTIVITY_STREAM_EVENT_STARTED = 1, OS_ACTIVITY_STREAM_EVENT_STOPPED = 2, OS_ACTIVITY_STREAM_EVENT_FAILED = 3, OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED = 4, OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED = 5, }; // Types typedef uint64_t os_activity_id_t; typedef struct os_activity_stream_s *os_activity_stream_t; typedef struct os_activity_stream_entry_s *os_activity_stream_entry_t; #define OS_ACTIVITY_STREAM_COMMON() \ uint64_t trace_id; \ uint64_t timestamp; \ uint64_t thread; \ const uint8_t *image_uuid; \ const char *image_path; \ struct timeval tv_gmt; \ struct timezone tz; \ uint32_t offset typedef struct os_activity_stream_common_s { OS_ACTIVITY_STREAM_COMMON(); } * os_activity_stream_common_t; struct os_activity_create_s { OS_ACTIVITY_STREAM_COMMON(); const char *name; os_activity_id_t creator_aid; uint64_t unique_pid; }; struct os_activity_transition_s { OS_ACTIVITY_STREAM_COMMON(); os_activity_id_t transition_id; }; typedef struct os_log_message_s { OS_ACTIVITY_STREAM_COMMON(); const char *format; const uint8_t *buffer; size_t buffer_sz; const uint8_t *privdata; size_t privdata_sz; const char *subsystem; const char *category; uint32_t oversize_id; uint8_t ttl; bool persisted; } * os_log_message_t; typedef struct os_trace_message_v2_s { OS_ACTIVITY_STREAM_COMMON(); const char *format; const void *buffer; size_t bufferLen; xpc_object_t __unsafe_unretained payload; } * os_trace_message_v2_t; typedef struct os_activity_useraction_s { OS_ACTIVITY_STREAM_COMMON(); const char *action; bool persisted; } * os_activity_useraction_t; typedef struct os_signpost_s { OS_ACTIVITY_STREAM_COMMON(); const char *format; const uint8_t *buffer; size_t buffer_sz; const uint8_t *privdata; size_t privdata_sz; const char *subsystem; const char *category; uint64_t duration_nsec; uint32_t callstack_depth; uint64_t callstack[OS_ACTIVITY_MAX_CALLSTACK]; } * os_signpost_t; typedef struct os_activity_statedump_s { OS_ACTIVITY_STREAM_COMMON(); char *message; size_t message_size; char image_path_buffer[PATH_MAX]; } * os_activity_statedump_t; struct os_activity_stream_entry_s { os_activity_stream_type_t type; // information about the process streaming the data pid_t pid; uint64_t proc_id; const uint8_t *proc_imageuuid; const char *proc_imagepath; // the activity associated with this streamed event os_activity_id_t activity_id; os_activity_id_t parent_id; union { struct os_activity_stream_common_s common; struct os_activity_create_s activity_create; struct os_activity_transition_s activity_transition; struct os_log_message_s log_message; struct os_trace_message_v2_s trace_message; struct os_activity_useraction_s useraction; struct os_signpost_s signpost; struct os_activity_statedump_s statedump; }; }; // Blocks typedef bool (^os_activity_stream_block_t)(os_activity_stream_entry_t entry, int error); typedef void (^os_activity_stream_event_block_t)( os_activity_stream_t stream, os_activity_stream_event_t event); // SPI entry point prototypes typedef os_activity_stream_t (*os_activity_stream_for_pid_t)( pid_t pid, os_activity_stream_flag_t flags, os_activity_stream_block_t stream_block); typedef void (*os_activity_stream_resume_t)(os_activity_stream_t stream); typedef void (*os_activity_stream_cancel_t)(os_activity_stream_t stream); typedef char *(*os_log_copy_formatted_message_t)(os_log_message_t log_message); typedef void (*os_activity_stream_set_event_handler_t)( os_activity_stream_t stream, os_activity_stream_event_block_t block); #endif /* ActivityStreamSPI_h */ ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXASLLogController.h ================================================ // // FLEXASLLogController.h // FLEX // // Created by Tanner on 3/14/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXLogController.h" @interface FLEXASLLogController : NSObject /// Guaranteed to call back on the main thread. + (instancetype)withUpdateHandler:(void(^)(NSArray *newMessages))newMessagesHandler; - (BOOL)startMonitoring; @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXASLLogController.m ================================================ // // FLEXASLLogController.m // FLEX // // Created by Tanner on 3/14/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXASLLogController.h" #import // Querying the ASL is much slower in the simulator. We need a longer polling interval to keep things responsive. #if TARGET_IPHONE_SIMULATOR #define updateInterval 5.0 #else #define updateInterval 0.5 #endif @interface FLEXASLLogController () @property (nonatomic, readonly) void (^updateHandler)(NSArray *); @property (nonatomic) NSTimer *logUpdateTimer; @property (nonatomic, readonly) NSMutableIndexSet *logMessageIdentifiers; // ASL stuff @property (nonatomic) NSUInteger heapSize; @property (nonatomic) dispatch_queue_t logQueue; @property (nonatomic) dispatch_io_t io; @property (nonatomic) NSString *remaining; @property (nonatomic) int stderror; @property (nonatomic) NSString *lastTimestamp; @end @implementation FLEXASLLogController + (instancetype)withUpdateHandler:(void(^)(NSArray *newMessages))newMessagesHandler { return [[self alloc] initWithUpdateHandler:newMessagesHandler]; } - (id)initWithUpdateHandler:(void(^)(NSArray *newMessages))newMessagesHandler { NSParameterAssert(newMessagesHandler); self = [super init]; if (self) { _updateHandler = newMessagesHandler; _logMessageIdentifiers = [NSMutableIndexSet new]; self.logUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:updateInterval target:self selector:@selector(updateLogMessages) userInfo:nil repeats:YES]; } return self; } - (void)dealloc { [self.logUpdateTimer invalidate]; } - (BOOL)startMonitoring { [self.logUpdateTimer fire]; return YES; } - (void)updateLogMessages { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSArray *newMessages; @synchronized (self) { newMessages = [self newLogMessagesForCurrentProcess]; if (!newMessages.count) { return; } for (FLEXSystemLogMessage *message in newMessages) { [self.logMessageIdentifiers addIndex:(NSUInteger)message.messageID]; } self.lastTimestamp = @(asl_get(newMessages.lastObject.aslMessage, ASL_KEY_TIME) ?: "null"); } dispatch_async(dispatch_get_main_queue(), ^{ self.updateHandler(newMessages); }); }); } #pragma mark - Log Message Fetching - (NSArray *)newLogMessagesForCurrentProcess { if (!self.logMessageIdentifiers.count) { return [self allLogMessagesForCurrentProcess]; } aslresponse response = [self ASLMessageListForCurrentProcess]; aslmsg aslMessage = NULL; NSMutableArray *newMessages = [NSMutableArray new]; while ((aslMessage = asl_next(response))) { NSUInteger messageID = (NSUInteger)atoll(asl_get(aslMessage, ASL_KEY_MSG_ID)); if (![self.logMessageIdentifiers containsIndex:messageID]) { [newMessages addObject:[FLEXSystemLogMessage logMessageFromASLMessage:aslMessage]]; } } asl_release(response); return newMessages; } - (aslresponse)ASLMessageListForCurrentProcess { static NSString *pidString = nil; if (!pidString) { pidString = @([NSProcessInfo.processInfo processIdentifier]).stringValue; } // Create system log query object. asl_object_t query = asl_new(ASL_TYPE_QUERY); // Filter for messages from the current process. // Note that this appears to happen by default on device, but is required in the simulator. asl_set_query(query, ASL_KEY_PID, pidString.UTF8String, ASL_QUERY_OP_EQUAL); // Filter for messages after the last retrieved message. if (self.lastTimestamp) { asl_set_query(query, ASL_KEY_TIME, self.lastTimestamp.UTF8String, ASL_QUERY_OP_GREATER); } return asl_search(NULL, query); } - (NSArray *)allLogMessagesForCurrentProcess { aslresponse response = [self ASLMessageListForCurrentProcess]; aslmsg aslMessage = NULL; NSMutableArray *logMessages = [NSMutableArray new]; while ((aslMessage = asl_next(response))) { [logMessages addObject:[FLEXSystemLogMessage logMessageFromASLMessage:aslMessage]]; } asl_release(response); return logMessages; } @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXLogController.h ================================================ // // FLEXLogController.h // FLEX // // Created by Tanner on 3/17/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import "FLEXSystemLogMessage.h" @protocol FLEXLogController /// Guaranteed to call back on the main thread. + (instancetype)withUpdateHandler:(void(^)(NSArray *newMessages))newMessagesHandler; - (BOOL)startMonitoring; @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXOSLogController.h ================================================ // // FLEXOSLogController.h // FLEX // // Created by Tanner on 12/19/18. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXLogController.h" #define FLEXOSLogAvailable() (NSProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 10) /// The log controller used for iOS 10 and up. @interface FLEXOSLogController : NSObject + (instancetype)withUpdateHandler:(void(^)(NSArray *newMessages))newMessagesHandler; - (BOOL)startMonitoring; /// Whether log messages are to be recorded and kept in-memory in the background. /// You do not need to initialize this value, only change it. @property (nonatomic) BOOL persistent; /// Used mostly internally, but also used by the log VC to persist messages /// that were created prior to enabling persistence. @property (nonatomic) NSMutableArray *messages; @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXOSLogController.m ================================================ // // FLEXOSLogController.m // FLEX // // Created by Tanner on 12/19/18. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXOSLogController.h" #import "NSUserDefaults+FLEX.h" #include #include "ActivityStreamAPI.h" static os_activity_stream_for_pid_t OSActivityStreamForPID; static os_activity_stream_resume_t OSActivityStreamResume; static os_activity_stream_cancel_t OSActivityStreamCancel; static os_log_copy_formatted_message_t OSLogCopyFormattedMessage; static os_activity_stream_set_event_handler_t OSActivityStreamSetEventHandler; static int (*proc_name)(int, char *, unsigned int); static int (*proc_listpids)(uint32_t, uint32_t, void*, int); static uint8_t (*OSLogGetType)(void *); @interface FLEXOSLogController () + (FLEXOSLogController *)sharedLogController; @property (nonatomic) void (^updateHandler)(NSArray *); @property (nonatomic) BOOL canPrint; @property (nonatomic) int filterPid; @property (nonatomic) BOOL levelInfo; @property (nonatomic) BOOL subsystemInfo; @property (nonatomic) os_activity_stream_t stream; @end @implementation FLEXOSLogController + (void)load { // Persist logs when the app launches on iOS 10 if we have persistent logs turned on if (FLEXOSLogAvailable()) { if (NSUserDefaults.standardUserDefaults.flex_cacheOSLogMessages) { [self sharedLogController].persistent = YES; [[self sharedLogController] startMonitoring]; } } } + (instancetype)sharedLogController { static FLEXOSLogController *shared = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shared = [self new]; }); return shared; } + (instancetype)withUpdateHandler:(void(^)(NSArray *newMessages))newMessagesHandler { FLEXOSLogController *shared = [self sharedLogController]; shared.updateHandler = newMessagesHandler; return shared; } - (id)init { NSAssert(FLEXOSLogAvailable(), @"os_log is only available on iOS 10 and up"); self = [super init]; if (self) { _filterPid = NSProcessInfo.processInfo.processIdentifier; _levelInfo = NO; _subsystemInfo = NO; } return self; } - (void)dealloc { OSActivityStreamCancel(self.stream); _stream = nil; } - (void)setPersistent:(BOOL)persistent { if (_persistent == persistent) return; _persistent = persistent; self.messages = persistent ? [NSMutableArray new] : nil; } - (BOOL)startMonitoring { if (![self lookupSPICalls]) { // >= iOS 10 is required return NO; } // Are we already monitoring? if (self.stream) { // Should we send out the "persisted" messages? if (self.updateHandler && self.messages.count) { dispatch_async(dispatch_get_main_queue(), ^{ self.updateHandler(self.messages); }); } return YES; } // Stream entry handler os_activity_stream_block_t block = ^bool(os_activity_stream_entry_t entry, int error) { return [self handleStreamEntry:entry error:error]; }; // Controls which types of messages we see // 'Historical' appears to just show NSLog stuff uint32_t activity_stream_flags = OS_ACTIVITY_STREAM_HISTORICAL; activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY; // activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY; self.stream = OSActivityStreamForPID(self.filterPid, activity_stream_flags, block); // Specify the stream-related event handler OSActivityStreamSetEventHandler(self.stream, [self streamEventHandlerBlock]); // Start the stream OSActivityStreamResume(self.stream); return YES; } - (BOOL)lookupSPICalls { static BOOL hasSPI = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ void *handle = dlopen("/System/Library/PrivateFrameworks/LoggingSupport.framework/LoggingSupport", RTLD_NOW); OSActivityStreamForPID = (os_activity_stream_for_pid_t)dlsym(handle, "os_activity_stream_for_pid"); OSActivityStreamResume = (os_activity_stream_resume_t)dlsym(handle, "os_activity_stream_resume"); OSActivityStreamCancel = (os_activity_stream_cancel_t)dlsym(handle, "os_activity_stream_cancel"); OSLogCopyFormattedMessage = (os_log_copy_formatted_message_t)dlsym(handle, "os_log_copy_formatted_message"); OSActivityStreamSetEventHandler = (os_activity_stream_set_event_handler_t)dlsym(handle, "os_activity_stream_set_event_handler"); proc_name = (int(*)(int, char *, unsigned int))dlsym(handle, "proc_name"); proc_listpids = (int(*)(uint32_t, uint32_t, void*, int))dlsym(handle, "proc_listpids"); OSLogGetType = (uint8_t(*)(void *))dlsym(handle, "os_log_get_type"); hasSPI = (OSActivityStreamForPID != NULL) && (OSActivityStreamResume != NULL) && (OSActivityStreamCancel != NULL) && (OSLogCopyFormattedMessage != NULL) && (OSActivityStreamSetEventHandler != NULL) && (OSLogGetType != NULL) && (proc_name != NULL); }); return hasSPI; } - (BOOL)handleStreamEntry:(os_activity_stream_entry_t)entry error:(int)error { if (!self.canPrint || (self.filterPid != -1 && entry->pid != self.filterPid)) { return YES; } if (!error && entry) { if (entry->type == OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE || entry->type == OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE) { os_log_message_t log_message = &entry->log_message; // Get date NSDate *date = [NSDate dateWithTimeIntervalSince1970:log_message->tv_gmt.tv_sec]; // Get log message text // https://github.com/limneos/oslog/issues/1 // https://github.com/FLEXTool/FLEX/issues/564 const char *messageText = OSLogCopyFormattedMessage(log_message) ?: ""; // move messageText from stack to heap NSString *msg = [NSString stringWithUTF8String:messageText]; dispatch_async(dispatch_get_main_queue(), ^{ FLEXSystemLogMessage *message = [FLEXSystemLogMessage logMessageFromDate:date text:msg]; if (self.persistent) { [self.messages addObject:message]; } if (self.updateHandler) { self.updateHandler(@[message]); } }); } } return YES; } - (os_activity_stream_event_block_t)streamEventHandlerBlock { return [^void(os_activity_stream_t stream, os_activity_stream_event_t event) { switch (event) { case OS_ACTIVITY_STREAM_EVENT_STARTED: self.canPrint = YES; break; case OS_ACTIVITY_STREAM_EVENT_STOPPED: break; case OS_ACTIVITY_STREAM_EVENT_FAILED: break; case OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED: break; case OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED: break; default: printf("=== Unhandled case ===\n"); break; } } copy]; } @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogCell.h ================================================ // // FLEXSystemLogCell.h // FLEX // // Created by Ryan Olson on 1/25/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewCell.h" @class FLEXSystemLogMessage; extern NSString *const kFLEXSystemLogCellIdentifier; @interface FLEXSystemLogCell : FLEXTableViewCell @property (nonatomic) FLEXSystemLogMessage *logMessage; @property (nonatomic, copy) NSString *highlightedText; + (NSString *)displayedTextForLogMessage:(FLEXSystemLogMessage *)logMessage; + (CGFloat)preferredHeightForLogMessage:(FLEXSystemLogMessage *)logMessage inWidth:(CGFloat)width; @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogCell.m ================================================ // // FLEXSystemLogCell.m // FLEX // // Created by Ryan Olson on 1/25/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXSystemLogCell.h" #import "FLEXSystemLogMessage.h" #import "UIFont+FLEX.h" #import "NSDateFormatter+FLEX.h" NSString *const kFLEXSystemLogCellIdentifier = @"FLEXSystemLogCellIdentifier"; @interface FLEXSystemLogCell () @property (nonatomic) UILabel *logMessageLabel; @property (nonatomic) NSAttributedString *logMessageAttributedText; @end @implementation FLEXSystemLogCell - (void)postInit { [super postInit]; self.logMessageLabel = [UILabel new]; self.logMessageLabel.numberOfLines = 0; self.separatorInset = UIEdgeInsetsZero; self.selectionStyle = UITableViewCellSelectionStyleNone; [self.contentView addSubview:self.logMessageLabel]; } - (void)setLogMessage:(FLEXSystemLogMessage *)logMessage { if (![_logMessage isEqual:logMessage]) { _logMessage = logMessage; self.logMessageAttributedText = nil; [self setNeedsLayout]; } } - (void)setHighlightedText:(NSString *)highlightedText { if (![_highlightedText isEqual:highlightedText]) { _highlightedText = highlightedText; self.logMessageAttributedText = nil; [self setNeedsLayout]; } } - (NSAttributedString *)logMessageAttributedText { if (!_logMessageAttributedText) { _logMessageAttributedText = [[self class] attributedTextForLogMessage:self.logMessage highlightedText:self.highlightedText]; } return _logMessageAttributedText; } static const UIEdgeInsets kFLEXLogMessageCellInsets = {10.0, 10.0, 10.0, 10.0}; - (void)layoutSubviews { [super layoutSubviews]; self.logMessageLabel.attributedText = self.logMessageAttributedText; self.logMessageLabel.frame = UIEdgeInsetsInsetRect(self.contentView.bounds, kFLEXLogMessageCellInsets); } #pragma mark - Stateless helpers + (NSAttributedString *)attributedTextForLogMessage:(FLEXSystemLogMessage *)logMessage highlightedText:(NSString *)highlightedText { NSString *text = [self displayedTextForLogMessage:logMessage]; NSDictionary *attributes = @{ NSFontAttributeName : UIFont.flex_codeFont }; NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes]; if (highlightedText.length > 0) { NSMutableAttributedString *mutableAttributedText = attributedText.mutableCopy; NSMutableDictionary *highlightAttributes = attributes.mutableCopy; highlightAttributes[NSBackgroundColorAttributeName] = UIColor.yellowColor; NSRange remainingSearchRange = NSMakeRange(0, text.length); while (remainingSearchRange.location < text.length) { remainingSearchRange.length = text.length - remainingSearchRange.location; NSRange foundRange = [text rangeOfString:highlightedText options:NSCaseInsensitiveSearch range:remainingSearchRange]; if (foundRange.location != NSNotFound) { remainingSearchRange.location = foundRange.location + foundRange.length; [mutableAttributedText setAttributes:highlightAttributes range:foundRange]; } else { break; } } attributedText = mutableAttributedText; } return attributedText; } + (NSString *)displayedTextForLogMessage:(FLEXSystemLogMessage *)logMessage { return [NSString stringWithFormat:@"%@: %@", [self logTimeStringFromDate:logMessage.date], logMessage.messageText]; } + (CGFloat)preferredHeightForLogMessage:(FLEXSystemLogMessage *)logMessage inWidth:(CGFloat)width { UIEdgeInsets insets = kFLEXLogMessageCellInsets; CGFloat availableWidth = width - insets.left - insets.right; NSAttributedString *attributedLogText = [self attributedTextForLogMessage:logMessage highlightedText:nil]; CGSize labelSize = [attributedLogText boundingRectWithSize:CGSizeMake(availableWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil].size; return labelSize.height + insets.top + insets.bottom; } + (NSString *)logTimeStringFromDate:(NSDate *)date { return [NSDateFormatter flex_stringFrom:date format:FLEXDateFormatVerbose]; } @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogMessage.h ================================================ // // FLEXSystemLogMessage.h // FLEX // // Created by Ryan Olson on 1/25/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import #import "ActivityStreamAPI.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXSystemLogMessage : NSObject + (instancetype)logMessageFromASLMessage:(aslmsg)aslMessage; + (instancetype)logMessageFromDate:(NSDate *)date text:(NSString *)text; // ASL specific properties @property (nonatomic, readonly, nullable) NSString *sender; @property (nonatomic, readonly, nullable) aslmsg aslMessage; @property (nonatomic, readonly) NSDate *date; @property (nonatomic, readonly) NSString *messageText; @property (nonatomic, readonly) long long messageID; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogMessage.m ================================================ // // FLEXSystemLogMessage.m // FLEX // // Created by Ryan Olson on 1/25/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXSystemLogMessage.h" @implementation FLEXSystemLogMessage + (instancetype)logMessageFromASLMessage:(aslmsg)aslMessage { NSDate *date = nil; NSString *sender = nil, *text = nil; long long identifier = 0; const char *timestamp = asl_get(aslMessage, ASL_KEY_TIME); if (timestamp) { NSTimeInterval timeInterval = [@(timestamp) integerValue]; const char *nanoseconds = asl_get(aslMessage, ASL_KEY_TIME_NSEC); if (nanoseconds) { timeInterval += [@(nanoseconds) doubleValue] / NSEC_PER_SEC; } date = [NSDate dateWithTimeIntervalSince1970:timeInterval]; } const char *s = asl_get(aslMessage, ASL_KEY_SENDER); if (s) { sender = @(s); } const char *messageText = asl_get(aslMessage, ASL_KEY_MSG); if (messageText) { text = @(messageText); } const char *messageID = asl_get(aslMessage, ASL_KEY_MSG_ID); if (messageID) { identifier = [@(messageID) longLongValue]; } FLEXSystemLogMessage *message = [[self alloc] initWithDate:date sender:sender text:text messageID:identifier]; message->_aslMessage = aslMessage; return message; } + (instancetype)logMessageFromDate:(NSDate *)date text:(NSString *)text { return [[self alloc] initWithDate:date sender:nil text:text messageID:0]; } - (id)initWithDate:(NSDate *)date sender:(NSString *)sender text:(NSString *)text messageID:(long long)identifier { self = [super init]; if (self) { _date = date; _sender = sender; _messageText = text; _messageID = identifier; } return self; } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[self class]]) { if (self.messageID) { // Only ASL uses messageID, otherwise it is 0 return self.messageID == [object messageID]; } else { // Test message texts and dates for OS Log return [self.messageText isEqual:[object messageText]] && [self.date isEqualToDate:[object date]]; } } return NO; } - (NSUInteger)hash { return (NSUInteger)self.messageID; } - (NSString *)description { NSString *escaped = [self.messageText stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; return [NSString stringWithFormat:@"(%@) %@", @(self.messageText.length), escaped]; } @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogViewController.h ================================================ // // FLEXSystemLogViewController.h // FLEX // // Created by Ryan Olson on 1/19/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXFilteringTableViewController.h" #import "FLEXGlobalsEntry.h" @interface FLEXSystemLogViewController : FLEXFilteringTableViewController @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogViewController.m ================================================ // // FLEXSystemLogViewController.m // FLEX // // Created by Ryan Olson on 1/19/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXSystemLogViewController.h" #import "FLEXASLLogController.h" #import "FLEXOSLogController.h" #import "FLEXSystemLogCell.h" #import "FLEXMutableListSection.h" #import "FLEXUtility.h" #import "FLEXColor.h" #import "FLEXResources.h" #import "UIBarButtonItem+FLEX.h" #import "NSUserDefaults+FLEX.h" #import "flex_fishhook.h" #import @interface FLEXSystemLogViewController () @property (nonatomic, readonly) FLEXMutableListSection *logMessages; @property (nonatomic, readonly) id logController; @end static void (*MSHookFunction)(void *symbol, void *replace, void **result); static BOOL FLEXDidHookNSLog = NO; static BOOL FLEXNSLogHookWorks = NO; BOOL (*os_log_shim_enabled)(void *addr) = nil; BOOL (*orig_os_log_shim_enabled)(void *addr) = nil; static BOOL my_os_log_shim_enabled(void *addr) { return NO; } @implementation FLEXSystemLogViewController #pragma mark - Initialization + (void)load { // User must opt-into disabling os_log if (!NSUserDefaults.standardUserDefaults.flex_disableOSLog) { return; } // Thanks to @Ram4096 on GitHub for telling me that // os_log is conditionally enabled by the SDK version void *addr = __builtin_return_address(0); void *libsystem_trace = dlopen("/usr/lib/system/libsystem_trace.dylib", RTLD_LAZY); os_log_shim_enabled = dlsym(libsystem_trace, "os_log_shim_enabled"); if (!os_log_shim_enabled) { return; } FLEXDidHookNSLog = flex_rebind_symbols((struct rebinding[1]) {{ "os_log_shim_enabled", (void *)my_os_log_shim_enabled, (void **)&orig_os_log_shim_enabled }}, 1) == 0; if (FLEXDidHookNSLog && orig_os_log_shim_enabled != nil) { // Check if our rebinding worked FLEXNSLogHookWorks = my_os_log_shim_enabled(addr) == NO; } // So, just because we rebind the lazily loaded symbol for // this function doesn't mean it's even going to be used. // While it seems to be sufficient for the simulator, for // whatever reason it is not sufficient on-device. We need // to actually hook the function with something like Substrate. // Check if we have substrate, and if so use that instead void *handle = dlopen("/usr/lib/libsubstrate.dylib", RTLD_LAZY); if (handle) { MSHookFunction = dlsym(handle, "MSHookFunction"); if (MSHookFunction) { // Set the hook and check if it worked void *unused; MSHookFunction(os_log_shim_enabled, my_os_log_shim_enabled, &unused); FLEXNSLogHookWorks = os_log_shim_enabled(addr) == NO; } } } - (id)init { return [super initWithStyle:UITableViewStylePlain]; } #pragma mark - Overrides - (void)viewDidLoad { [super viewDidLoad]; self.showsSearchBar = YES; self.pinSearchBar = YES; weakify(self) id logHandler = ^(NSArray *newMessages) { strongify(self) [self handleUpdateWithNewMessages:newMessages]; }; if (FLEXOSLogAvailable() && !FLEXNSLogHookWorks) { _logController = [FLEXOSLogController withUpdateHandler:logHandler]; } else { _logController = [FLEXASLLogController withUpdateHandler:logHandler]; } self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.title = @"Waiting for Logs..."; // Toolbar buttons // UIBarButtonItem *scrollDown = [UIBarButtonItem flex_itemWithImage:FLEXResources.scrollToBottomIcon target:self action:@selector(scrollToLastRow) ]; UIBarButtonItem *settings = [UIBarButtonItem flex_itemWithImage:FLEXResources.gearIcon target:self action:@selector(showLogSettings) ]; [self addToolbarItems:@[scrollDown, settings]]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.logController startMonitoring]; } - (NSArray *)makeSections { weakify(self) _logMessages = [FLEXMutableListSection list:@[] cellConfiguration:^(FLEXSystemLogCell *cell, FLEXSystemLogMessage *message, NSInteger row) { strongify(self) cell.logMessage = message; cell.highlightedText = self.filterText; if (row % 2 == 0) { cell.backgroundColor = FLEXColor.primaryBackgroundColor; } else { cell.backgroundColor = FLEXColor.secondaryBackgroundColor; } } filterMatcher:^BOOL(NSString *filterText, FLEXSystemLogMessage *message) { NSString *displayedText = [FLEXSystemLogCell displayedTextForLogMessage:message]; return [displayedText localizedCaseInsensitiveContainsString:filterText]; } ]; self.logMessages.cellRegistrationMapping = @{ kFLEXSystemLogCellIdentifier : [FLEXSystemLogCell class] }; return @[self.logMessages]; } - (NSArray *)nonemptySections { return @[self.logMessages]; } #pragma mark - Private - (void)handleUpdateWithNewMessages:(NSArray *)newMessages { self.title = [self.class globalsEntryTitle:FLEXGlobalsRowSystemLog]; [self.logMessages mutate:^(NSMutableArray *list) { [list addObjectsFromArray:newMessages]; }]; // Re-filter messages to filter against new messages if (self.filterText.length) { [self updateSearchResults:self.filterText]; } // "Follow" the log as new messages stream in if we were previously near the bottom. UITableView *tv = self.tableView; BOOL wasNearBottom = tv.contentOffset.y >= tv.contentSize.height - tv.frame.size.height - 100.0; [self reloadData]; if (wasNearBottom) { [self scrollToLastRow]; } } - (void)scrollToLastRow { NSInteger numberOfRows = [self.tableView numberOfRowsInSection:0]; if (numberOfRows > 0) { NSIndexPath *last = [NSIndexPath indexPathForRow:numberOfRows - 1 inSection:0]; [self.tableView scrollToRowAtIndexPath:last atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } } - (void)showLogSettings { NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; BOOL disableOSLog = defaults.flex_disableOSLog; BOOL persistent = defaults.flex_cacheOSLogMessages; NSString *aslToggle = disableOSLog ? @"Enable os_log (default)" : @"Disable os_log"; NSString *persistence = persistent ? @"Disable persistent logging" : @"Enable persistent logging"; NSString *title = @"System Log Settings"; NSString *body = @"In iOS 10 and up, ASL has been replaced by os_log. " "The os_log API is much more limited. Below, you can opt-into the old behavior " "if you want cleaner, more reliable logs within FLEX, but this will break " "anything that expects os_log to be working, such as Console.app. " "This setting requires the app to restart to take effect. \n\n" "To get as close to the old behavior as possible with os_log enabled, logs must " "be collected manually at launch and stored. This setting has no effect " "on iOS 9 and below, or if os_log is disabled. " "You should only enable persistent logging when you need it."; FLEXOSLogController *logController = (FLEXOSLogController *)self.logController; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(title).message(body); make.button(aslToggle).destructiveStyle().handler(^(NSArray *strings) { [defaults flex_toggleBoolForKey:kFLEXDefaultsDisableOSLogForceASLKey]; }); make.button(persistence).handler(^(NSArray *strings) { [defaults flex_toggleBoolForKey:kFLEXDefaultsiOSPersistentOSLogKey]; logController.persistent = !persistent; [logController.messages addObjectsFromArray:self.logMessages.list]; }); make.button(@"Dismiss").cancelStyle(); } showFrom:self]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"⚠️ System Log"; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { return [self new]; } #pragma mark - Table view data source - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { FLEXSystemLogMessage *logMessage = self.logMessages.filteredList[indexPath.row]; return [FLEXSystemLogCell preferredHeightForLogMessage:logMessage inWidth:self.tableView.bounds.size.width]; } #pragma mark - Copy on long press - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { return action == @selector(copy:); } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if (action == @selector(copy:)) { // We usually only want to copy the log message itself, not any metadata associated with it. UIPasteboard.generalPasteboard.string = self.logMessages.filteredList[indexPath.row].messageText ?: @""; } } - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) { weakify(self) return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu *(NSArray *suggestedActions) { UIAction *copy = [UIAction actionWithTitle:@"Copy" image:nil identifier:@"Copy" handler:^(UIAction *action) { strongify(self) // We usually only want to copy the log message itself, not any metadata associated with it. UIPasteboard.generalPasteboard.string = self.logMessages.filteredList[indexPath.row].messageText ?: @""; }]; return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[copy]]; } ]; } @end ================================================ FILE: Classes/GlobalStateExplorers/SystemLog/LLVM_LICENSE.TXT ================================================ ============================================================================== The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: ============================================================================== Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---- LLVM Exceptions to the Apache 2.0 License ---- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. ============================================================================== Software from third parties included in the LLVM Project: ============================================================================== The LLVM Project contains third party software which is under different license terms. All such code will be identified clearly using at least one of two mechanisms: 1) It will be in a separate directory tree with its own `LICENSE.txt` or `LICENSE` file at the top containing the specific license and restrictions which apply to that software, or 2) It will contain specific license and restriction terms at the top of every file. ============================================================================== Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): ============================================================================== University of Illinois/NCSA Open Source License Copyright (c) 2010 Apple Inc. All rights reserved. Developed by: LLDB Team http://lldb.llvm.org/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLDB Team, copyright holders, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. ================================================ FILE: Classes/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: Classes/Manager/FLEXManager+Extensibility.h ================================================ // // FLEXManager+Extensibility.h // FLEX // // Created by Tanner on 2/2/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXManager.h" #import "FLEXGlobalsEntry.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXManager (Extensibility) #pragma mark - Globals Screen Entries /// Adds an entry at the top of the list of Global State items. /// Call this method before this view controller is displayed. /// @param entryName The string to be displayed in the cell. /// @param objectFutureBlock When you tap on the row, information about the object returned /// by this block will be displayed. Passing a block that returns an object allows you to display /// information about an object whose actual pointer may change at runtime (e.g. +currentUser) /// @note This method must be called from the main thread. /// The objectFutureBlock will be invoked from the main thread and may return nil. /// @note The passed block will be copied and retain for the duration of the application, /// you may want to use __weak references. - (void)registerGlobalEntryWithName:(NSString *)entryName objectFutureBlock:(id (^)(void))objectFutureBlock; /// Adds an entry at the top of the list of Global State items. /// Call this method before this view controller is displayed. /// @param entryName The string to be displayed in the cell. /// @param viewControllerFutureBlock When you tap on the row, view controller returned /// by this block will be pushed on the navigation controller stack. /// @note This method must be called from the main thread. /// The viewControllerFutureBlock will be invoked from the main thread and may not return nil. /// @note The passed block will be copied and retain for the duration of the application, /// you may want to use __weak references as needed. - (void)registerGlobalEntryWithName:(NSString *)entryName viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock; /// Adds an entry at the top of the list of Global State items. /// @param entryName The string to be displayed in the cell. /// @param rowSelectedAction When you tap on the row, this block will be invoked /// with the host table view view controller. Use it to deselect the row or present an alert. /// @note This method must be called from the main thread. /// The rowSelectedAction will be invoked from the main thread. /// @note The passed block will be copied and retain for the duration of the application, /// you may want to use __weak references as needed. - (void)registerGlobalEntryWithName:(NSString *)entryName action:(FLEXGlobalsEntryRowAction)rowSelectedAction; /// Removes all registered global entries. - (void)clearGlobalEntries; #pragma mark - Editing /// Enable displaying ivar names for custom struct types + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding; #pragma mark - Simulator Shortcuts /// Simulator keyboard shortcuts are enabled by default. /// The shortcuts will not fire when there is an active text field, text view, or other responder /// accepting key input. You can disable keyboard shortcuts if you have existing keyboard shortcuts /// that conflict with FLEX, or if you like doing things the hard way ;) /// Keyboard shortcuts are always disabled (and support is #if'd out) in non-simulator builds @property (nonatomic) BOOL simulatorShortcutsEnabled; /// Adds an action to run when the specified key & modifier combination is pressed /// @param key A single character string matching a key on the keyboard /// @param modifiers Modifier keys such as shift, command, or alt/option /// @param action The block to run on the main thread when the key & modifier combination is recognized. /// @param description Shown the the keyboard shortcut help menu, which is accessed via the '?' key. /// @note The action block will be retained for the duration of the application. You may want to use weak references. /// @note FLEX registers several default keyboard shortcuts. Use the '?' key to see a list of shortcuts. - (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Manager/FLEXManager+Extensibility.m ================================================ // // FLEXManager+Extensibility.m // FLEX // // Created by Tanner on 2/2/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXManager+Extensibility.h" #import "FLEXManager+Private.h" #import "FLEXNavigationController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXKeyboardShortcutManager.h" #import "FLEXExplorerViewController.h" #import "FLEXNetworkMITMViewController.h" #import "FLEXKeyboardHelpViewController.h" #import "FLEXFileBrowserController.h" #import "FLEXArgumentInputStructView.h" #import "FLEXUtility.h" @interface FLEXManager (ExtensibilityPrivate) @property (nonatomic, readonly) UIViewController *topViewController; @end @implementation FLEXManager (Extensibility) #pragma mark - Globals Screen Entries - (void)registerGlobalEntryWithName:(NSString *)entryName objectFutureBlock:(id (^)(void))objectFutureBlock { NSParameterAssert(entryName); NSParameterAssert(objectFutureBlock); NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); entryName = entryName.copy; FLEXGlobalsEntry *entry = [FLEXGlobalsEntry entryWithNameFuture:^NSString *{ return entryName; } viewControllerFuture:^UIViewController *{ return [FLEXObjectExplorerFactory explorerViewControllerForObject:objectFutureBlock()]; }]; [self.userGlobalEntries addObject:entry]; } - (void)registerGlobalEntryWithName:(NSString *)entryName viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock { NSParameterAssert(entryName); NSParameterAssert(viewControllerFutureBlock); NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); entryName = entryName.copy; FLEXGlobalsEntry *entry = [FLEXGlobalsEntry entryWithNameFuture:^NSString *{ return entryName; } viewControllerFuture:^UIViewController *{ UIViewController *viewController = viewControllerFutureBlock(); NSCAssert(viewController, @"'%@' entry returned nil viewController. viewControllerFutureBlock should never return nil.", entryName); return viewController; }]; [self.userGlobalEntries addObject:entry]; } - (void)registerGlobalEntryWithName:(NSString *)entryName action:(FLEXGlobalsEntryRowAction)rowSelectedAction { NSParameterAssert(entryName); NSParameterAssert(rowSelectedAction); NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); entryName = entryName.copy; FLEXGlobalsEntry *entry = [FLEXGlobalsEntry entryWithNameFuture:^NSString * _Nonnull{ return entryName; } action:rowSelectedAction]; [self.userGlobalEntries addObject:entry]; } - (void)clearGlobalEntries { [self.userGlobalEntries removeAllObjects]; } #pragma mark - Editing + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding { [FLEXArgumentInputStructView registerFieldNames:names forTypeEncoding:typeEncoding]; } #pragma mark - Simulator Shortcuts - (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description { #if TARGET_OS_SIMULATOR [FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description allowOverride:YES]; #endif } - (void)setSimulatorShortcutsEnabled:(BOOL)simulatorShortcutsEnabled { #if TARGET_OS_SIMULATOR [FLEXKeyboardShortcutManager.sharedManager setEnabled:simulatorShortcutsEnabled]; #endif } - (BOOL)simulatorShortcutsEnabled { #if TARGET_OS_SIMULATOR return FLEXKeyboardShortcutManager.sharedManager.isEnabled; #else return NO; #endif } #pragma mark - Shortcuts Defaults - (void)registerDefaultSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description { #if TARGET_OS_SIMULATOR // Don't allow override to avoid changing keys registered by the app [FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description allowOverride:NO]; #endif } - (void)registerDefaultSimulatorShortcuts { [self registerDefaultSimulatorShortcutWithKey:@"f" modifiers:0 action:^{ [self toggleExplorer]; } description:@"Toggle FLEX toolbar"]; [self registerDefaultSimulatorShortcutWithKey:@"g" modifiers:0 action:^{ [self showExplorerIfNeeded]; [self.explorerViewController toggleMenuTool]; } description:@"Toggle FLEX globals menu"]; [self registerDefaultSimulatorShortcutWithKey:@"v" modifiers:0 action:^{ [self showExplorerIfNeeded]; [self.explorerViewController toggleViewsTool]; } description:@"Toggle view hierarchy menu"]; [self registerDefaultSimulatorShortcutWithKey:@"s" modifiers:0 action:^{ [self showExplorerIfNeeded]; [self.explorerViewController toggleSelectTool]; } description:@"Toggle select tool"]; [self registerDefaultSimulatorShortcutWithKey:@"m" modifiers:0 action:^{ [self showExplorerIfNeeded]; [self.explorerViewController toggleMoveTool]; } description:@"Toggle move tool"]; [self registerDefaultSimulatorShortcutWithKey:@"n" modifiers:0 action:^{ [self toggleTopViewControllerOfClass:[FLEXNetworkMITMViewController class]]; } description:@"Toggle network history view"]; [self registerDefaultSimulatorShortcutWithKey:UIKeyInputDownArrow modifiers:0 action:^{ if (self.isHidden || ![self.explorerViewController handleDownArrowKeyPressed]) { [self tryScrollDown]; } } description:@"Cycle view selection\n\t\tMove view down\n\t\tScroll down"]; [self registerDefaultSimulatorShortcutWithKey:UIKeyInputUpArrow modifiers:0 action:^{ if (self.isHidden || ![self.explorerViewController handleUpArrowKeyPressed]) { [self tryScrollUp]; } } description:@"Cycle view selection\n\t\tMove view up\n\t\tScroll up"]; [self registerDefaultSimulatorShortcutWithKey:UIKeyInputRightArrow modifiers:0 action:^{ if (!self.isHidden) { [self.explorerViewController handleRightArrowKeyPressed]; } } description:@"Move selected view right"]; [self registerDefaultSimulatorShortcutWithKey:UIKeyInputLeftArrow modifiers:0 action:^{ if (self.isHidden) { [self tryGoBack]; } else { [self.explorerViewController handleLeftArrowKeyPressed]; } } description:@"Move selected view left"]; [self registerDefaultSimulatorShortcutWithKey:@"?" modifiers:0 action:^{ [self toggleTopViewControllerOfClass:[FLEXKeyboardHelpViewController class]]; } description:@"Toggle (this) help menu"]; [self registerDefaultSimulatorShortcutWithKey:UIKeyInputEscape modifiers:0 action:^{ [[self.topViewController presentingViewController] dismissViewControllerAnimated:YES completion:nil]; } description:@"End editing text\n\t\tDismiss top view controller"]; [self registerDefaultSimulatorShortcutWithKey:@"o" modifiers:UIKeyModifierCommand|UIKeyModifierShift action:^{ [self toggleTopViewControllerOfClass:[FLEXFileBrowserController class]]; } description:@"Toggle file browser menu"]; } + (void)load { dispatch_async(dispatch_get_main_queue(), ^{ [self.sharedManager registerDefaultSimulatorShortcuts]; }); } #pragma mark - Private - (UIEdgeInsets)contentInsetsOfScrollView:(UIScrollView *)scrollView { if (@available(iOS 11, *)) { return scrollView.adjustedContentInset; } return scrollView.contentInset; } - (void)tryScrollDown { UIScrollView *scrollview = [self firstScrollView]; UIEdgeInsets insets = [self contentInsetsOfScrollView:scrollview]; CGPoint contentOffset = scrollview.contentOffset; CGFloat maxYOffset = scrollview.contentSize.height - scrollview.bounds.size.height + insets.bottom; contentOffset.y = MIN(contentOffset.y + 200, maxYOffset); [scrollview setContentOffset:contentOffset animated:YES]; } - (void)tryScrollUp { UIScrollView *scrollview = [self firstScrollView]; UIEdgeInsets insets = [self contentInsetsOfScrollView:scrollview]; CGPoint contentOffset = scrollview.contentOffset; contentOffset.y = MAX(contentOffset.y - 200, -insets.top); [scrollview setContentOffset:contentOffset animated:YES]; } - (UIScrollView *)firstScrollView { NSMutableArray *views = FLEXUtility.appKeyWindow.subviews.mutableCopy; UIScrollView *scrollView = nil; while (views.count > 0) { UIView *view = views.firstObject; [views removeObjectAtIndex:0]; if ([view isKindOfClass:[UIScrollView class]]) { scrollView = (UIScrollView *)view; break; } else { [views addObjectsFromArray:view.subviews]; } } return scrollView; } - (void)tryGoBack { UINavigationController *navigationController = nil; UIViewController *topViewController = self.topViewController; if ([topViewController isKindOfClass:[UINavigationController class]]) { navigationController = (UINavigationController *)topViewController; } else { navigationController = topViewController.navigationController; } [navigationController popViewControllerAnimated:YES]; } - (UIViewController *)topViewController { return [FLEXUtility topViewControllerInWindow:UIApplication.sharedApplication.keyWindow]; } - (void)toggleTopViewControllerOfClass:(Class)class { UINavigationController *topViewController = (id)self.topViewController; if ([topViewController isKindOfClass:[FLEXNavigationController class]]) { if ([topViewController.topViewController isKindOfClass:[class class]]) { if (topViewController.viewControllers.count == 1) { // Dismiss since we are already presenting it [topViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } else { // Pop since we are viewing it but it's not the only thing on the stack [topViewController popViewControllerAnimated:YES]; } } else { // Push it on the existing navigation stack [topViewController pushViewController:[class new] animated:YES]; } } else { // Present it in an entirely new navigation controller [self.explorerViewController presentViewController: [FLEXNavigationController withRootViewController:[class new]] animated:YES completion:nil]; } } - (void)showExplorerIfNeeded { if (self.isHidden) { [self showExplorer]; } } @end ================================================ FILE: Classes/Manager/FLEXManager+Networking.h ================================================ // // FLEXManager+Networking.h // FLEX // // Created by Tanner on 2/1/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXManager.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXManager (Networking) /// If this property is set to YES, FLEX will swizzle NSURLConnection*Delegate and NSURLSession*Delegate methods /// on classes that conform to the protocols. This allows you to view network activity history from the main FLEX menu. /// Full responses are kept temporarily in a size-limited cache and may be pruned under memory pressure. @property (nonatomic, getter=isNetworkDebuggingEnabled) BOOL networkDebuggingEnabled; /// Defaults to 25 MB if never set. Values set here are persisted across launches of the app. /// The response cache uses an NSCache, so it may purge prior to hitting the limit when the app is under memory pressure. @property (nonatomic) NSUInteger networkResponseCacheByteLimit; /// Requests whose host ends with one of the excluded entries in this array will be not be recorded (eg. google.com). /// Wildcard or subdomain entries are not required (eg. google.com will match any subdomain under google.com). /// Useful to remove requests that are typically noisy, such as analytics requests that you aren't interested in tracking. @property (nonatomic) NSMutableArray *networkRequestHostDenylist; /// Sets custom viewer for specific content type. /// @param contentType Mime type like application/json /// @param viewControllerFutureBlock Viewer (view controller) creation block /// @note This method must be called from the main thread. /// The viewControllerFutureBlock will be invoked from the main thread and may not return nil. /// @note The passed block will be copied and retain for the duration of the application, you may want to use __weak references. - (void)setCustomViewerForContentType:(NSString *)contentType viewControllerFutureBlock:(FLEXCustomContentViewerFuture)viewControllerFutureBlock; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Manager/FLEXManager+Networking.m ================================================ // // FLEXManager+Networking.m // FLEX // // Created by Tanner on 2/1/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXManager+Networking.h" #import "FLEXManager+Private.h" #import "FLEXNetworkObserver.h" #import "FLEXNetworkRecorder.h" #import "FLEXObjectExplorerFactory.h" #import "NSUserDefaults+FLEX.h" @implementation FLEXManager (Networking) + (void)load { if (NSUserDefaults.standardUserDefaults.flex_registerDictionaryJSONViewerOnLaunch) { dispatch_async(dispatch_get_main_queue(), ^{ // Register array/dictionary viewer for JSON responses [self.sharedManager setCustomViewerForContentType:@"application/json" viewControllerFutureBlock:^UIViewController *(NSData *data) { id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; if (jsonObject) { return [FLEXObjectExplorerFactory explorerViewControllerForObject:jsonObject]; } return nil; } ]; }); } } - (BOOL)isNetworkDebuggingEnabled { return FLEXNetworkObserver.isEnabled; } - (void)setNetworkDebuggingEnabled:(BOOL)networkDebuggingEnabled { FLEXNetworkObserver.enabled = networkDebuggingEnabled; } - (NSUInteger)networkResponseCacheByteLimit { return FLEXNetworkRecorder.defaultRecorder.responseCacheByteLimit; } - (void)setNetworkResponseCacheByteLimit:(NSUInteger)networkResponseCacheByteLimit { FLEXNetworkRecorder.defaultRecorder.responseCacheByteLimit = networkResponseCacheByteLimit; } - (NSMutableArray *)networkRequestHostDenylist { return FLEXNetworkRecorder.defaultRecorder.hostDenylist; } - (void)setNetworkRequestHostDenylist:(NSMutableArray *)networkRequestHostDenylist { FLEXNetworkRecorder.defaultRecorder.hostDenylist = networkRequestHostDenylist; } - (void)setCustomViewerForContentType:(NSString *)contentType viewControllerFutureBlock:(FLEXCustomContentViewerFuture)viewControllerFutureBlock { NSParameterAssert(contentType.length); NSParameterAssert(viewControllerFutureBlock); NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); self.customContentTypeViewers[contentType.lowercaseString] = viewControllerFutureBlock; } @end ================================================ FILE: Classes/Manager/FLEXManager.h ================================================ // // FLEXManager.h // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXExplorerToolbar.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXManager : NSObject @property (nonatomic, readonly, class) FLEXManager *sharedManager; @property (nonatomic, readonly) BOOL isHidden; @property (nonatomic, readonly) FLEXExplorerToolbar *toolbar; - (void)showExplorer; - (void)hideExplorer; - (void)toggleExplorer; /// Programmatically dismiss anything presented by FLEX, leaving only the toolbar visible. - (void)dismissAnyPresentedTools:(void (^_Nullable)(void))completion; /// Programmatically present something on top of the FLEX toolbar. /// This method will automatically dismiss any currently presented tool, /// so you do not need to call \c dismissAnyPresentedTools: yourself. - (void)presentTool:(UINavigationController *(^)(void))viewControllerFuture completion:(void (^_Nullable)(void))completion; /// Programmatically presents a new navigation controller with the given view controller. /// The completion block is passed this new navigation controller. - (void)presentEmbeddedTool:(UIViewController *)viewController completion:(void (^_Nullable)(UINavigationController *))completion; /// Programmatically presents a new navigation controller exploring the given object. /// The completion block is passed this new navigation controller. - (void)presentObjectExplorer:(id)object completion:(void (^_Nullable)(UINavigationController *))completion; /// Use this to present the explorer in a specific scene when the one /// it chooses by default is not the one you wish to display it in. - (void)showExplorerFromScene:(UIWindowScene *)scene API_AVAILABLE(ios(13.0)); #pragma mark - Misc /// Default database password is @c nil by default. /// Set this to the password you want the databases to open with. @property (copy, nonatomic) NSString *defaultSqliteDatabasePassword; @end typedef UIViewController * _Nullable(^FLEXCustomContentViewerFuture)(NSData *data); NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Manager/FLEXManager.m ================================================ // // FLEXManager.m // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXManager.h" #import "FLEXUtility.h" #import "FLEXExplorerViewController.h" #import "FLEXWindow.h" #import "FLEXNavigationController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXFileBrowserController.h" @interface FLEXManager () @property (nonatomic, readonly, getter=isHidden) BOOL hidden; @property (nonatomic) FLEXWindow *explorerWindow; @property (nonatomic) FLEXExplorerViewController *explorerViewController; @property (nonatomic, readonly) NSMutableArray *userGlobalEntries; @property (nonatomic, readonly) NSMutableDictionary *customContentTypeViewers; @end @implementation FLEXManager + (instancetype)sharedManager { static FLEXManager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedManager = [self new]; }); return sharedManager; } - (instancetype)init { self = [super init]; if (self) { _userGlobalEntries = [NSMutableArray new]; _customContentTypeViewers = [NSMutableDictionary new]; } return self; } - (FLEXWindow *)explorerWindow { NSAssert(NSThread.isMainThread, @"You must use %@ from the main thread only.", NSStringFromClass([self class])); if (!_explorerWindow) { _explorerWindow = [[FLEXWindow alloc] initWithFrame:FLEXUtility.appKeyWindow.bounds]; _explorerWindow.eventDelegate = self; _explorerWindow.rootViewController = self.explorerViewController; } return _explorerWindow; } - (FLEXExplorerViewController *)explorerViewController { if (!_explorerViewController) { _explorerViewController = [FLEXExplorerViewController new]; _explorerViewController.delegate = self; } return _explorerViewController; } - (void)showExplorer { UIWindow *flex = self.explorerWindow; flex.hidden = NO; if (@available(iOS 13.0, *)) { // Only look for a new scene if we don't have one if (!flex.windowScene) { flex.windowScene = FLEXUtility.appKeyWindow.windowScene; } } } - (void)hideExplorer { self.explorerWindow.hidden = YES; } - (void)toggleExplorer { if (self.explorerWindow.isHidden) { if (@available(iOS 13.0, *)) { [self showExplorerFromScene:FLEXUtility.appKeyWindow.windowScene]; } else { [self showExplorer]; } } else { [self hideExplorer]; } } - (void)dismissAnyPresentedTools:(void (^)(void))completion { if (self.explorerViewController.presentedViewController) { [self.explorerViewController dismissViewControllerAnimated:YES completion:completion]; } else if (completion) { completion(); } } - (void)presentTool:(UINavigationController * _Nonnull (^)(void))future completion:(void (^)(void))completion { [self showExplorer]; [self.explorerViewController presentTool:future completion:completion]; } - (void)presentEmbeddedTool:(UIViewController *)tool completion:(void (^)(UINavigationController *))completion { FLEXNavigationController *nav = [FLEXNavigationController withRootViewController:tool]; [self presentTool:^UINavigationController *{ return nav; } completion:^{ if (completion) completion(nav); }]; } - (void)presentObjectExplorer:(id)object completion:(void (^)(UINavigationController *))completion { UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:object]; [self presentEmbeddedTool:explorer completion:completion]; } - (void)showExplorerFromScene:(UIWindowScene *)scene { if (@available(iOS 13.0, *)) { self.explorerWindow.windowScene = scene; } self.explorerWindow.hidden = NO; } - (BOOL)isHidden { return self.explorerWindow.isHidden; } - (FLEXExplorerToolbar *)toolbar { return self.explorerViewController.explorerToolbar; } #pragma mark - FLEXWindowEventDelegate - (BOOL)shouldHandleTouchAtPoint:(CGPoint)pointInWindow { // Ask the explorer view controller return [self.explorerViewController shouldReceiveTouchAtWindowPoint:pointInWindow]; } - (BOOL)canBecomeKeyWindow { // Only when the explorer view controller wants it because // it needs to accept key input & affect the status bar. return self.explorerViewController.wantsWindowToBecomeKey; } #pragma mark - FLEXExplorerViewControllerDelegate - (void)explorerViewControllerDidFinish:(FLEXExplorerViewController *)explorerViewController { [self hideExplorer]; } @end ================================================ FILE: Classes/Manager/Private/FLEXManager+Private.h ================================================ // // FLEXManager+Private.h // PebbleApp // // Created by Javier Soto on 7/26/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXManager.h" #import "FLEXWindow.h" @class FLEXGlobalsEntry, FLEXExplorerViewController; @interface FLEXManager (Private) @property (nonatomic, readonly) FLEXWindow *explorerWindow; @property (nonatomic, readonly) FLEXExplorerViewController *explorerViewController; /// An array of FLEXGlobalsEntry objects that have been registered by the user. @property (nonatomic, readonly) NSMutableArray *userGlobalEntries; @property (nonatomic, readonly) NSMutableDictionary *customContentTypeViewers; @end ================================================ FILE: Classes/Network/FLEXFirebaseTransaction.mm ================================================ // // FLEXFirebaseTransaction.m // FLEX // // Created by Tanner Bennett on 12/24/21. // #import "FLEXNetworkTransaction.h" #import "FLEXUtility.h" #import #include typedef std::string (*ReturnsString)(void *); @implementation FLEXFirebaseSetDataInfo + (instancetype)data:(NSDictionary *)data merge:(NSNumber *)merge mergeFields:(NSArray *)mergeFields { FLEXFirebaseSetDataInfo *info = [self new]; info->_documentData = data; info->_merge = merge; info->_mergeFields = mergeFields; return info; } @end static NSString *FLEXStringFromFIRRequestType(FLEXFIRRequestType type) { switch (type) { case FLEXFIRRequestTypeNotFirebase: return @"not firebase"; case FLEXFIRRequestTypeFetchQuery: return @"query fetch"; case FLEXFIRRequestTypeFetchDocument: return @"document fetch"; case FLEXFIRRequestTypeSetData: return @"set data"; case FLEXFIRRequestTypeUpdateData: return @"update data"; case FLEXFIRRequestTypeAddDocument: return @"create"; case FLEXFIRRequestTypeDeleteDocument: return @"delete"; } return nil; } static FLEXFIRTransactionDirection FIRDirectionFromRequestType(FLEXFIRRequestType type) { switch (type) { case FLEXFIRRequestTypeNotFirebase: return FLEXFIRTransactionDirectionNone; case FLEXFIRRequestTypeFetchQuery: case FLEXFIRRequestTypeFetchDocument: return FLEXFIRTransactionDirectionPull; case FLEXFIRRequestTypeSetData: case FLEXFIRRequestTypeUpdateData: case FLEXFIRRequestTypeAddDocument: case FLEXFIRRequestTypeDeleteDocument: return FLEXFIRTransactionDirectionPush; } return FLEXFIRTransactionDirectionNone; } @interface FLEXFirebaseTransaction () @property (nonatomic) id extraData; @property (nonatomic, readonly) NSString *queryDescription; @end @implementation FLEXFirebaseTransaction @synthesize queryDescription = _queryDescription; + (instancetype)initiator:(id)initiator requestType:(FLEXFIRRequestType)type extraData:(id)data { FLEXFirebaseTransaction *fire = [FLEXFirebaseTransaction withStartTime:NSDate.date]; fire->_direction = FIRDirectionFromRequestType(type); fire->_initiator = initiator; fire->_requestType = type; fire->_extraData = data; return fire; } + (instancetype)queryFetch:(FIRQuery *)initiator { return [self initiator:initiator requestType:FLEXFIRRequestTypeFetchQuery extraData:nil]; } + (instancetype)documentFetch:(FIRDocumentReference *)initiator { return [self initiator:initiator requestType:FLEXFIRRequestTypeFetchDocument extraData:nil]; } + (instancetype)setData:(FIRDocumentReference *)initiator data:(NSDictionary *)data merge:(NSNumber *)merge mergeFields:(NSArray *)mergeFields { FLEXFirebaseSetDataInfo *info = [FLEXFirebaseSetDataInfo data:data merge:merge mergeFields:mergeFields]; return [self initiator:initiator requestType:FLEXFIRRequestTypeSetData extraData:info]; } + (instancetype)updateData:(FIRDocumentReference *)initiator data:(NSDictionary *)data { return [self initiator:initiator requestType:FLEXFIRRequestTypeUpdateData extraData:data]; } + (instancetype)addDocument:(FIRCollectionReference *)initiator document:(FIRDocumentReference *)doc { return [self initiator:initiator requestType:FLEXFIRRequestTypeAddDocument extraData:doc]; } + (instancetype)deleteDocument:(FIRDocumentReference *)initiator { return [self initiator:initiator requestType:FLEXFIRRequestTypeDeleteDocument extraData:nil]; } - (NSString *)queryDescription { if (_queryDescription) { return _queryDescription; } // Grab C++ symbol to describe FIRQuery.query static ReturnsString firebase_firestore_core_query_tostring = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // Is Firebase available? if (NSClassFromString(@"FIRDocumentReference")) { firebase_firestore_core_query_tostring = (ReturnsString)dlsym( RTLD_DEFAULT, "_ZNK8firebase9firestore4core5Query8ToStringEv" ); } }); if (!firebase_firestore_core_query_tostring) { return @"nil"; } FIRQuery *query = self.initiator_query; if (!query) return nil; void *core_query = query.query; std::string description = firebase_firestore_core_query_tostring(core_query); // Query strings are like 'Query(canonical_id=...)' so I remove the leading part, and the () NSString *prefix = @"Query(canonical_id="; NSString *desc = @(description.c_str()); desc = [desc stringByReplacingOccurrencesOfString:prefix withString:@""]; desc = [desc stringByReplacingCharactersInRange:NSMakeRange(desc.length-1, 1) withString:@""]; _queryDescription = desc; return _queryDescription; } - (FIRDocumentReference *)initiator_doc { if ([_initiator isKindOfClass:cFIRDocumentReference]) { return _initiator; } return nil; } - (FIRQuery *)initiator_query { if ([_initiator isKindOfClass:cFIRQuery]) { return _initiator; } return nil; } - (FIRCollectionReference *)initiator_collection { if ([_initiator isKindOfClass:cFIRCollectionReference]) { return _initiator; } return nil; } - (FLEXFirebaseSetDataInfo *)setDataInfo { if (self.requestType == FLEXFIRRequestTypeSetData) { return self.extraData; } return nil; } - (NSDictionary *)updateData { if (self.requestType == FLEXFIRRequestTypeUpdateData) { return self.extraData; } return nil; } - (NSString *)path { switch (self.direction) { case FLEXFIRTransactionDirectionNone: return nil; case FLEXFIRTransactionDirectionPush: case FLEXFIRTransactionDirectionPull: { switch (self.requestType) { case FLEXFIRRequestTypeNotFirebase: @throw NSInternalInconsistencyException; case FLEXFIRRequestTypeFetchQuery: case FLEXFIRRequestTypeAddDocument: return self.initiator_collection.path ?: self.queryDescription; case FLEXFIRRequestTypeFetchDocument: case FLEXFIRRequestTypeSetData: case FLEXFIRRequestTypeUpdateData: case FLEXFIRRequestTypeDeleteDocument: return self.initiator_doc.path; } } } return nil; } - (NSString *)primaryDescription { if (!_primaryDescription) { _primaryDescription = self.path.lastPathComponent; } return _primaryDescription; } - (NSString *)secondaryDescription { if (!_secondaryDescription) { _secondaryDescription = self.path.stringByDeletingLastPathComponent; } return _secondaryDescription; } - (NSString *)tertiaryDescription { if (!_tertiaryDescription) { NSMutableArray *detailComponents = [NSMutableArray new]; NSString *timestamp = [self timestampStringFromRequestDate:self.startTime]; if (timestamp.length > 0) { [detailComponents addObject:timestamp]; } [detailComponents addObject:self.direction == FLEXFIRTransactionDirectionPush ? @"Push ↑" : @"Pull ↓" ]; if (self.direction == FLEXFIRTransactionDirectionPush) { [detailComponents addObjectsFromArray:@[FLEXStringFromFIRRequestType(self.requestType)]]; } if (self.state == FLEXNetworkTransactionStateFinished || self.state == FLEXNetworkTransactionStateFailed) { if (self.direction == FLEXFIRTransactionDirectionPull) { NSString *docCount = [NSString stringWithFormat:@"%@ document(s)", @(self.documents.count)]; [detailComponents addObjectsFromArray:@[docCount]]; } } else { // Unstarted, Awaiting Response, Receiving Data, etc. NSString *state = [self.class readableStringFromTransactionState:self.state]; [detailComponents addObject:state]; } _tertiaryDescription = [detailComponents componentsJoinedByString:@" ・ "]; } return _tertiaryDescription; } - (NSString *)copyString { return self.path; } - (BOOL)matchesQuery:(NSString *)filterString { if ([self.path localizedCaseInsensitiveContainsString:filterString]) { return YES; } BOOL isPull = self.direction == FLEXFIRTransactionDirectionPull; BOOL isPush = self.direction == FLEXFIRTransactionDirectionPush; // Allow filtering for push or pull directly if (isPull && [filterString localizedCaseInsensitiveCompare:@"pull"] == NSOrderedSame) { return YES; } if (isPush && [filterString localizedCaseInsensitiveCompare:@"push"] == NSOrderedSame) { return YES; } return NO; } //- (NSString *)responseString { // if (!_responseString) { // _responseString = [NSString stringWithUTF8String:(char *)self.response.bytes]; // } // // return _responseString; //} // //- (NSDictionary *)responseObject { // if (!_responseObject) { // _responseObject = [NSJSONSerialization JSONObjectWithData:self.response options:0 error:nil]; // } // // return _responseObject; //} @end ================================================ FILE: Classes/Network/FLEXHTTPTransactionDetailController.h ================================================ // // FLEXHTTPTransactionDetailController.h // Flipboard // // Created by Ryan Olson on 2/10/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXHTTPTransaction; @interface FLEXHTTPTransactionDetailController : UITableViewController + (instancetype)withTransaction:(FLEXHTTPTransaction *)transaction; @end ================================================ FILE: Classes/Network/FLEXHTTPTransactionDetailController.m ================================================ // // FLEXNetworkTransactionDetailController.m // Flipboard // // Created by Ryan Olson on 2/10/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXHTTPTransactionDetailController.h" #import "FLEXNetworkCurlLogger.h" #import "FLEXNetworkRecorder.h" #import "FLEXNetworkTransaction.h" #import "FLEXWebViewController.h" #import "FLEXImagePreviewViewController.h" #import "FLEXMultilineTableViewCell.h" #import "FLEXUtility.h" #import "FLEXManager+Private.h" #import "FLEXTableView.h" #import "UIBarButtonItem+FLEX.h" #import "NSDateFormatter+FLEX.h" typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void); @interface FLEXNetworkDetailRow : NSObject @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *detailText; @property (nonatomic, copy) FLEXNetworkDetailRowSelectionFuture selectionFuture; @end @implementation FLEXNetworkDetailRow @end @interface FLEXNetworkDetailSection : NSObject @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSArray *rows; @end @implementation FLEXNetworkDetailSection @end @interface FLEXHTTPTransactionDetailController () @property (nonatomic, readonly) FLEXHTTPTransaction *transaction; @property (nonatomic, copy) NSArray *sections; @end @implementation FLEXHTTPTransactionDetailController + (instancetype)withTransaction:(FLEXHTTPTransaction *)transaction { FLEXHTTPTransactionDetailController *controller = [self new]; controller.transaction = transaction; return controller; } - (instancetype)initWithStyle:(UITableViewStyle)style { // Force grouped style return [super initWithStyle:UITableViewStyleGrouped]; } - (void)viewDidLoad { [super viewDidLoad]; [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(handleTransactionUpdatedNotification:) name:kFLEXNetworkRecorderTransactionUpdatedNotification object:nil ]; self.toolbarItems = @[ UIBarButtonItem.flex_flexibleSpace, [UIBarButtonItem flex_itemWithTitle:@"Copy curl" target:self action:@selector(copyButtonPressed:) ] ]; [self.tableView registerClass:[FLEXMultilineTableViewCell class] forCellReuseIdentifier:kFLEXMultilineCell]; } - (void)setTransaction:(FLEXHTTPTransaction *)transaction { if (![_transaction isEqual:transaction]) { _transaction = transaction; self.title = [transaction.request.URL lastPathComponent]; [self rebuildTableSections]; } } - (void)setSections:(NSArray *)sections { if (![_sections isEqual:sections]) { _sections = [sections copy]; [self.tableView reloadData]; } } - (void)rebuildTableSections { NSMutableArray *sections = [NSMutableArray new]; FLEXNetworkDetailSection *generalSection = [[self class] generalSectionForTransaction:self.transaction]; if (generalSection.rows.count > 0) { [sections addObject:generalSection]; } FLEXNetworkDetailSection *requestHeadersSection = [[self class] requestHeadersSectionForTransaction:self.transaction]; if (requestHeadersSection.rows.count > 0) { [sections addObject:requestHeadersSection]; } FLEXNetworkDetailSection *queryParametersSection = [[self class] queryParametersSectionForTransaction:self.transaction]; if (queryParametersSection.rows.count > 0) { [sections addObject:queryParametersSection]; } FLEXNetworkDetailSection *postBodySection = [[self class] postBodySectionForTransaction:self.transaction]; if (postBodySection.rows.count > 0) { [sections addObject:postBodySection]; } FLEXNetworkDetailSection *responseHeadersSection = [[self class] responseHeadersSectionForTransaction:self.transaction]; if (responseHeadersSection.rows.count > 0) { [sections addObject:responseHeadersSection]; } self.sections = sections; } - (void)handleTransactionUpdatedNotification:(NSNotification *)notification { FLEXNetworkTransaction *transaction = [[notification userInfo] objectForKey:kFLEXNetworkRecorderUserInfoTransactionKey]; if (transaction == self.transaction) { [self rebuildTableSections]; } } - (void)copyButtonPressed:(id)sender { [UIPasteboard.generalPasteboard setString:[FLEXNetworkCurlLogger curlCommandString:_transaction.request]]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return self.sections.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { FLEXNetworkDetailSection *sectionModel = self.sections[section]; return sectionModel.rows.count; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { FLEXNetworkDetailSection *sectionModel = self.sections[section]; return sectionModel.title; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { FLEXMultilineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXMultilineCell forIndexPath:indexPath]; FLEXNetworkDetailRow *rowModel = [self rowModelAtIndexPath:indexPath]; cell.textLabel.attributedText = [[self class] attributedTextForRow:rowModel]; cell.accessoryType = rowModel.selectionFuture ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; cell.selectionStyle = rowModel.selectionFuture ? UITableViewCellSelectionStyleDefault : UITableViewCellSelectionStyleNone; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { FLEXNetworkDetailRow *rowModel = [self rowModelAtIndexPath:indexPath]; UIViewController *viewController = nil; if (rowModel.selectionFuture) { viewController = rowModel.selectionFuture(); } if ([viewController isKindOfClass:UIAlertController.class]) { [self presentViewController:viewController animated:YES completion:nil]; } else if (viewController) { [self.navigationController pushViewController:viewController animated:YES]; } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath]; NSAttributedString *attributedText = [[self class] attributedTextForRow:row]; BOOL showsAccessory = row.selectionFuture != nil; return [FLEXMultilineTableViewCell preferredHeightWithAttributedText:attributedText maxWidth:tableView.bounds.size.width style:tableView.style showsAccessory:showsAccessory ]; } - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return [NSArray flex_forEachUpTo:self.sections.count map:^id(NSUInteger i) { return @"⦁"; }]; } - (FLEXNetworkDetailRow *)rowModelAtIndexPath:(NSIndexPath *)indexPath { FLEXNetworkDetailSection *sectionModel = self.sections[indexPath.section]; return sectionModel.rows[indexPath.row]; } #pragma mark - Cell Copying - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { return action == @selector(copy:); } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if (action == @selector(copy:)) { FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath]; UIPasteboard.generalPasteboard.string = row.detailText; } } - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) { return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu *(NSArray *suggestedActions) { UIAction *copy = [UIAction actionWithTitle:@"Copy" image:nil identifier:nil handler:^(__kindof UIAction *action) { FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath]; UIPasteboard.generalPasteboard.string = row.detailText; } ]; return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[copy] ]; } ]; } #pragma mark - View Configuration + (NSAttributedString *)attributedTextForRow:(FLEXNetworkDetailRow *)row { NSDictionary *titleAttributes = @{ NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Medium" size:12.0], NSForegroundColorAttributeName : [UIColor colorWithWhite:0.5 alpha:1.0] }; NSDictionary *detailAttributes = @{ NSFontAttributeName : UIFont.flex_defaultTableCellFont, NSForegroundColorAttributeName : FLEXColor.primaryTextColor }; NSString *title = [NSString stringWithFormat:@"%@: ", row.title]; NSString *detailText = row.detailText ?: @""; NSMutableAttributedString *attributedText = [NSMutableAttributedString new]; [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:title attributes:titleAttributes]]; [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:detailText attributes:detailAttributes]]; return attributedText; } #pragma mark - Table Data Generation + (FLEXNetworkDetailSection *)generalSectionForTransaction:(FLEXHTTPTransaction *)transaction { NSMutableArray *rows = [NSMutableArray new]; FLEXNetworkDetailRow *requestURLRow = [FLEXNetworkDetailRow new]; requestURLRow.title = @"Request URL"; NSURL *url = transaction.request.URL; requestURLRow.detailText = url.absoluteString; requestURLRow.selectionFuture = ^{ UIViewController *urlWebViewController = [[FLEXWebViewController alloc] initWithURL:url]; urlWebViewController.title = url.absoluteString; return urlWebViewController; }; [rows addObject:requestURLRow]; FLEXNetworkDetailRow *requestMethodRow = [FLEXNetworkDetailRow new]; requestMethodRow.title = @"Request Method"; requestMethodRow.detailText = transaction.request.HTTPMethod; [rows addObject:requestMethodRow]; if (transaction.cachedRequestBody.length > 0) { FLEXNetworkDetailRow *postBodySizeRow = [FLEXNetworkDetailRow new]; postBodySizeRow.title = @"Request Body Size"; postBodySizeRow.detailText = [NSByteCountFormatter stringFromByteCount:transaction.cachedRequestBody.length countStyle:NSByteCountFormatterCountStyleBinary]; [rows addObject:postBodySizeRow]; FLEXNetworkDetailRow *postBodyRow = [FLEXNetworkDetailRow new]; postBodyRow.title = @"Request Body"; postBodyRow.detailText = @"tap to view"; postBodyRow.selectionFuture = ^UIViewController * () { // Show the body if we can NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"]; NSData *body = [self postBodyDataForTransaction:transaction]; UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:body]; if (detailViewController) { detailViewController.title = @"Request Body"; return detailViewController; } // We can't show the body, alert user return [FLEXAlert makeAlert:^(FLEXAlert *make) { if (!body) { make.title(@"Empty HTTP Body"); } else { make.title(@"Cannot View HTTP Body Data"); make.message(@"FLEX does not have a viewer for request body data with MIME type: "); } make.message(contentType); make.button(@"Dismiss").cancelStyle(); }]; }; [rows addObject:postBodyRow]; } NSString *statusCodeString = [FLEXUtility statusCodeStringFromURLResponse:transaction.response]; if (statusCodeString.length > 0) { FLEXNetworkDetailRow *statusCodeRow = [FLEXNetworkDetailRow new]; statusCodeRow.title = @"Status Code"; statusCodeRow.detailText = statusCodeString; [rows addObject:statusCodeRow]; } if (transaction.error) { FLEXNetworkDetailRow *errorRow = [FLEXNetworkDetailRow new]; errorRow.title = @"Error"; errorRow.detailText = transaction.error.localizedDescription; [rows addObject:errorRow]; } FLEXNetworkDetailRow *responseBodyRow = [FLEXNetworkDetailRow new]; responseBodyRow.title = @"Response Body"; NSData *responseData = [FLEXNetworkRecorder.defaultRecorder cachedResponseBodyForTransaction:transaction]; if (responseData.length > 0) { responseBodyRow.detailText = @"tap to view"; // Avoid a long lived strong reference to the response data in case we need to purge it from the cache. weakify(responseData) responseBodyRow.selectionFuture = ^UIViewController *() { strongify(responseData) // Show the response if we can NSString *contentType = transaction.response.MIMEType; if (responseData) { UIViewController *bodyDetails = [self detailViewControllerForMIMEType:contentType data:responseData]; if (bodyDetails) { bodyDetails.title = @"Response"; return bodyDetails; } } // We can't show the response, alert user return [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Unable to View Response"); if (responseData) { make.message(@"No viewer content type: ").message(contentType); } else { make.message(@"The response has been purged from the cache"); } make.button(@"OK").cancelStyle(); }]; }; } else { BOOL emptyResponse = transaction.receivedDataLength == 0; responseBodyRow.detailText = emptyResponse ? @"empty" : @"not in cache"; } [rows addObject:responseBodyRow]; FLEXNetworkDetailRow *responseSizeRow = [FLEXNetworkDetailRow new]; responseSizeRow.title = @"Response Size"; responseSizeRow.detailText = [NSByteCountFormatter stringFromByteCount:transaction.receivedDataLength countStyle:NSByteCountFormatterCountStyleBinary]; [rows addObject:responseSizeRow]; FLEXNetworkDetailRow *mimeTypeRow = [FLEXNetworkDetailRow new]; mimeTypeRow.title = @"MIME Type"; mimeTypeRow.detailText = transaction.response.MIMEType; [rows addObject:mimeTypeRow]; FLEXNetworkDetailRow *mechanismRow = [FLEXNetworkDetailRow new]; mechanismRow.title = @"Mechanism"; mechanismRow.detailText = transaction.requestMechanism; [rows addObject:mechanismRow]; FLEXNetworkDetailRow *localStartTimeRow = [FLEXNetworkDetailRow new]; localStartTimeRow.title = [NSString stringWithFormat:@"Start Time (%@)", [NSTimeZone.localTimeZone abbreviationForDate:transaction.startTime]]; localStartTimeRow.detailText = [NSDateFormatter flex_stringFrom:transaction.startTime format:FLEXDateFormatVerbose]; [rows addObject:localStartTimeRow]; FLEXNetworkDetailRow *utcStartTimeRow = [FLEXNetworkDetailRow new]; utcStartTimeRow.title = @"Start Time (UTC)"; utcStartTimeRow.detailText = [NSDateFormatter flex_stringFrom:transaction.startTime format:FLEXDateFormatVerbose]; [rows addObject:utcStartTimeRow]; FLEXNetworkDetailRow *unixStartTime = [FLEXNetworkDetailRow new]; unixStartTime.title = @"Unix Start Time"; unixStartTime.detailText = [NSString stringWithFormat:@"%f", [transaction.startTime timeIntervalSince1970]]; [rows addObject:unixStartTime]; FLEXNetworkDetailRow *durationRow = [FLEXNetworkDetailRow new]; durationRow.title = @"Total Duration"; durationRow.detailText = [FLEXUtility stringFromRequestDuration:transaction.duration]; [rows addObject:durationRow]; FLEXNetworkDetailRow *latencyRow = [FLEXNetworkDetailRow new]; latencyRow.title = @"Latency"; latencyRow.detailText = [FLEXUtility stringFromRequestDuration:transaction.latency]; [rows addObject:latencyRow]; FLEXNetworkDetailSection *generalSection = [FLEXNetworkDetailSection new]; generalSection.title = @"General"; generalSection.rows = rows; return generalSection; } + (FLEXNetworkDetailSection *)requestHeadersSectionForTransaction:(FLEXHTTPTransaction *)transaction { FLEXNetworkDetailSection *requestHeadersSection = [FLEXNetworkDetailSection new]; requestHeadersSection.title = @"Request Headers"; requestHeadersSection.rows = [self networkDetailRowsFromDictionary:transaction.request.allHTTPHeaderFields]; return requestHeadersSection; } + (FLEXNetworkDetailSection *)postBodySectionForTransaction:(FLEXHTTPTransaction *)transaction { FLEXNetworkDetailSection *postBodySection = [FLEXNetworkDetailSection new]; postBodySection.title = @"Request Body Parameters"; if (transaction.cachedRequestBody.length > 0) { NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"]; if ([contentType hasPrefix:@"application/x-www-form-urlencoded"]) { NSData *body = [self postBodyDataForTransaction:transaction]; NSString *bodyString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]; postBodySection.rows = [self networkDetailRowsFromQueryItems:[FLEXUtility itemsFromQueryString:bodyString]]; } } return postBodySection; } + (FLEXNetworkDetailSection *)queryParametersSectionForTransaction:(FLEXHTTPTransaction *)transaction { NSArray *queries = [FLEXUtility itemsFromQueryString:transaction.request.URL.query]; FLEXNetworkDetailSection *querySection = [FLEXNetworkDetailSection new]; querySection.title = @"Query Parameters"; querySection.rows = [self networkDetailRowsFromQueryItems:queries]; return querySection; } + (FLEXNetworkDetailSection *)responseHeadersSectionForTransaction:(FLEXHTTPTransaction *)transaction { FLEXNetworkDetailSection *responseHeadersSection = [FLEXNetworkDetailSection new]; responseHeadersSection.title = @"Response Headers"; if ([transaction.response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)transaction.response; responseHeadersSection.rows = [self networkDetailRowsFromDictionary:httpResponse.allHeaderFields]; } return responseHeadersSection; } + (NSArray *)networkDetailRowsFromDictionary:(NSDictionary *)dictionary { NSMutableArray *rows = [NSMutableArray new]; NSArray *sortedKeys = [dictionary.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; for (NSString *key in sortedKeys) { id value = dictionary[key]; FLEXNetworkDetailRow *row = [FLEXNetworkDetailRow new]; row.title = key; row.detailText = [value description]; [rows addObject:row]; } return rows.copy; } + (NSArray *)networkDetailRowsFromQueryItems:(NSArray *)items { // Sort the items by name items = [items sortedArrayUsingComparator:^NSComparisonResult(NSURLQueryItem *item1, NSURLQueryItem *item2) { return [item1.name caseInsensitiveCompare:item2.name]; }]; NSMutableArray *rows = [NSMutableArray new]; for (NSURLQueryItem *item in items) { FLEXNetworkDetailRow *row = [FLEXNetworkDetailRow new]; row.title = item.name; row.detailText = item.value; [rows addObject:row]; } return [rows copy]; } + (UIViewController *)detailViewControllerForMIMEType:(NSString *)mimeType data:(NSData *)data { if (!data) { return nil; // An alert will be presented in place of this screen } FLEXCustomContentViewerFuture makeCustomViewer = FLEXManager.sharedManager.customContentTypeViewers[mimeType.lowercaseString]; if (makeCustomViewer) { UIViewController *viewer = makeCustomViewer(data); if (viewer) { return viewer; } } // FIXME (RKO): Don't rely on UTF8 string encoding UIViewController *detailViewController = nil; if ([FLEXUtility isValidJSONData:data]) { NSString *prettyJSON = [FLEXUtility prettyJSONStringFromData:data]; if (prettyJSON.length > 0) { detailViewController = [[FLEXWebViewController alloc] initWithText:prettyJSON]; } } else if ([mimeType hasPrefix:@"image/"]) { UIImage *image = [UIImage imageWithData:data]; detailViewController = [FLEXImagePreviewViewController forImage:image]; } else if ([mimeType isEqual:@"application/x-plist"]) { id propertyList = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL]; detailViewController = [[FLEXWebViewController alloc] initWithText:[propertyList description]]; } // Fall back to trying to show the response as text if (!detailViewController) { NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; if (text.length > 0) { detailViewController = [[FLEXWebViewController alloc] initWithText:text]; } } return detailViewController; } + (NSData *)postBodyDataForTransaction:(FLEXHTTPTransaction *)transaction { NSData *bodyData = transaction.cachedRequestBody; if (bodyData.length > 0 && [FLEXUtility hasCompressedContentEncoding:transaction.request]) { bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData]; } return bodyData; } @end ================================================ FILE: Classes/Network/FLEXMITMDataSource.h ================================================ // // FLEXMITMDataSource.h // FLEX // // Created by Tanner Bennett on 8/22/21. // #import NS_ASSUME_NONNULL_BEGIN @interface FLEXMITMDataSource<__covariant TransactionType> : NSObject + (instancetype)dataSourceWithProvider:(NSArray *(^)(void))future; /// Whether or not the data in \c transactions and \c bytesReceived are actually filtered yet or not @property (nonatomic, readonly) BOOL isFiltered; /// The content of this array is filtered to match the input of \c filter:completion: @property (nonatomic, readonly) NSArray *transactions; @property (nonatomic, readonly) NSArray *allTransactions; /// The content of this array is filtered to match the input of \c filter:completion: @property (nonatomic) NSInteger bytesReceived; @property (nonatomic) NSInteger totalBytesReceived; - (void)reloadByteCounts; - (void)reloadData:(void (^_Nullable)(FLEXMITMDataSource *dataSource))completion; - (void)filter:(NSString *)searchString completion:(void(^_Nullable)(FLEXMITMDataSource *dataSource))completion; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Network/FLEXMITMDataSource.m ================================================ // // FLEXMITMDataSource.m // FLEX // // Created by Tanner Bennett on 8/22/21. // #import "FLEXMITMDataSource.h" #import "FLEXNetworkTransaction.h" #import "FLEXUtility.h" @interface FLEXMITMDataSource () @property (nonatomic, readonly) NSArray *(^dataProvider)(void); @property (nonatomic) NSString *filterString; @end @implementation FLEXMITMDataSource + (instancetype)dataSourceWithProvider:(NSArray *(^)(void))future { FLEXMITMDataSource *ds = [self new]; ds->_dataProvider = future; [ds reloadData:nil]; return ds; } - (BOOL)isFiltered { return self.filterString.length > 0; } - (void)reloadByteCounts { [self updateBytesReceived]; [self updateFilteredBytesReceived]; } - (void)reloadData:(void (^)(FLEXMITMDataSource *dataSource))completion { self.allTransactions = self.dataProvider(); [self filter:self.filterString completion:completion]; } - (void)filter:(NSString *)searchString completion:(void (^)(FLEXMITMDataSource *dataSource))completion { self.filterString = searchString; if (!searchString.length) { self.filteredTransactions = self.allTransactions; if (completion) completion(self); } else { NSArray *allTransactions = self.allTransactions.copy; [self onBackgroundQueue:^NSArray *{ return [allTransactions flex_filtered:^BOOL(FLEXNetworkTransaction *entry, NSUInteger idx) { return [entry matchesQuery:searchString]; }]; } thenOnMainQueue:^(NSArray *filteredNetworkTransactions) { if ([self.filterString isEqual:searchString]) { self.filteredTransactions = filteredNetworkTransactions; if (completion) completion(self); } }]; } } - (void)setAllTransactions:(NSArray *)transactions { _allTransactions = transactions.copy; [self updateBytesReceived]; } /// This is really just a semantic setter for \c _transactions - (void)setFilteredTransactions:(NSArray *)filteredTransactions { _transactions = filteredTransactions.copy; [self updateFilteredBytesReceived]; } - (void)setTransactions:(NSArray *)transactions { self.filteredTransactions = transactions; } - (void)updateBytesReceived { NSInteger bytesReceived = 0; for (FLEXNetworkTransaction *transaction in self.transactions) { bytesReceived += transaction.receivedDataLength; } self.bytesReceived = bytesReceived; } - (void)updateFilteredBytesReceived { NSInteger filteredBytesReceived = 0; for (FLEXNetworkTransaction *transaction in self.transactions) { filteredBytesReceived += transaction.receivedDataLength; } self.bytesReceived = filteredBytesReceived; } - (void)onBackgroundQueue:(NSArray *(^)(void))backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSArray *items = backgroundBlock(); dispatch_async(dispatch_get_main_queue(), ^{ mainBlock(items); }); }); } @end ================================================ FILE: Classes/Network/FLEXNetworkCurlLogger.h ================================================ // // FLEXCurlLogger.h // // // Created by Ji Pei on 07/27/16 // #import @interface FLEXNetworkCurlLogger : NSObject /** * Generates a cURL command equivalent to the given request. * * @param request The request to be translated */ + (NSString *)curlCommandString:(NSURLRequest *)request; @end ================================================ FILE: Classes/Network/FLEXNetworkCurlLogger.m ================================================ // // FLEXCurlLogger.m // // // Created by Ji Pei on 07/27/16 // #import "FLEXNetworkCurlLogger.h" #import "FLEXUtility.h" @implementation FLEXNetworkCurlLogger + (NSString *)curlCommandString:(NSURLRequest *)request { __block NSMutableString *curlCommandString = [NSMutableString stringWithFormat:@"curl -v -X %@ ", request.HTTPMethod]; [curlCommandString appendFormat:@"\'%@\' ", request.URL.absoluteString]; [request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *val, BOOL *stop) { [curlCommandString appendFormat:@"-H \'%@: %@\' ", key, val]; }]; NSArray *cookies = [NSHTTPCookieStorage.sharedHTTPCookieStorage cookiesForURL:request.URL]; if (cookies) { [curlCommandString appendFormat:@"-H \'Cookie:"]; for (NSHTTPCookie *cookie in cookies) { [curlCommandString appendFormat:@" %@=%@;", cookie.name, cookie.value]; } [curlCommandString appendFormat:@"\' "]; } if (request.HTTPBody) { NSData *bodyData = request.HTTPBody; if ([FLEXUtility hasCompressedContentEncoding:request]) { bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData]; } NSString *body = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding]; if (body != nil) { [curlCommandString appendFormat:@"-d \'%@\'", body]; } else { // Fallback to using base64 encoding [curlCommandString appendString:@"--data-binary @-"]; NSString *base64 = [request.HTTPBody base64EncodedStringWithOptions:0]; NSString *prefix = [NSString stringWithFormat:@"echo -n '%@' | base64 -D | ", base64]; [curlCommandString insertString:prefix atIndex:0]; } } return curlCommandString; } @end ================================================ FILE: Classes/Network/FLEXNetworkMITMViewController.h ================================================ // // FLEXNetworkMITMViewController.h // Flipboard // // Created by Ryan Olson on 2/8/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" #import "FLEXGlobalsEntry.h" /// The main screen for the network observer, which displays a list of network transactions. @interface FLEXNetworkMITMViewController : FLEXTableViewController @end ================================================ FILE: Classes/Network/FLEXNetworkMITMViewController.m ================================================ // // FLEXNetworkMITMViewController.m // Flipboard // // Created by Ryan Olson on 2/8/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXUtility.h" #import "FLEXMITMDataSource.h" #import "FLEXNetworkMITMViewController.h" #import "FLEXNetworkTransaction.h" #import "FLEXNetworkRecorder.h" #import "FLEXNetworkObserver.h" #import "FLEXNetworkTransactionCell.h" #import "FLEXHTTPTransactionDetailController.h" #import "FLEXNetworkSettingsController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXGlobalsViewController.h" #import "FLEXWebViewController.h" #import "UIBarButtonItem+FLEX.h" #import "FLEXResources.h" #import "NSUserDefaults+FLEX.h" #define kFirebaseAvailable NSClassFromString(@"FIRDocumentReference") #define kWebsocketsAvailable @available(iOS 13.0, *) typedef NS_ENUM(NSInteger, FLEXNetworkObserverMode) { FLEXNetworkObserverModeFirebase = 0, FLEXNetworkObserverModeREST, FLEXNetworkObserverModeWebsockets, }; @interface FLEXNetworkMITMViewController () @property (nonatomic) BOOL updateInProgress; @property (nonatomic) BOOL pendingReload; @property (nonatomic) FLEXNetworkObserverMode mode; @property (nonatomic, readonly) FLEXMITMDataSource *dataSource; @property (nonatomic, readonly) FLEXMITMDataSource *HTTPDataSource; @property (nonatomic, readonly) FLEXMITMDataSource *websocketDataSource; @property (nonatomic, readonly) FLEXMITMDataSource *firebaseDataSource; @end @implementation FLEXNetworkMITMViewController #pragma mark - Lifecycle - (id)init { return [self initWithStyle:UITableViewStylePlain]; } - (void)viewDidLoad { [super viewDidLoad]; self.showsSearchBar = YES; self.pinSearchBar = YES; self.showSearchBarInitially = NO; NSMutableArray *scopeTitles = [NSMutableArray arrayWithObject:@"REST"]; _HTTPDataSource = [FLEXMITMDataSource dataSourceWithProvider:^NSArray * { return FLEXNetworkRecorder.defaultRecorder.HTTPTransactions; }]; if (kFirebaseAvailable) { _firebaseDataSource = [FLEXMITMDataSource dataSourceWithProvider:^NSArray * { return FLEXNetworkRecorder.defaultRecorder.firebaseTransactions; }]; [scopeTitles insertObject:@"Firebase" atIndex:0]; // First space } if (kWebsocketsAvailable) { [scopeTitles addObject:@"Websockets"]; // Last space _websocketDataSource = [FLEXMITMDataSource dataSourceWithProvider:^NSArray * { return FLEXNetworkRecorder.defaultRecorder.websocketTransactions; }]; } // Scopes will only be shown if we have either firebase or websockets available self.searchController.searchBar.showsScopeBar = scopeTitles.count > 1; self.searchController.searchBar.scopeButtonTitles = scopeTitles; self.mode = NSUserDefaults.standardUserDefaults.flex_lastNetworkObserverMode; [self addToolbarItems:@[ [UIBarButtonItem flex_itemWithImage:FLEXResources.gearIcon target:self action:@selector(settingsButtonTapped:) ], [[UIBarButtonItem flex_systemItem:UIBarButtonSystemItemTrash target:self action:@selector(trashButtonTapped:) ] flex_withTintColor:UIColor.redColor] ]]; [self.tableView registerClass:FLEXNetworkTransactionCell.class forCellReuseIdentifier:FLEXNetworkTransactionCell.reuseID ]; self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.tableView.rowHeight = FLEXNetworkTransactionCell.preferredCellHeight; [self registerForNotifications]; [self updateTransactions:nil]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Reload the table if we received updates while not on-screen if (self.pendingReload) { [self.tableView reloadData]; self.pendingReload = NO; } } - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; } - (void)registerForNotifications { NSDictionary *notifications = @{ kFLEXNetworkRecorderNewTransactionNotification: NSStringFromSelector(@selector(handleNewTransactionRecordedNotification:)), kFLEXNetworkRecorderTransactionUpdatedNotification: NSStringFromSelector(@selector(handleTransactionUpdatedNotification:)), kFLEXNetworkRecorderTransactionsClearedNotification: NSStringFromSelector(@selector(handleTransactionsClearedNotification:)), kFLEXNetworkObserverEnabledStateChangedNotification: NSStringFromSelector(@selector(handleNetworkObserverEnabledStateChangedNotification:)), }; for (NSString *name in notifications.allKeys) { [NSNotificationCenter.defaultCenter addObserver:self selector:NSSelectorFromString(notifications[name]) name:name object:nil ]; } } #pragma mark - Private #pragma mark Button Actions - (void)settingsButtonTapped:(UIBarButtonItem *)sender { UIViewController *settings = [FLEXNetworkSettingsController new]; settings.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem( Done, self, @selector(settingsViewControllerDoneTapped:) ); settings.title = @"Network Debugging Settings"; // This is not a FLEXNavigationController because it is not intended as a new tab UIViewController *nav = [[UINavigationController alloc] initWithRootViewController:settings]; [self presentViewController:nav animated:YES completion:nil]; } - (void)trashButtonTapped:(UIBarButtonItem *)sender { [FLEXAlert makeSheet:^(FLEXAlert *make) { BOOL clearAll = !self.dataSource.isFiltered; if (!clearAll) { make.title(@"Clear Filtered Requests?"); make.message(@"This will only remove the requests matching your search string on this screen."); } else { make.title(@"Clear All Recorded Requests?"); make.message(@"This cannot be undone."); } make.button(@"Cancel").cancelStyle(); make.button(@"Clear").destructiveStyle().handler(^(NSArray *strings) { if (clearAll) { [FLEXNetworkRecorder.defaultRecorder clearRecordedActivity]; } else { FLEXNetworkTransactionKind kind = (FLEXNetworkTransactionKind)self.mode; [FLEXNetworkRecorder.defaultRecorder clearRecordedActivity:kind matching:self.searchText]; } }); } showFrom:self source:sender]; } - (void)settingsViewControllerDoneTapped:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark Transactions - (FLEXNetworkObserverMode)mode { FLEXNetworkObserverMode mode = self.searchController.searchBar.selectedScopeButtonIndex; switch (mode) { case FLEXNetworkObserverModeFirebase: if (kFirebaseAvailable) { return FLEXNetworkObserverModeFirebase; } return FLEXNetworkObserverModeREST; case FLEXNetworkObserverModeREST: if (kFirebaseAvailable) { return FLEXNetworkObserverModeREST; } return FLEXNetworkObserverModeWebsockets; case FLEXNetworkObserverModeWebsockets: return FLEXNetworkObserverModeWebsockets; } } - (void)setMode:(FLEXNetworkObserverMode)mode { // The segmentd control will have different appearances based on which APIs // are available. For example, when only Websockets is available: // // 0 1 // ┌───────────────────────────┬────────────────────────────┐ // │ REST │ Websockets │ // └───────────────────────────┴────────────────────────────┘ // // And when both Firebase and Websockets are available: // // 0 1 2 // ┌──────────────────┬──────────────────┬──────────────────┐ // │ Firebase │ REST │ Websockets │ // └──────────────────┴──────────────────┴──────────────────┘ // // As a result, we need to adjust the input mode variable accordingly // before we actually set it. When we try to set it to Firebase but // Firebase is not available, we don't do anything, because when Firebase // is unavailable, FLEXNetworkObserverModeFirebase represents the same index // as REST would without Firebase. For each of the others, we subtract 1 // from them for every relevant API that is unavailable. So for Websockets, // if it is unavailable, we subtract 1 and it becomes FLEXNetworkObserverModeREST. // And if Firebase is also unavailable, we subtract 1 again. switch (mode) { case FLEXNetworkObserverModeFirebase: // Will default to REST if Firebase is unavailable break; case FLEXNetworkObserverModeREST: // Firebase will become REST when Firebase is unavailable if (!kFirebaseAvailable) { mode--; } break; case FLEXNetworkObserverModeWebsockets: // Default to REST if Websockets are unavailable if (!kWebsocketsAvailable) { mode--; } // Firebase will become REST when Firebase is unavailable if (!kFirebaseAvailable) { mode--; } } self.searchController.searchBar.selectedScopeButtonIndex = mode; } - (FLEXMITMDataSource *)dataSource { switch (self.mode) { case FLEXNetworkObserverModeREST: return self.HTTPDataSource; case FLEXNetworkObserverModeWebsockets: return self.websocketDataSource; case FLEXNetworkObserverModeFirebase: return self.firebaseDataSource; } } - (void)updateTransactions:(void(^)(void))callback { id completion = ^(FLEXMITMDataSource *dataSource) { // Update byte count [self updateFirstSectionHeader]; if (callback && dataSource == self.dataSource) callback(); }; [self.HTTPDataSource reloadData:completion]; [self.websocketDataSource reloadData:completion]; [self.firebaseDataSource reloadData:completion]; } #pragma mark Header - (void)updateFirstSectionHeader { UIView *view = [self.tableView headerViewForSection:0]; if ([view isKindOfClass:[UITableViewHeaderFooterView class]]) { UITableViewHeaderFooterView *headerView = (UITableViewHeaderFooterView *)view; headerView.textLabel.text = [self headerText]; [headerView setNeedsLayout]; } } - (NSString *)headerText { long long bytesReceived = self.dataSource.bytesReceived; NSInteger totalRequests = self.dataSource.transactions.count; NSString *byteCountText = [NSByteCountFormatter stringFromByteCount:bytesReceived countStyle:NSByteCountFormatterCountStyleBinary ]; NSString *requestsText = totalRequests == 1 ? @"Request" : @"Requests"; // Exclude byte count from Firebase if (self.mode == FLEXNetworkObserverModeFirebase) { return [NSString stringWithFormat:@"%@ %@", @(totalRequests), requestsText ]; } return [NSString stringWithFormat:@"%@ %@ (%@ received)", @(totalRequests), requestsText, byteCountText ]; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { return @"📡 Network History"; } + (FLEXGlobalsEntryRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row { return ^(UITableViewController *host) { if (FLEXNetworkObserver.isEnabled) { [host.navigationController pushViewController:[ self globalsEntryViewController:row ] animated:YES]; } else { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Network Monitor Disabled"); make.message(@"You must enable network monitoring to proceed."); make.button(@"Turn On").preferred().handler(^(NSArray *strings) { FLEXNetworkObserver.enabled = YES; [host.navigationController pushViewController:[ self globalsEntryViewController:row ] animated:YES]; }); make.button(@"Dismiss").cancelStyle(); } showFrom:host]; } }; } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { UIViewController *controller = [self new]; controller.title = [self globalsEntryTitle:row]; return controller; } #pragma mark - Notification Handlers - (void)handleNewTransactionRecordedNotification:(NSNotification *)notification { [self tryUpdateTransactions]; } - (void)tryUpdateTransactions { // Don't do any view updating if we aren't in the view hierarchy if (!self.viewIfLoaded.window) { [self updateTransactions:nil]; self.pendingReload = YES; return; } // Let the previous row insert animation finish before starting a new one to avoid stomping. // We'll try calling the method again when the insertion completes, // and we properly no-op if there haven't been changes. if (self.updateInProgress) { return; } self.updateInProgress = YES; // Get state before update NSString *currentFilter = self.searchText; FLEXNetworkObserverMode currentMode = self.mode; NSInteger existingRowCount = self.dataSource.transactions.count; [self updateTransactions:^{ // Compare to state after update NSString *newFilter = self.searchText; FLEXNetworkObserverMode newMode = self.mode; NSInteger newRowCount = self.dataSource.transactions.count; NSInteger rowCountDiff = newRowCount - existingRowCount; // Abort if the observation mode changed, or if the search field text changed if (newMode != currentMode || ![currentFilter isEqualToString:newFilter]) { self.updateInProgress = NO; return; } if (rowCountDiff) { // Insert animation if we're at the top. if (self.tableView.contentOffset.y <= 0.0 && rowCountDiff > 0) { [CATransaction begin]; [CATransaction setCompletionBlock:^{ self.updateInProgress = NO; // This isn't an infinite loop, it won't run a third time // if there were no new transactions the second time [self tryUpdateTransactions]; }]; NSMutableArray *indexPathsToReload = [NSMutableArray new]; for (NSInteger row = 0; row < rowCountDiff; row++) { [indexPathsToReload addObject:[NSIndexPath indexPathForRow:row inSection:0]]; } [self.tableView insertRowsAtIndexPaths:indexPathsToReload withRowAnimation:UITableViewRowAnimationAutomatic]; [CATransaction commit]; } else { // Maintain the user's position if they've scrolled down. CGSize existingContentSize = self.tableView.contentSize; [self.tableView reloadData]; CGFloat contentHeightChange = self.tableView.contentSize.height - existingContentSize.height; self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, self.tableView.contentOffset.y + contentHeightChange); self.updateInProgress = NO; } } else { self.updateInProgress = NO; } }]; } - (void)handleTransactionUpdatedNotification:(NSNotification *)notification { [self.HTTPDataSource reloadByteCounts]; [self.websocketDataSource reloadByteCounts]; // Don't need to reload Firebase here FLEXNetworkTransaction *transaction = notification.userInfo[kFLEXNetworkRecorderUserInfoTransactionKey]; // Update both the main table view and search table view if needed. for (FLEXNetworkTransactionCell *cell in self.tableView.visibleCells) { if ([cell.transaction isEqual:transaction]) { // Using -[UITableView reloadRowsAtIndexPaths:withRowAnimation:] is overkill here and kicks off a lot of // work that can make the table view somewhat unresponsive when lots of updates are streaming in. // We just need to tell the cell that it needs to re-layout. [cell setNeedsLayout]; break; } } [self updateFirstSectionHeader]; } - (void)handleTransactionsClearedNotification:(NSNotification *)notification { [self updateTransactions:^{ [self.tableView reloadData]; }]; } - (void)handleNetworkObserverEnabledStateChangedNotification:(NSNotification *)notification { // Update the header, which displays a warning when network debugging is disabled [self updateFirstSectionHeader]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.dataSource.transactions.count; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [self headerText]; } - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section { if ([view isKindOfClass:[UITableViewHeaderFooterView class]]) { UITableViewHeaderFooterView *headerView = (UITableViewHeaderFooterView *)view; headerView.textLabel.font = [UIFont systemFontOfSize:14.0 weight:UIFontWeightSemibold]; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { FLEXNetworkTransactionCell *cell = [tableView dequeueReusableCellWithIdentifier:FLEXNetworkTransactionCell.reuseID forIndexPath:indexPath ]; cell.transaction = [self transactionAtIndexPath:indexPath]; // Since we insert from the top, assign background colors bottom up to keep them consistent for each transaction. NSInteger totalRows = [tableView numberOfRowsInSection:indexPath.section]; if ((totalRows - indexPath.row) % 2 == 0) { cell.backgroundColor = FLEXColor.secondaryBackgroundColor; } else { cell.backgroundColor = FLEXColor.primaryBackgroundColor; } return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { switch (self.mode) { case FLEXNetworkObserverModeREST: { FLEXHTTPTransaction *transaction = [self HTTPTransactionAtIndexPath:indexPath]; UIViewController *details = [FLEXHTTPTransactionDetailController withTransaction:transaction]; [self.navigationController pushViewController:details animated:YES]; break; } case FLEXNetworkObserverModeWebsockets: { if (@available(iOS 13.0, *)) { // This check will never fail FLEXWebsocketTransaction *transaction = [self websocketTransactionAtIndexPath:indexPath]; UIViewController *details = nil; if (transaction.message.type == NSURLSessionWebSocketMessageTypeData) { details = [FLEXObjectExplorerFactory explorerViewControllerForObject:transaction.message.data]; } else { details = [[FLEXWebViewController alloc] initWithText:transaction.message.string]; } [self.navigationController pushViewController:details animated:YES]; } break; } case FLEXNetworkObserverModeFirebase: { FLEXFirebaseTransaction *transaction = [self firebaseTransactionAtIndexPath:indexPath]; // id obj = transaction.documents.count == 1 ? transaction.documents.firstObject : transaction.documents; UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:transaction]; [self.navigationController pushViewController:explorer animated:YES]; } } } #pragma mark - Menu Actions - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { return action == @selector(copy:); } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if (action == @selector(copy:)) { UIPasteboard.generalPasteboard.string = [self transactionAtIndexPath:indexPath].copyString; } } - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) { FLEXNetworkTransaction *transaction = [self transactionAtIndexPath:indexPath]; return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu *(NSArray *suggestedActions) { UIAction *copy = [UIAction actionWithTitle:@"Copy URL" image:nil identifier:nil handler:^(__kindof UIAction *action) { UIPasteboard.generalPasteboard.string = transaction.copyString; } ]; NSArray *children = @[copy]; if (self.mode == FLEXNetworkObserverModeREST) { NSURLRequest *request = [self HTTPTransactionAtIndexPath:indexPath].request; UIAction *denylist = [UIAction actionWithTitle:[NSString stringWithFormat:@"Exclude '%@'", request.URL.host] image:nil identifier:nil handler:^(__kindof UIAction *action) { NSMutableArray *denylist = FLEXNetworkRecorder.defaultRecorder.hostDenylist; [denylist addObject:request.URL.host]; [FLEXNetworkRecorder.defaultRecorder clearExcludedTransactions]; [FLEXNetworkRecorder.defaultRecorder synchronizeDenylist]; [self tryUpdateTransactions]; } ]; children = [children arrayByAddingObject:denylist]; } return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:children ]; } ]; } - (FLEXNetworkTransaction *)transactionAtIndexPath:(NSIndexPath *)indexPath { return self.dataSource.transactions[indexPath.row]; } - (FLEXHTTPTransaction *)HTTPTransactionAtIndexPath:(NSIndexPath *)indexPath { return self.HTTPDataSource.transactions[indexPath.row]; } - (FLEXWebsocketTransaction *)websocketTransactionAtIndexPath:(NSIndexPath *)indexPath { return self.websocketDataSource.transactions[indexPath.row]; } - (FLEXFirebaseTransaction *)firebaseTransactionAtIndexPath:(NSIndexPath *)indexPath { return self.firebaseDataSource.transactions[indexPath.row]; } #pragma mark - Search Bar - (void)updateSearchResults:(NSString *)searchString { id callback = ^(FLEXMITMDataSource *dataSource) { if (self.dataSource == dataSource) { [self.tableView reloadData]; } }; [self.HTTPDataSource filter:searchString completion:callback]; [self.websocketDataSource filter:searchString completion:callback]; [self.firebaseDataSource filter:searchString completion:callback]; } - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)newScope { [self updateFirstSectionHeader]; [self.tableView reloadData]; NSUserDefaults.standardUserDefaults.flex_lastNetworkObserverMode = self.mode; } - (void)willDismissSearchController:(UISearchController *)searchController { [self.tableView reloadData]; } @end ================================================ FILE: Classes/Network/FLEXNetworkRecorder.h ================================================ // // FLEXNetworkRecorder.h // Flipboard // // Created by Ryan Olson on 2/4/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import // Notifications posted when the record is updated extern NSString *const kFLEXNetworkRecorderNewTransactionNotification; extern NSString *const kFLEXNetworkRecorderTransactionUpdatedNotification; extern NSString *const kFLEXNetworkRecorderUserInfoTransactionKey; extern NSString *const kFLEXNetworkRecorderTransactionsClearedNotification; @class FLEXNetworkTransaction, FLEXHTTPTransaction, FLEXWebsocketTransaction, FLEXFirebaseTransaction; @class FIRQuery, FIRDocumentReference, FIRCollectionReference, FIRDocumentSnapshot, FIRQuerySnapshot; typedef NS_ENUM(NSUInteger, FLEXNetworkTransactionKind) { FLEXNetworkTransactionKindFirebase = 0, FLEXNetworkTransactionKindREST, FLEXNetworkTransactionKindWebsockets, }; @interface FLEXNetworkRecorder : NSObject /// In general, it only makes sense to have one recorder for the entire application. @property (nonatomic, readonly, class) FLEXNetworkRecorder *defaultRecorder; /// Defaults to 25 MB if never set. Values set here are persisted across launches of the app. @property (nonatomic) NSUInteger responseCacheByteLimit; /// If NO, the recorder not cache will not cache response for content types /// with an "image", "video", or "audio" prefix. @property (nonatomic) BOOL shouldCacheMediaResponses; @property (nonatomic) NSMutableArray *hostDenylist; /// Call this after adding to or setting the \c hostDenylist to remove excluded transactions - (void)clearExcludedTransactions; /// Call this to save the denylist to the disk to be loaded next time - (void)synchronizeDenylist; #pragma mark Accessing recorded network activity /// Array of FLEXHTTPTransaction objects ordered by start time with the newest first. @property (nonatomic, readonly) NSArray *HTTPTransactions; /// Array of FLEXWebsocketTransaction objects ordered by start time with the newest first. @property (nonatomic, readonly) NSArray *websocketTransactions API_AVAILABLE(ios(13.0)); /// Array of FLEXFirebaseTransaction objects ordered by start time with the newest first. @property (nonatomic, readonly) NSArray *firebaseTransactions; /// The full response data IFF it hasn't been purged due to memory pressure. - (NSData *)cachedResponseBodyForTransaction:(FLEXHTTPTransaction *)transaction; /// Dumps all network transactions and cached response bodies. - (void)clearRecordedActivity; /// Clear only transactions matching the given query. - (void)clearRecordedActivity:(FLEXNetworkTransactionKind)kind matching:(NSString *)query; #pragma mark Recording network activity /// Call when app is about to send HTTP request. - (void)recordRequestWillBeSentWithRequestID:(NSString *)requestID request:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse; /// Call when HTTP response is available. - (void)recordResponseReceivedWithRequestID:(NSString *)requestID response:(NSURLResponse *)response; /// Call when data chunk is received over the network. - (void)recordDataReceivedWithRequestID:(NSString *)requestID dataLength:(int64_t)dataLength; /// Call when HTTP request has finished loading. - (void)recordLoadingFinishedWithRequestID:(NSString *)requestID responseBody:(NSData *)responseBody; /// Call when HTTP request has failed to load. - (void)recordLoadingFailedWithRequestID:(NSString *)requestID error:(NSError *)error; /// Call to set the request mechanism anytime after recordRequestWillBeSent... has been called. /// This string can be set to anything useful about the API used to make the request. - (void)recordMechanism:(NSString *)mechanism forRequestID:(NSString *)requestID; - (void)recordWebsocketMessageSend:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task API_AVAILABLE(ios(13.0)); - (void)recordWebsocketMessageSendCompletion:(NSURLSessionWebSocketMessage *)message error:(NSError *)error API_AVAILABLE(ios(13.0)); - (void)recordWebsocketMessageReceived:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task API_AVAILABLE(ios(13.0)); - (void)recordFIRQueryWillFetch:(FIRQuery *)query withTransactionID:(NSString *)transactionID; - (void)recordFIRDocumentWillFetch:(FIRDocumentReference *)document withTransactionID:(NSString *)transactionID; - (void)recordFIRQueryDidFetch:(FIRQuerySnapshot *)response error:(NSError *)error transactionID:(NSString *)transactionID; - (void)recordFIRDocumentDidFetch:(FIRDocumentSnapshot *)response error:(NSError *)error transactionID:(NSString *)transactionID; - (void)recordFIRWillSetData:(FIRDocumentReference *)doc data:(NSDictionary *)documentData merge:(NSNumber *)yesorno mergeFields:(NSArray *)fields transactionID:(NSString *)transactionID; - (void)recordFIRWillUpdateData:(FIRDocumentReference *)doc fields:(NSDictionary *)fields transactionID:(NSString *)transactionID; - (void)recordFIRWillDeleteDocument:(FIRDocumentReference *)doc transactionID:(NSString *)transactionID; - (void)recordFIRWillAddDocument:(FIRCollectionReference *)initiator document:(FIRDocumentReference *)doc transactionID:(NSString *)transactionID; - (void)recordFIRDidSetData:(NSError *)error transactionID:(NSString *)transactionID; - (void)recordFIRDidUpdateData:(NSError *)error transactionID:(NSString *)transactionID; - (void)recordFIRDidDeleteDocument:(NSError *)error transactionID:(NSString *)transactionID; - (void)recordFIRDidAddDocument:(NSError *)error transactionID:(NSString *)transactionID; @end ================================================ FILE: Classes/Network/FLEXNetworkRecorder.m ================================================ // // FLEXNetworkRecorder.m // Flipboard // // Created by Ryan Olson on 2/4/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXNetworkRecorder.h" #import "FLEXNetworkCurlLogger.h" #import "FLEXNetworkTransaction.h" #import "FLEXUtility.h" #import "FLEXResources.h" #import "NSUserDefaults+FLEX.h" #import "OSCache.h" #define Synchronized(queue, obj) ({ \ __block id __synchronized_retval = nil; \ dispatch_sync(queue, ^{ __synchronized_retval = obj; }); \ __synchronized_retval; \ }) NSString *const kFLEXNetworkRecorderNewTransactionNotification = @"kFLEXNetworkRecorderNewTransactionNotification"; NSString *const kFLEXNetworkRecorderTransactionUpdatedNotification = @"kFLEXNetworkRecorderTransactionUpdatedNotification"; NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction"; NSString *const kFLEXNetworkRecorderTransactionsClearedNotification = @"kFLEXNetworkRecorderTransactionsClearedNotification"; NSString *const kFLEXNetworkRecorderResponseCacheLimitDefaultsKey = @"com.flex.responseCacheLimit"; @interface FLEXNetworkRecorder () @property (nonatomic) OSCache *restCache; @property (atomic) NSMutableArray *orderedHTTPTransactions; @property (atomic) NSMutableArray *orderedWSTransactions; @property (atomic) NSMutableArray *orderedFirebaseTransactions; @property (atomic) NSMutableDictionary *requestIDsToTransactions; @property (nonatomic) dispatch_queue_t queue; @end @implementation FLEXNetworkRecorder - (instancetype)init { self = [super init]; if (self) { self.restCache = [OSCache new]; NSUInteger responseCacheLimit = [[NSUserDefaults.standardUserDefaults objectForKey:kFLEXNetworkRecorderResponseCacheLimitDefaultsKey] unsignedIntegerValue ]; // Default to 25 MB max. The cache will purge earlier if there is memory pressure. self.restCache.totalCostLimit = responseCacheLimit ?: 25 * 1024 * 1024; [self.restCache setTotalCostLimit:responseCacheLimit]; self.orderedWSTransactions = [NSMutableArray new]; self.orderedHTTPTransactions = [NSMutableArray new]; self.orderedFirebaseTransactions = [NSMutableArray new]; self.requestIDsToTransactions = [NSMutableDictionary new]; self.hostDenylist = NSUserDefaults.standardUserDefaults.flex_networkHostDenylist.mutableCopy; // Serial queue used because we use mutable objects that are not thread safe self.queue = dispatch_queue_create("com.flex.FLEXNetworkRecorder", DISPATCH_QUEUE_SERIAL); } return self; } + (instancetype)defaultRecorder { static FLEXNetworkRecorder *defaultRecorder = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ defaultRecorder = [self new]; }); return defaultRecorder; } #pragma mark - Public Data Access - (NSUInteger)responseCacheByteLimit { return self.restCache.totalCostLimit; } - (void)setResponseCacheByteLimit:(NSUInteger)responseCacheByteLimit { self.restCache.totalCostLimit = responseCacheByteLimit; [NSUserDefaults.standardUserDefaults setObject:@(responseCacheByteLimit) forKey:kFLEXNetworkRecorderResponseCacheLimitDefaultsKey ]; } - (NSArray *)HTTPTransactions { return Synchronized(self.queue, self.orderedHTTPTransactions.copy); } - (NSArray *)websocketTransactions { return Synchronized(self.queue, self.orderedWSTransactions.copy); } - (NSArray *)firebaseTransactions { return Synchronized(self.queue, self.orderedFirebaseTransactions.copy); } - (NSData *)cachedResponseBodyForTransaction:(FLEXHTTPTransaction *)transaction { return [self.restCache objectForKey:transaction.requestID]; } - (void)clearRecordedActivity { dispatch_async(self.queue, ^{ [self.restCache removeAllObjects]; [self.orderedWSTransactions removeAllObjects]; [self.orderedHTTPTransactions removeAllObjects]; [self.orderedFirebaseTransactions removeAllObjects]; [self.requestIDsToTransactions removeAllObjects]; [self notify:kFLEXNetworkRecorderTransactionsClearedNotification transaction:nil]; }); } - (void)clearRecordedActivity:(FLEXNetworkTransactionKind)kind matching:(NSString *)query { dispatch_async(self.queue, ^{ switch (kind) { case FLEXNetworkTransactionKindFirebase: { [self.orderedFirebaseTransactions flex_filter:^BOOL(FLEXFirebaseTransaction *obj, NSUInteger idx) { return ![obj matchesQuery:query]; }]; break; } case FLEXNetworkTransactionKindREST: { NSArray *toRemove; toRemove = [self.orderedHTTPTransactions flex_filtered:^BOOL(FLEXHTTPTransaction *obj, NSUInteger idx) { return [obj matchesQuery:query]; }]; // Remove from cache for (FLEXHTTPTransaction *t in toRemove) { [self.restCache removeObjectForKey:t.requestID]; } // Remove from list [self.orderedHTTPTransactions removeObjectsInArray:toRemove]; break; } case FLEXNetworkTransactionKindWebsockets: { [self.orderedWSTransactions flex_filter:^BOOL(FLEXWebsocketTransaction *obj, NSUInteger idx) { return ![obj matchesQuery:query]; }]; break; } } [self notify:kFLEXNetworkRecorderTransactionsClearedNotification transaction:nil]; }); } - (void)clearExcludedTransactions { dispatch_sync(self.queue, ^{ self.orderedHTTPTransactions = ({ [self.orderedHTTPTransactions flex_filtered:^BOOL(FLEXHTTPTransaction *ta, NSUInteger idx) { NSString *host = ta.request.URL.host; for (NSString *excluded in self.hostDenylist) { if ([host hasSuffix:excluded]) { return NO; } } return YES; }]; }); }); } - (void)synchronizeDenylist { NSUserDefaults.standardUserDefaults.flex_networkHostDenylist = self.hostDenylist; } #pragma mark - Network Events - (void)recordRequestWillBeSentWithRequestID:(NSString *)requestID request:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse { for (NSString *host in self.hostDenylist) { if ([request.URL.host hasSuffix:host]) { return; } } FLEXHTTPTransaction *transaction = [FLEXHTTPTransaction request:request identifier:requestID]; // Before async block to keep times accurate if (redirectResponse) { [self recordResponseReceivedWithRequestID:requestID response:redirectResponse]; [self recordLoadingFinishedWithRequestID:requestID responseBody:nil]; } // A redirect is always a new request dispatch_async(self.queue, ^{ [self.orderedHTTPTransactions insertObject:transaction atIndex:0]; self.requestIDsToTransactions[requestID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordResponseReceivedWithRequestID:(NSString *)requestID response:(NSURLResponse *)response { // Before async block to stay accurate NSDate *responseDate = [NSDate date]; dispatch_async(self.queue, ^{ FLEXHTTPTransaction *transaction = self.requestIDsToTransactions[requestID]; if (!transaction) { return; } transaction.response = response; transaction.state = FLEXNetworkTransactionStateReceivingData; transaction.latency = -[transaction.startTime timeIntervalSinceDate:responseDate]; [self postUpdateNotificationForTransaction:transaction]; }); } - (void)recordDataReceivedWithRequestID:(NSString *)requestID dataLength:(int64_t)dataLength { dispatch_async(self.queue, ^{ FLEXHTTPTransaction *transaction = self.requestIDsToTransactions[requestID]; if (!transaction) { return; } transaction.receivedDataLength += dataLength; [self postUpdateNotificationForTransaction:transaction]; }); } - (void)recordLoadingFinishedWithRequestID:(NSString *)requestID responseBody:(NSData *)responseBody { NSDate *finishedDate = [NSDate date]; dispatch_async(self.queue, ^{ FLEXHTTPTransaction *transaction = self.requestIDsToTransactions[requestID]; if (!transaction) { return; } transaction.state = FLEXNetworkTransactionStateFinished; transaction.duration = -[transaction.startTime timeIntervalSinceDate:finishedDate]; BOOL shouldCache = responseBody.length > 0; if (!self.shouldCacheMediaResponses) { NSArray *ignoredMIMETypePrefixes = @[ @"audio", @"image", @"video" ]; for (NSString *ignoredPrefix in ignoredMIMETypePrefixes) { shouldCache = shouldCache && ![transaction.response.MIMEType hasPrefix:ignoredPrefix]; } } if (shouldCache) { [self.restCache setObject:responseBody forKey:requestID cost:responseBody.length]; } NSString *mimeType = transaction.response.MIMEType; if ([mimeType hasPrefix:@"image/"] && responseBody.length > 0) { // Thumbnail image previews on a separate background queue dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSInteger maxPixelDimension = UIScreen.mainScreen.scale * 32.0; transaction.thumbnail = [FLEXUtility thumbnailedImageWithMaxPixelDimension:maxPixelDimension fromImageData:responseBody ]; [self postUpdateNotificationForTransaction:transaction]; }); } else if ([mimeType isEqual:@"application/json"]) { transaction.thumbnail = FLEXResources.jsonIcon; } else if ([mimeType isEqual:@"text/plain"]){ transaction.thumbnail = FLEXResources.textPlainIcon; } else if ([mimeType isEqual:@"text/html"]) { transaction.thumbnail = FLEXResources.htmlIcon; } else if ([mimeType isEqual:@"application/x-plist"]) { transaction.thumbnail = FLEXResources.plistIcon; } else if ([mimeType isEqual:@"application/octet-stream"] || [mimeType isEqual:@"application/binary"]) { transaction.thumbnail = FLEXResources.binaryIcon; } else if ([mimeType containsString:@"javascript"]) { transaction.thumbnail = FLEXResources.jsIcon; } else if ([mimeType containsString:@"xml"]) { transaction.thumbnail = FLEXResources.xmlIcon; } else if ([mimeType hasPrefix:@"audio"]) { transaction.thumbnail = FLEXResources.audioIcon; } else if ([mimeType hasPrefix:@"video"]) { transaction.thumbnail = FLEXResources.videoIcon; } else if ([mimeType hasPrefix:@"text"]) { transaction.thumbnail = FLEXResources.textIcon; } [self postUpdateNotificationForTransaction:transaction]; }); } - (void)recordLoadingFailedWithRequestID:(NSString *)requestID error:(NSError *)error { dispatch_async(self.queue, ^{ FLEXHTTPTransaction *transaction = self.requestIDsToTransactions[requestID]; if (!transaction) { return; } transaction.state = FLEXNetworkTransactionStateFailed; transaction.duration = -[transaction.startTime timeIntervalSinceNow]; transaction.error = error; [self postUpdateNotificationForTransaction:transaction]; }); } - (void)recordMechanism:(NSString *)mechanism forRequestID:(NSString *)requestID { dispatch_async(self.queue, ^{ FLEXHTTPTransaction *transaction = self.requestIDsToTransactions[requestID]; if (!transaction) { return; } transaction.requestMechanism = mechanism; [self postUpdateNotificationForTransaction:transaction]; }); } #pragma mark - Websocket Events - (void)recordWebsocketMessageSend:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task { dispatch_async(self.queue, ^{ FLEXWebsocketTransaction *send = [FLEXWebsocketTransaction withMessage:message task:task direction:FLEXWebsocketOutgoing ]; [self.orderedWSTransactions insertObject:send atIndex:0]; [self postNewTransactionNotificationWithTransaction:send]; }); } - (void)recordWebsocketMessageSendCompletion:(NSURLSessionWebSocketMessage *)message error:(NSError *)error { dispatch_async(self.queue, ^{ FLEXWebsocketTransaction *send = [self.orderedWSTransactions flex_firstWhere:^BOOL(FLEXWebsocketTransaction *t) { return t.message == message; }]; send.error = error; send.state = error ? FLEXNetworkTransactionStateFailed : FLEXNetworkTransactionStateFinished; [self postUpdateNotificationForTransaction:send]; }); } - (void)recordWebsocketMessageReceived:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task { dispatch_async(self.queue, ^{ FLEXWebsocketTransaction *receive = [FLEXWebsocketTransaction withMessage:message task:task direction:FLEXWebsocketIncoming ]; [self.orderedWSTransactions insertObject:receive atIndex:0]; [self postNewTransactionNotificationWithTransaction:receive]; }); } #pragma mark - Firebase, Reading - (void)recordFIRQueryWillFetch:(FIRQuery *)query withTransactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = [FLEXFirebaseTransaction queryFetch:query]; self.requestIDsToTransactions[transactionID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordFIRDocumentWillFetch:(FIRDocumentReference *)document withTransactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = [FLEXFirebaseTransaction documentFetch:document]; self.requestIDsToTransactions[transactionID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordFIRQueryDidFetch:(FIRQuerySnapshot *)response error:(NSError *)error transactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = self.requestIDsToTransactions[transactionID]; if (!transaction) { return; } transaction.error = error; transaction.documents = response.documents; transaction.state = FLEXNetworkTransactionStateFinished; [self.orderedFirebaseTransactions insertObject:transaction atIndex:0]; [self postUpdateNotificationForTransaction:transaction]; }); } - (void)recordFIRDocumentDidFetch:(FIRDocumentSnapshot *)response error:(NSError *)error transactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = self.requestIDsToTransactions[transactionID]; if (!transaction) { return; } transaction.error = error; transaction.documents = response ? @[response] : @[]; transaction.state = FLEXNetworkTransactionStateFinished; [self.orderedFirebaseTransactions insertObject:transaction atIndex:0]; [self postUpdateNotificationForTransaction:transaction]; }); } #pragma mark Firebase, Writing - (void)recordFIRWillSetData:(FIRDocumentReference *)doc data:(NSDictionary *)documentData merge:(NSNumber *)yesorno mergeFields:(NSArray *)fields transactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = [FLEXFirebaseTransaction setData:doc data:documentData merge:yesorno mergeFields:fields ]; self.requestIDsToTransactions[transactionID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordFIRWillUpdateData:(FIRDocumentReference *)doc fields:(NSDictionary *)fields transactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = [FLEXFirebaseTransaction updateData:doc data:fields]; self.requestIDsToTransactions[transactionID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordFIRWillDeleteDocument:(FIRDocumentReference *)doc transactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = [FLEXFirebaseTransaction deleteDocument:doc]; self.requestIDsToTransactions[transactionID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordFIRWillAddDocument:(FIRCollectionReference *)initiator document:(FIRDocumentReference *)doc transactionID:(NSString *)transactionID { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = [FLEXFirebaseTransaction addDocument:initiator document:doc ]; self.requestIDsToTransactions[transactionID] = transaction; [self postNewTransactionNotificationWithTransaction:transaction]; }); } - (void)recordFIRDidSetData:(NSError *)error transactionID:(NSString *)transactionID { [self firebaseTransaction:transactionID didUpdate:error]; } - (void)recordFIRDidUpdateData:(NSError *)error transactionID:(NSString *)transactionID { [self firebaseTransaction:transactionID didUpdate:error]; } - (void)recordFIRDidDeleteDocument:(NSError *)error transactionID:(NSString *)transactionID { [self firebaseTransaction:transactionID didUpdate:error]; } - (void)recordFIRDidAddDocument:(NSError *)error transactionID:(NSString *)transactionID { [self firebaseTransaction:transactionID didUpdate:error]; } - (void)firebaseTransaction:(NSString *)transactionID didUpdate:(NSError *)error { dispatch_async(self.queue, ^{ FLEXFirebaseTransaction *transaction = self.requestIDsToTransactions[transactionID]; if (!transaction) { return; } transaction.error = error; transaction.state = FLEXNetworkTransactionStateFinished; [self.orderedFirebaseTransactions insertObject:transaction atIndex:0]; [self postUpdateNotificationForTransaction:transaction]; }); } #pragma mark - Notification Posting - (void)postNewTransactionNotificationWithTransaction:(FLEXNetworkTransaction *)transaction { [self notify:kFLEXNetworkRecorderNewTransactionNotification transaction:transaction]; } - (void)postUpdateNotificationForTransaction:(FLEXNetworkTransaction *)transaction { [self notify:kFLEXNetworkRecorderTransactionUpdatedNotification transaction:transaction]; } - (void)notify:(NSString *)name transaction:(FLEXNetworkTransaction *)transaction { NSDictionary *userInfo = nil; if (transaction) { userInfo = @{ kFLEXNetworkRecorderUserInfoTransactionKey : transaction }; } dispatch_async(dispatch_get_main_queue(), ^{ [NSNotificationCenter.defaultCenter postNotificationName:name object:self userInfo:userInfo]; }); } @end ================================================ FILE: Classes/Network/FLEXNetworkSettingsController.h ================================================ // // FLEXNetworkSettingsController.h // FLEXInjected // // Created by Ryan Olson on 2/20/15. // #import "FLEXTableViewController.h" @interface FLEXNetworkSettingsController : FLEXTableViewController @end ================================================ FILE: Classes/Network/FLEXNetworkSettingsController.m ================================================ // // FLEXNetworkSettingsController.m // FLEXInjected // // Created by Ryan Olson on 2/20/15. // #import "FLEXNetworkSettingsController.h" #import "FLEXNetworkObserver.h" #import "FLEXNetworkRecorder.h" #import "FLEXUtility.h" #import "FLEXTableView.h" #import "FLEXColor.h" #import "NSUserDefaults+FLEX.h" @interface FLEXNetworkSettingsController () @property (nonatomic) float cacheLimitValue; @property (nonatomic, readonly) NSString *cacheLimitCellTitle; @property (nonatomic, readonly) UISwitch *observerSwitch; @property (nonatomic, readonly) UISwitch *cacheMediaSwitch; @property (nonatomic, readonly) UISwitch *jsonViewerSwitch; @property (nonatomic, readonly) UISlider *cacheLimitSlider; @property (nonatomic) UILabel *cacheLimitLabel; @property (nonatomic) NSMutableArray *hostDenylist; @end @implementation FLEXNetworkSettingsController - (void)viewDidLoad { [super viewDidLoad]; [self disableToolbar]; self.hostDenylist = FLEXNetworkRecorder.defaultRecorder.hostDenylist.mutableCopy; NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; _observerSwitch = [UISwitch new]; _cacheMediaSwitch = [UISwitch new]; _jsonViewerSwitch = [UISwitch new]; _cacheLimitSlider = [UISlider new]; self.observerSwitch.on = FLEXNetworkObserver.enabled; [self.observerSwitch addTarget:self action:@selector(networkDebuggingToggled:) forControlEvents:UIControlEventValueChanged ]; self.cacheMediaSwitch.on = FLEXNetworkRecorder.defaultRecorder.shouldCacheMediaResponses; [self.cacheMediaSwitch addTarget:self action:@selector(cacheMediaResponsesToggled:) forControlEvents:UIControlEventValueChanged ]; self.jsonViewerSwitch.on = defaults.flex_registerDictionaryJSONViewerOnLaunch; [self.jsonViewerSwitch addTarget:self action:@selector(jsonViewerSettingToggled:) forControlEvents:UIControlEventValueChanged ]; [self.cacheLimitSlider addTarget:self action:@selector(cacheLimitAdjusted:) forControlEvents:UIControlEventValueChanged ]; UISlider *slider = self.cacheLimitSlider; self.cacheLimitValue = FLEXNetworkRecorder.defaultRecorder.responseCacheByteLimit; const NSUInteger fiftyMega = 50 * 1024 * 1024; slider.minimumValue = 0; slider.maximumValue = fiftyMega; slider.value = self.cacheLimitValue; } - (void)setCacheLimitValue:(float)cacheLimitValue { _cacheLimitValue = cacheLimitValue; self.cacheLimitLabel.text = self.cacheLimitCellTitle; [FLEXNetworkRecorder.defaultRecorder setResponseCacheByteLimit:cacheLimitValue]; } - (NSString *)cacheLimitCellTitle { NSInteger cacheLimit = self.cacheLimitValue; NSInteger limitInMB = round(cacheLimit / (1024 * 1024)); return [NSString stringWithFormat:@"Cache Limit (%@ MB)", @(limitInMB)]; } #pragma mark - Settings Actions - (void)networkDebuggingToggled:(UISwitch *)sender { FLEXNetworkObserver.enabled = sender.isOn; } - (void)cacheMediaResponsesToggled:(UISwitch *)sender { FLEXNetworkRecorder.defaultRecorder.shouldCacheMediaResponses = sender.isOn; } - (void)jsonViewerSettingToggled:(UISwitch *)sender { [NSUserDefaults.standardUserDefaults flex_toggleBoolForKey:kFLEXDefaultsRegisterJSONExplorerKey]; } - (void)cacheLimitAdjusted:(UISlider *)sender { self.cacheLimitValue = sender.value; } #pragma mark - Table View Data Source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { switch (section) { case 0: return 5; case 1: return self.hostDenylist.count; default: return 0; } } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { switch (section) { case 0: return @"General"; case 1: return @"Host Denylist"; default: return nil; } } - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { if (section == 0) { return @"By default, JSON is rendered in a webview. Turn on " "\"View JSON as a dictionary/array\" to convert JSON payloads " "to objects and view them in an object explorer. " "This setting requires a restart of the app."; } return nil; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kFLEXDefaultCell forIndexPath:indexPath ]; cell.accessoryView = nil; cell.textLabel.textColor = FLEXColor.primaryTextColor; switch (indexPath.section) { // Settings case 0: { switch (indexPath.row) { case 0: cell.textLabel.text = @"Network Debugging"; cell.accessoryView = self.observerSwitch; break; case 1: cell.textLabel.text = @"Cache Media Responses"; cell.accessoryView = self.cacheMediaSwitch; break; case 2: cell.textLabel.text = @"View JSON as a dictionary/array"; cell.accessoryView = self.jsonViewerSwitch; break; case 3: cell.textLabel.text = @"Reset Host Denylist"; cell.textLabel.textColor = tableView.tintColor; break; case 4: cell.textLabel.text = self.cacheLimitCellTitle; self.cacheLimitLabel = cell.textLabel; [self.cacheLimitSlider removeFromSuperview]; [cell.contentView addSubview:self.cacheLimitSlider]; CGRect container = cell.contentView.frame; UISlider *slider = self.cacheLimitSlider; [slider sizeToFit]; CGFloat sliderWidth = 150.f; CGFloat sliderOriginY = FLEXFloor((container.size.height - slider.frame.size.height) / 2.0); CGFloat sliderOriginX = CGRectGetMaxX(container) - sliderWidth - tableView.separatorInset.left; self.cacheLimitSlider.frame = CGRectMake( sliderOriginX, sliderOriginY, sliderWidth, slider.frame.size.height ); // Make wider, keep in middle of cell, keep to trailing edge of cell self.cacheLimitSlider.autoresizingMask = ({ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; }); break; } break; } // Denylist entries case 1: { cell.textLabel.text = self.hostDenylist[indexPath.row]; break; } default: @throw NSInternalInconsistencyException; break; } return cell; } #pragma mark - Table View Delegate - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)ip { // Can only select the "Reset Host Denylist" row return ip.section == 0 && ip.row == 2; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Reset Host Denylist"); make.message(@"You cannot undo this action. Are you sure?"); make.button(@"Reset").destructiveStyle().handler(^(NSArray *strings) { self.hostDenylist = nil; [FLEXNetworkRecorder.defaultRecorder.hostDenylist removeAllObjects]; [FLEXNetworkRecorder.defaultRecorder synchronizeDenylist]; [self.tableView deleteSections: [NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self]; } - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return indexPath.section == 1; } - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)style forRowAtIndexPath:(NSIndexPath *)indexPath { NSParameterAssert(style == UITableViewCellEditingStyleDelete); NSString *host = self.hostDenylist[indexPath.row]; [self.hostDenylist removeObjectAtIndex:indexPath.row]; [FLEXNetworkRecorder.defaultRecorder.hostDenylist removeObject:host]; [FLEXNetworkRecorder.defaultRecorder synchronizeDenylist]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } @end ================================================ FILE: Classes/Network/FLEXNetworkTransaction.h ================================================ // // FLEXNetworkTransaction.h // Flipboard // // Created by Ryan Olson on 2/8/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import "Firestore.h" typedef NS_ENUM(NSInteger, FLEXNetworkTransactionState) { FLEXNetworkTransactionStateUnstarted = -1, /// This is the default; it's usually nonsense for a request to be marked as "unstarted" FLEXNetworkTransactionStateAwaitingResponse = 0, FLEXNetworkTransactionStateReceivingData, FLEXNetworkTransactionStateFinished, FLEXNetworkTransactionStateFailed }; typedef NS_ENUM(NSUInteger, FLEXWebsocketMessageDirection) { FLEXWebsocketIncoming = 1, FLEXWebsocketOutgoing, }; /// The shared base class for all types of network transactions. /// Subclasses should implement the descriptions and details properties, and assign a thumbnail. @interface FLEXNetworkTransaction : NSObject { @protected NSString *_primaryDescription; NSString *_secondaryDescription; NSString *_tertiaryDescription; } + (instancetype)withStartTime:(NSDate *)startTime; + (NSString *)readableStringFromTransactionState:(FLEXNetworkTransactionState)state; @property (nonatomic) NSError *error; /// Subclasses can override to provide error state based on response data as well @property (nonatomic, readonly) BOOL displayAsError; @property (nonatomic, readonly) NSDate *startTime; @property (nonatomic) FLEXNetworkTransactionState state; @property (nonatomic) int64_t receivedDataLength; /// A small thumbnail to preview the type of/the response @property (nonatomic) UIImage *thumbnail; /// The most prominent line of the cell. Typically a URL endpoint or other distinguishing attribute. /// This line turns red when the transaction indicates an error. @property (nonatomic, readonly) NSString *primaryDescription; /// Something less important, such as a blob of data or the URL's domain. @property (nonatomic, readonly) NSString *secondaryDescription; /// Minor details to display at the bottom of the cell, such as a timestamp, HTTP method, or status. @property (nonatomic, readonly) NSString *tertiaryDescription; /// The string to copy when the user selects the "copy" action @property (nonatomic, readonly) NSString *copyString; /// Whether or not this request should show up when the user searches for a given string - (BOOL)matchesQuery:(NSString *)filterString; /// For internal use - (NSString *)timestampStringFromRequestDate:(NSDate *)date; @end /// The shared base class for all NSURL-API-related transactions. /// Descriptions are generated by this class using the URL provided by subclasses. @interface FLEXURLTransaction : FLEXNetworkTransaction + (instancetype)withRequest:(NSURLRequest *)request startTime:(NSDate *)startTime; @property (nonatomic, readonly) NSURLRequest *request; /// Subclasses should implement for when the transaction is complete @property (nonatomic, readonly) NSArray *details; @end @interface FLEXHTTPTransaction : FLEXURLTransaction + (instancetype)request:(NSURLRequest *)request identifier:(NSString *)requestID; @property (nonatomic, readonly) NSString *requestID; @property (nonatomic) NSURLResponse *response; @property (nonatomic, copy) NSString *requestMechanism; @property (nonatomic) NSTimeInterval latency; @property (nonatomic) NSTimeInterval duration; /// Populated lazily, nullable. Handles both normal HTTPBody data and HTTPBodyStreams. @property (nonatomic, readonly) NSData *cachedRequestBody; @end @interface FLEXWebsocketTransaction : FLEXURLTransaction + (instancetype)withMessage:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task direction:(FLEXWebsocketMessageDirection)direction API_AVAILABLE(ios(13.0)); + (instancetype)withMessage:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task direction:(FLEXWebsocketMessageDirection)direction startTime:(NSDate *)started API_AVAILABLE(ios(13.0)); //@property (nonatomic, readonly) NSURLSessionWebSocketTask *task; @property (nonatomic, readonly) NSURLSessionWebSocketMessage *message API_AVAILABLE(ios(13.0)); @property (nonatomic, readonly) FLEXWebsocketMessageDirection direction API_AVAILABLE(ios(13.0)); @property (nonatomic, readonly) int64_t dataLength API_AVAILABLE(ios(13.0)); @end typedef NS_ENUM(NSUInteger, FLEXFIRTransactionDirection) { FLEXFIRTransactionDirectionNone, FLEXFIRTransactionDirectionPush, FLEXFIRTransactionDirectionPull, }; typedef NS_ENUM(NSUInteger, FLEXFIRRequestType) { FLEXFIRRequestTypeNotFirebase, FLEXFIRRequestTypeFetchQuery, FLEXFIRRequestTypeFetchDocument, FLEXFIRRequestTypeSetData, FLEXFIRRequestTypeUpdateData, FLEXFIRRequestTypeAddDocument, FLEXFIRRequestTypeDeleteDocument, }; @interface FLEXFirebaseSetDataInfo : NSObject /// The data that was set @property (nonatomic, readonly) NSDictionary *documentData; /// \c nil if \c mergeFields is populated @property (nonatomic, readonly) NSNumber *merge; /// \c nil if \c merge is populated @property (nonatomic, readonly) NSArray *mergeFields; @end @interface FLEXFirebaseTransaction : FLEXNetworkTransaction + (instancetype)queryFetch:(FIRQuery *)initiator; + (instancetype)documentFetch:(FIRDocumentReference *)initiator; + (instancetype)setData:(FIRDocumentReference *)initiator data:(NSDictionary *)data merge:(NSNumber *)merge mergeFields:(NSArray *)mergeFields; + (instancetype)updateData:(FIRDocumentReference *)initiator data:(NSDictionary *)data; + (instancetype)addDocument:(FIRCollectionReference *)initiator document:(FIRDocumentReference *)doc; + (instancetype)deleteDocument:(FIRDocumentReference *)initiator; @property (nonatomic, readonly) FLEXFIRTransactionDirection direction; @property (nonatomic, readonly) FLEXFIRRequestType requestType; @property (nonatomic, readonly) id initiator; @property (nonatomic, readonly) FIRQuery *initiator_query; @property (nonatomic, readonly) FIRDocumentReference *initiator_doc; @property (nonatomic, readonly) FIRCollectionReference *initiator_collection; /// Only used for fetch types @property (nonatomic, copy) NSArray *documents; /// Only used for the "set data" type @property (nonatomic, readonly) FLEXFirebaseSetDataInfo *setDataInfo; /// Only used for the "update data" type @property (nonatomic, readonly) NSDictionary *updateData; /// Only used for the "add document" type @property (nonatomic, readonly) FIRDocumentReference *addedDocument; @property (nonatomic, readonly) NSString *path; //@property (nonatomic, readonly) NSString *responseString; //@property (nonatomic, readonly) NSDictionary *responseObject; @end ================================================ FILE: Classes/Network/FLEXNetworkTransaction.m ================================================ // // FLEXNetworkTransaction.m // Flipboard // // Created by Ryan Olson on 2/8/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXNetworkTransaction.h" #import "FLEXResources.h" #import "FLEXUtility.h" #import "NSDateFormatter+FLEX.h" @implementation FLEXNetworkTransaction + (NSString *)readableStringFromTransactionState:(FLEXNetworkTransactionState)state { NSString *readableString = nil; switch (state) { case FLEXNetworkTransactionStateUnstarted: readableString = @"Unstarted"; break; case FLEXNetworkTransactionStateAwaitingResponse: readableString = @"Awaiting Response"; break; case FLEXNetworkTransactionStateReceivingData: readableString = @"Receiving Data"; break; case FLEXNetworkTransactionStateFinished: readableString = @"Finished"; break; case FLEXNetworkTransactionStateFailed: readableString = @"Failed"; break; } return readableString; } + (instancetype)withStartTime:(NSDate *)startTime { FLEXNetworkTransaction *transaction = [self new]; transaction->_startTime = startTime; return transaction; } - (NSString *)timestampStringFromRequestDate:(NSDate *)date { return [NSDateFormatter flex_stringFrom:date format:FLEXDateFormatPreciseClock]; } - (void)setState:(FLEXNetworkTransactionState)transactionState { _state = transactionState; // Reset bottom description _tertiaryDescription = nil; } - (BOOL)displayAsError { return _error != nil; } - (NSString *)copyString { return nil; } - (BOOL)matchesQuery:(NSString *)filterString { return NO; } @end @interface FLEXURLTransaction () @end @implementation FLEXURLTransaction + (instancetype)withRequest:(NSURLRequest *)request startTime:(NSDate *)startTime { FLEXURLTransaction *transaction = [self withStartTime:startTime]; transaction->_request = request; return transaction; } - (NSString *)primaryDescription { if (!_primaryDescription) { NSString *name = self.request.URL.lastPathComponent; if (!name.length) { name = @"/"; } if (_request.URL.query) { name = [name stringByAppendingFormat:@"?%@", self.request.URL.query]; } _primaryDescription = name; } return _primaryDescription; } - (NSString *)secondaryDescription { if (!_secondaryDescription) { NSMutableArray *mutablePathComponents = self.request.URL.pathComponents.mutableCopy; if (mutablePathComponents.count > 0) { [mutablePathComponents removeLastObject]; } NSString *path = self.request.URL.host; for (NSString *pathComponent in mutablePathComponents) { path = [path stringByAppendingPathComponent:pathComponent]; } _secondaryDescription = path; } return _secondaryDescription; } - (NSString *)tertiaryDescription { if (!_tertiaryDescription) { NSMutableArray *detailComponents = [NSMutableArray new]; NSString *timestamp = [self timestampStringFromRequestDate:self.startTime]; if (timestamp.length > 0) { [detailComponents addObject:timestamp]; } // Omit method for GET (assumed as default) NSString *httpMethod = self.request.HTTPMethod; if (httpMethod.length > 0) { [detailComponents addObject:httpMethod]; } if (self.state == FLEXNetworkTransactionStateFinished || self.state == FLEXNetworkTransactionStateFailed) { [detailComponents addObjectsFromArray:self.details]; } else { // Unstarted, Awaiting Response, Receiving Data, etc. NSString *state = [self.class readableStringFromTransactionState:self.state]; [detailComponents addObject:state]; } _tertiaryDescription = [detailComponents componentsJoinedByString:@" ・ "]; } return _tertiaryDescription; } - (NSString *)copyString { return self.request.URL.absoluteString; } - (BOOL)matchesQuery:(NSString *)filterString { return [self.request.URL.absoluteString localizedCaseInsensitiveContainsString:filterString]; } @end @interface FLEXHTTPTransaction () @property (nonatomic, readwrite) NSData *cachedRequestBody; @end @implementation FLEXHTTPTransaction + (instancetype)request:(NSURLRequest *)request identifier:(NSString *)requestID { FLEXHTTPTransaction *httpt = [self withRequest:request startTime:NSDate.date]; httpt->_requestID = requestID; return httpt; } - (NSString *)description { NSString *description = [super description]; description = [description stringByAppendingFormat:@" id = %@;", self.requestID]; description = [description stringByAppendingFormat:@" url = %@;", self.request.URL]; description = [description stringByAppendingFormat:@" duration = %f;", self.duration]; description = [description stringByAppendingFormat:@" receivedDataLength = %lld", self.receivedDataLength]; return description; } - (NSData *)cachedRequestBody { if (!_cachedRequestBody) { if (self.request.HTTPBody != nil) { _cachedRequestBody = self.request.HTTPBody; } else if ([self.request.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { NSInputStream *bodyStream = [self.request.HTTPBodyStream copy]; const NSUInteger bufferSize = 1024; uint8_t buffer[bufferSize]; NSMutableData *data = [NSMutableData new]; [bodyStream open]; NSInteger readBytes = 0; do { readBytes = [bodyStream read:buffer maxLength:bufferSize]; [data appendBytes:buffer length:readBytes]; } while (readBytes > 0); [bodyStream close]; _cachedRequestBody = data; } } return _cachedRequestBody; } - (NSArray *)detailString { NSMutableArray *detailComponents = [NSMutableArray new]; NSString *statusCodeString = [FLEXUtility statusCodeStringFromURLResponse:self.response]; if (statusCodeString.length > 0) { [detailComponents addObject:statusCodeString]; } if (self.receivedDataLength > 0) { NSString *responseSize = [NSByteCountFormatter stringFromByteCount:self.receivedDataLength countStyle:NSByteCountFormatterCountStyleBinary ]; [detailComponents addObject:responseSize]; } NSString *totalDuration = [FLEXUtility stringFromRequestDuration:self.duration]; NSString *latency = [FLEXUtility stringFromRequestDuration:self.latency]; NSString *duration = [NSString stringWithFormat:@"%@ (%@)", totalDuration, latency]; [detailComponents addObject:duration]; return detailComponents; } - (BOOL)displayAsError { return [FLEXUtility isErrorStatusCodeFromURLResponse:self.response] || super.displayAsError; } @end @implementation FLEXWebsocketTransaction + (instancetype)withMessage:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task direction:(FLEXWebsocketMessageDirection)direction startTime:(NSDate *)started { FLEXWebsocketTransaction *wst = [self withRequest:task.originalRequest startTime:started]; wst->_message = message; wst->_direction = direction; // Populate receivedDataLength if (direction == FLEXWebsocketIncoming) { wst.receivedDataLength = wst.dataLength; wst.state = FLEXNetworkTransactionStateFinished; } // Populate thumbnail image if (message.type == NSURLSessionWebSocketMessageTypeData) { wst.thumbnail = FLEXResources.binaryIcon; } else { wst.thumbnail = FLEXResources.textIcon; } return wst; } + (instancetype)withMessage:(NSURLSessionWebSocketMessage *)message task:(NSURLSessionWebSocketTask *)task direction:(FLEXWebsocketMessageDirection)direction { return [self withMessage:message task:task direction:direction startTime:NSDate.date]; } - (NSArray *)details API_AVAILABLE(ios(13.0)) { return @[ self.direction == FLEXWebsocketOutgoing ? @"SENT →" : @"→ RECEIVED", [NSByteCountFormatter stringFromByteCount:self.dataLength countStyle:NSByteCountFormatterCountStyleBinary ] ]; } - (int64_t)dataLength { if (self.message) { if (self.message.type == NSURLSessionWebSocketMessageTypeString) { return self.message.string.length; } return self.message.data.length; } return 0; } @end ================================================ FILE: Classes/Network/FLEXNetworkTransactionCell.h ================================================ // // FLEXNetworkTransactionCell.h // Flipboard // // Created by Ryan Olson on 2/8/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXNetworkTransaction; @interface FLEXNetworkTransactionCell : UITableViewCell @property (nonatomic) FLEXNetworkTransaction *transaction; @property (nonatomic, readonly, class) NSString *reuseID; @property (nonatomic, readonly, class) CGFloat preferredCellHeight; @end ================================================ FILE: Classes/Network/FLEXNetworkTransactionCell.m ================================================ // // FLEXNetworkTransactionCell.m // Flipboard // // Created by Ryan Olson on 2/8/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXNetworkTransactionCell.h" #import "FLEXNetworkTransaction.h" #import "FLEXUtility.h" #import "FLEXResources.h" NSString * const kFLEXNetworkTransactionCellIdentifier = @"kFLEXNetworkTransactionCellIdentifier"; @interface FLEXNetworkTransactionCell () @property (nonatomic) UIImageView *thumbnailImageView; @property (nonatomic) UILabel *nameLabel; @property (nonatomic) UILabel *pathLabel; @property (nonatomic) UILabel *transactionDetailsLabel; @end @implementation FLEXNetworkTransactionCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; self.nameLabel = [UILabel new]; self.nameLabel.font = UIFont.flex_defaultTableCellFont; [self.contentView addSubview:self.nameLabel]; self.pathLabel = [UILabel new]; self.pathLabel.font = UIFont.flex_defaultTableCellFont; self.pathLabel.textColor = [UIColor colorWithWhite:0.4 alpha:1.0]; self.pathLabel.lineBreakMode = NSLineBreakByTruncatingMiddle; [self.contentView addSubview:self.pathLabel]; self.thumbnailImageView = [UIImageView new]; self.thumbnailImageView.layer.borderColor = UIColor.blackColor.CGColor; self.thumbnailImageView.layer.borderWidth = 1.0; self.thumbnailImageView.contentMode = UIViewContentModeScaleAspectFit; [self.contentView addSubview:self.thumbnailImageView]; self.transactionDetailsLabel = [UILabel new]; self.transactionDetailsLabel.font = [UIFont systemFontOfSize:10.0]; self.transactionDetailsLabel.textColor = [UIColor colorWithWhite:0.65 alpha:1.0]; [self.contentView addSubview:self.transactionDetailsLabel]; } return self; } - (void)setTransaction:(FLEXNetworkTransaction *)transaction { if (_transaction != transaction) { _transaction = transaction; [self setNeedsLayout]; } } - (void)layoutSubviews { [super layoutSubviews]; const CGFloat kVerticalPadding = 8.0; const CGFloat kLeftPadding = 10.0; const CGFloat kImageDimension = 32.0; CGFloat thumbnailOriginY = round((self.contentView.bounds.size.height - kImageDimension) / 2.0); self.thumbnailImageView.frame = CGRectMake(kLeftPadding, thumbnailOriginY, kImageDimension, kImageDimension); self.thumbnailImageView.image = self.transaction.thumbnail; CGFloat textOriginX = CGRectGetMaxX(self.thumbnailImageView.frame) + kLeftPadding; CGFloat availableTextWidth = self.contentView.bounds.size.width - textOriginX; self.nameLabel.text = [self nameLabelText]; CGSize nameLabelPreferredSize = [self.nameLabel sizeThatFits:CGSizeMake(availableTextWidth, CGFLOAT_MAX)]; self.nameLabel.frame = CGRectMake(textOriginX, kVerticalPadding, availableTextWidth, nameLabelPreferredSize.height); self.nameLabel.textColor = self.transaction.displayAsError ? UIColor.redColor : FLEXColor.primaryTextColor; self.pathLabel.text = [self pathLabelText]; CGSize pathLabelPreferredSize = [self.pathLabel sizeThatFits:CGSizeMake(availableTextWidth, CGFLOAT_MAX)]; CGFloat pathLabelOriginY = ceil((self.contentView.bounds.size.height - pathLabelPreferredSize.height) / 2.0); self.pathLabel.frame = CGRectMake(textOriginX, pathLabelOriginY, availableTextWidth, pathLabelPreferredSize.height); self.transactionDetailsLabel.text = [self transactionDetailsLabelText]; CGSize transactionLabelPreferredSize = [self.transactionDetailsLabel sizeThatFits:CGSizeMake(availableTextWidth, CGFLOAT_MAX)]; CGFloat transactionDetailsOriginX = textOriginX; CGFloat transactionDetailsLabelOriginY = CGRectGetMaxY(self.contentView.bounds) - kVerticalPadding - transactionLabelPreferredSize.height; CGFloat transactionDetailsLabelWidth = self.contentView.bounds.size.width - transactionDetailsOriginX; self.transactionDetailsLabel.frame = CGRectMake(transactionDetailsOriginX, transactionDetailsLabelOriginY, transactionDetailsLabelWidth, transactionLabelPreferredSize.height); } - (NSString *)nameLabelText { return self.transaction.primaryDescription; } - (NSString *)pathLabelText { return self.transaction.secondaryDescription; } - (NSString *)transactionDetailsLabelText { return self.transaction.tertiaryDescription; } + (CGFloat)preferredCellHeight { return 65.0; } + (NSString *)reuseID { return kFLEXNetworkTransactionCellIdentifier; } @end ================================================ FILE: Classes/Network/Firestore.h ================================================ // // Firestore.h // Pods // // Created by Tanner Bennett on 10/13/21. // #import NS_ASSUME_NONNULL_BEGIN #pragma mark - Forward Declarations @class FIRQuery; @class FIRQuerySnapshot; @class FIRDocumentReference; @class FIRDocumentSnapshot; @class FIRQueryDocumentSnapshot; @class FIRCollectionReference; @class FIRFirestore; @protocol FIRListenerRegistration; #define cFIRQuery objc_getClass("FIRQuery") #define cFIRCollectionReference objc_getClass("FIRCollectionReference") #define cFIRDocumentReference objc_getClass("FIRDocumentReference") typedef void (^FIRDocumentSnapshotBlock)(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error); typedef void (^FIRQuerySnapshotBlock)(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error); typedef NS_ENUM(NSUInteger, FIRFirestoreSource) { FIRFirestoreSourceDefault, FIRFirestoreSourceServer, FIRFirestoreSourceCache } NS_SWIFT_NAME(FirestoreSource); #pragma mark - Query @interface FIRQuery : NSObject - (id)init __attribute__((unavailable())); @property(nonatomic, readonly) FIRFirestore *firestore; @property(nonatomic, readonly) void *query; - (void)getDocumentsWithCompletion:(FIRQuerySnapshotBlock)completion NS_SWIFT_NAME(getDocuments(completion:)); - (void)getDocumentsWithSource:(FIRFirestoreSource)source completion:(FIRQuerySnapshotBlock)completion NS_SWIFT_NAME(getDocuments(source:completion:)); @end typedef void (^FIRDocumentSnapshotBlock)(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error); #pragma mark - DocumentReference NS_SWIFT_NAME(DocumentReference) @interface FIRDocumentReference : NSObject - (instancetype)init __attribute__((unavailable)); @property(nonatomic, readonly) NSString *documentID; @property(nonatomic, readonly) FIRCollectionReference *parent; @property(nonatomic, readonly) FIRFirestore *firestore; @property(nonatomic, readonly) NSString *path; - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath NS_SWIFT_NAME(collection(_:)); #pragma mark Writing Data - (void)setData:(NSDictionary *)documentData; - (void)setData:(NSDictionary *)documentData merge:(BOOL)merge; - (void)setData:(NSDictionary *)documentData mergeFields:(NSArray *)mergeFields; - (void)setData:(NSDictionary *)documentData completion:(nullable void (^)(NSError *_Nullable error))completion; - (void)setData:(NSDictionary *)documentData merge:(BOOL)merge completion:(nullable void (^)(NSError *_Nullable error))completion; - (void)setData:(NSDictionary *)documentData mergeFields:(NSArray *)mergeFields completion:(nullable void (^)(NSError *_Nullable error))completion; - (void)updateData:(NSDictionary *)fields; - (void)updateData:(NSDictionary *)fields completion:(nullable void (^)(NSError *_Nullable error))completion; - (void)deleteDocument NS_SWIFT_NAME(delete()); - (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error))completion NS_SWIFT_NAME(delete(completion:)); #pragma mark Retrieving Data - (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion NS_SWIFT_NAME(getDocument(completion:)); - (void)getDocumentWithSource:(FIRFirestoreSource)source completion:(FIRDocumentSnapshotBlock)completion NS_SWIFT_NAME(getDocument(source:completion:)); - (id)addSnapshotListener:(FIRDocumentSnapshotBlock)listener NS_SWIFT_NAME(addSnapshotListener(_:)); - (id)addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges listener:(FIRDocumentSnapshotBlock)listener NS_SWIFT_NAME(addSnapshotListener(includeMetadataChanges:listener:)); @end #pragma mark - CollectionReference NS_SWIFT_NAME(CollectionReference) @interface FIRCollectionReference : FIRQuery - (id)init __attribute__((unavailable())); @property(nonatomic, readonly) NSString *collectionID; @property(nonatomic, nullable, readonly) FIRDocumentReference *parent; @property(nonatomic, readonly) NSString *path; - (FIRDocumentReference *)documentWithAutoID NS_SWIFT_NAME(document()); - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath NS_SWIFT_NAME(document(_:)); - (FIRDocumentReference *)addDocumentWithData:(NSDictionary *)data NS_SWIFT_NAME(addDocument(data:)); - (FIRDocumentReference *)addDocumentWithData:(NSDictionary *)data completion:(nullable void (^)(NSError *_Nullable error))completion NS_SWIFT_NAME(addDocument(data:completion:)); @end #pragma mark - QuerySnapshot NS_SWIFT_NAME(QuerySnapshot) @interface FIRQuerySnapshot : NSObject - (id)init __attribute__((unavailable())); @property(nonatomic, readonly) FIRQuery *query; @property(nonatomic, readonly, getter=isEmpty) BOOL empty; @property(nonatomic, readonly) NSInteger count; @property(nonatomic, readonly) NSArray *documents; @end #pragma mark - DocumentSnapshot NS_SWIFT_NAME(DocumentSnapshot) @interface FIRDocumentSnapshot : NSObject - (instancetype)init __attribute__((unavailable())); @property(nonatomic, readonly) BOOL exists; @property(nonatomic, readonly) FIRDocumentReference *reference; @property(nonatomic, copy, readonly) NSString *documentID; @property(nonatomic, readonly, nullable) NSDictionary *data; - (nullable id)valueForField:(id)field NS_SWIFT_NAME(get(_:)); - (nullable id)objectForKeyedSubscript:(id)key; @end #pragma mark - QueryDocumentSnapshot NS_SWIFT_NAME(QueryDocumentSnapshot) @interface FIRQueryDocumentSnapshot : FIRDocumentSnapshot - (instancetype)init __attribute__((unavailable())); @property(nonatomic, readonly) NSDictionary *data; @end NS_ASSUME_NONNULL_END #if defined(__clang__) #if __has_feature(objc_arc) #define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained #define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed)) #define _LOGOS_SELF_CONST const #define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained)) #else #define _LOGOS_SELF_TYPE_NORMAL #define _LOGOS_SELF_TYPE_INIT #define _LOGOS_SELF_CONST #define _LOGOS_RETURN_RETAINED #endif #else #define _LOGOS_SELF_TYPE_NORMAL #define _LOGOS_SELF_TYPE_INIT #define _LOGOS_SELF_CONST #define _LOGOS_RETURN_RETAINED #endif ================================================ FILE: Classes/Network/OSCache/LICENSE.md ================================================ OSCache version 1.2.1, Decembet 18th, 2015 Copyright (C) 2014 Charcoal Design This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ================================================ FILE: Classes/Network/OSCache/OSCache.h ================================================ // // OSCache.h // // Version 1.2.1 // // Created by Nick Lockwood on 01/01/2014. // Copyright (C) 2014 Charcoal Design // // Distributed under the permissive zlib License // Get the latest version from here: // // https://github.com/nicklockwood/OSCache // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // #import NS_ASSUME_NONNULL_BEGIN @interface OSCache : NSCache @property (nonatomic, readonly) NSUInteger count; @property (nonatomic, readonly) NSUInteger totalCost; - (id)objectForKeyedSubscript:(KeyType )key; - (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType )key; - (void)enumerateKeysAndObjectsUsingBlock:(void (^)(KeyType key, ObjectType obj, BOOL *stop))block; @end @protocol OSCacheDelegate @optional - (BOOL)cache:(OSCache *)cache shouldEvictObject:(id)entry; - (void)cache:(OSCache *)cache willEvictObject:(id)entry; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Network/OSCache/OSCache.m ================================================ // // OSCache.m // // Version 1.2.1 // // Created by Nick Lockwood on 01/01/2014. // Copyright (C) 2014 Charcoal Design // // Distributed under the permissive zlib License // Get the latest version from here: // // https://github.com/nicklockwood/OSCache // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // #import "OSCache.h" #import #if TARGET_OS_IPHONE #import #endif #import #if !__has_feature(objc_arc) #error This class requires automatic reference counting #endif #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" #pragma GCC diagnostic ignored "-Wdirect-ivar-access" #pragma GCC diagnostic ignored "-Wgnu" @interface OSCacheEntry : NSObject @property (nonatomic, strong) NSObject *object; @property (nonatomic, assign) NSUInteger cost; @property (nonatomic, assign) NSInteger sequenceNumber; @end @implementation OSCacheEntry @end @interface OSCache_Private : NSObject @property (nonatomic, unsafe_unretained) id delegate; @property (nonatomic, assign) NSUInteger countLimit; @property (nonatomic, assign) NSUInteger totalCostLimit; @property (nonatomic, copy) NSString *name; @property (nonatomic, strong) NSMutableDictionary *cache; @property (nonatomic, assign) NSUInteger totalCost; @property (nonatomic, assign) NSInteger sequenceNumber; @end @implementation OSCache_Private { BOOL _delegateRespondsToWillEvictObject; BOOL _delegateRespondsToShouldEvictObject; BOOL _currentlyCleaning; NSMutableArray *_entryPool; NSLock *_lock; } - (instancetype)init { if ((self = [super init])) { //create storage _cache = [[NSMutableDictionary alloc] init]; _entryPool = [[NSMutableArray alloc] init]; _lock = [[NSLock alloc] init]; _totalCost = 0; #if TARGET_OS_IPHONE //clean up in the event of a memory warning [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanUpAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; #endif } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)setDelegate:(id)delegate { _delegate = delegate; _delegateRespondsToShouldEvictObject = [delegate respondsToSelector:@selector(cache:shouldEvictObject:)]; _delegateRespondsToWillEvictObject = [delegate respondsToSelector:@selector(cache:willEvictObject:)]; } - (void)setCountLimit:(NSUInteger)countLimit { [_lock lock]; _countLimit = countLimit; [_lock unlock]; [self cleanUp:NO]; } - (void)setTotalCostLimit:(NSUInteger)totalCostLimit { [_lock lock]; _totalCostLimit = totalCostLimit; [_lock unlock]; [self cleanUp:NO]; } - (NSUInteger)count { return [_cache count]; } - (void)cleanUp:(BOOL)keepEntries { [_lock lock]; NSUInteger maxCount = _countLimit ?: INT_MAX; NSUInteger maxCost = _totalCostLimit ?: INT_MAX; NSUInteger totalCount = _cache.count; NSMutableArray *keys = [_cache.allKeys mutableCopy]; while (totalCount > maxCount || _totalCost > maxCost) { NSInteger lowestSequenceNumber = INT_MAX; OSCacheEntry *lowestEntry = nil; id lowestKey = nil; //remove oldest items until within limit for (id key in keys) { OSCacheEntry *entry = _cache[key]; if (entry.sequenceNumber < lowestSequenceNumber) { lowestSequenceNumber = entry.sequenceNumber; lowestEntry = entry; lowestKey = key; } } if (lowestKey) { [keys removeObject:lowestKey]; if (!_delegateRespondsToShouldEvictObject || [_delegate cache:(OSCache *)self shouldEvictObject:lowestEntry.object]) { if (_delegateRespondsToWillEvictObject) { _currentlyCleaning = YES; [self.delegate cache:(OSCache *)self willEvictObject:lowestEntry.object]; _currentlyCleaning = NO; } [_cache removeObjectForKey:lowestKey]; _totalCost -= lowestEntry.cost; totalCount --; if (keepEntries) { [_entryPool addObject:lowestEntry]; lowestEntry.object = nil; } } } } [_lock unlock]; } - (void)cleanUpAllObjects { [_lock lock]; if (_delegateRespondsToShouldEvictObject || _delegateRespondsToWillEvictObject) { NSArray *keys = [_cache allKeys]; if (_delegateRespondsToShouldEvictObject) { //sort, oldest first (in case we want to use that information in our eviction test) keys = [keys sortedArrayUsingComparator:^NSComparisonResult(id key1, id key2) { OSCacheEntry *entry1 = self->_cache[key1]; OSCacheEntry *entry2 = self->_cache[key2]; return (NSComparisonResult)MIN(1, MAX(-1, entry1.sequenceNumber - entry2.sequenceNumber)); }]; } //remove all items individually for (id key in keys) { OSCacheEntry *entry = _cache[key]; if (!_delegateRespondsToShouldEvictObject || [_delegate cache:(OSCache *)self shouldEvictObject:entry.object]) { if (_delegateRespondsToWillEvictObject) { _currentlyCleaning = YES; [_delegate cache:(OSCache *)self willEvictObject:entry.object]; _currentlyCleaning = NO; } [_cache removeObjectForKey:key]; _totalCost -= entry.cost; } } } else { _totalCost = 0; [_cache removeAllObjects]; _sequenceNumber = 0; } [_lock unlock]; } - (void)resequence { //sort, oldest first NSArray *entries = [[_cache allValues] sortedArrayUsingComparator:^NSComparisonResult(OSCacheEntry *entry1, OSCacheEntry *entry2) { return (NSComparisonResult)MIN(1, MAX(-1, entry1.sequenceNumber - entry2.sequenceNumber)); }]; //renumber items NSInteger index = 0; for (OSCacheEntry *entry in entries) { entry.sequenceNumber = index++; } } - (id)objectForKey:(id)key { [_lock lock]; OSCacheEntry *entry = _cache[key]; entry.sequenceNumber = _sequenceNumber++; if (_sequenceNumber < 0) { [self resequence]; } id object = entry.object; [_lock unlock]; return object; } - (id)objectForKeyedSubscript:(id)key { return [self objectForKey:key]; } - (void)setObject:(id)obj forKey:(id)key { [self setObject:obj forKey:key cost:0]; } - (void)setObject:(id)obj forKeyedSubscript:(id)key { [self setObject:obj forKey:key cost:0]; } - (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g { if (!obj) { [self removeObjectForKey:key]; return; } NSAssert(!_currentlyCleaning, @"It is not possible to modify cache from within the implementation of this delegate method."); [_lock lock]; _totalCost -= [_cache[key] cost]; _totalCost += g; OSCacheEntry *entry = _cache[key]; if (!entry) { entry = [[OSCacheEntry alloc] init]; _cache[key] = entry; } entry.object = obj; entry.cost = g; entry.sequenceNumber = _sequenceNumber++; if (_sequenceNumber < 0) { [self resequence]; } [_lock unlock]; [self cleanUp:YES]; } - (void)removeObjectForKey:(id)key { NSAssert(!_currentlyCleaning, @"It is not possible to modify cache from within the implementation of this delegate method."); [_lock lock]; OSCacheEntry *entry = _cache[key]; if (entry) { _totalCost -= entry.cost; entry.object = nil; [_entryPool addObject:entry]; [_cache removeObjectForKey:key]; } [_lock unlock]; } - (void)removeAllObjects { NSAssert(!_currentlyCleaning, @"It is not possible to modify cache from within the implementation of this delegate method."); [_lock lock]; _totalCost = 0; _sequenceNumber = 0; for (OSCacheEntry *entry in _cache.allValues) { entry.object = nil; [_entryPool addObject:entry]; } [_cache removeAllObjects]; [_lock unlock]; } - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { [_lock lock]; NSUInteger count = [_cache countByEnumeratingWithState:state objects:buffer count:len]; [_lock unlock]; return count; } - (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block { if (block) { [_lock lock]; [_cache enumerateKeysAndObjectsUsingBlock:^(id key, OSCacheEntry *entry, BOOL *stop) { block(key, entry.object, stop); }]; [_lock unlock]; } } //handle unimplemented methods - (BOOL)isKindOfClass:(Class)aClass { //pretend that we're an NSCache if anyone asks if (aClass == [OSCache class] || aClass == [NSCache class]) { return YES; } return [super isKindOfClass:aClass]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { //protect against calls to unimplemented NSCache methods NSMethodSignature *signature = [super methodSignatureForSelector:selector]; if (!signature) { signature = [NSCache instanceMethodSignatureForSelector:selector]; } return signature; } - (void)forwardInvocation:(NSInvocation *)invocation { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" [invocation invokeWithTarget:nil]; #pragma clang diagnostic pop } @end @implementation OSCache + (instancetype)allocWithZone:(struct _NSZone *)zone { return (OSCache *)[OSCache_Private allocWithZone:zone]; } - (id)objectForKeyedSubscript:(__unused id)key { return nil; } - (void)setObject:(__unused id)obj forKeyedSubscript:(__unused id)key {} - (void)enumerateKeysAndObjectsUsingBlock:(__unused void (^)(id, id, BOOL *))block { } - (NSUInteger)countByEnumeratingWithState:(__unused NSFastEnumerationState *)state objects:(__unused __unsafe_unretained id [])buffer count:(__unused NSUInteger)len { return 0; } @end ================================================ FILE: Classes/Network/PonyDebugger/FLEXNetworkObserver.h ================================================ // // FLEXNetworkObserver.h // Derived from: // // PDAFNetworkDomainController.h // PonyDebugger // // Created by Mike Lewis on 2/27/12. // // Licensed to Square, Inc. under one or more contributor license agreements. // See the LICENSE file distributed with this work for the terms under // which Square, Inc. licenses this file to you. // #import FOUNDATION_EXTERN NSString *const kFLEXNetworkObserverEnabledStateChangedNotification; /// This class swizzles NSURLConnection and NSURLSession delegate methods to observe events in the URL loading system. /// High level network events are sent to the default FLEXNetworkRecorder instance which maintains the request history and caches response bodies. @interface FLEXNetworkObserver : NSObject /// Swizzling occurs when the observer is enabled for the first time. /// This reduces the impact of FLEX if network debugging is not desired. /// NOTE: this setting persists between launches of the app. @property (nonatomic, class, getter=isEnabled) BOOL enabled; @end ================================================ FILE: Classes/Network/PonyDebugger/FLEXNetworkObserver.m ================================================ // // FLEXNetworkObserver.m // Derived from: // // PDAFNetworkDomainController.m // PonyDebugger // // Created by Mike Lewis on 2/27/12. // // Licensed to Square, Inc. under one or more contributor license agreements. // See the LICENSE file distributed with this work for the terms under // which Square, Inc. licenses this file to you. // // Heavily modified and added to by Tanner Bennett and various other contributors. // git blame details these modifications. // #import "FLEXNetworkObserver.h" #import "FLEXNetworkRecorder.h" #import "FLEXUtility.h" #import "NSUserDefaults+FLEX.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXMethod.h" #import "Firestore.h" #import #import #import #import #include NSString *const kFLEXNetworkObserverEnabledStateChangedNotification = @"kFLEXNetworkObserverEnabledStateChangedNotification"; typedef void (^NSURLSessionAsyncCompletion)(id fileURLOrData, NSURLResponse *response, NSError *error); typedef NSURLSessionTask * (^NSURLSessionNewTaskMethod)(NSURLSession *, id, NSURLSessionAsyncCompletion); @interface FLEXInternalRequestState : NSObject @property (nonatomic, copy) NSURLRequest *request; @property (nonatomic) NSMutableData *dataAccumulator; @end @implementation FLEXInternalRequestState @end @interface FLEXNetworkObserver (NSURLConnectionHelpers) - (void)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response delegate:(id)delegate; - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response delegate:(id)delegate; - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data delegate:(id)delegate; - (void)connectionDidFinishLoading:(NSURLConnection *)connection delegate:(id)delegate; - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error delegate:(id)delegate; - (void)connectionWillCancel:(NSURLConnection *)connection; @end @interface FLEXNetworkObserver (NSURLSessionTaskHelpers) - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler delegate:(id)delegate; - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler delegate:(id)delegate; - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data delegate:(id)delegate; - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id)delegate; - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error delegate:(id)delegate; - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite delegate:(id)delegate; - (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data delegate:(id)delegate; - (void)URLSessionTaskWillResume:(NSURLSessionTask *)task; - (void)websocketTask:(NSURLSessionWebSocketTask *)task sendMessagage:(NSURLSessionWebSocketMessage *)message API_AVAILABLE(ios(13.0)); - (void)websocketTaskMessageSendCompletion:(NSURLSessionWebSocketMessage *)message error:(NSError *)error API_AVAILABLE(ios(13.0)); - (void)websocketTask:(NSURLSessionWebSocketTask *)task receiveMessagage:(NSURLSessionWebSocketMessage *)message error:(NSError *)error API_AVAILABLE(ios(13.0)); @end @interface FLEXNetworkObserver () @property (nonatomic) NSMutableDictionary *requestStatesForRequestIDs; @property (nonatomic) dispatch_queue_t queue; @end @implementation FLEXNetworkObserver #pragma mark - Public Methods + (void)setEnabled:(BOOL)enabled { BOOL previouslyEnabled = [self isEnabled]; NSUserDefaults.standardUserDefaults.flex_networkObserverEnabled = enabled; if (enabled) { // Inject if needed. This injection is protected with a dispatch_once, so we're ok calling it multiple times. // By doing the injection lazily, we keep the impact of the tool lower when this feature isn't enabled. [self setNetworkMonitorHooks]; } if (previouslyEnabled != enabled) { [NSNotificationCenter.defaultCenter postNotificationName:kFLEXNetworkObserverEnabledStateChangedNotification object:self]; } } + (BOOL)isEnabled { return NSUserDefaults.standardUserDefaults.flex_networkObserverEnabled; } + (void)load { // We don't want to do the swizzling from +load because not all the // delegate classes we want to hook may be loaded at this point. // However, Firebase classes will definitely be loaded by now, // so we can definitely hook those sooner if need be. dispatch_async(dispatch_get_main_queue(), ^{ if ([self isEnabled]) { [self setNetworkMonitorHooks]; } }); } #pragma mark - Statics + (instancetype)sharedObserver { static FLEXNetworkObserver *sharedObserver = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedObserver = [self new]; }); return sharedObserver; } + (NSString *)nextRequestID { return NSUUID.UUID.UUIDString; } #pragma mark Delegate Injection Convenience Methods /// All swizzled delegate methods should make use of this guard. /// This will prevent duplicated sniffing when the original implementation calls up to a superclass /// implementation which we've also swizzled. The superclass implementation (and implementations in /// classes above that) will be executed without interference if called from the original implementation. + (void)sniffWithoutDuplicationForObject:(NSObject *)object selector:(SEL)selector sniffingBlock:(void (^)(void))sniffingBlock originalImplementationBlock:(void (^)(void))originalImplementationBlock { // If we don't have an object to detect nested calls on, just run the original implementation and bail. // This case can happen if someone besides the URL loading system calls the delegate methods directly. // See https://github.com/Flipboard/FLEX/issues/61 for an example. if (!object) { originalImplementationBlock(); return; } const void *key = selector; // Don't run the sniffing block if we're inside a nested call if (!objc_getAssociatedObject(object, key)) { sniffingBlock(); } // Mark that we're calling through to the original so we can detect nested calls objc_setAssociatedObject(object, key, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC); originalImplementationBlock(); objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - Hooking static void (*_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$)( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, FIRDocumentSnapshotBlock); static void _logos_method$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, FIRDocumentSnapshotBlock); static void (*_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$)( _LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST, SEL, FIRQuerySnapshotBlock); static void _logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$( _LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST, SEL, FIRQuerySnapshotBlock); static void (*_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$)( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, BOOL, void (^)(NSError *)); static void (*_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$)( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, NSArray *, void (^)(NSError *)); static void (*_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$)( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, void (^)(NSError *)); static void (*_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$)( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, void (^)(NSError *)); static void _logos_register_hook(Class _class, SEL _cmd, IMP _new, IMP *_old) { unsigned int _count, _i; Class _searchedClass = _class; Method *_methods; while (_searchedClass) { _methods = class_copyMethodList(_searchedClass, &_count); for (_i = 0; _i < _count; _i++) { if (method_getName(_methods[_i]) == _cmd) { if (_class == _searchedClass) { *_old = method_getImplementation(_methods[_i]); *_old = class_replaceMethod(_class, _cmd, _new, method_getTypeEncoding(_methods[_i])); } else { class_addMethod(_class, _cmd, _new, method_getTypeEncoding(_methods[_i])); } free(_methods); return; } } free(_methods); _searchedClass = class_getSuperclass(_searchedClass); } } static Class _logos_superclass$_ungrouped$FIRDocumentReference; static void (*_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$)( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST, SEL, FIRDocumentSnapshotBlock); static Class _logos_superclass$_ungrouped$FIRQuery; static void (*_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$)( _LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST, SEL, FIRQuerySnapshotBlock); static Class _logos_superclass$_ungrouped$FIRCollectionReference; static FIRDocumentReference * (*_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$)( _LOGOS_SELF_TYPE_NORMAL FIRCollectionReference * _LOGOS_SELF_CONST, SEL, NSDictionary *, void (^)(NSError *error)); #pragma mark Firebase, Reading Data static void _logos_method$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST self, SEL _cmd, FIRDocumentSnapshotBlock completion) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRDocumentWillFetch:self withTransactionID:requestID]; // Hook callback FIRDocumentSnapshotBlock orig = completion; completion = ^(FIRDocumentSnapshot *document, NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRDocumentDidFetch:document error:error transactionID:requestID]; if (orig != nil) { orig(document, error); } }; // Forward invocation (_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$ ? _logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(getDocumentWithCompletion:)))(self, _cmd, completion); } static void _logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$( _LOGOS_SELF_TYPE_NORMAL FIRQuery * _LOGOS_SELF_CONST self, SEL _cmd, FIRQuerySnapshotBlock completion) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRQueryWillFetch:self withTransactionID:requestID]; // Hook callback FIRQuerySnapshotBlock orig = completion; completion = ^(FIRQuerySnapshot *query, NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRQueryDidFetch:query error:error transactionID:requestID]; if (orig != nil) { orig(query, error); } }; // Forward invocation (_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$ ? _logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$ : (__typeof__(_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRQuery, @selector(getDocumentsWithCompletion:)))(self, _cmd, completion); } #pragma mark Firebase, Writing Data static void _logos_method$_ungrouped$FIRDocumentReference$setData$merge$completion$( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, NSDictionary * documentData, BOOL merge, void (^completion)(NSError *)) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRWillSetData:self data:documentData merge:@(merge) mergeFields:nil transactionID:requestID ]; // Hook callback void (^orig)(NSError *) = completion; completion = ^(NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID]; if (orig != nil) { orig(error); } }; // Forward invocation (_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$ ? _logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(setData:merge:completion:)))(self, _cmd, documentData, merge, completion); } static void _logos_method$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, NSDictionary * documentData, NSArray * mergeFields, void (^completion)(NSError *)) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRWillSetData:self data:documentData merge:nil mergeFields:mergeFields transactionID:requestID ]; // Hook callback void (^orig)(NSError *) = completion; completion = ^(NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRDidSetData:error transactionID:requestID]; if (orig != nil) { orig(error); } }; // Forward invocation (_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$ ? _logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(setData:mergeFields:completion:)))(self, _cmd, documentData, mergeFields, completion); } static void _logos_method$_ungrouped$FIRDocumentReference$updateData$completion$( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, NSDictionary * fields, void (^completion)(NSError *)) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRWillUpdateData:self fields:fields transactionID:requestID]; // Hook callback void (^orig)(NSError *) = completion; completion = ^(NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRDidUpdateData:error transactionID:requestID]; if (orig != nil) { orig(error); } }; // Forward invocation (_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$ ? _logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(updateData:completion:)))(self, _cmd, fields, completion); } static void _logos_method$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$( _LOGOS_SELF_TYPE_NORMAL FIRDocumentReference * _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, void (^completion)(NSError *)) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRWillDeleteDocument:self transactionID:requestID]; // Hook callback void (^orig)(NSError *) = completion; completion = ^(NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRDidDeleteDocument:error transactionID:requestID]; if (orig != nil) { orig(error); } }; // Forward invocation (_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$ ? _logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$ : (__typeof__(_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRDocumentReference, @selector(deleteDocumentWithCompletion:)))(self, _cmd, completion); } static FIRDocumentReference * _logos_method$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$( _LOGOS_SELF_TYPE_NORMAL FIRCollectionReference * _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, NSDictionary * data, void (^completion)(NSError *error)) { // Generate transaction ID NSString *requestID = [FLEXNetworkObserver nextRequestID]; // Hook callback void (^orig)(NSError *) = completion; completion = ^(NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordFIRDidAddDocument:error transactionID:requestID]; if (orig != nil) { orig(error); } }; // Forward invocation FIRDocumentReference *ret = (_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$ ? _logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$ : (__typeof__(_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$))class_getMethodImplementation(_logos_superclass$_ungrouped$FIRCollectionReference, @selector(addDocumentWithData:completion:)))(self, _cmd, data, completion); // Record transaction start [FLEXNetworkRecorder.defaultRecorder recordFIRWillAddDocument:self document:ret transactionID:requestID]; // Return return ret; } + (void)setNetworkMonitorHooks { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self hookFirebaseThings]; [self injectIntoAllNSURLThings]; }); } + (void)hookFirebaseThings { Class _logos_class$_ungrouped$FIRDocumentReference = objc_getClass("FIRDocumentReference"); _logos_superclass$_ungrouped$FIRDocumentReference = class_getSuperclass(_logos_class$_ungrouped$FIRDocumentReference); Class _logos_class$_ungrouped$FIRQuery = objc_getClass("FIRQuery"); _logos_superclass$_ungrouped$FIRQuery = class_getSuperclass(_logos_class$_ungrouped$FIRQuery); Class _logos_class$_ungrouped$FIRCollectionReference = objc_getClass("FIRCollectionReference"); _logos_superclass$_ungrouped$FIRCollectionReference = class_getSuperclass(_logos_class$_ungrouped$FIRCollectionReference); // Reading // _logos_register_hook( _logos_class$_ungrouped$FIRDocumentReference, @selector(getDocumentWithCompletion:), (IMP)&_logos_method$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$, (IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$getDocumentWithCompletion$ ); _logos_register_hook( _logos_class$_ungrouped$FIRQuery, @selector(getDocumentsWithCompletion:), (IMP)&_logos_method$_ungrouped$FIRQuery$getDocumentsWithCompletion$, (IMP *)&_logos_orig$_ungrouped$FIRQuery$getDocumentsWithCompletion$ ); // Writing // _logos_register_hook( _logos_class$_ungrouped$FIRDocumentReference, @selector(setData:merge:completion:), (IMP)&_logos_method$_ungrouped$FIRDocumentReference$setData$merge$completion$, (IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$setData$merge$completion$ ); _logos_register_hook( _logos_class$_ungrouped$FIRDocumentReference, @selector(setData:mergeFields:completion:), (IMP)&_logos_method$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$, (IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$setData$mergeFields$completion$ ); _logos_register_hook( _logos_class$_ungrouped$FIRDocumentReference, @selector(updateData:completion:), (IMP)&_logos_method$_ungrouped$FIRDocumentReference$updateData$completion$, (IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$updateData$completion$ ); _logos_register_hook( _logos_class$_ungrouped$FIRDocumentReference, @selector(deleteDocumentWithCompletion:), (IMP)&_logos_method$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$, (IMP *)&_logos_orig$_ungrouped$FIRDocumentReference$deleteDocumentWithCompletion$ ); _logos_register_hook( _logos_class$_ungrouped$FIRCollectionReference, @selector(addDocumentWithData:completion:), (IMP)&_logos_method$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$, (IMP *)&_logos_orig$_ungrouped$FIRCollectionReference$addDocumentWithData$completion$ ); } + (void)injectIntoAllNSURLThings { // Only allow swizzling once. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // Swizzle any classes that implement one of these selectors. const SEL selectors[] = { @selector(connectionDidFinishLoading:), @selector(connection:willSendRequest:redirectResponse:), @selector(connection:didReceiveResponse:), @selector(connection:didReceiveData:), @selector(connection:didFailWithError:), @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:), @selector(URLSession:dataTask:didReceiveData:), @selector(URLSession:dataTask:didReceiveResponse:completionHandler:), @selector(URLSession:task:didCompleteWithError:), @selector(URLSession:dataTask:didBecomeDownloadTask:), @selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:), @selector(URLSession:downloadTask:didFinishDownloadingToURL:) }; const int numSelectors = sizeof(selectors) / sizeof(SEL); // T251807321: Use objc_copyClassList to get a fresh snapshot of registered classes. // This is safer than objc_getClassList because it returns an owned copy that won't // have stale pointers from classes that were unloaded during iteration. unsigned int numClasses = 0; Class *classes = objc_copyClassList(&numClasses); if (numClasses > 0 && classes != NULL) { for (unsigned int classIndex = 0; classIndex < numClasses; ++classIndex) { Class class = classes[classIndex]; if (class == NULL || class == [FLEXNetworkObserver class]) { continue; } // Skip metaclasses - we only want to swizzle instance methods on regular classes. if (class_isMetaClass(class)) { continue; } // Double-check the class is still registered by looking it up by name. const char *className = class_getName(class); if (className == NULL || objc_lookUpClass(className) != class) { continue; } // Use the C API rather than NSObject methods to avoid sending messages // to classes we're not interested in swizzling, which could result // in us calling +initialize on potentially uninitialized classes. // NOTE: calling class_getInstanceMethod() DOES send +initialize // to the class. That's why we iterate through the method list. unsigned int methodCount = 0; Method *methods = class_copyMethodList(class, &methodCount); if (methods == NULL) { continue; } BOOL matchingSelectorFound = NO; for (unsigned int methodIndex = 0; methodIndex < methodCount; methodIndex++) { for (int selectorIndex = 0; selectorIndex < numSelectors; ++selectorIndex) { if (method_getName(methods[methodIndex]) == selectors[selectorIndex]) { [self injectIntoDelegateClass:class]; matchingSelectorFound = YES; break; } } if (matchingSelectorFound) { break; } } free(methods); } free(classes); } [self injectIntoNSURLConnectionCancel]; [self injectIntoNSURLSessionTaskResume]; [self injectIntoNSURLConnectionAsynchronousClassMethod]; [self injectIntoNSURLConnectionSynchronousClassMethod]; Class URLSession = [NSURLSession class]; [self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:URLSession]; [self injectIntoNSURLSessionAsyncUploadTaskMethods:URLSession]; // At some point, NSURLSession.sharedSession became an __NSURLSessionLocal, // which is not the class returned by [NSURLSession class], of course Class URLSessionLocal = NSClassFromString(@"__NSURLSessionLocal"); if (URLSessionLocal && (URLSession != URLSessionLocal)) { [self injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:URLSessionLocal]; [self injectIntoNSURLSessionAsyncUploadTaskMethods:URLSessionLocal]; } if (@available(iOS 13.0, *)) { Class websocketTask = NSClassFromString(@"__NSURLSessionWebSocketTask"); [self injectWebsocketSendMessage:websocketTask]; [self injectWebsocketReceiveMessage:websocketTask]; websocketTask = [NSURLSessionWebSocketTask class]; [self injectWebsocketSendMessage:websocketTask]; [self injectWebsocketReceiveMessage:websocketTask]; } }); } + (void)injectIntoDelegateClass:(Class)cls { // Connections [self injectWillSendRequestIntoDelegateClass:cls]; [self injectDidReceiveDataIntoDelegateClass:cls]; [self injectDidReceiveResponseIntoDelegateClass:cls]; [self injectDidFinishLoadingIntoDelegateClass:cls]; [self injectDidFailWithErrorIntoDelegateClass:cls]; // Sessions [self injectTaskWillPerformHTTPRedirectionIntoDelegateClass:cls]; [self injectTaskDidReceiveDataIntoDelegateClass:cls]; [self injectTaskDidReceiveResponseIntoDelegateClass:cls]; [self injectTaskDidCompleteWithErrorIntoDelegateClass:cls]; [self injectRespondsToSelectorIntoDelegateClass:cls]; // Data tasks [self injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:cls]; // Download tasks [self injectDownloadTaskDidWriteDataIntoDelegateClass:cls]; [self injectDownloadTaskDidFinishDownloadingIntoDelegateClass:cls]; } + (void)injectIntoNSURLConnectionCancel { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [NSURLConnection class]; SEL selector = @selector(cancel); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; Method originalCancel = class_getInstanceMethod(class, selector); void (^swizzleBlock)(NSURLConnection *) = ^(NSURLConnection *slf) { [FLEXNetworkObserver.sharedObserver connectionWillCancel:slf]; ((void(*)(id, SEL))objc_msgSend)( slf, swizzledSelector ); }; IMP implementation = imp_implementationWithBlock(swizzleBlock); class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalCancel)); Method newCancel = class_getInstanceMethod(class, swizzledSelector); method_exchangeImplementations(originalCancel, newCancel); }); } + (void)injectIntoNSURLSessionTaskResume { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // In iOS 7 resume lives in __NSCFLocalSessionTask // In iOS 8 resume lives in NSURLSessionTask // In iOS 9 resume lives in __NSCFURLSessionTask // In iOS 14 resume lives in NSURLSessionTask Class baseResumeClass = Nil; if (![NSProcessInfo.processInfo respondsToSelector:@selector(operatingSystemVersion)]) { // iOS ... 7 baseResumeClass = NSClassFromString(@"__NSCFLocalSessionTask"); } else { NSInteger majorVersion = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion; if (majorVersion < 9 || majorVersion >= 14) { // iOS 8 or iOS 14+ baseResumeClass = [NSURLSessionTask class]; } else { // iOS 9 ... 13 baseResumeClass = NSClassFromString(@"__NSCFURLSessionTask"); } } // Hook the base implementation of -resume IMP originalResume = [baseResumeClass instanceMethodForSelector:@selector(resume)]; [self swizzleResumeSelector:@selector(resume) forClass:baseResumeClass]; // *Sigh* // // So, multiple versions of AFNetworking 2.5.X swizzle -resume in various and // short-sighted ways. If you look through the version history from 2.5.0 upwards, // you'll see a variety of techniques were tried, including taking a private // subclass of NSURLSessionTask and calling class_addMethod with `originalResume` // below, so that a duplicate implementation of -resume exists in that class. // // This technique in particular is troublesome, because the implementation in // `baseResumeClass` is never called at all, which means our swizzle is never invoked. // // The only solution is a brute-force one: we must loop over the class tree // below `baseResumeClass` and check for all classes that implement `af_resume`. // if the IMP corresponding to that method is equal to `originalResume` then we // swizzle that in addition to swizzling `resume` on `baseResumeClass` above. // // However, we only go to the trouble at all if NSSelectorFromString // can even find an `"af_resume"` selector in the first place. SEL sel_af_resume = NSSelectorFromString(@"af_resume"); if (sel_af_resume) { NSMutableArray *classTree = FLEXGetAllSubclasses(baseResumeClass, NO).mutableCopy; for (NSInteger i = 0; i < classTree.count; i++) { [classTree addObjectsFromArray:FLEXGetAllSubclasses(classTree[i], NO)]; } for (Class current in classTree) { IMP af_resume = [current instanceMethodForSelector:sel_af_resume]; if (af_resume == originalResume) { [self swizzleResumeSelector:sel_af_resume forClass:current]; } } } }); } + (void)swizzleResumeSelector:(SEL)selector forClass:(Class)class { SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; Method originalResume = class_getInstanceMethod(class, selector); IMP implementation = imp_implementationWithBlock(^(NSURLSessionTask *slf) { if (@available(iOS 11.0, *)) { // AVAggregateAssetDownloadTask deeply does not like to be looked at. Accessing -currentRequest or // -originalRequest will crash. Do not try to observe these. https://github.com/FLEXTool/FLEX/issues/276 if (![slf isKindOfClass:[AVAggregateAssetDownloadTask class]]) { // iOS's internal HTTP parser finalization code is mysteriously not thread safe, // invoking it asynchronously has a chance to cause a `double free` crash. // This line below will ask for HTTPBody synchronously, make the HTTPParser // parse the request, and cache them in advance. After that the HTTPParser // will be finalized. Make sure other threads inspecting the request // won't trigger a race to finalize the parser. [slf.currentRequest HTTPBody]; [FLEXNetworkObserver.sharedObserver URLSessionTaskWillResume:slf]; } } ((void(*)(id, SEL))objc_msgSend)( slf, swizzledSelector ); }); class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalResume)); Method newResume = class_getInstanceMethod(class, swizzledSelector); method_exchangeImplementations(originalResume, newResume); } + (void)injectIntoNSURLConnectionAsynchronousClassMethod { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = objc_getMetaClass(class_getName([NSURLConnection class])); SEL selector = @selector(sendAsynchronousRequest:queue:completionHandler:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; typedef void (^AsyncCompletion)( NSURLResponse *response, NSData *data, NSError *error ); typedef void (^SendAsyncRequestBlock)( Class, NSURLRequest *, NSOperationQueue *, AsyncCompletion ); SendAsyncRequestBlock swizzleBlock = ^(Class slf, NSURLRequest *request, NSOperationQueue *queue, AsyncCompletion completion) { if (FLEXNetworkObserver.isEnabled) { NSString *requestID = [self nextRequestID]; [FLEXNetworkRecorder.defaultRecorder recordRequestWillBeSentWithRequestID:requestID request:request redirectResponse:nil ]; NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class]; [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID]; AsyncCompletion wrapper = ^(NSURLResponse *response, NSData *data, NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordResponseReceivedWithRequestID:requestID response:response ]; [FLEXNetworkRecorder.defaultRecorder recordDataReceivedWithRequestID:requestID dataLength:data.length ]; if (error) { [FLEXNetworkRecorder.defaultRecorder recordLoadingFailedWithRequestID:requestID error:error ]; } else { [FLEXNetworkRecorder.defaultRecorder recordLoadingFinishedWithRequestID:requestID responseBody:data ]; } // Call through to the original completion handler if (completion) { completion(response, data, error); } }; ((void(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, request, queue, wrapper ); } else { ((void(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, request, queue, completion ); } }; [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector ]; }); } + (void)injectIntoNSURLConnectionSynchronousClassMethod { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = objc_getMetaClass(class_getName([NSURLConnection class])); SEL selector = @selector(sendSynchronousRequest:returningResponse:error:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; typedef NSData * (^AsyncCompletion)(Class, NSURLRequest *, NSURLResponse **, NSError **); AsyncCompletion swizzleBlock = ^NSData *(Class slf, NSURLRequest *request, NSURLResponse **response, NSError **error) { NSData *data = nil; if (FLEXNetworkObserver.isEnabled) { NSString *requestID = [self nextRequestID]; [FLEXNetworkRecorder.defaultRecorder recordRequestWillBeSentWithRequestID:requestID request:request redirectResponse:nil ]; NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class]; [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID]; NSError *temporaryError = nil; NSURLResponse *temporaryResponse = nil; data = ((id(*)(id, SEL, id, NSURLResponse **, NSError **))objc_msgSend)( slf, swizzledSelector, request, &temporaryResponse, &temporaryError ); [FLEXNetworkRecorder.defaultRecorder recordResponseReceivedWithRequestID:requestID response:temporaryResponse ]; [FLEXNetworkRecorder.defaultRecorder recordDataReceivedWithRequestID:requestID dataLength:data.length ]; if (temporaryError) { [FLEXNetworkRecorder.defaultRecorder recordLoadingFailedWithRequestID:requestID error:temporaryError ]; } else { [FLEXNetworkRecorder.defaultRecorder recordLoadingFinishedWithRequestID:requestID responseBody:data ]; } if (error) { *error = temporaryError; } if (response) { *response = temporaryResponse; } } else { data = ((id(*)(id, SEL, id, NSURLResponse **, NSError **))objc_msgSend)( slf, swizzledSelector, request, response, error ); } return data; }; [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector ]; }); } + (void)injectIntoNSURLSessionAsyncDataAndDownloadTaskMethods:(Class)sessionClass { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = sessionClass; // The method signatures here are close enough that // we can use the same logic to inject into all of them. const SEL selectors[] = { @selector(dataTaskWithRequest:completionHandler:), @selector(dataTaskWithURL:completionHandler:), @selector(downloadTaskWithRequest:completionHandler:), @selector(downloadTaskWithResumeData:completionHandler:), @selector(downloadTaskWithURL:completionHandler:) }; const int numSelectors = sizeof(selectors) / sizeof(SEL); for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) { SEL selector = selectors[selectorIndex]; SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) { // iOS 7 does not implement these methods on NSURLSession. We actually want to // swizzle __NSCFURLSession, which we can get from the class of the shared session class = [NSURLSession.sharedSession class]; } typedef NSURLSessionTask * (^NSURLSessionNewTaskMethod)( NSURLSession *, id, NSURLSessionAsyncCompletion ); NSURLSessionNewTaskMethod swizzleBlock = ^NSURLSessionTask *(NSURLSession *slf, id argument, NSURLSessionAsyncCompletion completion) { NSURLSessionTask *task = nil; // Check if network observing is on and a callback was provided if (FLEXNetworkObserver.isEnabled && completion) { NSString *requestID = [self nextRequestID]; NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class]; // "Hook" the completion block NSURLSessionAsyncCompletion completionWrapper = [self asyncCompletionWrapperForRequestID:requestID mechanism:mechanism completion:completion ]; // Call the original method task = ((id(*)(id, SEL, id, id))objc_msgSend)( slf, swizzledSelector, argument, completionWrapper ); [self setRequestID:requestID forConnectionOrTask:task]; } else { // Network observer disabled or no callback provided, // just pass through to the original method task = ((id(*)(id, SEL, id, id))objc_msgSend)( slf, swizzledSelector, argument, completion ); } return task; }; // Actually swizzle [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector ]; } }); } + (void)injectIntoNSURLSessionAsyncUploadTaskMethods:(Class)sessionClass { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = sessionClass; // The method signatures here are close enough that we can use the same logic to inject into both of them. // Note that they have 3 arguments, so we can't easily combine with the data and download method above. typedef NSURLSessionUploadTask *(^UploadTaskMethod)( NSURLSession *, NSURLRequest *, id, NSURLSessionAsyncCompletion ); const SEL selectors[] = { @selector(uploadTaskWithRequest:fromData:completionHandler:), @selector(uploadTaskWithRequest:fromFile:completionHandler:) }; const int numSelectors = sizeof(selectors) / sizeof(SEL); for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) { SEL selector = selectors[selectorIndex]; SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) { // iOS 7 does not implement these methods on NSURLSession. We actually want to // swizzle __NSCFURLSession, which we can get from the class of the shared session class = [NSURLSession.sharedSession class]; } UploadTaskMethod swizzleBlock = ^NSURLSessionUploadTask *(NSURLSession * slf, NSURLRequest *request, id argument, NSURLSessionAsyncCompletion completion) { NSURLSessionUploadTask *task = nil; if (FLEXNetworkObserver.isEnabled && completion) { NSString *requestID = [self nextRequestID]; NSString *mechanism = [self mechanismFromClassMethod:selector onClass:class]; NSURLSessionAsyncCompletion completionWrapper = [self asyncCompletionWrapperForRequestID:requestID mechanism:mechanism completion:completion ]; task = ((id(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, request, argument, completionWrapper ); [self setRequestID:requestID forConnectionOrTask:task]; } else { task = ((id(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, request, argument, completion ); } return task; }; [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:swizzleBlock swizzledSelector:swizzledSelector ]; } }); } + (NSString *)mechanismFromClassMethod:(SEL)selector onClass:(Class)class { return [NSString stringWithFormat:@"+[%@ %@]", NSStringFromClass(class), NSStringFromSelector(selector)]; } + (NSURLSessionAsyncCompletion)asyncCompletionWrapperForRequestID:(NSString *)requestID mechanism:(NSString *)mechanism completion:(NSURLSessionAsyncCompletion)completion { NSURLSessionAsyncCompletion completionWrapper = ^(id fileURLOrData, NSURLResponse *response, NSError *error) { [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID]; [FLEXNetworkRecorder.defaultRecorder recordResponseReceivedWithRequestID:requestID response:response ]; NSData *data = nil; if ([fileURLOrData isKindOfClass:[NSURL class]]) { data = [NSData dataWithContentsOfURL:fileURLOrData]; } else if ([fileURLOrData isKindOfClass:[NSData class]]) { data = fileURLOrData; } [FLEXNetworkRecorder.defaultRecorder recordDataReceivedWithRequestID:requestID dataLength:data.length ]; if (error) { [FLEXNetworkRecorder.defaultRecorder recordLoadingFailedWithRequestID:requestID error:error ]; } else { [FLEXNetworkRecorder.defaultRecorder recordLoadingFinishedWithRequestID:requestID responseBody:data ]; } // Call through to the original completion handler if (completion) { completion(fileURLOrData, response, error); } }; return completionWrapper; } + (void)injectWillSendRequestIntoDelegateClass:(Class)cls { SEL selector = @selector(connection:willSendRequest:redirectResponse:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; Protocol *protocol = @protocol(NSURLConnectionDataDelegate); protocol = protocol ?: @protocol(NSURLConnectionDelegate); struct objc_method_description methodDescription = protocol_getMethodDescription( protocol, selector, NO, YES ); typedef NSURLRequest *(^WillSendRequestBlock)( id slf, NSURLConnection *connection, NSURLRequest *request, NSURLResponse *response ); WillSendRequestBlock undefinedBlock = ^NSURLRequest *(id slf, NSURLConnection *connection, NSURLRequest *request, NSURLResponse *response) { [FLEXNetworkObserver.sharedObserver connection:connection willSendRequest:request redirectResponse:response delegate:slf ]; return request; }; WillSendRequestBlock implementationBlock = ^NSURLRequest *(id slf, NSURLConnection *connection, NSURLRequest *request, NSURLResponse *response) { __block NSURLRequest *returnValue = nil; [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{ undefinedBlock(slf, connection, request, response); } originalImplementationBlock:^{ returnValue = ((id(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, connection, request, response ); }]; return returnValue; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDidReceiveResponseIntoDelegateClass:(Class)cls { SEL selector = @selector(connection:didReceiveResponse:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; Protocol *protocol = @protocol(NSURLConnectionDataDelegate); protocol = protocol ?: @protocol(NSURLConnectionDelegate); struct objc_method_description description = protocol_getMethodDescription( protocol, selector, NO, YES ); typedef void (^DidReceiveResponseBlock)( id slf, NSURLConnection *connection, NSURLResponse *response ); DidReceiveResponseBlock undefinedBlock = ^(id slf, NSURLConnection *connection, NSURLResponse *response) { [FLEXNetworkObserver.sharedObserver connection:connection didReceiveResponse:response delegate:slf ]; }; DidReceiveResponseBlock implementationBlock = ^(id slf, NSURLConnection *connection, NSURLResponse *response) { [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{ undefinedBlock(slf, connection, response); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id))objc_msgSend)( slf, swizzledSelector, connection, response ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDidReceiveDataIntoDelegateClass:(Class)cls { SEL selector = @selector(connection:didReceiveData:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; Protocol *protocol = @protocol(NSURLConnectionDataDelegate); protocol = protocol ?: @protocol(NSURLConnectionDelegate); struct objc_method_description description = protocol_getMethodDescription( protocol, selector, NO, YES ); typedef void (^DidReceiveDataBlock)( id slf, NSURLConnection *connection, NSData *data ); DidReceiveDataBlock undefinedBlock = ^(id slf, NSURLConnection *connection, NSData *data) { [FLEXNetworkObserver.sharedObserver connection:connection didReceiveData:data delegate:slf ]; }; DidReceiveDataBlock implementationBlock = ^(id slf, NSURLConnection *connection, NSData *data) { [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{ undefinedBlock(slf, connection, data); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id))objc_msgSend)( slf, swizzledSelector, connection, data ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDidFinishLoadingIntoDelegateClass:(Class)cls { SEL selector = @selector(connectionDidFinishLoading:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; Protocol *protocol = @protocol(NSURLConnectionDataDelegate); protocol = protocol ?: @protocol(NSURLConnectionDelegate); struct objc_method_description description = protocol_getMethodDescription( protocol, selector, NO, YES ); typedef void (^FinishLoadingBlock)(id slf, NSURLConnection *connection); FinishLoadingBlock undefinedBlock = ^(id slf, NSURLConnection *connection) { [FLEXNetworkObserver.sharedObserver connectionDidFinishLoading:connection delegate:slf]; }; FinishLoadingBlock implementationBlock = ^(id slf, NSURLConnection *connection) { [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{ undefinedBlock(slf, connection); } originalImplementationBlock:^{ ((void(*)(id, SEL, id))objc_msgSend)( slf, swizzledSelector, connection ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDidFailWithErrorIntoDelegateClass:(Class)cls { SEL selector = @selector(connection:didFailWithError:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLConnectionDelegate), selector, NO, YES ); typedef void (^DidFailWithErrorBlock)( id slf, NSURLConnection *connection, NSError *error ); DidFailWithErrorBlock undefinedBlock = ^(id slf, NSURLConnection *connection, NSError *error) { [FLEXNetworkObserver.sharedObserver connection:connection didFailWithError:error delegate:slf ]; }; DidFailWithErrorBlock implementationBlock = ^(id slf, NSURLConnection *connection, NSError *error) { [self sniffWithoutDuplicationForObject:connection selector:selector sniffingBlock:^{ undefinedBlock(slf, connection, error); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id))objc_msgSend)( slf, swizzledSelector, connection, error ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectTaskWillPerformHTTPRedirectionIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionTaskDelegate), selector, NO, YES ); typedef void (^HTTPRedirectionBlock)(id slf, NSURLSession *session, NSURLSessionTask *task, NSHTTPURLResponse *response, NSURLRequest *newRequest, void(^completionHandler)(NSURLRequest *)); HTTPRedirectionBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionTask *task, NSHTTPURLResponse *response, NSURLRequest *newRequest, void(^completionHandler)(NSURLRequest *)) { [FLEXNetworkObserver.sharedObserver URLSession:session task:task willPerformHTTPRedirection:response newRequest:newRequest completionHandler:completionHandler delegate:slf ]; completionHandler(newRequest); }; HTTPRedirectionBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionTask *task, NSHTTPURLResponse *response, NSURLRequest *newRequest, void(^completionHandler)(NSURLRequest *)) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ [FLEXNetworkObserver.sharedObserver URLSession:session task:task willPerformHTTPRedirection:response newRequest:newRequest completionHandler:completionHandler delegate:slf ]; } originalImplementationBlock:^{ ((id(*)(id, SEL, id, id, id, id, void(^)(NSURLRequest *)))objc_msgSend)( slf, swizzledSelector, session, task, response, newRequest, completionHandler ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectTaskDidReceiveDataIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:dataTask:didReceiveData:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionDataDelegate), selector, NO, YES ); typedef void (^DidReceiveDataBlock)(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); DidReceiveDataBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data) { [FLEXNetworkObserver.sharedObserver URLSession:session dataTask:dataTask didReceiveData:data delegate:slf ]; }; DidReceiveDataBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ undefinedBlock(slf, session, dataTask, data); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, session, dataTask, data ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:dataTask:didBecomeDownloadTask:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionDataDelegate), selector, NO, YES ); typedef void (^DidBecomeDownloadTaskBlock)(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); DidBecomeDownloadTaskBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask) { [FLEXNetworkObserver.sharedObserver URLSession:session dataTask:dataTask didBecomeDownloadTask:downloadTask delegate:slf ]; }; DidBecomeDownloadTaskBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ undefinedBlock(slf, session, dataTask, downloadTask); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, session, dataTask, downloadTask ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectTaskDidReceiveResponseIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:dataTask:didReceiveResponse:completionHandler:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionDataDelegate), selector, NO, YES ); typedef void (^DidReceiveResponseBlock)(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response, void(^completion)(NSURLSessionResponseDisposition)); DidReceiveResponseBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response, void(^completion)(NSURLSessionResponseDisposition)) { [FLEXNetworkObserver.sharedObserver URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completion delegate:slf ]; completion(NSURLSessionResponseAllow); }; DidReceiveResponseBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response, void(^completion)(NSURLSessionResponseDisposition )) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ [FLEXNetworkObserver.sharedObserver URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completion delegate:slf ]; } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id, id, void(^)(NSURLSessionResponseDisposition)))objc_msgSend)( slf, swizzledSelector, session, dataTask, response, completion ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectTaskDidCompleteWithErrorIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:task:didCompleteWithError:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionDataDelegate), selector, NO, YES ); typedef void (^DidCompleteWithErrorBlock)(id slf, NSURLSession *session, NSURLSessionTask *task, NSError *error); DidCompleteWithErrorBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionTask *task, NSError *error) { [FLEXNetworkObserver.sharedObserver URLSession:session task:task didCompleteWithError:error delegate:slf ]; }; DidCompleteWithErrorBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionTask *task, NSError *error) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ undefinedBlock(slf, session, task, error); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, session, task, error ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } // Used for overriding AFNetworking behavior + (void)injectRespondsToSelectorIntoDelegateClass:(Class)cls { SEL selector = @selector(respondsToSelector:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; //Protocol *protocol = @protocol(NSURLSessionTaskDelegate); Method method = class_getInstanceMethod(cls, selector); struct objc_method_description methodDescription = *method_getDescription(method); typedef BOOL (^RespondsToSelectorImpl)(id self, SEL sel); RespondsToSelectorImpl undefinedBlock = ^(id slf, SEL sel) { return YES; }; RespondsToSelectorImpl implementationBlock = ^(id slf, SEL sel) { if (sel == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { return undefinedBlock(slf, sel); } return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(slf, swizzledSelector, sel); }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDownloadTaskDidFinishDownloadingIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:downloadTask:didFinishDownloadingToURL:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionDownloadDelegate), selector, NO, YES ); typedef void (^DidFinishDownloadingBlock)(id slf, NSURLSession *session, NSURLSessionDownloadTask *task, NSURL *location); DidFinishDownloadingBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionDownloadTask *task, NSURL *location) { NSData *data = [NSData dataWithContentsOfFile:location.relativePath]; [FLEXNetworkObserver.sharedObserver URLSession:session task:task didFinishDownloadingToURL:location data:data delegate:slf ]; }; DidFinishDownloadingBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionDownloadTask *task, NSURL *location) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ undefinedBlock(slf, session, task, location); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id, id))objc_msgSend)( slf, swizzledSelector, session, task, location ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectDownloadTaskDidWriteDataIntoDelegateClass:(Class)cls { SEL selector = @selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; struct objc_method_description description = protocol_getMethodDescription( @protocol(NSURLSessionDownloadDelegate), selector, NO, YES ); typedef void (^DidWriteDataBlock)(id slf, NSURLSession *session, NSURLSessionDownloadTask *task, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); DidWriteDataBlock undefinedBlock = ^(id slf, NSURLSession *session, NSURLSessionDownloadTask *task, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) { [FLEXNetworkObserver.sharedObserver URLSession:session downloadTask:task didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite delegate:slf ]; }; DidWriteDataBlock implementationBlock = ^(id slf, NSURLSession *session, NSURLSessionDownloadTask *task, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) { [self sniffWithoutDuplicationForObject:session selector:selector sniffingBlock:^{ undefinedBlock( slf, session, task, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite ); } originalImplementationBlock:^{ ((void(*)(id, SEL, id, id, int64_t, int64_t, int64_t))objc_msgSend)( slf, swizzledSelector, session, task, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite ); }]; }; [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:description implementationBlock:implementationBlock undefinedBlock:undefinedBlock ]; } + (void)injectWebsocketSendMessage:(Class)cls API_AVAILABLE(ios(13.0)) { SEL selector = @selector(sendMessage:completionHandler:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; typedef void (^SendMessageBlock)( NSURLSessionWebSocketTask *slf, NSURLSessionWebSocketMessage *message, void (^completion)(NSError *error) ); SendMessageBlock implementationBlock = ^( NSURLSessionWebSocketTask *slf, NSURLSessionWebSocketMessage *message, void (^completion)(NSError *error) ) { [FLEXNetworkObserver.sharedObserver websocketTask:slf sendMessagage:message ]; id completionHook = ^(NSError *error) { [FLEXNetworkObserver.sharedObserver websocketTaskMessageSendCompletion:message error:error ]; if (completion) { completion(error); } }; ((void(*)(id, SEL, id, id))objc_msgSend)( slf, swizzledSelector, message, completionHook ); }; [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:cls withBlock:implementationBlock swizzledSelector:swizzledSelector ]; } + (void)injectWebsocketReceiveMessage:(Class)cls API_AVAILABLE(ios(13.0)) { SEL selector = @selector(receiveMessageWithCompletionHandler:); SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector]; typedef void (^SendMessageBlock)( NSURLSessionWebSocketTask *slf, void (^completion)(NSURLSessionWebSocketMessage *message, NSError *error) ); SendMessageBlock implementationBlock = ^( NSURLSessionWebSocketTask *slf, void (^completion)(NSURLSessionWebSocketMessage *message, NSError *error) ) { id completionHook = ^(NSURLSessionWebSocketMessage *message, NSError *error) { [FLEXNetworkObserver.sharedObserver websocketTask:slf receiveMessagage:message error:error ]; completion(message, error); }; ((void(*)(id, SEL, id))objc_msgSend)( slf, swizzledSelector, completionHook ); }; [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:cls withBlock:implementationBlock swizzledSelector:swizzledSelector ]; } static char const * const kFLEXRequestIDKey = "kFLEXRequestIDKey"; + (NSString *)requestIDForConnectionOrTask:(id)connectionOrTask { NSString *requestID = objc_getAssociatedObject(connectionOrTask, kFLEXRequestIDKey); if (!requestID) { requestID = [self nextRequestID]; [self setRequestID:requestID forConnectionOrTask:connectionOrTask]; } return requestID; } + (void)setRequestID:(NSString *)requestID forConnectionOrTask:(id)connectionOrTask { objc_setAssociatedObject( connectionOrTask, kFLEXRequestIDKey, requestID, OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } #pragma mark - Initialization - (id)init { self = [super init]; if (self) { self.requestStatesForRequestIDs = [NSMutableDictionary new]; self.queue = dispatch_queue_create( "com.flex.FLEXNetworkObserver", DISPATCH_QUEUE_SERIAL ); } return self; } #pragma mark - Private Methods - (void)performBlock:(dispatch_block_t)block { if ([[self class] isEnabled]) { dispatch_async(_queue, block); } } - (FLEXInternalRequestState *)requestStateForRequestID:(NSString *)requestID { FLEXInternalRequestState *requestState = self.requestStatesForRequestIDs[requestID]; if (!requestState) { requestState = [FLEXInternalRequestState new]; [self.requestStatesForRequestIDs setObject:requestState forKey:requestID]; } return requestState; } - (void)removeRequestStateForRequestID:(NSString *)requestID { [self.requestStatesForRequestIDs removeObjectForKey:requestID]; } @end @implementation FLEXNetworkObserver (NSURLConnectionHelpers) - (void)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:connection]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; requestState.request = request; [FLEXNetworkRecorder.defaultRecorder recordRequestWillBeSentWithRequestID:requestID request:request redirectResponse:response ]; NSString *mechanism = [NSString stringWithFormat: @"NSURLConnection (delegate: %@)", [delegate class] ]; [FLEXNetworkRecorder.defaultRecorder recordMechanism:mechanism forRequestID:requestID]; }]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:connection]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; requestState.dataAccumulator = [NSMutableData new]; [FLEXNetworkRecorder.defaultRecorder recordResponseReceivedWithRequestID:requestID response:response ]; }]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data delegate:(id)delegate { // Just to be safe since we're doing this async data = [data copy]; [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:connection]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; [requestState.dataAccumulator appendData:data]; [FLEXNetworkRecorder.defaultRecorder recordDataReceivedWithRequestID:requestID dataLength:data.length ]; }]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:connection]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; [FLEXNetworkRecorder.defaultRecorder recordLoadingFinishedWithRequestID:requestID responseBody:requestState.dataAccumulator ]; [self removeRequestStateForRequestID:requestID]; }]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:connection]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; // Cancellations can occur prior to the willSendRequest:... // NSURLConnection delegate call. These are pretty common // and clutter up the logs. Only record the failure if the // recorder already knows about the request through willSendRequest:... if (requestState.request) { [FLEXNetworkRecorder.defaultRecorder recordLoadingFailedWithRequestID:requestID error:error ]; } [self removeRequestStateForRequestID:requestID]; }]; } - (void)connectionWillCancel:(NSURLConnection *)connection { [self performBlock:^{ // Mimic the behavior of NSURLSession which is to create an error on cancellation. NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"cancelled" }; NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo ]; [self connection:connection didFailWithError:error delegate:nil]; }]; } @end @implementation FLEXNetworkObserver (NSURLSessionTaskHelpers) - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:task]; [FLEXNetworkRecorder.defaultRecorder recordRequestWillBeSentWithRequestID:requestID request:request redirectResponse:response ]; }]; } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; requestState.dataAccumulator = [NSMutableData new]; NSString *requestMechanism = [NSString stringWithFormat: @"NSURLSessionDataTask (delegate: %@)", [delegate class] ]; [FLEXNetworkRecorder.defaultRecorder recordMechanism:requestMechanism forRequestID:requestID ]; [FLEXNetworkRecorder.defaultRecorder recordResponseReceivedWithRequestID:requestID response:response ]; }]; } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id)delegate { [self performBlock:^{ // By setting the request ID of the download task to match the data task, // it can pick up where the data task left off. NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask]; [[self class] setRequestID:requestID forConnectionOrTask:downloadTask]; }]; } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data delegate:(id)delegate { // Just to be safe since we're doing this async data = [data copy]; [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:dataTask]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; // Fix for "Response body not in cache" issue reported by developers // See this github comment for detailed explanation on why this happens // https://github.com/FLEXTool/FLEX/issues/568#issuecomment-1141015572 if (requestState.dataAccumulator == nil) { requestState.dataAccumulator = [NSMutableData new]; } [requestState.dataAccumulator appendData:data]; [FLEXNetworkRecorder.defaultRecorder recordDataReceivedWithRequestID:requestID dataLength:data.length ]; }]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:task]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; if (error) { [FLEXNetworkRecorder.defaultRecorder recordLoadingFailedWithRequestID:requestID error:error ]; } else { [FLEXNetworkRecorder.defaultRecorder recordLoadingFinishedWithRequestID:requestID responseBody:requestState.dataAccumulator ]; } [self removeRequestStateForRequestID:requestID]; }]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite delegate:(id)delegate { [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:downloadTask]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; if (!requestState.dataAccumulator) { requestState.dataAccumulator = [NSMutableData new]; [FLEXNetworkRecorder.defaultRecorder recordResponseReceivedWithRequestID:requestID response:downloadTask.response ]; NSString *requestMechanism = [NSString stringWithFormat: @"NSURLSessionDownloadTask (delegate: %@)", [delegate class] ]; [FLEXNetworkRecorder.defaultRecorder recordMechanism:requestMechanism forRequestID:requestID ]; } [FLEXNetworkRecorder.defaultRecorder recordDataReceivedWithRequestID:requestID dataLength:bytesWritten ]; }]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location data:(NSData *)data delegate:(id)delegate { data = [data copy]; [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:downloadTask]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; [requestState.dataAccumulator appendData:data]; }]; } - (void)URLSessionTaskWillResume:(NSURLSessionTask *)task { if (@available(iOS 11.0, *)) { // AVAggregateAssetDownloadTask deeply does not like to be looked at. Accessing -currentRequest or // -originalRequest will crash. Do not try to observe these. https://github.com/FLEXTool/FLEX/issues/276 if ([task isKindOfClass:[AVAggregateAssetDownloadTask class]]) { return; } } // Since resume can be called multiple times on the same task, only treat the first resume as // the equivalent to connection:willSendRequest:... [self performBlock:^{ NSString *requestID = [[self class] requestIDForConnectionOrTask:task]; FLEXInternalRequestState *requestState = [self requestStateForRequestID:requestID]; if (!requestState.request) { requestState.request = task.currentRequest; [FLEXNetworkRecorder.defaultRecorder recordRequestWillBeSentWithRequestID:requestID request:task.currentRequest redirectResponse:nil ]; } }]; } - (void)websocketTask:(NSURLSessionWebSocketTask *)task sendMessagage:(NSURLSessionWebSocketMessage *)message { [self performBlock:^{ // NSString *requestID = [[self class] requestIDForConnectionOrTask:task]; [FLEXNetworkRecorder.defaultRecorder recordWebsocketMessageSend:message task:task]; }]; } - (void)websocketTaskMessageSendCompletion:(NSURLSessionWebSocketMessage *)message error:(NSError *)error { [self performBlock:^{ [FLEXNetworkRecorder.defaultRecorder recordWebsocketMessageSendCompletion:message error:error ]; }]; } - (void)websocketTask:(NSURLSessionWebSocketTask *)task receiveMessagage:(NSURLSessionWebSocketMessage *)message error:(NSError *)error { [self performBlock:^{ if (!error && message) { [FLEXNetworkRecorder.defaultRecorder recordWebsocketMessageReceived:message task:task ]; } }]; } @end ================================================ FILE: Classes/Network/PonyDebugger/LICENSE ================================================ PonyDebugger Copyright 2012 Square Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Classes/ObjectExplorers/FLEXObjectExplorer.h ================================================ // // FLEXObjectExplorer.h // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXRuntime+UIKitHelpers.h" /// Carries state about the current user defaults settings @interface FLEXObjectExplorerDefaults : NSObject + (instancetype)canEdit:(BOOL)editable wantsPreviews:(BOOL)showPreviews; /// Only \c YES for properties and ivars @property (nonatomic, readonly) BOOL isEditable; /// Only affects properties and ivars @property (nonatomic, readonly) BOOL wantsDynamicPreviews; @end @interface FLEXObjectExplorer : NSObject + (instancetype)forObject:(id)objectOrClass; + (void)configureDefaultsForItems:(NSArray> *)items; @property (nonatomic, readonly) id object; /// Subclasses can override to provide a more useful description @property (nonatomic, readonly) NSString *objectDescription; /// @return \c YES if \c object is an instance of a class, /// or \c NO if \c object is a class itself. @property (nonatomic, readonly) BOOL objectIsInstance; /// An index into the `classHierarchy` array. /// /// This property determines which set of data comes out of the metadata arrays below /// For example, \c properties contains the properties of the selected class scope, /// while \c allProperties is an array of arrays where each array is a set of /// properties for a class in the class hierarchy of the current object. @property (nonatomic) NSInteger classScope; @property (nonatomic, readonly) NSArray *> *allProperties; @property (nonatomic, readonly) NSArray *properties; @property (nonatomic, readonly) NSArray *> *allClassProperties; @property (nonatomic, readonly) NSArray *classProperties; @property (nonatomic, readonly) NSArray *> *allIvars; @property (nonatomic, readonly) NSArray *ivars; @property (nonatomic, readonly) NSArray *> *allMethods; @property (nonatomic, readonly) NSArray *methods; @property (nonatomic, readonly) NSArray *> *allClassMethods; @property (nonatomic, readonly) NSArray *classMethods; @property (nonatomic, readonly) NSArray *classHierarchyClasses; @property (nonatomic, readonly) NSArray *classHierarchy; @property (nonatomic, readonly) NSArray *> *allConformedProtocols; @property (nonatomic, readonly) NSArray *conformedProtocols; @property (nonatomic, readonly) NSArray *allInstanceSizes; @property (nonatomic, readonly) FLEXStaticMetadata *instanceSize; @property (nonatomic, readonly) NSArray *allImageNames; @property (nonatomic, readonly) FLEXStaticMetadata *imageName; - (void)reloadMetadata; - (void)reloadClassHierarchy; @end @interface FLEXObjectExplorer (Reflex) /// Do not enable this property manually; Reflex will flip the switch when it is loaded. /// If you wish, you may \e disable it manually. @property (nonatomic, class) BOOL reflexAvailable; @end ================================================ FILE: Classes/ObjectExplorers/FLEXObjectExplorer.m ================================================ // // FLEXObjectExplorer.m // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXObjectExplorer.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXRuntime+Compare.h" #import "FLEXRuntime+UIKitHelpers.h" #import "FLEXPropertyAttributes.h" #import "FLEXMetadataSection.h" #import "NSUserDefaults+FLEX.h" #import "FLEXMirror.h" #import "FLEXSwiftInternal.h" @implementation FLEXObjectExplorerDefaults + (instancetype)canEdit:(BOOL)editable wantsPreviews:(BOOL)showPreviews { FLEXObjectExplorerDefaults *defaults = [self new]; defaults->_isEditable = editable; defaults->_wantsDynamicPreviews = showPreviews; return defaults; } @end @interface FLEXObjectExplorer () { NSMutableArray *> *_allProperties; NSMutableArray *> *_allClassProperties; NSMutableArray *> *_allIvars; NSMutableArray *> *_allMethods; NSMutableArray *> *_allClassMethods; NSMutableArray *> *_allConformedProtocols; NSMutableArray *_allInstanceSizes; NSMutableArray *_allImageNames; NSString *_objectDescription; } @property (nonatomic, readonly) id initialMirror; @end @implementation FLEXObjectExplorer + (void)initialize { if (self == FLEXObjectExplorer.class) { FLEXObjectExplorer.reflexAvailable = NSClassFromString(@"FLEXSwiftMirror") != nil; } } #pragma mark - Initialization + (id)forObject:(id)objectOrClass { return [[self alloc] initWithObject:objectOrClass]; } - (id)initWithObject:(id)objectOrClass { NSParameterAssert(objectOrClass); self = [super init]; if (self) { _object = objectOrClass; _objectIsInstance = !object_isClass(objectOrClass); [self reloadMetadata]; } return self; } - (id)mirrorForClass:(Class)cls { static Class FLEXSwiftMirror = nil; // Should we use Reflex? if (FLEXIsSwiftObjectOrClass(cls) && FLEXObjectExplorer.reflexAvailable) { // Initialize FLEXSwiftMirror class if needed if (!FLEXSwiftMirror) { FLEXSwiftMirror = NSClassFromString(@"FLEXSwiftMirror"); } return [(id)[FLEXSwiftMirror alloc] initWithSubject:cls]; } // No; not swift object, or Reflex unavailable return [FLEXMirror reflect:cls]; } #pragma mark - Public + (void)configureDefaultsForItems:(NSArray> *)items { BOOL hidePreviews = NSUserDefaults.standardUserDefaults.flex_explorerHidesVariablePreviews; FLEXObjectExplorerDefaults *mutable = [FLEXObjectExplorerDefaults canEdit:YES wantsPreviews:!hidePreviews ]; FLEXObjectExplorerDefaults *immutable = [FLEXObjectExplorerDefaults canEdit:NO wantsPreviews:!hidePreviews ]; // .tag is used to cache whether the value of .isEditable; // This could change at runtime so it is important that // it is cached every time shortcuts are requeted and not // just once at as shortcuts are initially registered for (id metadata in items) { metadata.defaults = metadata.isEditable ? mutable : immutable; } } - (NSString *)objectDescription { if (!_objectDescription) { // Hard-code UIColor description if ([FLEXRuntimeUtility safeObject:self.object isKindOfClass:[UIColor class]]) { CGFloat h, s, l, r, g, b, a; [self.object getRed:&r green:&g blue:&b alpha:&a]; [self.object getHue:&h saturation:&s brightness:&l alpha:nil]; return [NSString stringWithFormat: @"HSL: (%.3f, %.3f, %.3f)\nRGB: (%.3f, %.3f, %.3f)\nAlpha: %.3f", h, s, l, r, g, b, a ]; } NSString *description = [FLEXRuntimeUtility safeDescriptionForObject:self.object]; if (!description.length) { NSString *address = [FLEXUtility addressOfObject:self.object]; return [NSString stringWithFormat:@"Object at %@ returned empty description", address]; } if (description.length > 10000) { description = [description substringToIndex:10000]; } _objectDescription = description; } return _objectDescription; } - (void)setClassScope:(NSInteger)classScope { _classScope = classScope; [self reloadScopedMetadata]; } - (void)reloadMetadata { _allProperties = [NSMutableArray new]; _allClassProperties = [NSMutableArray new]; _allIvars = [NSMutableArray new]; _allMethods = [NSMutableArray new]; _allClassMethods = [NSMutableArray new]; _allConformedProtocols = [NSMutableArray new]; _allInstanceSizes = [NSMutableArray new]; _allImageNames = [NSMutableArray new]; _objectDescription = nil; [self reloadClassHierarchy]; NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; BOOL hideBackingIvars = defaults.flex_explorerHidesPropertyIvars; BOOL hidePropertyMethods = defaults.flex_explorerHidesPropertyMethods; BOOL hidePrivateMethods = defaults.flex_explorerHidesPrivateMethods; BOOL showMethodOverrides = defaults.flex_explorerShowsMethodOverrides; NSMutableArray *> *allProperties = [NSMutableArray new]; NSMutableArray *> *allClassProps = [NSMutableArray new]; NSMutableArray *> *allMethods = [NSMutableArray new]; NSMutableArray *> *allClassMethods = [NSMutableArray new]; // Loop over each class and each superclass, collect // the fresh and unique metadata in each category Class superclass = nil; NSInteger count = self.classHierarchyClasses.count; NSInteger rootIdx = count - 1; for (NSInteger i = 0; i < count; i++) { Class cls = self.classHierarchyClasses[i]; id mirror = [self mirrorForClass:cls]; superclass = (i < rootIdx) ? self.classHierarchyClasses[i+1] : nil; [allProperties addObject:[self metadataUniquedByName:mirror.properties superclass:superclass kind:FLEXMetadataKindProperties skip:showMethodOverrides ]]; [allClassProps addObject:[self metadataUniquedByName:mirror.classProperties superclass:superclass kind:FLEXMetadataKindClassProperties skip:showMethodOverrides ]]; [_allIvars addObject:[self metadataUniquedByName:mirror.ivars superclass:nil kind:FLEXMetadataKindIvars skip:NO ]]; [allMethods addObject:[self metadataUniquedByName:mirror.methods superclass:superclass kind:FLEXMetadataKindMethods skip:showMethodOverrides ]]; [allClassMethods addObject:[self metadataUniquedByName:mirror.classMethods superclass:superclass kind:FLEXMetadataKindClassMethods skip:showMethodOverrides ]]; [_allConformedProtocols addObject:[self metadataUniquedByName:mirror.protocols superclass:superclass kind:FLEXMetadataKindProtocols skip:NO ]]; // TODO: join instance size, image name, and class hierarchy into a single model object // This would greatly reduce the laziness that has begun to manifest itself here [_allInstanceSizes addObject:[FLEXStaticMetadata style:FLEXStaticMetadataRowStyleKeyValue title:@"Instance Size" number:@(class_getInstanceSize(cls)) ]]; [_allImageNames addObject:[FLEXStaticMetadata style:FLEXStaticMetadataRowStyleDefault title:@"Image Name" string:@(class_getImageName(cls) ?: "Created at Runtime") ]]; } _classHierarchy = [FLEXStaticMetadata classHierarchy:self.classHierarchyClasses]; NSArray *> *properties = allProperties; // Potentially filter property-backing ivars if (hideBackingIvars) { NSArray *> *ivars = _allIvars.copy; _allIvars = [ivars flex_mapped:^id(NSArray *list, NSUInteger idx) { // Get a set of all backing ivar names for the current class in the hierarchy NSSet *ivarNames = [NSSet setWithArray:({ [properties[idx] flex_mapped:^id(FLEXProperty *p, NSUInteger idx) { // Nil if no ivar, and array is flatted return p.likelyIvarName; }]; })]; // Remove ivars whose name is in the ivar names list return [list flex_filtered:^BOOL(FLEXIvar *ivar, NSUInteger idx) { return ![ivarNames containsObject:ivar.name]; }]; }]; } // Potentially filter property-backing methods if (hidePropertyMethods) { allMethods = [allMethods flex_mapped:^id(NSArray *list, NSUInteger idx) { // Get a set of all property method names for the current class in the hierarchy NSSet *methodNames = [NSSet setWithArray:({ [properties[idx] flex_flatmapped:^NSArray *(FLEXProperty *p, NSUInteger idx) { if (p.likelyGetterExists) { if (p.likelySetterExists) { return @[p.likelyGetterString, p.likelySetterString]; } return @[p.likelyGetterString]; } else if (p.likelySetterExists) { return @[p.likelySetterString]; } return nil; }]; })]; // Remove methods whose name is in the property method names list return [list flex_filtered:^BOOL(FLEXMethod *method, NSUInteger idx) { return ![methodNames containsObject:method.selectorString]; }]; }]; } if (hidePrivateMethods) { id methodMapBlock = ^id(NSArray *list, NSUInteger idx) { // Remove methods which contain an underscore return [list flex_filtered:^BOOL(FLEXMethod *method, NSUInteger idx) { return ![method.selectorString containsString:@"_"]; }]; }; id propertyMapBlock = ^id(NSArray *list, NSUInteger idx) { // Remove methods which contain an underscore return [list flex_filtered:^BOOL(FLEXProperty *prop, NSUInteger idx) { return ![prop.name containsString:@"_"]; }]; }; allMethods = [allMethods flex_mapped:methodMapBlock]; allClassMethods = [allClassMethods flex_mapped:methodMapBlock]; allProperties = [allProperties flex_mapped:propertyMapBlock]; allClassProps = [allClassProps flex_mapped:propertyMapBlock]; } _allProperties = allProperties; _allClassProperties = allClassProps; _allMethods = allMethods; _allClassMethods = allClassMethods; // Set up UIKit helper data // Really, we only need to call this on properties and ivars // because no other metadata types support editing. NSArray*metadatas = @[ _allProperties, _allClassProperties, _allIvars, /* _allMethods, _allClassMethods, _allConformedProtocols */ ]; for (NSArray *matrix in metadatas) { for (NSArray *metadataByClass in matrix) { [FLEXObjectExplorer configureDefaultsForItems:metadataByClass]; } } [self reloadScopedMetadata]; } #pragma mark - Private - (void)reloadScopedMetadata { _properties = self.allProperties[self.classScope]; _classProperties = self.allClassProperties[self.classScope]; _ivars = self.allIvars[self.classScope]; _methods = self.allMethods[self.classScope]; _classMethods = self.allClassMethods[self.classScope]; _conformedProtocols = self.allConformedProtocols[self.classScope]; _instanceSize = self.allInstanceSizes[self.classScope]; _imageName = self.allImageNames[self.classScope]; } /// Accepts an array of flex metadata objects and discards objects /// with duplicate names, as well as properties and methods which /// aren't "new" (i.e. those which the superclass responds to) - (NSArray *)metadataUniquedByName:(NSArray *)list superclass:(Class)superclass kind:(FLEXMetadataKind)kind skip:(BOOL)skipUniquing { if (skipUniquing) { return list; } // Remove items with same name and return filtered list NSMutableSet *names = [NSMutableSet new]; return [list flex_filtered:^BOOL(id obj, NSUInteger idx) { NSString *name = [obj name]; if ([names containsObject:name]) { return NO; } else { if (!name) { return NO; } [names addObject:name]; // Skip methods and properties which are just overrides, // potentially skip ivars and methods associated with properties switch (kind) { case FLEXMetadataKindProperties: if ([superclass instancesRespondToSelector:[obj likelyGetter]]) { return NO; } break; case FLEXMetadataKindClassProperties: if ([superclass respondsToSelector:[obj likelyGetter]]) { return NO; } break; case FLEXMetadataKindMethods: if ([superclass instancesRespondToSelector:NSSelectorFromString(name)]) { return NO; } break; case FLEXMetadataKindClassMethods: if ([superclass respondsToSelector:NSSelectorFromString(name)]) { return NO; } break; case FLEXMetadataKindProtocols: case FLEXMetadataKindClassHierarchy: case FLEXMetadataKindOther: return YES; // These types are already uniqued break; // Ivars cannot be overidden case FLEXMetadataKindIvars: break; } return YES; } }]; } #pragma mark - Superclasses - (void)reloadClassHierarchy { // The class hierarchy will never contain metaclass objects by this logic; // it is always the same for a given class and instances of it _classHierarchyClasses = [[self.object class] flex_classHierarchy]; } @end #pragma mark - Reflex @implementation FLEXObjectExplorer (Reflex) static BOOL _reflexAvailable = NO; + (BOOL)reflexAvailable { return _reflexAvailable; } + (void)setReflexAvailable:(BOOL)enable { _reflexAvailable = enable; } @end ================================================ FILE: Classes/ObjectExplorers/FLEXObjectExplorerFactory.h ================================================ // // FLEXObjectExplorerFactory.h // Flipboard // // Created by Ryan Olson on 5/15/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXGlobalsEntry.h" #ifndef _FLEXObjectExplorerViewController_h #import "FLEXObjectExplorerViewController.h" #else @class FLEXObjectExplorerViewController; #endif NS_ASSUME_NONNULL_BEGIN @interface FLEXObjectExplorerFactory : NSObject + (nullable FLEXObjectExplorerViewController *)explorerViewControllerForObject:(nullable id)object; /// Register a specific explorer view controller class to be used when exploring /// an object of a specific class. Calls will overwrite existing registrations. /// Sections must be initialized using \c forObject: like + (void)registerExplorerSection:(Class)sectionClass forClass:(Class)objectClass; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ObjectExplorers/FLEXObjectExplorerFactory.m ================================================ // // FLEXObjectExplorerFactory.m // Flipboard // // Created by Ryan Olson on 5/15/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXObjectExplorerFactory.h" #import "FLEXGlobalsViewController.h" #import "FLEXClassShortcuts.h" #import "FLEXViewShortcuts.h" #import "FLEXWindowShortcuts.h" #import "FLEXViewControllerShortcuts.h" #import "FLEXUIAppShortcuts.h" #import "FLEXImageShortcuts.h" #import "FLEXLayerShortcuts.h" #import "FLEXColorPreviewSection.h" #import "FLEXDefaultsContentSection.h" #import "FLEXBundleShortcuts.h" #import "FLEXNSStringShortcuts.h" #import "FLEXNSDataShortcuts.h" #import "FLEXBlockShortcuts.h" #import "FLEXUtility.h" @implementation FLEXObjectExplorerFactory static NSMutableDictionary, Class> *classesToRegisteredSections = nil; + (void)initialize { if (self == [FLEXObjectExplorerFactory class]) { // DO NOT USE STRING KEYS HERE // We NEED to use the class as a key, because we CANNOT // differentiate a class's name from the metaclass's name. // These mappings are per-class-object, not per-class-name. // // For example, if we used class names, this would result in // the object explorer trying to render a color preview for // the UIColor class object, which is not a color itself. #define ClassKey(name) (id)[name class] #define ClassKeyByName(str) (id)NSClassFromString(@ #str) #define MetaclassKey(meta) (id)object_getClass([meta class]) classesToRegisteredSections = [NSMutableDictionary dictionaryWithDictionary:@{ MetaclassKey(NSObject) : [FLEXClassShortcuts class], ClassKey(NSArray) : [FLEXCollectionContentSection class], ClassKey(NSSet) : [FLEXCollectionContentSection class], ClassKey(NSDictionary) : [FLEXCollectionContentSection class], ClassKey(NSOrderedSet) : [FLEXCollectionContentSection class], ClassKey(NSUserDefaults) : [FLEXDefaultsContentSection class], ClassKey(UIViewController) : [FLEXViewControllerShortcuts class], ClassKey(UIApplication) : [FLEXUIAppShortcuts class], ClassKey(UIView) : [FLEXViewShortcuts class], ClassKey(UIWindow) : [FLEXWindowShortcuts class], ClassKey(UIImage) : [FLEXImageShortcuts class], ClassKey(CALayer) : [FLEXLayerShortcuts class], ClassKey(UIColor) : [FLEXColorPreviewSection class], ClassKey(NSBundle) : [FLEXBundleShortcuts class], ClassKey(NSString) : [FLEXNSStringShortcuts class], ClassKey(NSData) : [FLEXNSDataShortcuts class], ClassKeyByName(NSBlock) : [FLEXBlockShortcuts class], }]; #undef ClassKey #undef ClassKeyByName #undef MetaclassKey } } + (FLEXObjectExplorerViewController *)explorerViewControllerForObject:(id)object { // Can't explore nil if (!object) { return nil; } // If we're given an object, this will look up it's class hierarchy // until it finds a registration. This will work for KVC classes, // since they are children of the original class, and not siblings. // If we are given an object, object_getClass will return a metaclass, // and the same thing will happen. FLEXClassShortcuts is the default // shortcut section for NSObject. // // TODO: rename it to FLEXNSObjectShortcuts or something? FLEXShortcutsSection *shortcutsSection = [FLEXShortcutsSection forObject:object]; NSArray *sections = @[shortcutsSection]; Class customSectionClass = nil; Class cls = object_getClass(object); do { customSectionClass = classesToRegisteredSections[(id)cls]; } while (!customSectionClass && (cls = [cls superclass])); if (customSectionClass) { id customSection = [customSectionClass forObject:object]; BOOL isFLEXShortcutSection = [customSection respondsToSelector:@selector(isNewSection)]; // If the section "replaces" the default shortcuts section, // only return that section. Otherwise, return both this // section and the default shortcuts section. if (isFLEXShortcutSection && ![customSection isNewSection]) { sections = @[customSection]; } else { // Custom section will go before shortcuts sections = @[customSection, shortcutsSection]; } } return [FLEXObjectExplorerViewController exploringObject:object customSections:sections ]; } + (void)registerExplorerSection:(Class)explorerClass forClass:(Class)objectClass { classesToRegisteredSections[(id)objectClass] = explorerClass; } #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { switch (row) { case FLEXGlobalsRowAppDelegate: return @"🎟 App Delegate"; case FLEXGlobalsRowKeyWindow: return @"🔑 Key Window"; case FLEXGlobalsRowRootViewController: return @"🌴 Root View Controller"; case FLEXGlobalsRowProcessInfo: return @"🚦 NSProcessInfo.processInfo"; case FLEXGlobalsRowUserDefaults: return @"💾 Preferences"; case FLEXGlobalsRowMainBundle: return @"📦 NSBundle.mainBundle"; case FLEXGlobalsRowApplication: return @"🚀 UIApplication.sharedApplication"; case FLEXGlobalsRowMainScreen: return @"💻 UIScreen.mainScreen"; case FLEXGlobalsRowCurrentDevice: return @"📱 UIDevice.currentDevice"; case FLEXGlobalsRowPasteboard: return @"📋 UIPasteboard.generalPasteboard"; case FLEXGlobalsRowURLSession: return @"📡 NSURLSession.sharedSession"; case FLEXGlobalsRowURLCache: return @"⏳ NSURLCache.sharedURLCache"; case FLEXGlobalsRowNotificationCenter: return @"🔔 NSNotificationCenter.defaultCenter"; case FLEXGlobalsRowMenuController: return @"📎 UIMenuController.sharedMenuController"; case FLEXGlobalsRowFileManager: return @"🗄 NSFileManager.defaultManager"; case FLEXGlobalsRowTimeZone: return @"🌎 NSTimeZone.systemTimeZone"; case FLEXGlobalsRowLocale: return @"🗣 NSLocale.currentLocale"; case FLEXGlobalsRowCalendar: return @"📅 NSCalendar.currentCalendar"; case FLEXGlobalsRowMainRunLoop: return @"🏃🏻‍♂️ NSRunLoop.mainRunLoop"; case FLEXGlobalsRowMainThread: return @"🧵 NSThread.mainThread"; case FLEXGlobalsRowOperationQueue: return @"📚 NSOperationQueue.mainQueue"; default: return nil; } } + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { switch (row) { case FLEXGlobalsRowAppDelegate: { id appDelegate = UIApplication.sharedApplication.delegate; return [self explorerViewControllerForObject:appDelegate]; } case FLEXGlobalsRowProcessInfo: return [self explorerViewControllerForObject:NSProcessInfo.processInfo]; case FLEXGlobalsRowUserDefaults: return [self explorerViewControllerForObject:NSUserDefaults.standardUserDefaults]; case FLEXGlobalsRowMainBundle: return [self explorerViewControllerForObject:NSBundle.mainBundle]; case FLEXGlobalsRowApplication: return [self explorerViewControllerForObject:UIApplication.sharedApplication]; case FLEXGlobalsRowMainScreen: return [self explorerViewControllerForObject:UIScreen.mainScreen]; case FLEXGlobalsRowCurrentDevice: return [self explorerViewControllerForObject:UIDevice.currentDevice]; case FLEXGlobalsRowPasteboard: return [self explorerViewControllerForObject:UIPasteboard.generalPasteboard]; case FLEXGlobalsRowURLSession: return [self explorerViewControllerForObject:NSURLSession.sharedSession]; case FLEXGlobalsRowURLCache: return [self explorerViewControllerForObject:NSURLCache.sharedURLCache]; case FLEXGlobalsRowNotificationCenter: return [self explorerViewControllerForObject:NSNotificationCenter.defaultCenter]; case FLEXGlobalsRowMenuController: return [self explorerViewControllerForObject:UIMenuController.sharedMenuController]; case FLEXGlobalsRowFileManager: return [self explorerViewControllerForObject:NSFileManager.defaultManager]; case FLEXGlobalsRowTimeZone: return [self explorerViewControllerForObject:NSTimeZone.systemTimeZone]; case FLEXGlobalsRowLocale: return [self explorerViewControllerForObject:NSLocale.currentLocale]; case FLEXGlobalsRowCalendar: return [self explorerViewControllerForObject:NSCalendar.currentCalendar]; case FLEXGlobalsRowMainRunLoop: return [self explorerViewControllerForObject:NSRunLoop.mainRunLoop]; case FLEXGlobalsRowMainThread: return [self explorerViewControllerForObject:NSThread.mainThread]; case FLEXGlobalsRowOperationQueue: return [self explorerViewControllerForObject:NSOperationQueue.mainQueue]; case FLEXGlobalsRowKeyWindow: return [FLEXObjectExplorerFactory explorerViewControllerForObject:FLEXUtility.appKeyWindow ]; case FLEXGlobalsRowRootViewController: { id delegate = UIApplication.sharedApplication.delegate; if ([delegate respondsToSelector:@selector(window)]) { return [self explorerViewControllerForObject:delegate.window.rootViewController]; } return nil; } case FLEXGlobalsRowNetworkHistory: case FLEXGlobalsRowSystemLog: case FLEXGlobalsRowLiveObjects: case FLEXGlobalsRowAddressInspector: case FLEXGlobalsRowCookies: case FLEXGlobalsRowBrowseRuntime: case FLEXGlobalsRowAppKeychainItems: case FLEXGlobalsRowPushNotifications: case FLEXGlobalsRowBrowseBundle: case FLEXGlobalsRowBrowseContainer: case FLEXGlobalsRowCount: return nil; } return nil; } + (FLEXGlobalsEntryRowAction)globalsEntryRowAction:(FLEXGlobalsRow)row { switch (row) { case FLEXGlobalsRowRootViewController: { // Check if the app delegate responds to -window. If not, present an alert return ^(UITableViewController *host) { id delegate = UIApplication.sharedApplication.delegate; if ([delegate respondsToSelector:@selector(window)]) { UIViewController *explorer = [self explorerViewControllerForObject: delegate.window.rootViewController ]; [host.navigationController pushViewController:explorer animated:YES]; } else { NSString *msg = @"The app delegate doesn't respond to -window"; [FLEXAlert showAlert:@":(" message:msg from:host]; } }; } default: return nil; } } @end ================================================ FILE: Classes/ObjectExplorers/FLEXObjectExplorerViewController.h ================================================ // // FLEXObjectExplorerViewController.h // Flipboard // // Created by Ryan Olson on 2014-05-03. // Copyright (c) 2020 FLEX Team. All rights reserved. // #ifndef _FLEXObjectExplorerViewController_h #define _FLEXObjectExplorerViewController_h #endif #import "FLEXFilteringTableViewController.h" #import "FLEXObjectExplorer.h" @class FLEXTableViewSection; NS_ASSUME_NONNULL_BEGIN /// A class that displays information about an object or class. /// /// The explorer view controller uses \c FLEXObjectExplorer to provide a description /// of the object and list it's properties, ivars, methods, and it's superclasses. /// Below the description and before properties, some shortcuts will be displayed /// for certain classes like UIViews. At very bottom, there is an option to view /// a list of other objects found to be referencing the object being explored. @interface FLEXObjectExplorerViewController : FLEXFilteringTableViewController /// Uses the default \c FLEXShortcutsSection for this object as a custom section. + (instancetype)exploringObject:(id)objectOrClass; /// No custom section unless you provide one. + (instancetype)exploringObject:(id)objectOrClass customSection:(nullable FLEXTableViewSection *)customSection; /// No custom sections unless you provide some. + (instancetype)exploringObject:(id)objectOrClass customSections:(nullable NSArray *)customSections; /// The object being explored, which may be an instance of a class or a class itself. @property (nonatomic, readonly) id object; /// This object provides the object's metadata for the explorer view controller. @property (nonatomic, readonly) FLEXObjectExplorer *explorer; /// Called once to initialize the list of section objects. /// /// Subclasses can override this to add, remove, or rearrange sections of the explorer. - (NSArray *)makeSections; /// Whether to allow showing/drilling in to current values for ivars and properties. Default is YES. @property (nonatomic, readonly) BOOL canHaveInstanceState; /// Whether to allow drilling in to method calling interfaces for instance methods. Default is YES. @property (nonatomic, readonly) BOOL canCallInstanceMethods; /// If the custom section data makes the description redundant, subclasses can choose to hide it. Default is YES. @property (nonatomic, readonly) BOOL shouldShowDescription; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ObjectExplorers/FLEXObjectExplorerViewController.m ================================================ // // FLEXObjectExplorerViewController.m // Flipboard // // Created by Ryan Olson on 2014-05-03. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXObjectExplorerViewController.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "UIBarButtonItem+FLEX.h" #import "FLEXMultilineTableViewCell.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXFieldEditorViewController.h" #import "FLEXMethodCallingViewController.h" #import "FLEXObjectListViewController.h" #import "FLEXTabsViewController.h" #import "FLEXBookmarkManager.h" #import "FLEXTableView.h" #import "FLEXResources.h" #import "FLEXTableViewCell.h" #import "FLEXScopeCarousel.h" #import "FLEXMetadataSection.h" #import "FLEXSingleRowSection.h" #import "FLEXShortcutsSection.h" #import "NSUserDefaults+FLEX.h" #import #pragma mark - Private properties @interface FLEXObjectExplorerViewController () @property (nonatomic, readonly) FLEXSingleRowSection *descriptionSection; @property (nonatomic, readonly) NSArray *customSections; @property (nonatomic) NSIndexSet *customSectionVisibleIndexes; @property (nonatomic, readonly) NSArray *observedNotifications; @end @implementation FLEXObjectExplorerViewController #pragma mark - Initialization + (instancetype)exploringObject:(id)target { return [self exploringObject:target customSection:[FLEXShortcutsSection forObject:target]]; } + (instancetype)exploringObject:(id)target customSection:(FLEXTableViewSection *)section { return [self exploringObject:target customSections:@[section]]; } + (instancetype)exploringObject:(id)target customSections:(NSArray *)customSections { return [[self alloc] initWithObject:target explorer:[FLEXObjectExplorer forObject:target] customSections:customSections ]; } - (id)initWithObject:(id)target explorer:(__kindof FLEXObjectExplorer *)explorer customSections:(NSArray *)customSections { NSParameterAssert(target); self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { _object = target; _explorer = explorer; _customSections = customSections; } return self; } - (NSArray *)observedNotifications { return @[ kFLEXDefaultsHidePropertyIvarsKey, kFLEXDefaultsHidePropertyMethodsKey, kFLEXDefaultsHidePrivateMethodsKey, kFLEXDefaultsShowMethodOverridesKey, kFLEXDefaultsHideVariablePreviewsKey, ]; } #pragma mark - View controller lifecycle - (void)viewDidLoad { [super viewDidLoad]; self.showsShareToolbarItem = YES; self.wantsSectionIndexTitles = YES; // Use [object class] here rather than object_getClass // to avoid the KVO prefix for observed objects self.title = [FLEXRuntimeUtility safeClassNameForObject:self.object]; // Search self.showsSearchBar = YES; self.searchBarDebounceInterval = kFLEXDebounceInstant; self.showsCarousel = YES; // Carousel scope bar [self.explorer reloadClassHierarchy]; self.carousel.items = [self.explorer.classHierarchyClasses flex_mapped:^id(Class cls, NSUInteger idx) { return NSStringFromClass(cls); }]; // ... button for extra options [self addToolbarItems:@[[UIBarButtonItem flex_itemWithImage:FLEXResources.moreIcon target:self action:@selector(moreButtonPressed:) ]]]; // Swipe gestures to swipe between classes in the hierarchy UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:) ]; UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:) ]; leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft; rightSwipe.direction = UISwipeGestureRecognizerDirectionRight; leftSwipe.delegate = self; rightSwipe.delegate = self; [self.tableView addGestureRecognizer:leftSwipe]; [self.tableView addGestureRecognizer:rightSwipe]; // Observe preferences which may change on other screens // // "If your app targets iOS 9.0 and later or macOS 10.11 and later, // you don't need to unregister an observer in its dealloc method." for (NSString *pref in self.observedNotifications) { [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(fullyReloadData) name:pref object:nil ]; } } - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { [self.navigationController setToolbarHidden:NO animated:YES]; return YES; } #pragma mark - Overrides /// Override to hide the description section when searching - (NSArray *)nonemptySections { if (self.shouldShowDescription) { return super.nonemptySections; } return [super.nonemptySections flex_filtered:^BOOL(FLEXTableViewSection *section, NSUInteger idx) { return section != self.descriptionSection; }]; } - (NSArray *)makeSections { FLEXObjectExplorer *explorer = self.explorer; // Description section is only for instances if (self.explorer.objectIsInstance) { _descriptionSection = [FLEXSingleRowSection title:@"Description" reuse:kFLEXMultilineCell cell:^(FLEXTableViewCell *cell) { cell.titleLabel.font = UIFont.flex_defaultTableCellFont; cell.titleLabel.text = explorer.objectDescription; } ]; self.descriptionSection.filterMatcher = ^BOOL(NSString *filterText) { return [explorer.objectDescription localizedCaseInsensitiveContainsString:filterText]; }; } // Object graph section FLEXSingleRowSection *referencesSection = [FLEXSingleRowSection title:@"Object Graph" reuse:kFLEXDefaultCell cell:^(FLEXTableViewCell *cell) { cell.titleLabel.text = @"See Objects with References to This Object"; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } ]; referencesSection.selectionAction = ^(UIViewController *host) { UIViewController *references = [FLEXObjectListViewController objectsWithReferencesToObject:explorer.object retained:NO ]; [host.navigationController pushViewController:references animated:YES]; }; NSMutableArray *sections = [NSMutableArray arrayWithArray:@[ [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindProperties], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindClassProperties], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindIvars], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindMethods], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindClassMethods], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindClassHierarchy], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindProtocols], [FLEXMetadataSection explorer:self.explorer kind:FLEXMetadataKindOther], referencesSection ]]; if (self.customSections) { [sections insertObjects:self.customSections atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.customSections.count) ]]; } if (self.descriptionSection) { [sections insertObject:self.descriptionSection atIndex:0]; } return sections.copy; } /// In our case, all this does is reload the table view, /// or reload the sections' data if we changed places /// in the class hierarchy. Doesn't refresh \c self.explorer - (void)reloadData { // Check to see if class scope changed, update accordingly if (self.explorer.classScope != self.selectedScope) { self.explorer.classScope = self.selectedScope; [self reloadSections]; } [super reloadData]; } - (void)shareButtonPressed:(UIBarButtonItem *)sender { [FLEXAlert makeSheet:^(FLEXAlert *make) { make.button(@"Add to Bookmarks").handler(^(NSArray *strings) { [FLEXBookmarkManager.bookmarks addObject:self.object]; }); make.button(@"Copy Description").handler(^(NSArray *strings) { UIPasteboard.generalPasteboard.string = self.explorer.objectDescription; }); make.button(@"Copy Address").handler(^(NSArray *strings) { UIPasteboard.generalPasteboard.string = [FLEXUtility addressOfObject:self.object]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } #pragma mark - Private /// Unlike \c -reloadData, this refreshes everything, including the explorer. - (void)fullyReloadData { [self.explorer reloadMetadata]; [self reloadSections]; [self reloadData]; } - (void)handleSwipeGesture:(UISwipeGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateEnded) { switch (gesture.direction) { case UISwipeGestureRecognizerDirectionRight: if (self.selectedScope > 0) { self.selectedScope -= 1; } break; case UISwipeGestureRecognizerDirectionLeft: if (self.selectedScope != self.explorer.classHierarchy.count - 1) { self.selectedScope += 1; } break; default: break; } } } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 { // Prioritize important pan gestures over our swipe gesture if ([g2 isKindOfClass:[UIPanGestureRecognizer class]]) { if (g2 == self.navigationController.interactivePopGestureRecognizer) { return NO; } if (g2 == self.tableView.panGestureRecognizer) { return NO; } } return YES; } - (BOOL)gestureRecognizerShouldBegin:(UISwipeGestureRecognizer *)gesture { // Don't allow swiping from the carousel CGPoint location = [gesture locationInView:self.tableView]; if ([self.carousel hitTest:location withEvent:nil]) { return NO; } return YES; } - (void)moreButtonPressed:(UIBarButtonItem *)sender { NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; // Maps preference keys to a description of what they affect NSDictionary *explorerToggles = @{ kFLEXDefaultsHidePropertyIvarsKey: @"Property-Backing Ivars", kFLEXDefaultsHidePropertyMethodsKey: @"Property-Backing Methods", kFLEXDefaultsHidePrivateMethodsKey: @"Likely Private Methods", kFLEXDefaultsShowMethodOverridesKey: @"Method Overrides", kFLEXDefaultsHideVariablePreviewsKey: @"Variable Previews" }; // Maps the key of the action itself to a map of a description // of the action ("hide X") mapped to the current state. // // So keys that are hidden by default have NO mapped to "Show" NSDictionary *nextStateDescriptions = @{ kFLEXDefaultsHidePropertyIvarsKey: @{ @NO: @"Hide ", @YES: @"Show " }, kFLEXDefaultsHidePropertyMethodsKey: @{ @NO: @"Hide ", @YES: @"Show " }, kFLEXDefaultsHidePrivateMethodsKey: @{ @NO: @"Hide ", @YES: @"Show " }, kFLEXDefaultsShowMethodOverridesKey: @{ @NO: @"Show ", @YES: @"Hide " }, kFLEXDefaultsHideVariablePreviewsKey: @{ @NO: @"Hide ", @YES: @"Show " }, }; [FLEXAlert makeSheet:^(FLEXAlert *make) { make.title(@"Options"); for (NSString *option in explorerToggles.allKeys) { BOOL current = [defaults boolForKey:option]; NSString *title = [nextStateDescriptions[option][@(current)] stringByAppendingString:explorerToggles[option] ]; make.button(title).handler(^(NSArray *strings) { [NSUserDefaults.standardUserDefaults flex_toggleBoolForKey:option]; [self fullyReloadData]; }); } make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } #pragma mark - Description - (BOOL)shouldShowDescription { // Hide if we have filter text; it is rarely // useful to see the description when searching // since it's already at the top of the screen if (self.filterText.length) { return NO; } return YES; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // For the description section, we want that nice slim/snug looking row. // Other rows use the automatic size. FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section]; if (section == self.descriptionSection) { NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.explorer.objectDescription attributes:@{ NSFontAttributeName : UIFont.flex_defaultTableCellFont } ]; return [FLEXMultilineTableViewCell preferredHeightWithAttributedText:attributedText maxWidth:tableView.frame.size.width - tableView.separatorInset.right style:tableView.style showsAccessory:NO ]; } return UITableViewAutomaticDimension; } - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { return self.filterDelegate.sections[indexPath.section] == self.descriptionSection; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { // Only the description section has "actions" if (self.filterDelegate.sections[indexPath.section] == self.descriptionSection) { return action == @selector(copy:); } return NO; } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if (action == @selector(copy:)) { UIPasteboard.generalPasteboard.string = self.explorer.objectDescription; } } @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXCollectionContentSection.h ================================================ // // FLEXCollectionContentSection.h // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewSection.h" #import "FLEXObjectInfoSection.h" @class FLEXCollectionContentSection, FLEXTableViewCell; @protocol FLEXCollection, FLEXMutableCollection; /// Any foundation collection implicitly conforms to FLEXCollection. /// This future should return one. We don't explicitly put FLEXCollection /// here because making generic collections conform to FLEXCollection breaks /// compile-time features of generic arrays, such as \c someArray[0].property typedef id(^FLEXCollectionContentFuture)(__kindof FLEXCollectionContentSection *section); #pragma mark Collection /// A protocol that enables \c FLEXCollectionContentSection to operate on any arbitrary collection. /// \c NSArray, \c NSDictionary, \c NSSet, and \c NSOrderedSet all conform to this protocol. @protocol FLEXCollection @property (nonatomic, readonly) NSUInteger count; - (id)copy; - (id)mutableCopy; @optional /// Unordered, unkeyed collections must implement this @property (nonatomic, readonly) NSArray *allObjects; /// Keyed collections must implement this and \c objectForKeyedSubscript: @property (nonatomic, readonly) NSArray *allKeys; /// Ordered, indexed collections must implement this. - (id)objectAtIndexedSubscript:(NSUInteger)idx; /// Keyed, unordered collections must implement this and \c allKeys - (id)objectForKeyedSubscript:(id)idx; @end @protocol FLEXMutableCollection - (void)filterUsingPredicate:(NSPredicate *)predicate; @end #pragma mark - FLEXCollectionContentSection /// A custom section for viewing collection elements. /// /// Tapping on a row pushes an object explorer for that element. @interface FLEXCollectionContentSection<__covariant ObjectType> : FLEXTableViewSection { @protected /// Unused if initialized with a future id _collection; /// Unused if initialized with a collection FLEXCollectionContentFuture _collectionFuture; /// The filtered collection from \c _collection or \c _collectionFuture id _cachedCollection; } + (instancetype)forCollection:(id)collection; /// The future given should be safe to call more than once. /// The result of calling this future multiple times may yield /// different results each time if the data is changing by nature. + (instancetype)forReusableFuture:(FLEXCollectionContentFuture)collectionFuture; /// Defaults to \c NO @property (nonatomic) BOOL hideSectionTitle; /// Defaults to \c nil @property (nonatomic, copy) NSString *customTitle; /// Defaults to \c NO /// /// Settings this to \c NO will not display the element index for ordered collections. /// This property only applies to \c NSArray or \c NSOrderedSet and their subclasses. @property (nonatomic) BOOL hideOrderIndexes; /// Set this property to provide a custom filter matcher. /// /// By default, the collection will filter on the title and subtitle of the row. /// So if you don't ever call \c configureCell: for example, you will need to set /// this property so that your filter logic will match how you're setting up the cell. @property (nonatomic) BOOL (^customFilter)(NSString *filterText, ObjectType element); /// Get the object in the collection associated with the given row. /// For dictionaries, this returns the value, not the key. - (ObjectType)objectForRow:(NSInteger)row; /// Subclasses may override. - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row; @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXCollectionContentSection.m ================================================ // // FLEXCollectionContentSection.m // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXCollectionContentSection.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "FLEXSubtitleTableViewCell.h" #import "FLEXTableView.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXDefaultEditorViewController.h" typedef NS_ENUM(NSUInteger, FLEXCollectionType) { FLEXUnsupportedCollection, FLEXOrderedCollection, FLEXUnorderedCollection, FLEXKeyedCollection }; @interface NSArray (FLEXCollection) @end @interface NSSet (FLEXCollection) @end @interface NSOrderedSet (FLEXCollection) @end @interface NSDictionary (FLEXCollection) @end @interface NSMutableArray (FLEXMutableCollection) @end @interface NSMutableSet (FLEXMutableCollection) @end @interface NSMutableOrderedSet (FLEXMutableCollection) @end @interface NSMutableDictionary (FLEXMutableCollection) - (void)filterUsingPredicate:(NSPredicate *)predicate; @end @interface FLEXCollectionContentSection () /// Generated from \c collectionFuture or \c collection @property (nonatomic, copy) id cachedCollection; /// A static collection to display @property (nonatomic, readonly) id collection; /// A collection that may change over time and can be called upon for new data @property (nonatomic, readonly) FLEXCollectionContentFuture collectionFuture; @property (nonatomic, readonly) FLEXCollectionType collectionType; @property (nonatomic, readonly) BOOL isMutable; @end @implementation FLEXCollectionContentSection @synthesize filterText = _filterText; #pragma mark Initialization + (instancetype)forObject:(id)object { return [self forCollection:object]; } + (id)forCollection:(id)collection { FLEXCollectionContentSection *section = [self new]; section->_collectionType = [self typeForCollection:collection]; section->_collection = collection; section.cachedCollection = collection; section->_isMutable = [collection respondsToSelector:@selector(filterUsingPredicate:)]; return section; } + (id)forReusableFuture:(FLEXCollectionContentFuture)collectionFuture { FLEXCollectionContentSection *section = [self new]; section->_collectionFuture = collectionFuture; section.cachedCollection = (id)collectionFuture(section); section->_collectionType = [self typeForCollection:section.cachedCollection]; section->_isMutable = [section->_cachedCollection respondsToSelector:@selector(filterUsingPredicate:)]; return section; } #pragma mark - Misc + (FLEXCollectionType)typeForCollection:(id)collection { // Order matters here, as NSDictionary is keyed but it responds to allObjects if ([collection respondsToSelector:@selector(objectAtIndex:)]) { return FLEXOrderedCollection; } if ([collection respondsToSelector:@selector(objectForKey:)]) { return FLEXKeyedCollection; } if ([collection respondsToSelector:@selector(allObjects)]) { return FLEXUnorderedCollection; } [NSException raise:NSInvalidArgumentException format:@"Given collection does not properly conform to FLEXCollection"]; return FLEXUnsupportedCollection; } /// Row titles /// - Ordered: the index /// - Unordered: the object /// - Keyed: the key - (NSString *)titleForRow:(NSInteger)row { switch (self.collectionType) { case FLEXOrderedCollection: if (!self.hideOrderIndexes) { return @(row).stringValue; } // Fall-through case FLEXUnorderedCollection: return [self describe:[self objectForRow:row]]; case FLEXKeyedCollection: return [self describe:self.cachedCollection.allKeys[row]]; case FLEXUnsupportedCollection: return nil; } } /// Row subtitles /// - Ordered: the object /// - Unordered: nothing /// - Keyed: the value - (NSString *)subtitleForRow:(NSInteger)row { switch (self.collectionType) { case FLEXOrderedCollection: if (!self.hideOrderIndexes) { nil; } // Fall-through case FLEXKeyedCollection: return [self describe:[self objectForRow:row]]; case FLEXUnorderedCollection: return nil; case FLEXUnsupportedCollection: return nil; } } - (NSString *)describe:(id)object { return [FLEXRuntimeUtility summaryForObject:object]; } - (id)objectForRow:(NSInteger)row { switch (self.collectionType) { case FLEXOrderedCollection: return self.cachedCollection[row]; case FLEXUnorderedCollection: return self.cachedCollection.allObjects[row]; case FLEXKeyedCollection: return self.cachedCollection[self.cachedCollection.allKeys[row]]; case FLEXUnsupportedCollection: return nil; } } - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row { return UITableViewCellAccessoryDisclosureIndicator; // return self.isMutable ? UITableViewCellAccessoryDetailDisclosureButton : UITableViewCellAccessoryDisclosureIndicator; } #pragma mark - Overrides - (NSString *)title { if (!self.hideSectionTitle) { if (self.customTitle) { return self.customTitle; } return FLEXPluralString(self.cachedCollection.count, @"Entries", @"Entry"); } return nil; } - (NSInteger)numberOfRows { return self.cachedCollection.count; } - (void)setFilterText:(NSString *)filterText { super.filterText = filterText; if (filterText.length) { BOOL (^matcher)(id, id) = self.customFilter ?: ^BOOL(NSString *query, id obj) { return [[self describe:obj] localizedCaseInsensitiveContainsString:query]; }; NSPredicate *filter = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) { return matcher(filterText, obj); }]; id tmp = self.cachedCollection.mutableCopy; [tmp filterUsingPredicate:filter]; self.cachedCollection = tmp; } else { self.cachedCollection = self.collection ?: (id)self.collectionFuture(self); } } - (void)reloadData { if (self.collectionFuture) { self.cachedCollection = (id)self.collectionFuture(self); } else { self.cachedCollection = self.collection.copy; } } - (BOOL)canSelectRow:(NSInteger)row { return YES; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { return [FLEXObjectExplorerFactory explorerViewControllerForObject:[self objectForRow:row]]; } - (NSString *)reuseIdentifierForRow:(NSInteger)row { return kFLEXDetailCell; } - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row { cell.titleLabel.text = [self titleForRow:row]; cell.subtitleLabel.text = [self subtitleForRow:row]; cell.accessoryType = [self accessoryTypeForRow:row]; } @end #pragma mark - NSMutableDictionary @implementation NSMutableDictionary (FLEXMutableCollection) - (void)filterUsingPredicate:(NSPredicate *)predicate { id test = ^BOOL(id key, NSUInteger idx, BOOL *stop) { if ([predicate evaluateWithObject:key]) { return NO; } return ![predicate evaluateWithObject:self[key]]; }; NSArray *keys = self.allKeys; NSIndexSet *remove = [keys indexesOfObjectsPassingTest:test]; [self removeObjectsForKeys:[keys objectsAtIndexes:remove]]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXColorPreviewSection.h ================================================ // // FLEXColorPreviewSection.h // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXSingleRowSection.h" #import "FLEXObjectInfoSection.h" @interface FLEXColorPreviewSection : FLEXSingleRowSection + (instancetype)forObject:(UIColor *)color; @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXColorPreviewSection.m ================================================ // // FLEXColorPreviewSection.m // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXColorPreviewSection.h" @implementation FLEXColorPreviewSection + (instancetype)forObject:(UIColor *)color { return [self title:@"Color" reuse:nil cell:^(__kindof UITableViewCell *cell) { cell.backgroundColor = color; }]; } - (BOOL)canSelectRow:(NSInteger)row { return NO; } - (BOOL (^)(NSString *))filterMatcher { return ^BOOL(NSString *filterText) { // Hide when searching return !filterText.length; }; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXDefaultsContentSection.h ================================================ // // FLEXDefaultsContentSection.h // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXCollectionContentSection.h" #import "FLEXObjectInfoSection.h" @interface FLEXDefaultsContentSection : FLEXCollectionContentSection /// Uses \c NSUserDefaults.standardUserDefaults + (instancetype)standard; + (instancetype)forDefaults:(NSUserDefaults *)userDefaults; /// Whether or not to filter out keys not present in the app's user defaults file. /// /// This is useful for filtering out some useless keys that seem to appear /// in every app's defaults but are never actually used or touched by the app. /// Only applies to instances using \c NSUserDefaults.standardUserDefaults. /// This is the default for any instance using \c standardUserDefaults, so /// you must opt-out in those instances if you don't want this behavior. @property (nonatomic) BOOL onlyShowKeysForAppPrefs; @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXDefaultsContentSection.m ================================================ // // FLEXDefaultsContentSection.m // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXDefaultsContentSection.h" #import "FLEXDefaultEditorViewController.h" #import "FLEXUtility.h" @interface FLEXDefaultsContentSection () @property (nonatomic) NSUserDefaults *defaults; @property (nonatomic) NSArray *keys; @property (nonatomic, readonly) NSDictionary *unexcludedDefaults; @end @implementation FLEXDefaultsContentSection @synthesize keys = _keys; #pragma mark Initialization + (instancetype)forObject:(id)object { return [self forDefaults:object]; } + (instancetype)standard { return [self forDefaults:NSUserDefaults.standardUserDefaults]; } + (instancetype)forDefaults:(NSUserDefaults *)userDefaults { FLEXDefaultsContentSection *section = [self forReusableFuture:^id(FLEXDefaultsContentSection *section) { section.defaults = userDefaults; section.onlyShowKeysForAppPrefs = YES; return section.unexcludedDefaults; }]; return section; } #pragma mark - Overrides - (NSString *)title { return @"Defaults"; } - (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row { return ^(UIViewController *host) { if ([FLEXDefaultEditorViewController canEditDefaultWithValue:[self objectForRow:row]]) { // We use titleForRow: to get the key because self.keys is not // necessarily in the same order as the keys being displayed FLEXVariableEditorViewController *controller = [FLEXDefaultEditorViewController target:self.defaults key:[self titleForRow:row] commitHandler:^{ [self reloadData:YES]; } ]; [host.navigationController pushViewController:controller animated:YES]; } else { [FLEXAlert showAlert:@"Oh No…" message:@"We can't edit this entry :(" from:host]; } }; } - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row { return UITableViewCellAccessoryDetailDisclosureButton; } #pragma mark - Private - (NSArray *)keys { if (!_keys) { if (self.onlyShowKeysForAppPrefs) { // Read keys from preferences file NSString *bundle = NSBundle.mainBundle.bundleIdentifier; NSString *prefsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences"]; NSString *filePath = [NSString stringWithFormat:@"%@/%@.plist", prefsPath, bundle]; self.keys = [NSDictionary dictionaryWithContentsOfFile:filePath].allKeys; } else { self.keys = self.defaults.dictionaryRepresentation.allKeys; } } return _keys; } - (void)setKeys:(NSArray *)keys { _keys = [keys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; } - (NSDictionary *)unexcludedDefaults { // Case: no excluding if (!self.onlyShowKeysForAppPrefs) { return self.defaults.dictionaryRepresentation; } // Always regenerate key allowlist when this method is called _keys = nil; // Generate new dictionary from unexcluded keys NSArray *values = [self.defaults.dictionaryRepresentation objectsForKeys:self.keys notFoundMarker:NSNull.null ]; return [NSDictionary dictionaryWithObjects:values forKeys:self.keys]; } #pragma mark - Public - (void)setOnlyShowKeysForAppPrefs:(BOOL)onlyShowKeysForAppPrefs { if (onlyShowKeysForAppPrefs) { // This property only applies if we're using standardUserDefaults if (self.defaults != NSUserDefaults.standardUserDefaults) return; } _onlyShowKeysForAppPrefs = onlyShowKeysForAppPrefs; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXMetadataSection.h ================================================ // // FLEXMetadataSection.h // FLEX // // Created by Tanner Bennett on 9/19/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewSection.h" #import "FLEXObjectExplorer.h" typedef NS_ENUM(NSUInteger, FLEXMetadataKind) { FLEXMetadataKindProperties = 1, FLEXMetadataKindClassProperties, FLEXMetadataKindIvars, FLEXMetadataKindMethods, FLEXMetadataKindClassMethods, FLEXMetadataKindClassHierarchy, FLEXMetadataKindProtocols, FLEXMetadataKindOther }; /// This section is used for displaying ObjC runtime metadata /// about a class or object, such as listing methods, properties, etc. @interface FLEXMetadataSection : FLEXTableViewSection + (instancetype)explorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind; @property (nonatomic, readonly) FLEXMetadataKind metadataKind; /// The names of metadata to exclude. Useful if you wish to group specific /// properties or methods together in their own section outside of this one. /// /// Setting this property calls \c reloadData on this section. @property (nonatomic) NSSet *excludedMetadata; @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXMetadataSection.m ================================================ // // FLEXMetadataSection.m // FLEX // // Created by Tanner Bennett on 9/19/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXMetadataSection.h" #import "FLEXTableView.h" #import "FLEXTableViewCell.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXFieldEditorViewController.h" #import "FLEXMethodCallingViewController.h" #import "FLEXIvar.h" #import "NSArray+FLEX.h" #import "FLEXRuntime+UIKitHelpers.h" @interface FLEXMetadataSection () @property (nonatomic, readonly) FLEXObjectExplorer *explorer; /// Filtered @property (nonatomic, copy) NSArray> *metadata; /// Unfiltered @property (nonatomic, copy) NSArray> *allMetadata; @end @implementation FLEXMetadataSection #pragma mark - Initialization + (instancetype)explorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind { return [[self alloc] initWithExplorer:explorer kind:metadataKind]; } - (id)initWithExplorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind { self = [super init]; if (self) { _explorer = explorer; _metadataKind = metadataKind; [self reloadData]; } return self; } #pragma mark - Private - (NSString *)titleWithBaseName:(NSString *)baseName { unsigned long totalCount = self.allMetadata.count; unsigned long filteredCount = self.metadata.count; if (totalCount == filteredCount) { return [baseName stringByAppendingFormat:@" (%lu)", totalCount]; } else { return [baseName stringByAppendingFormat:@" (%lu of %lu)", filteredCount, totalCount]; } } - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row { return [self.metadata[row] suggestedAccessoryTypeWithTarget:self.explorer.object]; } #pragma mark - Public - (void)setExcludedMetadata:(NSSet *)excludedMetadata { _excludedMetadata = excludedMetadata; [self reloadData]; } #pragma mark - Overrides - (NSString *)titleForRow:(NSInteger)row { return [self.metadata[row] description]; } - (NSString *)subtitleForRow:(NSInteger)row { return [self.metadata[row] previewWithTarget:self.explorer.object]; } - (NSString *)title { switch (self.metadataKind) { case FLEXMetadataKindProperties: return [self titleWithBaseName:@"Properties"]; case FLEXMetadataKindClassProperties: return [self titleWithBaseName:@"Class Properties"]; case FLEXMetadataKindIvars: return [self titleWithBaseName:@"Ivars"]; case FLEXMetadataKindMethods: return [self titleWithBaseName:@"Methods"]; case FLEXMetadataKindClassMethods: return [self titleWithBaseName:@"Class Methods"]; case FLEXMetadataKindClassHierarchy: return [self titleWithBaseName:@"Class Hierarchy"]; case FLEXMetadataKindProtocols: return [self titleWithBaseName:@"Protocols"]; case FLEXMetadataKindOther: return @"Miscellaneous"; } } - (NSInteger)numberOfRows { return self.metadata.count; } - (void)setFilterText:(NSString *)filterText { super.filterText = filterText; if (!self.filterText.length) { self.metadata = self.allMetadata; } else { self.metadata = [self.allMetadata flex_filtered:^BOOL(id obj, NSUInteger idx) { return [obj.description localizedCaseInsensitiveContainsString:self.filterText]; }]; } } - (void)reloadData { switch (self.metadataKind) { case FLEXMetadataKindProperties: self.allMetadata = self.explorer.properties; break; case FLEXMetadataKindClassProperties: self.allMetadata = self.explorer.classProperties; break; case FLEXMetadataKindIvars: self.allMetadata = self.explorer.ivars; break; case FLEXMetadataKindMethods: self.allMetadata = self.explorer.methods; break; case FLEXMetadataKindClassMethods: self.allMetadata = self.explorer.classMethods; break; case FLEXMetadataKindProtocols: self.allMetadata = self.explorer.conformedProtocols; break; case FLEXMetadataKindClassHierarchy: self.allMetadata = self.explorer.classHierarchy; break; case FLEXMetadataKindOther: self.allMetadata = @[self.explorer.instanceSize, self.explorer.imageName]; break; } // Remove excluded metadata if (self.excludedMetadata.count) { id filterBlock = ^BOOL(id obj, NSUInteger idx) { return ![self.excludedMetadata containsObject:obj.name]; }; // Filter exclusions and sort self.allMetadata = [[self.allMetadata flex_filtered:filterBlock] sortedArrayUsingSelector:@selector(compare:) ]; } // Re-filter data self.filterText = self.filterText; } - (BOOL)canSelectRow:(NSInteger)row { UITableViewCellAccessoryType accessory = [self accessoryTypeForRow:row]; return accessory == UITableViewCellAccessoryDisclosureIndicator || accessory == UITableViewCellAccessoryDetailDisclosureButton; } - (NSString *)reuseIdentifierForRow:(NSInteger)row { return [self.metadata[row] reuseIdentifierWithTarget:self.explorer.object] ?: kFLEXCodeFontCell; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { return [self.metadata[row] viewerWithTarget:self.explorer.object]; } - (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row { return ^(UIViewController *host) { [host.navigationController pushViewController:[self editorForRow:row] animated:YES]; }; } - (UIViewController *)editorForRow:(NSInteger)row { return [self.metadata[row] editorWithTarget:self.explorer.object section:self]; } - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row { cell.titleLabel.text = [self titleForRow:row]; cell.subtitleLabel.text = [self subtitleForRow:row]; cell.accessoryType = [self accessoryTypeForRow:row]; } - (NSString *)menuSubtitleForRow:(NSInteger)row { return [self.metadata[row] contextualSubtitleWithTarget:self.explorer.object]; } - (NSArray *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender { NSArray *existingItems = [super menuItemsForRow:row sender:sender]; // These two metadata kinds don't any of the additional options below switch (self.metadataKind) { case FLEXMetadataKindClassHierarchy: case FLEXMetadataKindOther: return existingItems; default: break; } id metadata = self.metadata[row]; NSMutableArray *menuItems = [NSMutableArray new]; [menuItems addObject:[UIAction actionWithTitle:@"Explore Metadata" image:nil identifier:nil handler:^(__kindof UIAction *action) { [sender.navigationController pushViewController:[FLEXObjectExplorerFactory explorerViewControllerForObject:metadata ] animated:YES]; } ]]; [menuItems addObjectsFromArray:[metadata additionalActionsWithTarget:self.explorer.object sender:sender ]]; [menuItems addObjectsFromArray:existingItems]; return menuItems.copy; } - (NSArray *)copyMenuItemsForRow:(NSInteger)row { return [self.metadata[row] copiableMetadataWithTarget:self.explorer.object]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXMutableListSection.h ================================================ // // FLEXMutableListSection.h // FLEX // // Created by Tanner on 3/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXCollectionContentSection.h" typedef void (^FLEXMutableListCellForElement)(__kindof UITableViewCell *cell, id element, NSInteger row); /// A section aimed at meeting the needs of table views with one section /// (or, a section that shouldn't warrant the code duplication that comes /// with creating a new section just for some specific table view) /// /// Use this section if you want to display a growing list of rows, /// or even if you want to display a static list of rows. /// /// To support editing or inserting, implement the appropriate /// table view delegate methods in your table view delegate class /// and call \c mutate: (or \c setList: ) before updating the table view. /// /// By default, no section title is shown. Assign one to \c customTitle /// /// By default, \c kFLEXDetailCell is the reuse identifier used. If you need /// to support multiple reuse identifiers in a single section, implement the /// \c cellForRowAtIndexPath: method, dequeue the cell yourself and call /// \c -configureCell: on the appropriate section object, passing in the cell @interface FLEXMutableListSection<__covariant ObjectType> : FLEXCollectionContentSection /// Initializes a section with an empty list. + (instancetype)list:(NSArray *)list cellConfiguration:(FLEXMutableListCellForElement)configurationBlock filterMatcher:(BOOL(^)(NSString *filterText, id element))filterBlock; /// By default, rows are not selectable. If you want rows /// to be selectable, provide a selection handler here. @property (nonatomic, copy) void (^selectionHandler)(__kindof UIViewController *host, ObjectType element); /// The objects representing all possible rows in the section. @property (nonatomic) NSArray *list; /// The objects representing the currently unfiltered rows in the section. @property (nonatomic, readonly) NSArray *filteredList; /// A readwrite version of the same property in \c FLEXTableViewSection.h /// /// This property expects one entry. An exception is thrown if more than one /// entry is supplied. If you need more than one reuse identifier within a single /// section, your view probably has more complexity than this class can handle. @property (nonatomic, readwrite) NSDictionary *cellRegistrationMapping; /// Call this method to mutate the full, unfiltered list. /// This ensures that \c filteredList is updated after any mutations. - (void)mutate:(void(^)(NSMutableArray *list))block; @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXMutableListSection.m ================================================ // // FLEXMutableListSection.m // FLEX // // Created by Tanner on 3/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXMutableListSection.h" #import "FLEXMacros.h" @interface FLEXMutableListSection () @property (nonatomic, readonly) FLEXMutableListCellForElement configureCell; @end @implementation FLEXMutableListSection @synthesize cellRegistrationMapping = _cellRegistrationMapping; #pragma mark - Initialization + (instancetype)list:(NSArray *)list cellConfiguration:(FLEXMutableListCellForElement)cellConfig filterMatcher:(BOOL(^)(NSString *, id))filterBlock { return [[self alloc] initWithList:list configurationBlock:cellConfig filterMatcher:filterBlock]; } - (id)initWithList:(NSArray *)list configurationBlock:(FLEXMutableListCellForElement)cellConfig filterMatcher:(BOOL(^)(NSString *, id))filterBlock { self = [super init]; if (self) { _configureCell = cellConfig; self.list = list.mutableCopy; self.customFilter = filterBlock; self.hideSectionTitle = YES; } return self; } #pragma mark - Public - (NSArray *)list { return (id)_collection; } - (void)setList:(NSMutableArray *)list { NSParameterAssert(list); _collection = (id)list; [self reloadData]; } - (NSArray *)filteredList { return (id)_cachedCollection; } - (void)mutate:(void (^)(NSMutableArray *))block { block((NSMutableArray *)_collection); [self reloadData]; } #pragma mark - Overrides - (void)setCustomTitle:(NSString *)customTitle { super.customTitle = customTitle; self.hideSectionTitle = customTitle == nil; } - (BOOL)canSelectRow:(NSInteger)row { return self.selectionHandler != nil; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { return nil; } - (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row { if (self.selectionHandler) { weakify(self) return ^(UIViewController *host) { strongify(self) if (self) { self.selectionHandler(host, self.filteredList[row]); } }; } return nil; } - (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row { self.configureCell(cell, self.filteredList[row], row); } - (NSString *)reuseIdentifierForRow:(NSInteger)row { if (self.cellRegistrationMapping.count) { return self.cellRegistrationMapping.allKeys.firstObject; } return [super reuseIdentifierForRow:row]; } - (void)setCellRegistrationMapping:(NSDictionary *)cellRegistrationMapping { NSParameterAssert(cellRegistrationMapping.count <= 1); _cellRegistrationMapping = cellRegistrationMapping; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/FLEXObjectInfoSection.h ================================================ // // FLEXObjectInfoSection.h // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import /// \c FLEXTableViewSection itself doesn't know about the object being explored. /// Subclasses might need this info to provide useful information about the object. Instead /// of adding an abstract class to the class hierarchy, subclasses can conform to this protocol /// to indicate that the only info they need to be initialized is the object being explored. @protocol FLEXObjectInfoSection + (instancetype)forObject:(id)object; @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXBlockShortcuts.h ================================================ // // FLEXBlockShortcuts.h // FLEX // // Created by Tanner on 1/30/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" NS_ASSUME_NONNULL_BEGIN /// Provides a description of the block's signature /// and access to an NSMethodSignature of the block @interface FLEXBlockShortcuts : FLEXShortcutsSection @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXBlockShortcuts.m ================================================ // // FLEXBlockShortcuts.m // FLEX // // Created by Tanner on 1/30/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXBlockShortcuts.h" #import "FLEXShortcut.h" #import "FLEXBlockDescription.h" #import "FLEXObjectExplorerFactory.h" #pragma mark - @implementation FLEXBlockShortcuts #pragma mark Overrides + (instancetype)forObject:(id)block { NSParameterAssert([block isKindOfClass:NSClassFromString(@"NSBlock")]); FLEXBlockDescription *blockInfo = [FLEXBlockDescription describing:block]; NSMethodSignature *signature = blockInfo.signature; NSArray *blockShortcutRows = @[blockInfo.summary]; if (signature) { blockShortcutRows = @[ blockInfo.summary, blockInfo.sourceDeclaration, signature.debugDescription, [FLEXActionShortcut title:@"View Method Signature" subtitle:^NSString *(id block) { return signature.description ?: @"unsupported signature"; } viewer:^UIViewController *(id block) { return [FLEXObjectExplorerFactory explorerViewControllerForObject:signature]; } accessoryType:^UITableViewCellAccessoryType(id view) { if (signature) { return UITableViewCellAccessoryDisclosureIndicator; } return UITableViewCellAccessoryNone; } ] ]; } return [self forObject:block additionalRows:blockShortcutRows]; } - (NSString *)title { return @"Metadata"; } - (NSInteger)numberOfLines { return 0; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXBundleShortcuts.h ================================================ // // FLEXBundleShortcuts.h // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" NS_ASSUME_NONNULL_BEGIN /// Provides a "Browse Bundle Directory" action @interface FLEXBundleShortcuts : FLEXShortcutsSection @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXBundleShortcuts.m ================================================ // // FLEXBundleShortcuts.m // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXBundleShortcuts.h" #import "FLEXShortcut.h" #import "FLEXAlert.h" #import "FLEXMacros.h" #import "FLEXRuntimeExporter.h" #import "FLEXTableListViewController.h" #import "FLEXFileBrowserController.h" #pragma mark - @implementation FLEXBundleShortcuts #pragma mark Overrides + (instancetype)forObject:(NSBundle *)bundle { weakify(self) return [self forObject:bundle additionalRows:@[ [FLEXActionShortcut title:@"Browse Bundle Directory" subtitle:nil viewer:^UIViewController *(NSBundle *bundle) { return [FLEXFileBrowserController path:bundle.bundlePath]; } accessoryType:^UITableViewCellAccessoryType(NSBundle *bundle) { return UITableViewCellAccessoryDisclosureIndicator; } ], [FLEXActionShortcut title:@"Browse Bundle as Database…" subtitle:nil selectionHandler:^(UIViewController *host, NSBundle *bundle) { strongify(self) [self promptToExportBundleAsDatabase:bundle host:host]; } accessoryType:^UITableViewCellAccessoryType(NSBundle *bundle) { return UITableViewCellAccessoryDisclosureIndicator; } ], ]]; } + (void)promptToExportBundleAsDatabase:(NSBundle *)bundle host:(UIViewController *)host { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Save As…").message( @"The database be saved in the Library folder. " "Depending on the number of classes, it may take " "10 minutes or more to finish exporting. 20,000 " "classes takes about 7 minutes." ); make.configuredTextField(^(UITextField *field) { field.placeholder = @"FLEXRuntimeExport.objc.db"; field.text = [NSString stringWithFormat: @"%@.objc.db", bundle.executablePath.lastPathComponent ]; }); make.button(@"Start").handler(^(NSArray *strings) { [self browseBundleAsDatabase:bundle host:host name:strings[0]]; }); make.button(@"Cancel").cancelStyle(); } showFrom:host]; } + (void)browseBundleAsDatabase:(NSBundle *)bundle host:(UIViewController *)host name:(NSString *)name { NSParameterAssert(name.length); UIAlertController *progress = [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Generating Database"); // Some iOS version glitch out of there is // no initial message and you add one later make.message(@"…"); }]; [host presentViewController:progress animated:YES completion:^{ // Generate path to store db NSString *path = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES )[0] stringByAppendingPathComponent:name]; progress.message = [path stringByAppendingString:@"\n\nCreating database…"]; // Generate db and show progress [FLEXRuntimeExporter createRuntimeDatabaseAtPath:path forImages:@[bundle.executablePath] progressHandler:^(NSString *status) { dispatch_async(dispatch_get_main_queue(), ^{ progress.message = [progress.message stringByAppendingFormat:@"\n%@", status ]; [progress.view setNeedsLayout]; [progress.view layoutIfNeeded]; }); } completion:^(NSString *error) { // Display error if any if (error) { progress.title = @"Error"; progress.message = error; [progress addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil] ]; } // Browse database else { [progress dismissViewControllerAnimated:YES completion:nil]; [host.navigationController pushViewController:[ [FLEXTableListViewController alloc] initWithPath:path ] animated:YES]; } } ]; }]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXClassShortcuts.h ================================================ // // FLEXClassShortcuts.h // FLEX // // Created by Tanner Bennett on 11/22/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" /// Provides handy shortcuts for class objects. /// This is the default section used for all class objects. @interface FLEXClassShortcuts : FLEXShortcutsSection + (instancetype)forObject:(Class)cls; @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXClassShortcuts.m ================================================ // // FLEXClassShortcuts.m // FLEX // // Created by Tanner Bennett on 11/22/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXClassShortcuts.h" #import "FLEXShortcut.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXObjectListViewController.h" #import "NSObject+FLEX_Reflection.h" @interface FLEXClassShortcuts () @property (nonatomic, readonly) Class cls; @end @implementation FLEXClassShortcuts + (instancetype)forObject:(Class)cls { // These additional rows will appear at the beginning of the shortcuts section. // The methods below are written in such a way that they will not interfere // with properties/etc being registered alongside these return [self forObject:cls additionalRows:@[ [FLEXActionShortcut title:@"Find Live Instances" subtitle:nil viewer:^UIViewController *(id obj) { return [FLEXObjectListViewController instancesOfClassWithName:NSStringFromClass(obj) retained:NO ]; } accessoryType:^UITableViewCellAccessoryType(id obj) { return UITableViewCellAccessoryDisclosureIndicator; } ], [FLEXActionShortcut title:@"List Subclasses" subtitle:nil viewer:^UIViewController *(id obj) { NSString *name = NSStringFromClass(obj); return [FLEXObjectListViewController subclassesOfClassWithName:name]; } accessoryType:^UITableViewCellAccessoryType(id view) { return UITableViewCellAccessoryDisclosureIndicator; } ], [FLEXActionShortcut title:@"Explore Bundle for Class" subtitle:^NSString *(id obj) { return [self shortNameForBundlePath:[NSBundle bundleForClass:obj].executablePath]; } viewer:^UIViewController *(id obj) { NSBundle *bundle = [NSBundle bundleForClass:obj]; return [FLEXObjectExplorerFactory explorerViewControllerForObject:bundle]; } accessoryType:^UITableViewCellAccessoryType(id view) { return UITableViewCellAccessoryDisclosureIndicator; } ], ]]; } + (NSString *)shortNameForBundlePath:(NSString *)imageName { NSArray *components = [imageName componentsSeparatedByString:@"/"]; if (components.count >= 2) { return [NSString stringWithFormat:@"%@/%@", components[components.count - 2], components[components.count - 1] ]; } return imageName.lastPathComponent; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXImageShortcuts.h ================================================ // // FLEXImageShortcuts.h // FLEX // // Created by Tanner Bennett on 8/29/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" /// Provides "view image" and "save image" shortcuts for UIImage objects @interface FLEXImageShortcuts : FLEXShortcutsSection + (instancetype)forObject:(UIImage *)image; @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXImageShortcuts.m ================================================ // // FLEXImageShortcuts.m // FLEX // // Created by Tanner Bennett on 8/29/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXImageShortcuts.h" #import "FLEXImagePreviewViewController.h" #import "FLEXShortcut.h" #import "FLEXAlert.h" #import "FLEXMacros.h" @interface UIAlertController (FLEXImageShortcuts) - (void)flex_image:(UIImage *)image disSaveWithError:(NSError *)error :(void *)context; @end @implementation FLEXImageShortcuts #pragma mark - Overrides + (instancetype)forObject:(UIImage *)image { // These additional rows will appear at the beginning of the shortcuts section. // The methods below are written in such a way that they will not interfere // with properties/etc being registered alongside these return [self forObject:image additionalRows:@[ [FLEXActionShortcut title:@"View Image" subtitle:nil viewer:^UIViewController *(id image) { return [FLEXImagePreviewViewController forImage:image]; } accessoryType:^UITableViewCellAccessoryType(id image) { return UITableViewCellAccessoryDisclosureIndicator; } ], [FLEXActionShortcut title:@"Save Image" subtitle:nil selectionHandler:^(UIViewController *host, id image) { // Present modal alerting user about saving UIAlertController *alert = [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Saving Image…"); }]; [host presentViewController:alert animated:YES completion:nil]; // Save the image UIImageWriteToSavedPhotosAlbum( image, alert, @selector(flex_image:disSaveWithError::), nil ); } accessoryType:^UITableViewCellAccessoryType(id image) { return UITableViewCellAccessoryDisclosureIndicator; } ] ]]; } @end @implementation UIAlertController (FLEXImageShortcuts) - (void)flex_image:(UIImage *)image disSaveWithError:(NSError *)error :(void *)context { self.title = @"Image Saved"; flex_dispatch_after(1, dispatch_get_main_queue(), ^{ [self dismissViewControllerAnimated:YES completion:nil]; }); } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXLayerShortcuts.h ================================================ // // FLEXLayerShortcuts.h // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" @interface FLEXLayerShortcuts : FLEXShortcutsSection + (instancetype)forObject:(CALayer *)layer; @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXLayerShortcuts.m ================================================ // // FLEXLayerShortcuts.m // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXLayerShortcuts.h" #import "FLEXShortcut.h" #import "FLEXImagePreviewViewController.h" @implementation FLEXLayerShortcuts + (instancetype)forObject:(CALayer *)layer { return [self forObject:layer additionalRows:@[ [FLEXActionShortcut title:@"Preview Image" subtitle:nil viewer:^UIViewController *(CALayer *layer) { return [FLEXImagePreviewViewController previewForLayer:layer]; } accessoryType:^UITableViewCellAccessoryType(CALayer *layer) { return CGRectIsEmpty(layer.bounds) ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator; } ] ]]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXNSDataShortcuts.h ================================================ // // FLEXNSDataShortcuts.h // FLEX // // Created by Tanner on 3/29/21. // #import "FLEXShortcutsSection.h" /// Adds a "UTF-8 String" shortcut @interface FLEXNSDataShortcuts : FLEXShortcutsSection @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXNSDataShortcuts.m ================================================ // // FLEXNSDataShortcuts.m // FLEX // // Created by Tanner on 3/29/21. // #import "FLEXNSDataShortcuts.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXShortcut.h" @implementation FLEXNSDataShortcuts + (instancetype)forObject:(NSData *)data { NSString *string = [self stringForData:data]; return [self forObject:data additionalRows:@[ [FLEXActionShortcut title:@"UTF-8 String" subtitle:^(NSData *object) { return string.length ? string : (string ? @"Data is not a UTF8 String" : @"Empty string" ); } viewer:^UIViewController *(id object) { return [FLEXObjectExplorerFactory explorerViewControllerForObject:string]; } accessoryType:^UITableViewCellAccessoryType(NSData *object) { if (string.length) { return UITableViewCellAccessoryDisclosureIndicator; } return UITableViewCellAccessoryNone; }] ]]; } + (NSString *)stringForData:(NSData *)data { return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } @end @interface NSData (Overrides) @end @implementation NSData (Overrides) // This normally crashes - (NSUInteger)length { return 0; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXNSStringShortcuts.h ================================================ // // FLEXNSStringShortcuts.h // FLEX // // Created by Tanner on 3/29/21. // #import "FLEXShortcutsSection.h" /// Adds a "UTF-8 Data" shortcut @interface FLEXNSStringShortcuts : FLEXShortcutsSection @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXNSStringShortcuts.m ================================================ // // FLEXNSStringShortcuts.m // FLEX // // Created by Tanner on 3/29/21. // #import "FLEXNSStringShortcuts.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXShortcut.h" @implementation FLEXNSStringShortcuts + (instancetype)forObject:(NSString *)string { NSUInteger length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; NSData *data = [NSData dataWithBytesNoCopy:(void *)string.UTF8String length:length freeWhenDone:NO]; return [self forObject:string additionalRows:@[ [FLEXActionShortcut title:@"UTF-8 Data" subtitle:^NSString *(id _) { return data.description; } viewer:^UIViewController *(id _) { return [FLEXObjectExplorerFactory explorerViewControllerForObject:data]; } accessoryType:^UITableViewCellAccessoryType(id _) { return UITableViewCellAccessoryDisclosureIndicator; }] ]]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h ================================================ // // FLEXShortcut.h // FLEX // // Created by Tanner Bennett on 12/10/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXObjectExplorer.h" NS_ASSUME_NONNULL_BEGIN /// Represents a row in a shortcut section. /// /// The purpsoe of this protocol is to allow delegating a small /// subset of the responsibilities of a \c FLEXShortcutsSection /// to another object, for a single arbitrary row. /// /// It is useful to make your own shortcuts to append/prepend /// them to the existing list of shortcuts for a class. @protocol FLEXShortcut - (nonnull NSString *)titleWith:(id)object; - (nullable NSString *)subtitleWith:(id)object; - (nullable void (^)(UIViewController *host))didSelectActionWith:(id)object; /// Called when the row is selected - (nullable UIViewController *)viewerWith:(id)object; /// Basically, whether or not to show a detail disclosure indicator - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object; /// If nil is returned, the default reuse identifier is used - (nullable NSString *)customReuseIdentifierWith:(id)object; @optional /// Called when the (i) button is pressed if the accessory type includes it - (UIViewController *)editorWith:(id)object forSection:(FLEXTableViewSection *)section; @end /// Provides default behavior for FLEX metadata objects. Also works in a limited way with strings. /// Used internally. If you wish to use this object, only pass in \c FLEX* metadata objects. @interface FLEXShortcut : NSObject /// @param item An \c NSString or \c FLEX* metadata object. /// @note You may also pass a \c FLEXShortcut conforming object, /// and that object will be returned instead. + (id)shortcutFor:(id)item; @end /// Provides a quick and dirty implementation of the \c FLEXShortcut protocol, /// allowing you to specify a static title and dynamic atttributes for everything else. /// The object passed into each block is the object passed to each \c FLEXShortcut method. /// /// Does not support the \c -editorWith: method. @interface FLEXActionShortcut : NSObject + (instancetype)title:(NSString *)title subtitle:(nullable NSString *(^)(id object))subtitleFuture viewer:(nullable UIViewController *(^)(id object))viewerFuture accessoryType:(nullable UITableViewCellAccessoryType(^)(id object))accessoryTypeFuture; + (instancetype)title:(NSString *)title subtitle:(nullable NSString *(^)(id object))subtitleFuture selectionHandler:(nullable void (^)(UIViewController *host, id object))tapAction accessoryType:(nullable UITableViewCellAccessoryType(^)(id object))accessoryTypeFuture; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.m ================================================ // // FLEXShortcut.m // FLEX // // Created by Tanner Bennett on 12/10/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcut.h" #import "FLEXProperty.h" #import "FLEXPropertyAttributes.h" #import "FLEXIvar.h" #import "FLEXMethod.h" #import "FLEXRuntime+UIKitHelpers.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXFieldEditorViewController.h" #import "FLEXMethodCallingViewController.h" #import "FLEXMetadataSection.h" #import "FLEXTableView.h" #pragma mark - FLEXShortcut @interface FLEXShortcut () { id _item; } @property (nonatomic, readonly) FLEXMetadataKind metadataKind; @property (nonatomic, readonly) FLEXProperty *property; @property (nonatomic, readonly) FLEXMethod *method; @property (nonatomic, readonly) FLEXIvar *ivar; @property (nonatomic, readonly) id metadata; @end @implementation FLEXShortcut @synthesize defaults = _defaults; + (id)shortcutFor:(id)item { if ([item conformsToProtocol:@protocol(FLEXShortcut)]) { return item; } FLEXShortcut *shortcut = [self new]; shortcut->_item = item; if ([item isKindOfClass:[FLEXProperty class]]) { if (shortcut.property.isClassProperty) { shortcut->_metadataKind = FLEXMetadataKindClassProperties; } else { shortcut->_metadataKind = FLEXMetadataKindProperties; } } if ([item isKindOfClass:[FLEXIvar class]]) { shortcut->_metadataKind = FLEXMetadataKindIvars; } if ([item isKindOfClass:[FLEXMethod class]]) { // We don't care if it's a class method or not shortcut->_metadataKind = FLEXMetadataKindMethods; } return shortcut; } - (id)propertyOrIvarValue:(id)object { return [self.metadata currentValueWithTarget:object]; } - (NSString *)titleWith:(id)object { switch (self.metadataKind) { case FLEXMetadataKindClassProperties: case FLEXMetadataKindProperties: // Since we're outside of the "properties" section, prepend @property for clarity. return [@"@property " stringByAppendingString:[_item description]]; default: return [_item description]; } NSAssert( [_item isKindOfClass:[NSString class]], @"Unexpected type: %@", [_item class] ); return _item; } - (NSString *)subtitleWith:(id)object { if (self.metadataKind) { return [self.metadata previewWithTarget:object]; } // Item is probably a string; must return empty string since // these will be gathered into an array. If the object is a // just a string, it doesn't get a subtitle. return @""; } - (void (^)(UIViewController *))didSelectActionWith:(id)object { return nil; } - (UIViewController *)viewerWith:(id)object { NSAssert(self.metadataKind, @"Static titles cannot be viewed"); return [self.metadata viewerWithTarget:object]; } - (UIViewController *)editorWith:(id)object forSection:(FLEXTableViewSection *)section { NSAssert(self.metadataKind, @"Static titles cannot be edited"); return [self.metadata editorWithTarget:object section:section]; } - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object { if (self.metadataKind) { return [self.metadata suggestedAccessoryTypeWithTarget:object]; } return UITableViewCellAccessoryNone; } - (NSString *)customReuseIdentifierWith:(id)object { if (self.metadataKind) { return kFLEXCodeFontCell; } return kFLEXMultilineCell; } #pragma mark FLEXObjectExplorerDefaults - (void)setDefaults:(FLEXObjectExplorerDefaults *)defaults { _defaults = defaults; if (_metadataKind) { self.metadata.defaults = defaults; } } - (BOOL)isEditable { if (_metadataKind) { return self.metadata.isEditable; } return NO; } - (BOOL)isCallable { if (_metadataKind) { return self.metadata.isCallable; } return NO; } #pragma mark - Helpers - (FLEXProperty *)property { return _item; } - (FLEXMethodBase *)method { return _item; } - (FLEXIvar *)ivar { return _item; } - (id)metadata { return _item; } @end #pragma mark - FLEXActionShortcut @interface FLEXActionShortcut () @property (nonatomic, readonly) NSString *title; @property (nonatomic, readonly) NSString *(^subtitleFuture)(id); @property (nonatomic, readonly) UIViewController *(^viewerFuture)(id); @property (nonatomic, readonly) void (^selectionHandler)(UIViewController *, id); @property (nonatomic, readonly) UITableViewCellAccessoryType (^accessoryTypeFuture)(id); @end @implementation FLEXActionShortcut @synthesize defaults = _defaults; + (instancetype)title:(NSString *)title subtitle:(NSString *(^)(id))subtitle viewer:(UIViewController *(^)(id))viewer accessoryType:(UITableViewCellAccessoryType (^)(id))type { return [[self alloc] initWithTitle:title subtitle:subtitle viewer:viewer selectionHandler:nil accessoryType:type]; } + (instancetype)title:(NSString *)title subtitle:(NSString * (^)(id))subtitle selectionHandler:(void (^)(UIViewController *, id))tapAction accessoryType:(UITableViewCellAccessoryType (^)(id))type { return [[self alloc] initWithTitle:title subtitle:subtitle viewer:nil selectionHandler:tapAction accessoryType:type]; } - (id)initWithTitle:(NSString *)title subtitle:(id)subtitleFuture viewer:(id)viewerFuture selectionHandler:(id)tapAction accessoryType:(id)accessoryTypeFuture { NSParameterAssert(title.length); self = [super init]; if (self) { id nilBlock = ^id (id obj) { return nil; }; _title = title; _subtitleFuture = subtitleFuture ?: nilBlock; _viewerFuture = viewerFuture ?: nilBlock; _selectionHandler = tapAction; _accessoryTypeFuture = accessoryTypeFuture ?: nilBlock; } return self; } - (NSString *)titleWith:(id)object { return self.title; } - (NSString *)subtitleWith:(id)object { if (self.defaults.wantsDynamicPreviews) { return self.subtitleFuture(object); } return nil; } - (void (^)(UIViewController *))didSelectActionWith:(id)object { if (self.selectionHandler) { return ^(UIViewController *host) { self.selectionHandler(host, object); }; } return nil; } - (UIViewController *)viewerWith:(id)object { return self.viewerFuture(object); } - (UITableViewCellAccessoryType)accessoryTypeWith:(id)object { return self.accessoryTypeFuture(object); } - (NSString *)customReuseIdentifierWith:(id)object { if (!self.subtitleFuture(object)) { // The text is more centered with this style if there is no subtitle return kFLEXDefaultCell; } return nil; } - (BOOL)isEditable { return NO; } - (BOOL)isCallable { return NO; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsFactory+Defaults.h ================================================ // // FLEXShortcutsFactory+Defaults.h // FLEX // // Created by Tanner Bennett on 8/29/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" @interface FLEXShortcutsFactory (UIApplication) @end @interface FLEXShortcutsFactory (Views) @end @interface FLEXShortcutsFactory (ViewControllers) @end @interface FLEXShortcutsFactory (UIImage) @end @interface FLEXShortcutsFactory (NSBundle) @end @interface FLEXShortcutsFactory (Classes) @end @interface FLEXShortcutsFactory (Activities) @end @interface FLEXShortcutsFactory (Blocks) @end @interface FLEXShortcutsFactory (Foundation) @end @interface FLEXShortcutsFactory (WebKit_Safari) @end @interface FLEXShortcutsFactory (Pasteboard) @end @interface FLEXShortcutsFactory (FirebaseFirestore) @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsFactory+Defaults.m ================================================ // // FLEXShortcutsFactory+Defaults.m // FLEX // // Created by Tanner Bennett on 8/29/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsFactory+Defaults.h" #import "FLEXShortcut.h" #import "FLEXMacros.h" #import "FLEXRuntimeUtility.h" #import "NSArray+FLEX.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXObjcInternal.h" #import "Cocoa+FLEXShortcuts.h" #pragma mark - UIApplication @implementation FLEXShortcutsFactory (UIApplication) + (void)load { FLEX_EXIT_IF_NO_CTORS() // sharedApplication class property possibly not added // as a literal class property until iOS 10 FLEXRuntimeUtilityTryAddObjectProperty( 2, sharedApplication, UIApplication.flex_metaclass, UIApplication, PropertyKey(ReadOnly) ); self.append.classProperties(@[@"sharedApplication"]).forClass(UIApplication.flex_metaclass); self.append.properties(@[ @"delegate", @"keyWindow", @"windows" ]).forClass(UIApplication.class); if (@available(iOS 13, *)) { self.append.properties(@[ @"connectedScenes", @"openSessions", @"supportsMultipleScenes" ]).forClass(UIApplication.class); } } @end #pragma mark - Views @implementation FLEXShortcutsFactory (Views) + (void)load { FLEX_EXIT_IF_NO_CTORS() // A quirk of UIView and some other classes: a lot of the `@property`s are // not actually properties from the perspective of the runtime. // // We add these properties to the class at runtime if they haven't been added yet. // This way, we can use our property editor to access and change them. // The property attributes match the declared attributes in their headers. // UIView, public Class UIView_ = UIView.class; FLEXRuntimeUtilityTryAddNonatomicProperty(2, frame, UIView_, CGRect); FLEXRuntimeUtilityTryAddNonatomicProperty(2, alpha, UIView_, CGFloat); FLEXRuntimeUtilityTryAddNonatomicProperty(2, clipsToBounds, UIView_, BOOL); FLEXRuntimeUtilityTryAddNonatomicProperty(2, opaque, UIView_, BOOL, PropertyKeyGetter(isOpaque)); FLEXRuntimeUtilityTryAddNonatomicProperty(2, hidden, UIView_, BOOL, PropertyKeyGetter(isHidden)); FLEXRuntimeUtilityTryAddObjectProperty(2, backgroundColor, UIView_, UIColor, PropertyKey(Copy)); FLEXRuntimeUtilityTryAddObjectProperty(6, constraints, UIView_, NSArray, PropertyKey(ReadOnly)); FLEXRuntimeUtilityTryAddObjectProperty(2, subviews, UIView_, NSArray, PropertyKey(ReadOnly)); FLEXRuntimeUtilityTryAddObjectProperty(2, superview, UIView_, UIView, PropertyKey(ReadOnly)); FLEXRuntimeUtilityTryAddObjectProperty(7, tintColor, UIView_, UIView); // UIButton, private FLEXRuntimeUtilityTryAddObjectProperty(2, font, UIButton.class, UIFont, PropertyKey(ReadOnly)); // Only available since iOS 3.2, but we never supported iOS 3, so who cares NSArray *ivars = @[@"_gestureRecognizers"]; NSArray *methods = @[@"sizeToFit", @"setNeedsLayout", @"removeFromSuperview"]; // UIView self.append.ivars(ivars).methods(methods).properties(@[ @"frame", @"bounds", @"center", @"transform", @"backgroundColor", @"alpha", @"opaque", @"hidden", @"clipsToBounds", @"userInteractionEnabled", @"layer", @"superview", @"subviews", @"accessibilityIdentifier", @"accessibilityLabel" ]).forClass(UIView.class); // UILabel self.append.ivars(ivars).methods(methods).properties(@[ @"text", @"attributedText", @"font", @"frame", @"textColor", @"textAlignment", @"numberOfLines", @"lineBreakMode", @"enabled", @"backgroundColor", @"alpha", @"hidden", @"preferredMaxLayoutWidth", @"superview", @"subviews", @"accessibilityIdentifier", @"accessibilityLabel" ]).forClass(UILabel.class); // UIWindow self.append.ivars(ivars).properties(@[ @"rootViewController", @"windowLevel", @"keyWindow", @"frame", @"bounds", @"center", @"transform", @"backgroundColor", @"alpha", @"opaque", @"hidden", @"clipsToBounds", @"userInteractionEnabled", @"layer", @"subviews" ]).forClass(UIWindow.class); if (@available(iOS 13, *)) { self.append.properties(@[@"windowScene"]).forClass(UIWindow.class); } ivars = @[@"_targetActions", @"_gestureRecognizers"]; // Property was added in iOS 10 but we want it on iOS 9 too FLEXRuntimeUtilityTryAddObjectProperty(9, allTargets, UIControl.class, NSArray, PropertyKey(ReadOnly)); // UIControl self.append.ivars(ivars).methods(methods).properties(@[ @"enabled", @"allTargets", @"frame", @"backgroundColor", @"hidden", @"clipsToBounds", @"userInteractionEnabled", @"superview", @"subviews", @"accessibilityIdentifier", @"accessibilityLabel" ]).forClass(UIControl.class); // UIButton self.append.ivars(ivars).properties(@[ @"titleLabel", @"font", @"imageView", @"tintColor", @"currentTitle", @"currentImage", @"enabled", @"frame", @"superview", @"subviews", @"accessibilityIdentifier", @"accessibilityLabel" ]).forClass(UIButton.class); // UIImageView self.append.properties(@[ @"image", @"animationImages", @"frame", @"bounds", @"center", @"transform", @"alpha", @"hidden", @"clipsToBounds", @"userInteractionEnabled", @"layer", @"superview", @"subviews", @"accessibilityIdentifier", @"accessibilityLabel" ]).forClass(UIImageView.class); } @end #pragma mark - View Controllers @implementation FLEXShortcutsFactory (ViewControllers) + (void)load { FLEX_EXIT_IF_NO_CTORS() // toolbarItems is not really a property, make it one FLEXRuntimeUtilityTryAddObjectProperty(3, toolbarItems, UIViewController.class, NSArray); // UIViewController self.append .properties(@[ @"viewIfLoaded", @"title", @"navigationItem", @"toolbarItems", @"tabBarItem", @"childViewControllers", @"navigationController", @"tabBarController", @"splitViewController", @"parentViewController", @"presentedViewController", @"presentingViewController", ]) .methods(@[@"view"]) .forClass(UIViewController.class); // UIAlertController NSMutableArray *alertControllerProps = @[ @"title", @"message", @"actions", @"textFields", @"preferredAction", @"presentingViewController", @"viewIfLoaded", ].mutableCopy; if (@available(iOS 14.0, *)) { [alertControllerProps insertObject:@"image" atIndex:4]; } self.append .properties(alertControllerProps) .methods(@[@"addAction:"]) .forClass(UIAlertController.class); self.append.properties(@[ @"title", @"style", @"enabled", @"flex_styleName", @"image", @"keyCommandInput", @"_isPreferred", @"_alertController", ]).forClass(UIAlertAction.class); } @end #pragma mark - UIImage @implementation FLEXShortcutsFactory (UIImage) + (void)load { FLEX_EXIT_IF_NO_CTORS() self.append.methods(@[ @"CGImage", @"CIImage" ]).properties(@[ @"scale", @"size", @"capInsets", @"alignmentRectInsets", @"duration", @"images" ]).forClass(UIImage.class); if (@available(iOS 13, *)) { self.append.properties(@[@"symbolImage"]).forClass(UIImage.class); } } @end #pragma mark - NSBundle @implementation FLEXShortcutsFactory (NSBundle) + (void)load { FLEX_EXIT_IF_NO_CTORS() self.append.properties(@[ @"bundleIdentifier", @"principalClass", @"infoDictionary", @"bundlePath", @"executablePath", @"loaded" ]).forClass(NSBundle.class); } @end #pragma mark - Classes @implementation FLEXShortcutsFactory (Classes) + (void)load { FLEX_EXIT_IF_NO_CTORS() self.append.classMethods(@[@"new", @"alloc"]).forClass(NSObject.flex_metaclass); } @end #pragma mark - Activities @implementation FLEXShortcutsFactory (Activities) + (void)load { FLEX_EXIT_IF_NO_CTORS() // Property was added in iOS 10 but we want it on iOS 9 too FLEXRuntimeUtilityTryAddNonatomicProperty(9, item, UIActivityItemProvider.class, id, PropertyKey(ReadOnly)); self.append.properties(@[ @"item", @"placeholderItem", @"activityType" ]).forClass(UIActivityItemProvider.class); self.append.properties(@[ @"activityItems", @"applicationActivities", @"excludedActivityTypes", @"completionHandler" ]).forClass(UIActivityViewController.class); } @end #pragma mark - Blocks @implementation FLEXShortcutsFactory (Blocks) + (void)load { FLEX_EXIT_IF_NO_CTORS() self.append.methods(@[@"invoke"]).forClass(NSClassFromString(@"NSBlock")); } @end #pragma mark - Foundation @implementation FLEXShortcutsFactory (Foundation) + (void)load { FLEX_EXIT_IF_NO_CTORS() self.append.properties(@[ @"configuration", @"delegate", @"delegateQueue", @"sessionDescription", ]).methods(@[ @"dataTaskWithURL:", @"finishTasksAndInvalidate", @"invalidateAndCancel", ]).forClass(NSURLSession.class); self.append.methods(@[ @"cachedResponseForRequest:", @"storeCachedResponse:forRequest:", @"storeCachedResponse:forDataTask:", @"removeCachedResponseForRequest:", @"removeCachedResponseForDataTask:", @"removeCachedResponsesSinceDate:", @"removeAllCachedResponses", ]).forClass(NSURLCache.class); self.append.methods(@[ @"postNotification:", @"postNotificationName:object:userInfo:", @"addObserver:selector:name:object:", @"removeObserver:", @"removeObserver:name:object:", ]).forClass(NSNotificationCenter.class); // NSTimeZone class properties aren't real properties FLEXRuntimeUtilityTryAddObjectProperty(2, localTimeZone, NSTimeZone.flex_metaclass, NSTimeZone); FLEXRuntimeUtilityTryAddObjectProperty(2, systemTimeZone, NSTimeZone.flex_metaclass, NSTimeZone); FLEXRuntimeUtilityTryAddObjectProperty(2, defaultTimeZone, NSTimeZone.flex_metaclass, NSTimeZone); FLEXRuntimeUtilityTryAddObjectProperty(2, knownTimeZoneNames, NSTimeZone.flex_metaclass, NSArray); FLEXRuntimeUtilityTryAddObjectProperty(2, abbreviationDictionary, NSTimeZone.flex_metaclass, NSDictionary); self.append.classMethods(@[ @"timeZoneWithName:", @"timeZoneWithAbbreviation:", @"timeZoneForSecondsFromGMT:", ]).forClass(NSTimeZone.flex_metaclass); self.append.classProperties(@[ @"defaultTimeZone", @"systemTimeZone", @"localTimeZone", ]).forClass(NSTimeZone.class); // UTF8String is not a real property under the hood FLEXRuntimeUtilityTryAddNonatomicProperty(2, UTF8String, NSString.class, const char *, PropertyKey(ReadOnly)); self.append.properties(@[@"length"]).methods(@[@"characterAtIndex:"]).forClass(NSString.class); self.append.methods(@[ @"writeToFile:atomically:", @"subdataWithRange:", @"isEqualToData:", ]).properties(@[ @"length", @"bytes", ]).forClass(NSData.class); self.append.classMethods(@[ @"dataWithJSONObject:options:error:", @"JSONObjectWithData:options:error:", @"isValidJSONObject:", ]).forClass(NSJSONSerialization.class); // NSArray self.append.classMethods(@[ @"arrayWithObject:", @"arrayWithContentsOfFile:" ]).forClass(NSArray.flex_metaclass); self.append.methods(@[ @"valueForKeyPath:", @"subarrayWithRange:", @"arrayByAddingObject:", @"arrayByAddingObjectsFromArray:", @"filteredArrayUsingPredicate:", @"subarrayWithRange:", @"containsObject:", @"objectAtIndex:", @"indexOfObject:", @"makeObjectsPerformSelector:", @"makeObjectsPerformSelector:withObject:", @"sortedArrayUsingSelector:", @"reverseObjectEnumerator", @"isEqualToArray:", @"mutableCopy", ]).forClass(NSArray.class); // NSDictionary self.append.methods(@[ @"objectForKey:", @"valueForKeyPath:", @"isEqualToDictionary:", @"mutableCopy", ]).forClass(NSDictionary.class); // NSSet self.append.classMethods(@[ @"setWithObject:", @"setWithArray:" ]).forClass(NSSet.flex_metaclass); self.append.methods(@[ @"allObjects", @"valueForKeyPath:", @"containsObject:", @"setByAddingObject:", @"setByAddingObjectsFromArray:", @"filteredSetUsingPredicate:", @"isSubsetOfSet:", @"makeObjectsPerformSelector:", @"makeObjectsPerformSelector:withObject:", @"reverseObjectEnumerator", @"isEqualToSet:", @"mutableCopy", ]).forClass(NSSet.class); // NSMutableArray self.prepend.methods(@[ @"addObject:", @"insertObject:atIndex:", @"addObjectsFromArray:", @"removeObject:", @"removeObjectAtIndex:", @"removeObjectsInArray:", @"removeAllObjects", @"removeLastObject", @"filterUsingPredicate:", @"sortUsingSelector:", @"copy", ]).forClass(NSMutableArray.class); // NSMutableDictionary self.prepend.methods(@[ @"setObject:forKey:", @"removeObjectForKey:", @"removeAllObjects", @"removeObjectsForKeys:", @"copy", ]).forClass(NSMutableDictionary.class); // NSMutableSet self.prepend.methods(@[ @"addObject:", @"removeObject:", @"filterUsingPredicate:", @"removeAllObjects", @"addObjectsFromArray:", @"unionSet:", @"minusSet:", @"intersectSet:", @"copy" ]).forClass(NSMutableSet.class); self.append.methods(@[@"nextObject", @"allObjects"]).forClass(NSEnumerator.class); self.append.properties(@[@"flex_observers"]).forClass(NSNotificationCenter.class); } @end #pragma mark - WebKit / Safari @implementation FLEXShortcutsFactory (WebKit_Safari) + (void)load { FLEX_EXIT_IF_NO_CTORS() Class WKWebView = NSClassFromString(@"WKWebView"); Class SafariVC = NSClassFromString(@"SFSafariViewController"); if (WKWebView) { self.append.properties(@[ @"configuration", @"scrollView", @"title", @"URL", @"customUserAgent", @"navigationDelegate" ]).methods(@[@"reload", @"stopLoading"]).forClass(WKWebView); } if (SafariVC) { self.append.properties(@[ @"delegate" ]).forClass(SafariVC); if (@available(iOS 10.0, *)) { self.append.properties(@[ @"preferredBarTintColor", @"preferredControlTintColor" ]).forClass(SafariVC); } if (@available(iOS 11.0, *)) { self.append.properties(@[ @"configuration", @"dismissButtonStyle" ]).forClass(SafariVC); } } } @end #pragma mark - Pasteboard @implementation FLEXShortcutsFactory (Pasteboard) + (void)load { FLEX_EXIT_IF_NO_CTORS() self.append.properties(@[ @"name", @"numberOfItems", @"items", @"string", @"image", @"color", @"URL", ]).forClass(UIPasteboard.class); } @end @interface NSNotificationCenter (Observers) @property (readonly) NSArray *flex_observers; @end @implementation NSNotificationCenter (Observers) - (id)flex_observers { NSString *debug = self.debugDescription; NSArray *observers = [debug componentsSeparatedByString:@"\n"]; NSArray *> *splitObservers = [observers flex_mapped:^id(NSString *entry, NSUInteger idx) { return [entry componentsSeparatedByString:@","]; }]; NSArray *names = [splitObservers flex_mapped:^id(NSArray *entry, NSUInteger idx) { return entry[0]; }]; NSArray *objects = [splitObservers flex_mapped:^id(NSArray *entry, NSUInteger idx) { if (entry.count < 2) return NSNull.null; NSScanner *scanner = [NSScanner scannerWithString:entry[1]]; unsigned long long objectPointerValue; if ([scanner scanHexLongLong:&objectPointerValue]) { void *objectPointer = (void *)objectPointerValue; if (FLEXPointerIsValidObjcObject(objectPointer)) return (__bridge id)(void *)objectPointer; } return NSNull.null; }]; return [NSArray flex_forEachUpTo:names.count map:^id(NSUInteger i) { return @[names[i], objects[i]]; }]; } @end #pragma mark - Firebase Firestore @implementation FLEXShortcutsFactory (FirebaseFirestore) + (void)load { FLEX_EXIT_IF_NO_CTORS() Class FIRDocumentSnap = NSClassFromString(@"FIRDocumentSnapshot"); if (FIRDocumentSnap) { FLEXRuntimeUtilityTryAddObjectProperty(2, data, FIRDocumentSnap, NSDictionary, PropertyKey(ReadOnly)); } } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h ================================================ // // FLEXShortcutsSection.h // FLEX // // Created by Tanner Bennett on 8/29/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewSection.h" #import "FLEXObjectInfoSection.h" @class FLEXProperty, FLEXIvar, FLEXMethod; NS_ASSUME_NONNULL_BEGIN /// An abstract base class for custom object "shortcuts" where every /// row can possibly have some action. The section title is "Shortcuts". /// /// You should only subclass this class if you need simple shortcuts /// with plain titles and/or subtitles. This class will automatically /// configure each cell appropriately. Since this is intended as a /// static section, subclasses should only need to implement the /// \c viewControllerToPushForRow: and/or \c didSelectRowAction: methods. /// /// If you create the section using \c forObject:rows:numberOfLines: /// then it will provide a view controller from \c viewControllerToPushForRow: /// automatically for rows that are a property/ivar/method. @interface FLEXShortcutsSection : FLEXTableViewSection /// Uses \c kFLEXDefaultCell + (instancetype)forObject:(id)objectOrClass rowTitles:(nullable NSArray *)titles; /// Uses \c kFLEXDetailCell for non-empty subtitles, otherwise uses \c kFLEXDefaultCell + (instancetype)forObject:(id)objectOrClass rowTitles:(nullable NSArray *)titles rowSubtitles:(nullable NSArray *)subtitles; /// Uses \c kFLEXDefaultCell for rows that are given a title, otherwise /// this uses \c kFLEXDetailCell for any other allowed object. /// /// The section provide a view controller from \c viewControllerToPushForRow: /// automatically for rows that are a property/ivar/method. /// /// @param rows A mixed array containing any of the following: /// - any \c FLEXShortcut conforming object /// - an \c NSString /// - a \c FLEXProperty /// - a \c FLEXIvar /// - a \c FLEXMethodBase (includes \c FLEXMethod of course) /// Passing one of the latter 3 will provide a shortcut to that property/ivar/method. + (instancetype)forObject:(id)objectOrClass rows:(nullable NSArray *)rows; /// Same as \c forObject:rows: but the given rows are prepended /// to the shortcuts already registered for the object's class. /// \c forObject:rows: does not use the registered shortcuts at all. + (instancetype)forObject:(id)objectOrClass additionalRows:(nullable NSArray *)rows; /// Calls into \c forObject:rows: using the registered shortcuts for the object's class. /// @return An empty section if the object has no shortcuts registered at all. + (instancetype)forObject:(id)objectOrClass; /// Subclasses \e may override this to hide the disclosure indicator /// for some rows. It is shown for all rows by default, unless /// you initialize it with \c forObject:rowTitles:rowSubtitles: /// /// When you hide the disclosure indicator, the row is not selectable. - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row; /// The number of lines for the title and subtitle labels. Defaults to 1. @property (nonatomic, readonly) NSInteger numberOfLines; /// The object used to initialize this section. @property (nonatomic, readonly) id object; /// Whether dynamic subtitles should always be computed as a cell is configured. /// Defaults to NO. Has no effect on static subtitles that are passed explicitly. @property (nonatomic) BOOL cacheSubtitles; /// Whether this shortcut section overrides the default section or not. /// Subclasses should not override this method. To provide a second /// section alongside the default shortcuts section, use \c forObject:rows: /// @return \c NO if initialized with \c forObject: or \c forObject:additionalRows: @property (nonatomic, readonly) BOOL isNewSection; @end @class FLEXShortcutsFactory; typedef FLEXShortcutsFactory *_Nonnull(^FLEXShortcutsFactoryNames)(NSArray *names); typedef void (^FLEXShortcutsFactoryTarget)(Class targetClass); /// The block properties below are to be used like SnapKit or Masonry. /// \c FLEXShortcutsSection.append.properties(@[@"frame",@"bounds"]).forClass(UIView.class); /// /// To safely register your own classes at launch, subclass this class, /// override \c +load, and call the appropriate methods on \c self @interface FLEXShortcutsFactory : NSObject /// Returns the list of all registered shortcuts for the given object in this order: /// Properties, ivars, methods. /// /// This method traverses up the object's class hierarchy until it finds /// something registered. This allows you to show different shortcuts for /// the same object in different parts of the class hierarchy. /// /// As an example, UIView may have a -layer shortcut registered. But if /// you're inspecting a UIControl, you may not care about the layer or other /// UIView-specific things; you might rather see the target-actions registered /// for this control, and so you would register that property or ivar to UIControl, /// And you would still be able to see the UIView-registered shorcuts by clicking /// on the UIView "lens" at the top the explorer view controller screen. + (NSArray *)shortcutsForObjectOrClass:(id)objectOrClass; @property (nonatomic, readonly, class) FLEXShortcutsFactory *append; @property (nonatomic, readonly, class) FLEXShortcutsFactory *prepend; @property (nonatomic, readonly, class) FLEXShortcutsFactory *replace; @property (nonatomic, readonly) FLEXShortcutsFactoryNames properties; /// Do not try to set \c classProperties at the same time as \c ivars or other instance things. @property (nonatomic, readonly) FLEXShortcutsFactoryNames classProperties; @property (nonatomic, readonly) FLEXShortcutsFactoryNames ivars; @property (nonatomic, readonly) FLEXShortcutsFactoryNames methods; /// Do not try to set \c classMethods at the same time as \c ivars or other instance things. @property (nonatomic, readonly) FLEXShortcutsFactoryNames classMethods; /// Accepts the target class. If you pass a regular class object, /// shortcuts will appear on instances. If you pass a metaclass object, /// shortcuts will appear when exploring a class object. /// /// For example, some class method shortcuts are added to the NSObject meta /// class by default so that you can see +alloc and +new when exploring /// a class object. If you wanted these to show up when exploring /// instances you would pass them to the classMethods method above. @property (nonatomic, readonly) FLEXShortcutsFactoryTarget forClass; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.m ================================================ // // FLEXShortcutsSection.m // FLEX // // Created by Tanner Bennett on 8/29/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" #import "FLEXTableView.h" #import "FLEXTableViewCell.h" #import "FLEXUtility.h" #import "FLEXShortcut.h" #import "FLEXProperty.h" #import "FLEXPropertyAttributes.h" #import "FLEXIvar.h" #import "FLEXMethod.h" #import "FLEXRuntime+UIKitHelpers.h" #import "FLEXObjectExplorer.h" #pragma mark Private @interface FLEXShortcutsSection () @property (nonatomic, copy) NSArray *titles; @property (nonatomic, copy) NSArray *subtitles; @property (nonatomic, copy) NSArray *allTitles; @property (nonatomic, copy) NSArray *allSubtitles; // Shortcuts are not used if initialized with static titles and subtitles @property (nonatomic, copy) NSArray> *shortcuts; @property (nonatomic, readonly) NSArray> *allShortcuts; @end @implementation FLEXShortcutsSection @synthesize isNewSection = _isNewSection; #pragma mark Initialization + (instancetype)forObject:(id)objectOrClass rowTitles:(NSArray *)titles { return [self forObject:objectOrClass rowTitles:titles rowSubtitles:nil]; } + (instancetype)forObject:(id)objectOrClass rowTitles:(NSArray *)titles rowSubtitles:(NSArray *)subtitles { return [[self alloc] initWithObject:objectOrClass titles:titles subtitles:subtitles]; } + (instancetype)forObject:(id)objectOrClass rows:(NSArray *)rows { return [[self alloc] initWithObject:objectOrClass rows:rows isNewSection:YES]; } + (instancetype)forObject:(id)objectOrClass additionalRows:(NSArray *)toPrepend { NSArray *rows = [FLEXShortcutsFactory shortcutsForObjectOrClass:objectOrClass]; NSArray *allRows = [toPrepend arrayByAddingObjectsFromArray:rows] ?: rows; return [[self alloc] initWithObject:objectOrClass rows:allRows isNewSection:NO]; } + (instancetype)forObject:(id)objectOrClass { return [self forObject:objectOrClass additionalRows:nil]; } - (id)initWithObject:(id)object titles:(NSArray *)titles subtitles:(NSArray *)subtitles { NSParameterAssert(titles.count == subtitles.count || !subtitles); NSParameterAssert(titles.count); self = [super init]; if (self) { _object = object; _allTitles = titles.copy; _allSubtitles = subtitles.copy; _isNewSection = YES; _numberOfLines = 1; } return self; } - (id)initWithObject:object rows:(NSArray *)rows isNewSection:(BOOL)newSection { self = [super init]; if (self) { _object = object; _isNewSection = newSection; _allShortcuts = [rows flex_mapped:^id(id obj, NSUInteger idx) { return [FLEXShortcut shortcutFor:obj]; }]; _numberOfLines = 1; // Populate titles and subtitles [self reloadData]; } return self; } #pragma mark - Public - (void)setCacheSubtitles:(BOOL)cacheSubtitles { if (_cacheSubtitles == cacheSubtitles) return; // cacheSubtitles only applies if we have shortcut objects if (self.allShortcuts) { _cacheSubtitles = cacheSubtitles; [self reloadData]; } else { NSLog(@"Warning: setting 'cacheSubtitles' on a shortcut section with static subtitles"); } } #pragma mark - Overrides - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row { if (_allShortcuts) { return [self.shortcuts[row] accessoryTypeWith:self.object]; } return UITableViewCellAccessoryNone; } - (void)setFilterText:(NSString *)filterText { super.filterText = filterText; NSAssert( self.allTitles.count == self.allSubtitles.count, @"Each title needs a (possibly empty) subtitle" ); if (filterText.length) { // Tally up indexes of titles and subtitles matching the filter NSMutableIndexSet *filterMatches = [NSMutableIndexSet new]; id filterBlock = ^BOOL(NSString *obj, NSUInteger idx) { if ([obj localizedCaseInsensitiveContainsString:filterText]) { [filterMatches addIndex:idx]; return YES; } return NO; }; // Get all matching indexes, including subtitles [self.allTitles flex_forEach:filterBlock]; [self.allSubtitles flex_forEach:filterBlock]; // Filter to matching indexes only self.titles = [self.allTitles objectsAtIndexes:filterMatches]; self.subtitles = [self.allSubtitles objectsAtIndexes:filterMatches]; self.shortcuts = [self.allShortcuts objectsAtIndexes:filterMatches]; } else { self.shortcuts = self.allShortcuts; self.titles = self.allTitles; self.subtitles = [self.allSubtitles flex_filtered:^BOOL(NSString *sub, NSUInteger idx) { return sub.length > 0; }]; } } - (void)reloadData { [FLEXObjectExplorer configureDefaultsForItems:self.allShortcuts]; // Generate all (sub)titles from shortcuts if (self.allShortcuts) { self.allTitles = [self.allShortcuts flex_mapped:^id(id s, NSUInteger idx) { return [s titleWith:self.object]; }]; self.allSubtitles = [self.allShortcuts flex_mapped:^id(id s, NSUInteger idx) { return [s subtitleWith:self.object] ?: @""; }]; } // Re-generate filtered (sub)titles and shortcuts self.filterText = self.filterText; } - (NSString *)title { return @"Shortcuts"; } - (NSInteger)numberOfRows { return self.titles.count; } - (BOOL)canSelectRow:(NSInteger)row { UITableViewCellAccessoryType type = [self.shortcuts[row] accessoryTypeWith:self.object]; BOOL hasDisclosure = NO; hasDisclosure |= type == UITableViewCellAccessoryDisclosureIndicator; hasDisclosure |= type == UITableViewCellAccessoryDetailDisclosureButton; return hasDisclosure; } - (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row { return [self.shortcuts[row] didSelectActionWith:self.object]; } - (UIViewController *)viewControllerToPushForRow:(NSInteger)row { /// Nil if shortcuts is nil, i.e. if initialized with forObject:rowTitles:rowSubtitles: return [self.shortcuts[row] viewerWith:self.object]; } - (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row { id shortcut = self.shortcuts[row]; if ([shortcut respondsToSelector:@selector(editorWith:forSection:)]) { id object = self.object; return ^(UIViewController *host) { UIViewController *editor = [shortcut editorWith:object forSection:self]; [host.navigationController pushViewController:editor animated:YES]; }; } return nil; } - (NSString *)reuseIdentifierForRow:(NSInteger)row { FLEXTableViewCellReuseIdentifier defaultReuse = kFLEXDetailCell; if (@available(iOS 11, *)) { defaultReuse = kFLEXMultilineDetailCell; } return [self.shortcuts[row] customReuseIdentifierWith:self.object] ?: defaultReuse; } - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row { cell.titleLabel.text = [self titleForRow:row]; cell.titleLabel.numberOfLines = self.numberOfLines; cell.subtitleLabel.text = [self subtitleForRow:row]; cell.subtitleLabel.numberOfLines = self.numberOfLines; cell.accessoryType = [self accessoryTypeForRow:row]; } - (NSString *)titleForRow:(NSInteger)row { return self.titles[row]; } - (NSString *)subtitleForRow:(NSInteger)row { // Case: dynamic, uncached subtitles if (!self.cacheSubtitles) { NSString *subtitle = [self.shortcuts[row] subtitleWith:self.object]; return subtitle.length ? subtitle : nil; } // Case: static subtitles, or cached subtitles return self.subtitles[row]; } @end #pragma mark - Global shortcut registration @interface FLEXShortcutsFactory () { BOOL _append, _prepend, _replace, _notInstance; NSArray *_properties, *_ivars, *_methods; } @end #define NewAndSet(ivar) ({ FLEXShortcutsFactory *r = [self sharedFactory]; r->ivar = YES; r; }) #define SetIvar(ivar) ({ self->ivar = YES; self; }) #define SetParamBlock(ivar) ^(NSArray *p) { self->ivar = p; return self; } typedef NSMutableDictionary> *> RegistrationBuckets; @implementation FLEXShortcutsFactory { // Class buckets RegistrationBuckets *cProperties; RegistrationBuckets *cIvars; RegistrationBuckets *cMethods; // Metaclass buckets RegistrationBuckets *mProperties; RegistrationBuckets *mMethods; } + (instancetype)sharedFactory { static FLEXShortcutsFactory *shared = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shared = [self new]; }); return shared; } - (id)init { self = [super init]; if (self) { cProperties = [NSMutableDictionary new]; cIvars = [NSMutableDictionary new]; cMethods = [NSMutableDictionary new]; mProperties = [NSMutableDictionary new]; mMethods = [NSMutableDictionary new]; } return self; } + (NSArray> *)shortcutsForObjectOrClass:(id)objectOrClass { return [[self sharedFactory] shortcutsForObjectOrClass:objectOrClass]; } - (NSArray> *)shortcutsForObjectOrClass:(id)objectOrClass { NSParameterAssert(objectOrClass); NSMutableArray> *shortcuts = [NSMutableArray new]; BOOL isClass = object_isClass(objectOrClass); // The -class does not give you a metaclass, and we want a metaclass // if a class is passed in, or a class if an object is passed in Class classKey = object_getClass(objectOrClass); RegistrationBuckets *propertyBucket = isClass ? mProperties : cProperties; RegistrationBuckets *methodBucket = isClass ? mMethods : cMethods; RegistrationBuckets *ivarBucket = isClass ? nil : cIvars; BOOL stop = NO; while (!stop && classKey) { NSArray *properties = propertyBucket[classKey]; NSArray *ivars = ivarBucket[classKey]; NSArray *methods = methodBucket[classKey]; // Stop if we found anything stop = properties || ivars || methods; if (stop) { // Add things we found to the list [shortcuts addObjectsFromArray:properties]; [shortcuts addObjectsFromArray:ivars]; [shortcuts addObjectsFromArray:methods]; } else { classKey = class_getSuperclass(classKey); } } [FLEXObjectExplorer configureDefaultsForItems:shortcuts]; return shortcuts; } + (FLEXShortcutsFactory *)append { return NewAndSet(_append); } + (FLEXShortcutsFactory *)prepend { return NewAndSet(_prepend); } + (FLEXShortcutsFactory *)replace { return NewAndSet(_replace); } - (void)_register:(NSArray> *)items to:(RegistrationBuckets *)global class:(Class)key { @synchronized (self) { // Get (or initialize) the bucket for this class NSMutableArray *bucket = ({ id bucket = global[key]; if (!bucket) { bucket = [NSMutableArray new]; global[(id)key] = bucket; } bucket; }); if (self->_append) { [bucket addObjectsFromArray:items]; } if (self->_replace) { [bucket setArray:items]; } if (self->_prepend) { if (bucket.count) { // Set new items as array, add old items behind them id copy = bucket.copy; [bucket setArray:items]; [bucket addObjectsFromArray:copy]; } else { [bucket addObjectsFromArray:items]; } } } } - (void)reset { _append = NO; _prepend = NO; _replace = NO; _notInstance = NO; _properties = nil; _ivars = nil; _methods = nil; } - (FLEXShortcutsFactory *)class { return SetIvar(_notInstance); } - (FLEXShortcutsFactoryNames)properties { NSAssert(!_notInstance, @"Do not try to set properties+classProperties at the same time"); return SetParamBlock(_properties); } - (FLEXShortcutsFactoryNames)classProperties { _notInstance = YES; return SetParamBlock(_properties); } - (FLEXShortcutsFactoryNames)ivars { return SetParamBlock(_ivars); } - (FLEXShortcutsFactoryNames)methods { NSAssert(!_notInstance, @"Do not try to set methods+classMethods at the same time"); return SetParamBlock(_methods); } - (FLEXShortcutsFactoryNames)classMethods { _notInstance = YES; return SetParamBlock(_methods); } - (FLEXShortcutsFactoryTarget)forClass { return ^(Class cls) { NSAssert( ( self->_append && !self->_prepend && !self->_replace) || (!self->_append && self->_prepend && !self->_replace) || (!self->_append && !self->_prepend && self->_replace), @"You can only do one of [append, prepend, replace]" ); /// Whether the metadata we're about to add is instance or /// class metadata, i.e. class properties vs instance properties BOOL instanceMetadata = !self->_notInstance; /// Whether the given class is a metaclass or not; we need to switch to /// the metaclass to add class metadata if we are given the normal class object BOOL isMeta = class_isMetaClass(cls); /// Whether the shortcuts we're about to add should appear for classes or instances BOOL instanceShortcut = !isMeta; if (instanceMetadata) { NSAssert(!isMeta, @"Instance metadata can only be added as an instance shortcut" ); } Class metaclass = isMeta ? cls : object_getClass(cls); Class clsForMetadata = instanceMetadata ? cls : metaclass; // The factory is a singleton so we don't need to worry about "leaking" it #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wimplicit-retain-self" RegistrationBuckets *propertyBucket = instanceShortcut ? cProperties : mProperties; RegistrationBuckets *methodBucket = instanceShortcut ? cMethods : mMethods; RegistrationBuckets *ivarBucket = instanceShortcut ? cIvars : nil; #pragma clang diagnostic pop if (self->_properties) { NSArray *items = [self->_properties flex_mapped:^id(NSString *name, NSUInteger idx) { return [FLEXProperty named:name onClass:clsForMetadata]; }]; [self _register:items to:propertyBucket class:cls]; } if (self->_methods) { NSArray *items = [self->_methods flex_mapped:^id(NSString *name, NSUInteger idx) { return [FLEXMethod selector:NSSelectorFromString(name) class:clsForMetadata]; }]; [self _register:items to:methodBucket class:cls]; } if (self->_ivars) { NSAssert(instanceMetadata, @"Instance metadata can only be added as an instance shortcut (%@)", cls); NSArray *items = [self->_ivars flex_mapped:^id(NSString *name, NSUInteger idx) { return [FLEXIvar named:name onClass:clsForMetadata]; }]; [self _register:items to:ivarBucket class:cls]; } [self reset]; }; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXUIAppShortcuts.h ================================================ // // FLEXUIAppShortcuts.h // FLEX // // Created by Tanner on 5/25/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" @interface FLEXUIAppShortcuts : FLEXShortcutsSection @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXUIAppShortcuts.m ================================================ // // FLEXUIAppShortcuts.m // FLEX // // Created by Tanner on 5/25/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXUIAppShortcuts.h" #import "FLEXRuntimeUtility.h" #import "FLEXShortcut.h" #import "FLEXAlert.h" @implementation FLEXUIAppShortcuts #pragma mark - Overrides + (instancetype)forObject:(UIApplication *)application { return [self forObject:application additionalRows:@[ [FLEXActionShortcut title:@"Open URL…" subtitle:^NSString *(UIViewController *controller) { return nil; } selectionHandler:^void(UIViewController *host, UIApplication *app) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Open URL"); make.message( @"This will call openURL: or openURL:options:completion: " "with the string below. 'Open if Universal' will only open " "the URL if it is a registered Universal Link." ); make.textField(@"twitter://user?id=12345"); make.button(@"Open").handler(^(NSArray *strings) { [self openURL:strings[0] inApp:app onlyIfUniveral:NO host:host]; }); make.button(@"Open if Universal").handler(^(NSArray *strings) { [self openURL:strings[0] inApp:app onlyIfUniveral:YES host:host]; }); make.button(@"Cancel").cancelStyle(); } showFrom:host]; } accessoryType:^UITableViewCellAccessoryType(UIViewController *controller) { return UITableViewCellAccessoryDisclosureIndicator; } ] ]]; } + (void)openURL:(NSString *)urlString inApp:(UIApplication *)app onlyIfUniveral:(BOOL)universalOnly host:(UIViewController *)host { NSURL *url = [NSURL URLWithString:urlString]; if (url) { [app openURL:url options:@{ UIApplicationOpenURLOptionUniversalLinksOnly: @(universalOnly) } completionHandler:^(BOOL success) { if (!success) { [FLEXAlert showAlert:@"No Universal Link Handler" message:@"No installed application is registered to handle this link." from:host ]; } }]; } else { [FLEXAlert showAlert:@"Error" message:@"Invalid URL" from:host]; } } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXViewControllerShortcuts.h ================================================ // // FLEXViewControllerShortcuts.h // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" @interface FLEXViewControllerShortcuts : FLEXShortcutsSection + (instancetype)forObject:(UIViewController *)viewController; @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXViewControllerShortcuts.m ================================================ // // FLEXViewControllerShortcuts.m // FLEX // // Created by Tanner Bennett on 12/12/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXViewControllerShortcuts.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXRuntimeUtility.h" #import "FLEXShortcut.h" #import "FLEXAlert.h" @interface FLEXViewControllerShortcuts () @end @implementation FLEXViewControllerShortcuts #pragma mark - Overrides + (instancetype)forObject:(UIViewController *)viewController { BOOL (^vcIsInuse)(UIViewController *) = ^BOOL(UIViewController *controller) { if (controller.viewIfLoaded.window) { return YES; } return controller.navigationController != nil; }; return [self forObject:viewController additionalRows:@[ [FLEXActionShortcut title:@"Push View Controller" subtitle:^NSString *(UIViewController *controller) { return vcIsInuse(controller) ? @"In use, cannot push" : nil; } selectionHandler:^void(UIViewController *host, UIViewController *controller) { if (!vcIsInuse(controller)) { [host.navigationController pushViewController:controller animated:YES]; } else { [FLEXAlert showAlert:@"Cannot Push View Controller" message:@"This view controller's view is currently in use." from:host ]; } } accessoryType:^UITableViewCellAccessoryType(UIViewController *controller) { if (!vcIsInuse(controller)) { return UITableViewCellAccessoryDisclosureIndicator; } else { return UITableViewCellAccessoryNone; } } ] ]]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXViewShortcuts.h ================================================ // // FLEXViewShortcuts.h // FLEX // // Created by Tanner Bennett on 12/11/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXShortcutsSection.h" /// Adds "Nearest View Controller" and "Preview Image" shortcuts to all views @interface FLEXViewShortcuts : FLEXShortcutsSection @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXViewShortcuts.m ================================================ // // FLEXViewShortcuts.m // FLEX // // Created by Tanner Bennett on 12/11/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXViewShortcuts.h" #import "FLEXShortcut.h" #import "FLEXRuntimeUtility.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXImagePreviewViewController.h" @interface FLEXViewShortcuts () @property (nonatomic, readonly) UIView *view; @end @implementation FLEXViewShortcuts #pragma mark - Internal - (UIView *)view { return self.object; } + (UIViewController *)viewControllerForView:(UIView *)view { NSString *viewDelegate = @"viewDelegate"; if ([view respondsToSelector:NSSelectorFromString(viewDelegate)]) { return [view valueForKey:viewDelegate]; } return nil; } + (UIViewController *)viewControllerForAncestralView:(UIView *)view { NSString *_viewControllerForAncestor = @"_viewControllerForAncestor"; if ([view respondsToSelector:NSSelectorFromString(_viewControllerForAncestor)]) { return [view valueForKey:_viewControllerForAncestor]; } return nil; } + (UIViewController *)nearestViewControllerForView:(UIView *)view { return [self viewControllerForView:view] ?: [self viewControllerForAncestralView:view]; } #pragma mark - Overrides + (instancetype)forObject:(UIView *)view { // In the past, FLEX would not hold a strong reference to something like this. // After using FLEX for so long, I am certain it is more useful to eagerly // reference something as useful as a view controller so that the reference // is not lost and swept out from under you before you can access it. // // The alternative here is to use a future in place of `controller` which would // dynamically grab a reference to the view controller. 99% of the time, however, // it is not all that useful. If you need it to refresh, you can simply go back // and go forward again and it will show if the view controller is nil or changed. UIViewController *controller = [FLEXViewShortcuts nearestViewControllerForView:view]; return [self forObject:view additionalRows:@[ [FLEXActionShortcut title:@"Nearest View Controller" subtitle:^NSString *(id view) { return [FLEXRuntimeUtility safeDescriptionForObject:controller]; } viewer:^UIViewController *(id view) { return [FLEXObjectExplorerFactory explorerViewControllerForObject:controller]; } accessoryType:^UITableViewCellAccessoryType(id view) { return controller ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; } ], [FLEXActionShortcut title:@"Preview Image" subtitle:^NSString *(UIView *view) { return !CGRectIsEmpty(view.bounds) ? @"" : @"Unavailable with empty bounds"; } viewer:^UIViewController *(UIView *view) { return [FLEXImagePreviewViewController previewForView:view]; } accessoryType:^UITableViewCellAccessoryType(UIView *view) { // Disable preview if bounds are CGRectZero return !CGRectIsEmpty(view.bounds) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; } ] ]]; } @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXWindowShortcuts.h ================================================ // // FLEXWindowShortcuts.h // FLEX // // Created by AnthoPak on 26/09/2022. // #import "FLEXShortcutsSection.h" /// Adds "Animations Speed" shortcut for all windows @interface FLEXWindowShortcuts : FLEXShortcutsSection @end ================================================ FILE: Classes/ObjectExplorers/Sections/Shortcuts/FLEXWindowShortcuts.m ================================================ // // FLEXWindowShortcuts.m // FLEX // // Created by AnthoPak on 26/09/2022. // #import "FLEXWindowShortcuts.h" #import "FLEXShortcut.h" #import "FLEXAlert.h" #import "FLEXObjectExplorerViewController.h" @implementation FLEXWindowShortcuts #pragma mark - Overrides + (instancetype)forObject:(UIView *)view { return [self forObject:view additionalRows:@[ [FLEXActionShortcut title:@"Animation Speed" subtitle:^NSString *(UIWindow *window) { return [NSString stringWithFormat:@"Current speed: %.2f", window.layer.speed]; } selectionHandler:^(UIViewController *host, UIWindow *window) { [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Change Animation Speed"); make.message([NSString stringWithFormat:@"Current speed: %.2f", window.layer.speed]); make.configuredTextField(^(UITextField * _Nonnull textField) { textField.placeholder = @"Default: 1.0"; textField.keyboardType = UIKeyboardTypeDecimalPad; }); make.button(@"OK").handler(^(NSArray *strings) { NSNumberFormatter *formatter = [NSNumberFormatter new]; formatter.numberStyle = NSNumberFormatterDecimalStyle; CGFloat speedValue = [formatter numberFromString:strings.firstObject].floatValue; window.layer.speed = speedValue; // Refresh the host view controller to update the shortcut subtitle, reflecting current speed // TODO: this shouldn't be necessary [(FLEXObjectExplorerViewController *)host reloadData]; }); make.button(@"Cancel").cancelStyle(); } showFrom:host]; } accessoryType:^UITableViewCellAccessoryType(id _Nonnull object) { return UITableViewCellAccessoryDisclosureIndicator; }] ]]; } @end ================================================ FILE: Classes/Toolbar/FLEXExplorerToolbar.h ================================================ // // FLEXExplorerToolbar.h // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXExplorerToolbarItem; NS_ASSUME_NONNULL_BEGIN /// Users of the toolbar can configure the enabled state /// and event target/actions for each item. @interface FLEXExplorerToolbar : UIView /// The items to be displayed in the toolbar. Defaults to: /// globalsItem, hierarchyItem, selectItem, moveItem, closeItem @property (nonatomic, copy) NSArray *toolbarItems; /// Toolbar item for selecting views. @property (nonatomic, readonly) FLEXExplorerToolbarItem *selectItem; /// Toolbar item for presenting a list with the view hierarchy. @property (nonatomic, readonly) FLEXExplorerToolbarItem *hierarchyItem; /// Toolbar item for moving views. /// Its \c sibling is the \c lastTabItem @property (nonatomic, readonly) FLEXExplorerToolbarItem *moveItem; /// Toolbar item for presenting the currently active tab. @property (nonatomic, readonly) FLEXExplorerToolbarItem *recentItem; /// Toolbar item for presenting a screen with various tools for inspecting the app. @property (nonatomic, readonly) FLEXExplorerToolbarItem *globalsItem; /// Toolbar item for hiding the explorer. @property (nonatomic, readonly) FLEXExplorerToolbarItem *closeItem; /// A view for moving the entire toolbar. /// Users of the toolbar can attach a pan gesture recognizer to decide how to reposition the toolbar. @property (nonatomic, readonly) UIView *dragHandle; /// A color matching the overlay on color on the selected view. @property (nonatomic) UIColor *selectedViewOverlayColor; /// Description text for the selected view displayed below the toolbar items. @property (nonatomic, copy) NSString *selectedViewDescription; /// Area where details of the selected view are shown /// Users of the toolbar can attach a tap gesture recognizer to show additional details. @property (nonatomic, readonly) UIView *selectedViewDescriptionContainer; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Toolbar/FLEXExplorerToolbar.m ================================================ // // FLEXExplorerToolbar.m // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXExplorerToolbar.h" #import "FLEXExplorerToolbarItem.h" #import "FLEXResources.h" #import "FLEXUtility.h" @interface FLEXExplorerToolbar () @property (nonatomic, readwrite) FLEXExplorerToolbarItem *globalsItem; @property (nonatomic, readwrite) FLEXExplorerToolbarItem *hierarchyItem; @property (nonatomic, readwrite) FLEXExplorerToolbarItem *selectItem; @property (nonatomic, readwrite) FLEXExplorerToolbarItem *recentItem; @property (nonatomic, readwrite) FLEXExplorerToolbarItem *moveItem; @property (nonatomic, readwrite) FLEXExplorerToolbarItem *closeItem; @property (nonatomic, readwrite) UIView *dragHandle; @property (nonatomic) UIImageView *dragHandleImageView; @property (nonatomic) UIView *selectedViewDescriptionContainer; @property (nonatomic) UIView *selectedViewDescriptionSafeAreaContainer; @property (nonatomic) UIView *selectedViewColorIndicator; @property (nonatomic) UILabel *selectedViewDescriptionLabel; @property (nonatomic,readwrite) UIView *backgroundView; @end @implementation FLEXExplorerToolbar - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Background self.backgroundView = [UIView new]; self.backgroundView.backgroundColor = [FLEXColor secondaryBackgroundColorWithAlpha:0.95]; [self addSubview:self.backgroundView]; // Drag handle self.dragHandle = [UIView new]; self.dragHandle.backgroundColor = UIColor.clearColor; self.dragHandleImageView = [[UIImageView alloc] initWithImage:FLEXResources.dragHandle]; self.dragHandleImageView.tintColor = [FLEXColor.iconColor colorWithAlphaComponent:0.666]; [self.dragHandle addSubview:self.dragHandleImageView]; [self addSubview:self.dragHandle]; // Buttons self.globalsItem = [FLEXExplorerToolbarItem itemWithTitle:@"menu" image:FLEXResources.globalsIcon]; self.hierarchyItem = [FLEXExplorerToolbarItem itemWithTitle:@"views" image:FLEXResources.hierarchyIcon]; self.selectItem = [FLEXExplorerToolbarItem itemWithTitle:@"select" image:FLEXResources.selectIcon]; self.recentItem = [FLEXExplorerToolbarItem itemWithTitle:@"recent" image:FLEXResources.recentIcon]; self.moveItem = [FLEXExplorerToolbarItem itemWithTitle:@"move" image:FLEXResources.moveIcon sibling:self.recentItem]; self.closeItem = [FLEXExplorerToolbarItem itemWithTitle:@"close" image:FLEXResources.closeIcon]; // Selected view box // self.selectedViewDescriptionContainer = [UIView new]; self.selectedViewDescriptionContainer.backgroundColor = [FLEXColor tertiaryBackgroundColorWithAlpha:0.95]; self.selectedViewDescriptionContainer.hidden = YES; [self addSubview:self.selectedViewDescriptionContainer]; self.selectedViewDescriptionSafeAreaContainer = [UIView new]; self.selectedViewDescriptionSafeAreaContainer.backgroundColor = UIColor.clearColor; [self.selectedViewDescriptionContainer addSubview:self.selectedViewDescriptionSafeAreaContainer]; self.selectedViewColorIndicator = [UIView new]; self.selectedViewColorIndicator.backgroundColor = UIColor.redColor; [self.selectedViewDescriptionSafeAreaContainer addSubview:self.selectedViewColorIndicator]; self.selectedViewDescriptionLabel = [UILabel new]; self.selectedViewDescriptionLabel.backgroundColor = UIColor.clearColor; self.selectedViewDescriptionLabel.font = [[self class] descriptionLabelFont]; [self.selectedViewDescriptionSafeAreaContainer addSubview:self.selectedViewDescriptionLabel]; // toolbarItems self.toolbarItems = @[_globalsItem, _hierarchyItem, _selectItem, _moveItem, _closeItem]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGRect safeArea = [self safeArea]; // Drag Handle const CGFloat kToolbarItemHeight = [[self class] toolbarItemHeight]; self.dragHandle.frame = CGRectMake(CGRectGetMinX(safeArea), CGRectGetMinY(safeArea), [[self class] dragHandleWidth], kToolbarItemHeight); CGRect dragHandleImageFrame = self.dragHandleImageView.frame; dragHandleImageFrame.origin.x = FLEXFloor((self.dragHandle.frame.size.width - dragHandleImageFrame.size.width) / 2.0); dragHandleImageFrame.origin.y = FLEXFloor((self.dragHandle.frame.size.height - dragHandleImageFrame.size.height) / 2.0); self.dragHandleImageView.frame = dragHandleImageFrame; // Toolbar Items CGFloat originX = CGRectGetMaxX(self.dragHandle.frame); CGFloat originY = CGRectGetMinY(safeArea); CGFloat height = kToolbarItemHeight; CGFloat width = FLEXFloor((CGRectGetWidth(safeArea) - CGRectGetWidth(self.dragHandle.frame)) / self.toolbarItems.count); for (FLEXExplorerToolbarItem *toolbarItem in self.toolbarItems) { toolbarItem.currentItem.frame = CGRectMake(originX, originY, width, height); originX = CGRectGetMaxX(toolbarItem.currentItem.frame); } // Make sure the last toolbar item goes to the edge to account for any accumulated rounding effects. UIView *lastToolbarItem = self.toolbarItems.lastObject.currentItem; CGRect lastToolbarItemFrame = lastToolbarItem.frame; lastToolbarItemFrame.size.width = CGRectGetMaxX(safeArea) - lastToolbarItemFrame.origin.x; lastToolbarItem.frame = lastToolbarItemFrame; self.backgroundView.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), kToolbarItemHeight); const CGFloat kSelectedViewColorDiameter = [[self class] selectedViewColorIndicatorDiameter]; const CGFloat kDescriptionLabelHeight = [[self class] descriptionLabelHeight]; const CGFloat kHorizontalPadding = [[self class] horizontalPadding]; const CGFloat kDescriptionVerticalPadding = [[self class] descriptionVerticalPadding]; const CGFloat kDescriptionContainerHeight = [[self class] descriptionContainerHeight]; CGRect descriptionContainerFrame = CGRectZero; descriptionContainerFrame.size.width = CGRectGetWidth(self.bounds); descriptionContainerFrame.size.height = kDescriptionContainerHeight; descriptionContainerFrame.origin.x = CGRectGetMinX(self.bounds); descriptionContainerFrame.origin.y = CGRectGetMaxY(self.bounds) - kDescriptionContainerHeight; self.selectedViewDescriptionContainer.frame = descriptionContainerFrame; CGRect descriptionSafeAreaContainerFrame = CGRectZero; descriptionSafeAreaContainerFrame.size.width = CGRectGetWidth(safeArea); descriptionSafeAreaContainerFrame.size.height = kDescriptionContainerHeight; descriptionSafeAreaContainerFrame.origin.x = CGRectGetMinX(safeArea); descriptionSafeAreaContainerFrame.origin.y = CGRectGetMinY(safeArea); self.selectedViewDescriptionSafeAreaContainer.frame = descriptionSafeAreaContainerFrame; // Selected View Color CGRect selectedViewColorFrame = CGRectZero; selectedViewColorFrame.size.width = kSelectedViewColorDiameter; selectedViewColorFrame.size.height = kSelectedViewColorDiameter; selectedViewColorFrame.origin.x = kHorizontalPadding; selectedViewColorFrame.origin.y = FLEXFloor((kDescriptionContainerHeight - kSelectedViewColorDiameter) / 2.0); self.selectedViewColorIndicator.frame = selectedViewColorFrame; self.selectedViewColorIndicator.layer.cornerRadius = ceil(selectedViewColorFrame.size.height / 2.0); // Selected View Description CGRect descriptionLabelFrame = CGRectZero; CGFloat descriptionOriginX = CGRectGetMaxX(selectedViewColorFrame) + kHorizontalPadding; descriptionLabelFrame.size.height = kDescriptionLabelHeight; descriptionLabelFrame.origin.x = descriptionOriginX; descriptionLabelFrame.origin.y = kDescriptionVerticalPadding; descriptionLabelFrame.size.width = CGRectGetMaxX(self.selectedViewDescriptionContainer.bounds) - kHorizontalPadding - descriptionOriginX; self.selectedViewDescriptionLabel.frame = descriptionLabelFrame; } #pragma mark - Setter Overrides - (void)setToolbarItems:(NSArray *)toolbarItems { if (_toolbarItems == toolbarItems) { return; } // Remove old toolbar items, if any for (FLEXExplorerToolbarItem *item in _toolbarItems) { [item.currentItem removeFromSuperview]; } // Trim to 5 items if necessary if (toolbarItems.count > 5) { toolbarItems = [toolbarItems subarrayWithRange:NSMakeRange(0, 5)]; } for (FLEXExplorerToolbarItem *item in toolbarItems) { [self addSubview:item.currentItem]; } _toolbarItems = toolbarItems.copy; // Lay out new items [self setNeedsLayout]; [self layoutIfNeeded]; } - (void)setSelectedViewOverlayColor:(UIColor *)selectedViewOverlayColor { if (![_selectedViewOverlayColor isEqual:selectedViewOverlayColor]) { _selectedViewOverlayColor = selectedViewOverlayColor; self.selectedViewColorIndicator.backgroundColor = selectedViewOverlayColor; } } - (void)setSelectedViewDescription:(NSString *)selectedViewDescription { if (![_selectedViewDescription isEqual:selectedViewDescription]) { _selectedViewDescription = selectedViewDescription; self.selectedViewDescriptionLabel.text = selectedViewDescription; BOOL showDescription = selectedViewDescription.length > 0; self.selectedViewDescriptionContainer.hidden = !showDescription; } } #pragma mark - Sizing Convenience Methods + (UIFont *)descriptionLabelFont { return [UIFont systemFontOfSize:12.0]; } + (CGFloat)toolbarItemHeight { return 44.0; } + (CGFloat)dragHandleWidth { return FLEXResources.dragHandle.size.width; } + (CGFloat)descriptionLabelHeight { return ceil([[self descriptionLabelFont] lineHeight]); } + (CGFloat)descriptionVerticalPadding { return 2.0; } + (CGFloat)descriptionContainerHeight { return [self descriptionVerticalPadding] * 2.0 + [self descriptionLabelHeight]; } + (CGFloat)selectedViewColorIndicatorDiameter { return ceil([self descriptionLabelHeight] / 2.0); } + (CGFloat)horizontalPadding { return 11.0; } - (CGSize)sizeThatFits:(CGSize)size { CGFloat height = 0.0; height += [[self class] toolbarItemHeight]; height += [[self class] descriptionContainerHeight]; return CGSizeMake(size.width, height); } - (CGRect)safeArea { CGRect safeArea = self.bounds; if (@available(iOS 11.0, *)) { safeArea = UIEdgeInsetsInsetRect(self.bounds, self.safeAreaInsets); } return safeArea; } @end ================================================ FILE: Classes/Toolbar/FLEXExplorerToolbarItem.h ================================================ // // FLEXExplorerToolbarItem.h // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface FLEXExplorerToolbarItem : UIButton + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image; /// @param backupItem a toolbar item to use in place of this item when it becomes disabled. /// Items without a sibling item exhibit expected behavior when they become disabled, and are greyed out. + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(nullable FLEXExplorerToolbarItem *)backupItem; /// If a toolbar item has a sibling, the item will replace itself with its /// sibling when it becomes disabled, and vice versa when it becomes enabled again. @property (nonatomic, readonly) FLEXExplorerToolbarItem *sibling; /// When a toolbar item has a sibling and it becomes disabled, the sibling is the view /// that should be added to or removed from a new or existing toolbar. This property /// alleviates the programmer from determining whether to use \c item or \c item.sibling /// or \c item.sibling.sibling and so on. Yes, sibling items can also have siblings so /// that each item which becomes disabled may present another item in its place, creating /// a "stack" of toolbar items. This behavior is useful for making buttons which occupy /// the same space under different states. /// /// With this in mind, you should never access a stored toolbar item's view properties /// such as \c frame or \c superview directly; you should access them on \c currentItem. /// If you are trying to modify the frame of an item, and the item itself is not currently /// displayed but instead its sibling is being displayed, then your changes could be ignored. /// /// @return the result of the item's sibling's \c currentItem, /// if this item has a sibling and this item is disabled, otherwise this item. @property (nonatomic, readonly) FLEXExplorerToolbarItem *currentItem; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Toolbar/FLEXExplorerToolbarItem.m ================================================ // // FLEXExplorerToolbarItem.m // Flipboard // // Created by Ryan Olson on 4/4/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXExplorerToolbarItem.h" #import "FLEXUtility.h" @interface FLEXExplorerToolbarItem () @property (nonatomic) FLEXExplorerToolbarItem *sibling; @property (nonatomic, copy) NSString *title; @property (nonatomic) UIImage *image; @property (nonatomic, readonly, class) UIColor *defaultBackgroundColor; @property (nonatomic, readonly, class) UIColor *highlightedBackgroundColor; @property (nonatomic, readonly, class) UIColor *selectedBackgroundColor; @end @implementation FLEXExplorerToolbarItem #pragma mark - Public + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image { return [self itemWithTitle:title image:image sibling:nil]; } + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(FLEXExplorerToolbarItem *)backupItem { NSParameterAssert(title); NSParameterAssert(image); FLEXExplorerToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeCustom]; toolbarItem.sibling = backupItem; toolbarItem.title = title; toolbarItem.image = image; toolbarItem.tintColor = FLEXColor.iconColor; toolbarItem.backgroundColor = self.defaultBackgroundColor; toolbarItem.titleLabel.font = [UIFont systemFontOfSize:12.0]; [toolbarItem setTitle:title forState:UIControlStateNormal]; [toolbarItem setImage:image forState:UIControlStateNormal]; [toolbarItem setTitleColor:FLEXColor.primaryTextColor forState:UIControlStateNormal]; [toolbarItem setTitleColor:FLEXColor.deemphasizedTextColor forState:UIControlStateDisabled]; return toolbarItem; } - (FLEXExplorerToolbarItem *)currentItem { if (!self.enabled && self.sibling) { return self.sibling.currentItem; } return self; } #pragma mark - Display Defaults + (NSDictionary *)titleAttributes { return @{ NSFontAttributeName : [UIFont systemFontOfSize:12.0] }; } + (UIColor *)highlightedBackgroundColor { return FLEXColor.toolbarItemHighlightedColor; } + (UIColor *)selectedBackgroundColor { return FLEXColor.toolbarItemSelectedColor; } + (UIColor *)defaultBackgroundColor { return UIColor.clearColor; } + (CGFloat)topMargin { return 2.0; } #pragma mark - State Changes - (void)setHighlighted:(BOOL)highlighted { super.highlighted = highlighted; [self updateColors]; } - (void)setSelected:(BOOL)selected { super.selected = selected; [self updateColors]; } - (void)setEnabled:(BOOL)enabled { if (self.enabled != enabled) { if (self.sibling) { if (enabled) { // Replace sibling with myself UIView *superview = self.sibling.superview; [self.sibling removeFromSuperview]; self.frame = self.sibling.frame; [superview addSubview:self]; } else { // Replace myself with sibling UIView *superview = self.superview; [self removeFromSuperview]; self.sibling.frame = self.frame; [superview addSubview:self.sibling]; } } super.enabled = enabled; } } + (id)_selectedIndicatorImage { return nil; } - (void)updateColors { // Background color if (self.highlighted) { self.backgroundColor = self.class.highlightedBackgroundColor; } else if (self.selected) { self.backgroundColor = self.class.selectedBackgroundColor; } else { self.backgroundColor = self.class.defaultBackgroundColor; } } #pragma mark - UIButton Layout Overrides - (CGRect)titleRectForContentRect:(CGRect)contentRect { NSDictionary *attrs = [[self class] titleAttributes]; // Bottom aligned and centered. CGRect titleRect = CGRectZero; CGSize titleSize = [self.title boundingRectWithSize:contentRect.size options:0 attributes:attrs context:nil].size; titleSize = CGSizeMake(ceil(titleSize.width), ceil(titleSize.height)); titleRect.size = titleSize; titleRect.origin.y = contentRect.origin.y + CGRectGetMaxY(contentRect) - titleSize.height; titleRect.origin.x = contentRect.origin.x + FLEXFloor((contentRect.size.width - titleSize.width) / 2.0); return titleRect; } - (CGRect)imageRectForContentRect:(CGRect)contentRect { CGSize imageSize = self.image.size; CGRect titleRect = [self titleRectForContentRect:contentRect]; CGFloat availableHeight = contentRect.size.height - titleRect.size.height - [[self class] topMargin]; CGFloat originY = [[self class] topMargin] + FLEXFloor((availableHeight - imageSize.height) / 2.0); CGFloat originX = FLEXFloor((contentRect.size.width - imageSize.width) / 2.0); CGRect imageRect = CGRectMake(originX, originY, imageSize.width, imageSize.height); return imageRect; } @end ================================================ FILE: Classes/Utility/APPLE_LICENSE ================================================ APPLE PUBLIC SOURCE LICENSE Version 2.0 - August 6, 2003 Please read this License carefully before downloading this software. By downloading or using this software, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use the software. 1. General; Definitions. This License applies to any program or other work which Apple Computer, Inc. ("Apple") makes publicly available and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 2.0 ("License"). As used in this License: 1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Apple and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code. 1.2 "Contributor" means any person or entity that creates or contributes to the creation of Modifications. 1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof. 1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise make Covered Code available, directly or indirectly, to anyone other than You; and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way to provide a service, including but not limited to delivery of content, through electronic communication with a client other than You. 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code. 1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Apple under this License. 1.8 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code). 1.9 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. 2. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Apple's Applicable Patent Rights and copyrights covering the Original Code, to do the following: 2.1 Unmodified Code. You may use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy verbatim, unmodified copies of the Original Code, for commercial or non-commercial purposes, provided that in each instance: (a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License; and (b) You must include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute or Externally Deploy, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6. 2.2 Modified Code. You may modify Covered Code and use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy Your Modifications and Covered Code, for commercial or non-commercial purposes, provided that in each instance You also meet all of these conditions: (a) You must satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code; (b) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change; and (c) If You Externally Deploy Your Modifications, You must make Source Code of all Your Externally Deployed Modifications either available to those to whom You have Externally Deployed Your Modifications, or publicly available. Source Code of Your Externally Deployed Modifications must be released under the terms set forth in this License, including the license grants set forth in Section 3 below, for as long as you Externally Deploy the Covered Code or twelve (12) months from the date of initial External Deployment, whichever is longer. You should preferably distribute the Source Code of Your Externally Deployed Modifications electronically (e.g. download from a web site). 2.3 Distribution of Executable Versions. In addition, if You Externally Deploy Covered Code (Original Code and/or Modifications) in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code. 2.4 Third Party Rights. You expressly acknowledge and agree that although Apple and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Apple or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Apple and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Covered Code, it is Your responsibility to acquire that license before distributing the Covered Code. 3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License, You hereby grant to any person or entity receiving or distributing Covered Code under this License a non-exclusive, royalty-free, perpetual, irrevocable license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify, sublicense, distribute and Externally Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2 above. 4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof. 5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion. 6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple and every Contributor harmless for any liability incurred by or claims asserted against Apple or such Contributor by reason of any such Additional Terms. 7. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License. 8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage. 9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Apple's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of fifty dollars ($50.00). 10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", "QuickTime", "QuickTime Streaming Server" or any other trademarks, service marks, logos or trade names belonging to Apple (collectively "Apple Marks") or to any trademark, service mark, logo or trade name belonging to any Contributor. You agree not to use any Apple Marks in or as part of the name of products derived from the Original Code or to endorse or promote products derived from the Original Code other than as expressly permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html. 11. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all. 12. Termination. 12.1 Termination. This License and the rights granted hereunder will terminate: (a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach; (b) immediately in the event of the circumstances described in Section 13.5(b); or (c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple; provided that Apple did not first commence an action for patent infringement against You in that instance. 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party. 13. Miscellaneous. 13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein. 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or among You, Apple or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise. 13.3 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute. 13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License. 13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control. 13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. 13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law. Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exigé que le présent contrat et tous les documents connexes soient rédigés en anglais. EXHIBIT A. "Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 2.0 (the 'License'). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.opensource.apple.com/apsl/ and read it before using this file. The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License." ================================================ FILE: Classes/Utility/Categories/CALayer+FLEX.h ================================================ // // CALayer+FLEX.h // FLEX // // Created by Tanner on 2/28/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface CALayer (FLEX) @property (nonatomic) BOOL flex_continuousCorners; @end ================================================ FILE: Classes/Utility/Categories/CALayer+FLEX.m ================================================ // // CALayer+FLEX.m // FLEX // // Created by Tanner on 2/28/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "CALayer+FLEX.h" @interface CALayer (Private) @property (nonatomic) BOOL continuousCorners; @end @implementation CALayer (FLEX) static BOOL respondsToContinuousCorners = NO; + (void)load { respondsToContinuousCorners = [CALayer instancesRespondToSelector:@selector(setContinuousCorners:) ]; } - (BOOL)flex_continuousCorners { if (respondsToContinuousCorners) { return self.continuousCorners; } return NO; } - (void)setFlex_continuousCorners:(BOOL)enabled { if (respondsToContinuousCorners) { if (@available(iOS 13, *)) { self.cornerCurve = kCACornerCurveContinuous; } else { self.continuousCorners = enabled; // self.masksToBounds = NO; // self.allowsEdgeAntialiasing = YES; // self.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge; } } } @end ================================================ FILE: Classes/Utility/Categories/FLEXRuntime+Compare.h ================================================ // // FLEXRuntime+Compare.h // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import "FLEXProperty.h" #import "FLEXIvar.h" #import "FLEXMethodBase.h" #import "FLEXProtocol.h" @interface FLEXProperty (Compare) - (NSComparisonResult)compare:(FLEXProperty *)other; @end @interface FLEXIvar (Compare) - (NSComparisonResult)compare:(FLEXIvar *)other; @end @interface FLEXMethodBase (Compare) - (NSComparisonResult)compare:(FLEXMethodBase *)other; @end @interface FLEXProtocol (Compare) - (NSComparisonResult)compare:(FLEXProtocol *)other; @end ================================================ FILE: Classes/Utility/Categories/FLEXRuntime+Compare.m ================================================ // // FLEXRuntime+Compare.m // FLEX // // Created by Tanner Bennett on 8/28/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXRuntime+Compare.h" @implementation FLEXProperty (Compare) - (NSComparisonResult)compare:(FLEXProperty *)other { NSComparisonResult r = [self.name caseInsensitiveCompare:other.name]; if (r == NSOrderedSame) { // TODO make sure empty image name sorts above an image name return [self.imageName ?: @"" compare:other.imageName]; } return r; } @end @implementation FLEXIvar (Compare) - (NSComparisonResult)compare:(FLEXIvar *)other { return [self.name caseInsensitiveCompare:other.name]; } @end @implementation FLEXMethodBase (Compare) - (NSComparisonResult)compare:(FLEXMethodBase *)other { return [self.name caseInsensitiveCompare:other.name]; } @end @implementation FLEXProtocol (Compare) - (NSComparisonResult)compare:(FLEXProtocol *)other { return [self.name caseInsensitiveCompare:other.name]; } @end ================================================ FILE: Classes/Utility/Categories/FLEXRuntime+UIKitHelpers.h ================================================ // // FLEXRuntime+UIKitHelpers.h // FLEX // // Created by Tanner Bennett on 12/16/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import "FLEXProperty.h" #import "FLEXIvar.h" #import "FLEXMethod.h" #import "FLEXProtocol.h" #import "FLEXTableViewSection.h" @class FLEXObjectExplorerDefaults; /// Model objects of an object explorer screen adopt this /// protocol in order respond to user defaults changes @protocol FLEXObjectExplorerItem /// Current explorer settings. Set when settings change. @property (nonatomic) FLEXObjectExplorerDefaults *defaults; /// YES for properties and ivars which surely support editing, NO for all methods. @property (nonatomic, readonly) BOOL isEditable; /// NO for ivars, YES for supported methods and properties @property (nonatomic, readonly) BOOL isCallable; @end @protocol FLEXRuntimeMetadata /// Used as the main title of the row - (NSString *)description; /// Used to compare metadata objects for uniqueness @property (nonatomic, readonly) NSString *name; /// For internal use @property (nonatomic) id tag; /// Should return \c nil if not applicable - (id)currentValueWithTarget:(id)object; /// Used as the subtitle or description of a property, ivar, or method - (NSString *)previewWithTarget:(id)object; /// For methods, a method calling screen. For all else, an object explorer. - (UIViewController *)viewerWithTarget:(id)object; /// For methods and protocols, nil. For all else, an a field editor screen. /// The given section is reloaded on commit of any changes. - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section; /// Used to determine present which interactions are possible to the user - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object; /// Return nil to use the default reuse identifier - (NSString *)reuseIdentifierWithTarget:(id)object; /// An array of actions to place in the first section of the context menu. - (NSArray *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender API_AVAILABLE(ios(13.0)); /// An array where every 2 elements are a key-value pair. The key is a description /// of what to copy like "Name" and the values are what will be copied. - (NSArray *)copiableMetadataWithTarget:(id)object; /// Properties and ivars return the address of an object, if they hold one. - (NSString *)contextualSubtitleWithTarget:(id)object; @end // Even if a property is readonly, it still may be editable // via a setter. Checking isEditable will not reflect that // unless the property was initialized with a class. @interface FLEXProperty (UIKitHelpers) @end @interface FLEXIvar (UIKitHelpers) @end @interface FLEXMethodBase (UIKitHelpers) @end @interface FLEXMethod (UIKitHelpers) @end @interface FLEXProtocol (UIKitHelpers) @end typedef NS_ENUM(NSUInteger, FLEXStaticMetadataRowStyle) { FLEXStaticMetadataRowStyleSubtitle, FLEXStaticMetadataRowStyleKeyValue, FLEXStaticMetadataRowStyleDefault = FLEXStaticMetadataRowStyleSubtitle, }; /// Displays a small row as a static key-value pair of information. @interface FLEXStaticMetadata : NSObject + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title string:(NSString *)string; + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title number:(NSNumber *)number; + (NSArray *)classHierarchy:(NSArray *)classes; @end /// This is assigned to the \c tag property of each metadata. ================================================ FILE: Classes/Utility/Categories/FLEXRuntime+UIKitHelpers.m ================================================ // // FLEXRuntime+UIKitHelpers.m // FLEX // // Created by Tanner Bennett on 12/16/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXRuntime+UIKitHelpers.h" #import "FLEXRuntimeUtility.h" #import "FLEXPropertyAttributes.h" #import "FLEXArgumentInputViewFactory.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXFieldEditorViewController.h" #import "FLEXMethodCallingViewController.h" #import "FLEXObjectListViewController.h" #import "FLEXTableView.h" #import "FLEXUtility.h" #import "NSArray+FLEX.h" #import "NSString+FLEX.h" #define FLEXObjectExplorerDefaultsImpl \ - (FLEXObjectExplorerDefaults *)defaults { \ return self.tag; \ } \ \ - (void)setDefaults:(FLEXObjectExplorerDefaults *)defaults { \ self.tag = defaults; \ } #pragma mark FLEXProperty @implementation FLEXProperty (UIKitHelpers) FLEXObjectExplorerDefaultsImpl /// Decide whether to use potentialTarget or [potentialTarget class] to get or set property - (id)appropriateTargetForPropertyType:(id)potentialTarget { if (!object_isClass(potentialTarget)) { if (self.isClassProperty) { return [potentialTarget class]; } else { return potentialTarget; } } else { if (self.isClassProperty) { return potentialTarget; } else { // Instance property with a class object return nil; } } } - (BOOL)isEditable { if (self.attributes.isReadOnly) { return self.likelySetterExists; } const FLEXTypeEncoding *typeEncoding = self.attributes.typeEncoding.UTF8String; return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:nil]; } - (BOOL)isCallable { return YES; } - (id)currentValueWithTarget:(id)object { return [self getPotentiallyUnboxedValue: [self appropriateTargetForPropertyType:object] ]; } - (id)currentValueBeforeUnboxingWithTarget:(id)object { return [self getValue: [self appropriateTargetForPropertyType:object] ]; } - (NSString *)previewWithTarget:(id)object { if (object_isClass(object) && !self.isClassProperty) { return self.attributes.fullDeclaration; } else if (self.defaults.wantsDynamicPreviews) { return [FLEXRuntimeUtility summaryForObject:[self currentValueWithTarget:object] ]; } return nil; } - (UIViewController *)viewerWithTarget:(id)object { id value = [self currentValueWithTarget:object]; return [FLEXObjectExplorerFactory explorerViewControllerForObject:value]; } - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { id target = [self appropriateTargetForPropertyType:object]; return [FLEXFieldEditorViewController target:target property:self commitHandler:^{ [section reloadData:YES]; }]; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { id targetForValueCheck = [self appropriateTargetForPropertyType:object]; if (!targetForValueCheck) { // Instance property with a class object return UITableViewCellAccessoryNone; } // We use .tag to store the cached value of .isEditable that is // initialized by FLEXObjectExplorer in -reloadMetada if ([self getPotentiallyUnboxedValue:targetForValueCheck]) { if (self.defaults.isEditable) { // Editable non-nil value, both return UITableViewCellAccessoryDetailDisclosureButton; } else { // Uneditable non-nil value, chevron only return UITableViewCellAccessoryDisclosureIndicator; } } else { if (self.defaults.isEditable) { // Editable nil value, just (i) return UITableViewCellAccessoryDetailButton; } else { // Non-editable nil value, neither return UITableViewCellAccessoryNone; } } } - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } - (NSArray *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { BOOL returnsObject = self.attributes.typeEncoding.flex_typeIsObjectOrClass; BOOL targetNotNil = [self appropriateTargetForPropertyType:object] != nil; // "Explore PropertyClass" for properties with a concrete class name if (returnsObject) { NSMutableArray *actions = [NSMutableArray new]; // Action for exploring class of this property Class propertyClass = self.attributes.typeEncoding.flex_typeClass; if (propertyClass) { NSString *title = [NSString stringWithFormat:@"Explore %@", NSStringFromClass(propertyClass)]; [actions addObject:[UIAction actionWithTitle:title image:nil identifier:nil handler:^(UIAction *action) { UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:propertyClass]; [sender.navigationController pushViewController:explorer animated:YES]; }]]; } // Action for exploring references to this object if (targetNotNil) { // Since the property holder is not nil, check if the property value is nil id value = [self currentValueBeforeUnboxingWithTarget:object]; if (value) { NSString *title = @"List all references"; [actions addObject:[UIAction actionWithTitle:title image:nil identifier:nil handler:^(UIAction *action) { UIViewController *list = [FLEXObjectListViewController objectsWithReferencesToObject:value retained:NO ]; [sender.navigationController pushViewController:list animated:YES]; }]]; } } return actions; } return nil; } - (NSArray *)copiableMetadataWithTarget:(id)object { BOOL returnsObject = self.attributes.typeEncoding.flex_typeIsObjectOrClass; BOOL targetNotNil = [self appropriateTargetForPropertyType:object] != nil; NSMutableArray *items = [NSMutableArray arrayWithArray:@[ @"Name", self.name ?: @"", @"Type", self.attributes.typeEncoding ?: @"", @"Declaration", self.fullDescription ?: @"", ]]; if (targetNotNil) { id value = [self currentValueBeforeUnboxingWithTarget:object]; [items addObjectsFromArray:@[ @"Value Preview", [self previewWithTarget:object] ?: @"", @"Value Address", returnsObject ? [FLEXUtility addressOfObject:value] : @"", ]]; } [items addObjectsFromArray:@[ @"Getter", NSStringFromSelector(self.likelyGetter) ?: @"", @"Setter", self.likelySetterExists ? NSStringFromSelector(self.likelySetter) : @"", @"Image Name", self.imageName ?: @"", @"Attributes", self.attributes.string ?: @"", @"objc_property", [FLEXUtility pointerToString:self.objc_property], @"objc_property_attribute_t", [FLEXUtility pointerToString:self.attributes.list], ]]; return items; } - (NSString *)contextualSubtitleWithTarget:(id)object { id target = [self appropriateTargetForPropertyType:object]; if (target && self.attributes.typeEncoding.flex_typeIsObjectOrClass) { return [FLEXUtility addressOfObject:[self currentValueBeforeUnboxingWithTarget:target]]; } return nil; } @end #pragma mark FLEXIvar @implementation FLEXIvar (UIKitHelpers) FLEXObjectExplorerDefaultsImpl - (BOOL)isEditable { const FLEXTypeEncoding *typeEncoding = self.typeEncoding.UTF8String; return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:nil]; } - (BOOL)isCallable { return NO; } - (id)currentValueWithTarget:(id)object { if (!object_isClass(object)) { return [self getPotentiallyUnboxedValue:object]; } return nil; } - (NSString *)previewWithTarget:(id)object { if (object_isClass(object)) { return self.details; } else if (self.defaults.wantsDynamicPreviews) { return [FLEXRuntimeUtility summaryForObject:[self currentValueWithTarget:object] ]; } return nil; } - (UIViewController *)viewerWithTarget:(id)object { NSAssert(!object_isClass(object), @"Unreachable state: viewing ivar on class object"); id value = [self currentValueWithTarget:object]; return [FLEXObjectExplorerFactory explorerViewControllerForObject:value]; } - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { NSAssert(!object_isClass(object), @"Unreachable state: editing ivar on class object"); return [FLEXFieldEditorViewController target:object ivar:self commitHandler:^{ [section reloadData:YES]; }]; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { if (object_isClass(object)) { return UITableViewCellAccessoryNone; } // Could use .isEditable here, but we use .tag for speed since it is cached if ([self getPotentiallyUnboxedValue:object]) { if (self.defaults.isEditable) { // Editable non-nil value, both return UITableViewCellAccessoryDetailDisclosureButton; } else { // Uneditable non-nil value, chevron only return UITableViewCellAccessoryDisclosureIndicator; } } else { if (self.defaults.isEditable) { // Editable nil value, just (i) return UITableViewCellAccessoryDetailButton; } else { // Non-editable nil value, neither return UITableViewCellAccessoryNone; } } } - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } - (NSArray *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { Class ivarClass = self.typeEncoding.flex_typeClass; // "Explore PropertyClass" for properties with a concrete class name if (ivarClass) { NSString *title = [NSString stringWithFormat:@"Explore %@", NSStringFromClass(ivarClass)]; return @[[UIAction actionWithTitle:title image:nil identifier:nil handler:^(UIAction *action) { UIViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:ivarClass]; [sender.navigationController pushViewController:explorer animated:YES]; }]]; } return nil; } - (NSArray *)copiableMetadataWithTarget:(id)object { BOOL isInstance = !object_isClass(object); BOOL returnsObject = self.typeEncoding.flex_typeIsObjectOrClass; id value = isInstance ? [self getValue:object] : nil; NSMutableArray *items = [NSMutableArray arrayWithArray:@[ @"Name", self.name ?: @"", @"Type", self.typeEncoding ?: @"", @"Declaration", self.description ?: @"", ]]; if (isInstance) { [items addObjectsFromArray:@[ @"Value Preview", isInstance ? [self previewWithTarget:object] : @"", @"Value Address", returnsObject ? [FLEXUtility addressOfObject:value] : @"", ]]; } [items addObjectsFromArray:@[ @"Size", @(self.size).stringValue, @"Offset", @(self.offset).stringValue, @"objc_ivar", [FLEXUtility pointerToString:self.objc_ivar], ]]; return items; } - (NSString *)contextualSubtitleWithTarget:(id)object { if (!object_isClass(object) && self.typeEncoding.flex_typeIsObjectOrClass) { return [FLEXUtility addressOfObject:[self getValue:object]]; } return nil; } @end #pragma mark FLEXMethod @implementation FLEXMethodBase (UIKitHelpers) FLEXObjectExplorerDefaultsImpl - (BOOL)isEditable { return NO; } - (BOOL)isCallable { return NO; } - (id)currentValueWithTarget:(id)object { // Methods can't be "edited" and have no "value" return nil; } - (NSString *)previewWithTarget:(id)object { return [self.selectorString stringByAppendingFormat:@" — %@", self.typeEncoding]; } - (UIViewController *)viewerWithTarget:(id)object { // We disallow calling of FLEXMethodBase methods @throw NSInternalInconsistencyException; return nil; } - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { // Methods cannot be edited @throw NSInternalInconsistencyException; return nil; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { // We shouldn't be using any FLEXMethodBase objects for this @throw NSInternalInconsistencyException; return UITableViewCellAccessoryNone; } - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } - (NSArray *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { return nil; } - (NSArray *)copiableMetadataWithTarget:(id)object { return @[ @"Selector", self.name ?: @"", @"Type Encoding", self.typeEncoding ?: @"", @"Declaration", self.description ?: @"", ]; } - (NSString *)contextualSubtitleWithTarget:(id)object { return nil; } @end @implementation FLEXMethod (UIKitHelpers) - (BOOL)isCallable { return self.signature != nil; } - (UIViewController *)viewerWithTarget:(id)object { object = self.isInstanceMethod ? object : (object_isClass(object) ? object : [object class]); return [FLEXMethodCallingViewController target:object method:self]; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { if (self.isInstanceMethod) { if (object_isClass(object)) { // Instance method from class, can't call return UITableViewCellAccessoryNone; } else { // Instance method from instance, can call return UITableViewCellAccessoryDisclosureIndicator; } } else { return UITableViewCellAccessoryDisclosureIndicator; } } - (NSArray *)copiableMetadataWithTarget:(id)object { return [[super copiableMetadataWithTarget:object] arrayByAddingObjectsFromArray:@[ @"NSMethodSignature *", [FLEXUtility addressOfObject:self.signature], @"Signature String", self.signatureString ?: @"", @"Number of Arguments", @(self.numberOfArguments).stringValue, @"Return Type", @(self.returnType ?: ""), @"Return Size", @(self.returnSize).stringValue, @"objc_method", [FLEXUtility pointerToString:self.objc_method], ]]; } @end #pragma mark FLEXProtocol @implementation FLEXProtocol (UIKitHelpers) FLEXObjectExplorerDefaultsImpl - (BOOL)isEditable { return NO; } - (BOOL)isCallable { return NO; } - (id)currentValueWithTarget:(id)object { return nil; } - (NSString *)previewWithTarget:(id)object { return nil; } - (UIViewController *)viewerWithTarget:(id)object { return [FLEXObjectExplorerFactory explorerViewControllerForObject:self]; } - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { // Protocols cannot be edited @throw NSInternalInconsistencyException; return nil; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { return UITableViewCellAccessoryDisclosureIndicator; } - (NSString *)reuseIdentifierWithTarget:(id)object { return nil; } - (NSArray *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { return nil; } - (NSArray *)copiableMetadataWithTarget:(id)object { NSArray *conformanceNames = [self.protocols valueForKeyPath:@"name"]; NSString *conformances = [conformanceNames componentsJoinedByString:@"\n"]; return @[ @"Name", self.name ?: @"", @"Conformances", conformances ?: @"", ]; } - (NSString *)contextualSubtitleWithTarget:(id)object { return nil; } @end #pragma mark FLEXStaticMetadata @interface FLEXStaticMetadata () { @protected NSString *_name; } @property (nonatomic) FLEXTableViewCellReuseIdentifier reuse; @property (nonatomic) NSString *subtitle; @property (nonatomic) id metadata; @end @interface FLEXStaticMetadata_Class : FLEXStaticMetadata + (instancetype)withClass:(Class)cls; @end @implementation FLEXStaticMetadata @synthesize name = _name; @synthesize tag = _tag; FLEXObjectExplorerDefaultsImpl + (NSArray *)classHierarchy:(NSArray *)classes { return [classes flex_mapped:^id(Class cls, NSUInteger idx) { return [FLEXStaticMetadata_Class withClass:cls]; }]; } + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title string:(NSString *)string { return [[self alloc] initWithStyle:style title:title subtitle:string]; } + (instancetype)style:(FLEXStaticMetadataRowStyle)style title:(NSString *)title number:(NSNumber *)number { return [[self alloc] initWithStyle:style title:title subtitle:number.stringValue]; } - (id)initWithStyle:(FLEXStaticMetadataRowStyle)style title:(NSString *)title subtitle:(NSString *)subtitle { self = [super init]; if (self) { if (style == FLEXStaticMetadataRowStyleKeyValue) { _reuse = kFLEXKeyValueCell; } else { _reuse = kFLEXMultilineDetailCell; } _name = title; _subtitle = subtitle; } return self; } - (NSString *)description { return self.name; } - (NSString *)reuseIdentifierWithTarget:(id)object { return self.reuse; } - (BOOL)isEditable { return NO; } - (BOOL)isCallable { return NO; } - (id)currentValueWithTarget:(id)object { return nil; } - (NSString *)previewWithTarget:(id)object { return self.subtitle; } - (UIViewController *)viewerWithTarget:(id)object { return nil; } - (UIViewController *)editorWithTarget:(id)object section:(FLEXTableViewSection *)section { // Static metadata cannot be edited @throw NSInternalInconsistencyException; return nil; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { return UITableViewCellAccessoryNone; } - (NSArray *)additionalActionsWithTarget:(id)object sender:(UIViewController *)sender __IOS_AVAILABLE(13.0) { return nil; } - (NSArray *)copiableMetadataWithTarget:(id)object { return @[self.name, self.subtitle]; } - (NSString *)contextualSubtitleWithTarget:(id)object { return nil; } @end #pragma mark FLEXStaticMetadata_Class @implementation FLEXStaticMetadata_Class + (instancetype)withClass:(Class)cls { NSParameterAssert(cls); FLEXStaticMetadata_Class *metadata = [self new]; metadata.metadata = cls; metadata->_name = NSStringFromClass(cls); metadata.reuse = kFLEXDefaultCell; return metadata; } - (id)initWithStyle:(FLEXStaticMetadataRowStyle)style title:(NSString *)title subtitle:(NSString *)subtitle { @throw NSInternalInconsistencyException; return nil; } - (UIViewController *)viewerWithTarget:(id)object { return [FLEXObjectExplorerFactory explorerViewControllerForObject:self.metadata]; } - (UITableViewCellAccessoryType)suggestedAccessoryTypeWithTarget:(id)object { return UITableViewCellAccessoryDisclosureIndicator; } - (NSArray *)copiableMetadataWithTarget:(id)object { return @[ @"Class Name", self.name, @"Class", [FLEXUtility addressOfObject:self.metadata] ]; } - (NSString *)contextualSubtitleWithTarget:(id)object { return [FLEXUtility addressOfObject:self.metadata]; } @end ================================================ FILE: Classes/Utility/Categories/NSArray+FLEX.h ================================================ // // NSArray+FLEX.h // FLEX // // Created by Tanner Bennett on 9/25/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface NSArray (Functional) /// Actually more like flatmap, but it seems like the objc way to allow returning nil to omit objects. /// So, return nil from the block to omit objects, and return an object to include it in the new array. /// Unlike flatmap, however, this will not flatten arrays of arrays into a single array. - (__kindof NSArray *)flex_mapped:(id(^)(T obj, NSUInteger idx))mapFunc; /// Like flex_mapped, but expects arrays to be returned, and flattens them into one array. - (__kindof NSArray *)flex_flatmapped:(NSArray *(^)(id, NSUInteger idx))block; - (instancetype)flex_filtered:(BOOL(^)(T obj, NSUInteger idx))filterFunc; - (void)flex_forEach:(void(^)(T obj, NSUInteger idx))block; /// Unlike \c subArrayWithRange: this will not throw an exception if \c maxLength /// is greater than the size of the array. If the array has one element and /// \c maxLength is greater than 1, you get an array with 1 element back. - (instancetype)flex_subArrayUpto:(NSUInteger)maxLength; + (instancetype)flex_forEachUpTo:(NSUInteger)bound map:(T(^)(NSUInteger i))block; + (instancetype)flex_mapped:(id)collection block:(id(^)(T obj, NSUInteger idx))mapFunc; - (instancetype)flex_sortedUsingSelector:(SEL)selector; - (T)flex_firstWhere:(BOOL(^)(T obj))meetingCriteria; @end @interface NSMutableArray (Functional) - (void)flex_filter:(BOOL(^)(T obj, NSUInteger idx))filterFunc; @end ================================================ FILE: Classes/Utility/Categories/NSArray+FLEX.m ================================================ // // NSArray+FLEX.m // FLEX // // Created by Tanner Bennett on 9/25/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "NSArray+FLEX.h" #define FLEXArrayClassIsMutable(me) ([[self class] isSubclassOfClass:[NSMutableArray class]]) @implementation NSArray (Functional) - (__kindof NSArray *)flex_mapped:(id (^)(id, NSUInteger))mapFunc { NSMutableArray *map = [NSMutableArray new]; [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { id ret = mapFunc(obj, idx); if (ret) { [map addObject:ret]; } }]; if (self.count < 2048 && !FLEXArrayClassIsMutable(self)) { return map.copy; } return map; } - (__kindof NSArray *)flex_flatmapped:(NSArray *(^)(id, NSUInteger))block { NSMutableArray *array = [NSMutableArray new]; [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSArray *toAdd = block(obj, idx); if (toAdd) { [array addObjectsFromArray:toAdd]; } }]; if (array.count < 2048 && !FLEXArrayClassIsMutable(self)) { return array.copy; } return array; } - (NSArray *)flex_filtered:(BOOL (^)(id, NSUInteger))filterFunc { return [self flex_mapped:^id(id obj, NSUInteger idx) { return filterFunc(obj, idx) ? obj : nil; }]; } - (void)flex_forEach:(void(^)(id, NSUInteger))block { [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { block(obj, idx); }]; } - (instancetype)flex_subArrayUpto:(NSUInteger)maxLength { if (maxLength > self.count) { if (FLEXArrayClassIsMutable(self)) { return self.mutableCopy; } return self; } return [self subarrayWithRange:NSMakeRange(0, maxLength)]; } + (__kindof NSArray *)flex_forEachUpTo:(NSUInteger)bound map:(id(^)(NSUInteger))block { NSMutableArray *array = [NSMutableArray new]; for (NSUInteger i = 0; i < bound; i++) { id obj = block(i); if (obj) { [array addObject:obj]; } } // For performance reasons, don't copy large arrays if (bound < 2048 && !FLEXArrayClassIsMutable(self)) { return array.copy; } return array; } + (instancetype)flex_mapped:(id)collection block:(id(^)(id obj, NSUInteger idx))mapFunc { NSMutableArray *array = [NSMutableArray new]; NSInteger idx = 0; for (id obj in collection) { id ret = mapFunc(obj, idx++); if (ret) { [array addObject:ret]; } } // For performance reasons, don't copy large arrays if (array.count < 2048) { return array.copy; } return array; } - (instancetype)flex_sortedUsingSelector:(SEL)selector { if (FLEXArrayClassIsMutable(self)) { NSMutableArray *me = (id)self; [me sortUsingSelector:selector]; return me; } else { return [self sortedArrayUsingSelector:selector]; } } - (id)flex_firstWhere:(BOOL (^)(id))meetsCriteria { for (id e in self) { if (meetsCriteria(e)) { return e; } } return nil; } @end @implementation NSMutableArray (Functional) - (void)flex_filter:(BOOL (^)(id, NSUInteger))keepObject { NSMutableIndexSet *toRemove = [NSMutableIndexSet new]; [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (!keepObject(obj, idx)) { [toRemove addIndex:idx]; } }]; [self removeObjectsAtIndexes:toRemove]; } @end ================================================ FILE: Classes/Utility/Categories/NSDateFormatter+FLEX.h ================================================ // // NSDateFormatter+FLEX.h // libflex:FLEX // // Created by Tanner Bennett on 7/24/22. // Copyright © 2022 Flipboard. All rights reserved. // #import typedef NS_ENUM(NSUInteger, FLEXDateFormat) { // hour:minute [AM|PM] FLEXDateFormatClock, // hour:minute:second [AM|PM] FLEXDateFormatPreciseClock, // year-month-day hour:minute:second.millisecond FLEXDateFormatVerbose, }; @interface NSDateFormatter (FLEX) + (NSString *)flex_stringFrom:(NSDate *)date format:(FLEXDateFormat)format; @end ================================================ FILE: Classes/Utility/Categories/NSDateFormatter+FLEX.m ================================================ // // NSDateFormatter+FLEX.m // libflex:FLEX // // Created by Tanner Bennett on 7/24/22. // Copyright © 2022 Flipboard. All rights reserved. // #import "NSDateFormatter+FLEX.h" @implementation NSDateFormatter (FLEX) + (NSString *)flex_stringFrom:(NSDate *)date format:(FLEXDateFormat)format { static NSDateFormatter *formatter = nil; if (!formatter) { formatter = [NSDateFormatter new]; } switch (format) { case FLEXDateFormatClock: formatter.dateFormat = @"h:mm a"; break; case FLEXDateFormatPreciseClock: formatter.dateFormat = @"h:mm:ss a"; break; case FLEXDateFormatVerbose: formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; break; } return [formatter stringFromDate:date]; } @end ================================================ FILE: Classes/Utility/Categories/NSObject+FLEX_Reflection.h ================================================ // // NSObject+FLEX_Reflection.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import @class FLEXMirror, FLEXMethod, FLEXIvar, FLEXProperty, FLEXMethodBase, FLEXPropertyAttributes, FLEXProtocol; NS_ASSUME_NONNULL_BEGIN /// Returns the type encoding string given the encoding for the return type and parameters, if any. /// @discussion Example usage for a \c void returning method which takes /// an \c int: @code FLEXTypeEncoding(@encode(void), @encode(int)); /// @param returnType The encoded return type. \c void for exmaple would be \c @encode(void). /// @param count The number of parameters in this type encoding string. /// @return The type encoding string, or \c nil if \e returnType is \c NULL. NSString * FLEXTypeEncodingString(const char *returnType, NSUInteger count, ...); NSArray * _Nullable FLEXGetAllSubclasses(_Nullable Class cls, BOOL includeSelf); NSArray * _Nullable FLEXGetClassHierarchy(_Nullable Class cls, BOOL includeSelf); NSArray * _Nullable FLEXGetConformedProtocols(_Nullable Class cls); NSArray * _Nullable FLEXGetAllIvars(_Nullable Class cls); /// @param cls a class object to get instance properties, /// or a metaclass object to get class properties NSArray * _Nullable FLEXGetAllProperties(_Nullable Class cls); /// @param cls a class object to get instance methods, /// or a metaclass object to get class methods /// @param instance used to mark methods as instance methods or not. /// Not used to determine whether to get instance or class methods. NSArray * _Nullable FLEXGetAllMethods(_Nullable Class cls, BOOL instance); /// @param cls a class object to get all instance and class methods. NSArray * _Nullable FLEXGetAllInstanceAndClassMethods(_Nullable Class cls); #pragma mark Reflection @interface NSObject (Reflection) @property (nonatomic, readonly ) FLEXMirror *flex_reflection; @property (nonatomic, readonly, class) FLEXMirror *flex_reflection; /// Calls into /c FLEXGetAllSubclasses /// @return Every subclass of the receiving class, including the receiver itself. @property (nonatomic, readonly, class) NSArray *flex_allSubclasses; /// @return The \c Class object for the metaclass of the recieving class, or \c Nil if the class is Nil or not registered. @property (nonatomic, readonly, class) Class flex_metaclass; /// @return The size in bytes of instances of the recieving class, or \c 0 if \e cls is \c Nil. @property (nonatomic, readonly, class) size_t flex_instanceSize; /// Changes the class of an object instance. /// @return The previous value of the objects \c class, or \c Nil if the object is \c nil. - (Class)flex_setClass:(Class)cls; /// Sets the recieving class's superclass. "You should not use this method" — Apple. /// @return The old superclass. + (Class)flex_setSuperclass:(Class)superclass; /// Calls into \c FLEXGetClassHierarchy() /// @return a list of classes going up the class hierarchy, /// starting with the receiver and ending with the root class. @property (nonatomic, readonly, class) NSArray *flex_classHierarchy; /// Calls into \c FLEXGetConformedProtocols /// @return a list of protocols this class itself conforms to. @property (nonatomic, readonly, class) NSArray *flex_protocols; @end #pragma mark Methods @interface NSObject (Methods) /// All instance and class methods specific to the recieving class. /// @discussion This method will only retrieve methods specific to the recieving class. /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. /// @return An array of \c FLEXMethod objects. @property (nonatomic, readonly, class) NSArray *flex_allMethods; /// All instance methods specific to the recieving class. /// @discussion This method will only retrieve methods specific to the recieving class. /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. /// @return An array of \c FLEXMethod objects. @property (nonatomic, readonly, class) NSArray *flex_allInstanceMethods; /// All class methods specific to the recieving class. /// @discussion This method will only retrieve methods specific to the recieving class. /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. /// @return An array of \c FLEXMethod objects. @property (nonatomic, readonly, class) NSArray *flex_allClassMethods; /// Retrieves the class's instance method with the given name. /// @return An initialized \c FLEXMethod object, or \c nil if the method wasn't found. + (FLEXMethod *)flex_methodNamed:(NSString *)name; /// Retrieves the class's class method with the given name. /// @return An initialized \c FLEXMethod object, or \c nil if the method wasn't found. + (FLEXMethod *)flex_classMethodNamed:(NSString *)name; /// Adds a new method to the recieving class with a given name and implementation. /// @discussion This method will add an override of a superclass's implementation, /// but will not replace an existing implementation in the class. /// To change an existing implementation, use \c replaceImplementationOfMethod:with:. /// /// Type encodings start with the return type and end with the parameter types in order. /// The type encoding for \c NSArray's \c count property getter looks like this: /// @code [NSString stringWithFormat:@"%s%s%s%s", @encode(void), @encode(id), @encode(SEL), @encode(NSUInteger)] @endcode /// Using the \c FLEXTypeEncoding function for the same method looks like this: /// @code FLEXTypeEncodingString(@encode(void), 1, @encode(NSUInteger)) @endcode /// @param typeEncoding The type encoding string. Consider using the \c FLEXTypeEncodingString() function. /// @param instanceMethod NO to add the method to the class itself or YES to add it as an instance method. /// @return YES if the method was added successfully, \c NO otherwise /// (for example, the class already contains a method implementation with that name). + (BOOL)addMethod:(SEL)selector typeEncoding:(NSString *)typeEncoding implementation:(IMP)implementaiton toInstances:(BOOL)instanceMethod; /// Replaces the implementation of a method in the recieving class. /// @param instanceMethod YES to replace the instance method, NO to replace the class method. /// @note This function behaves in two different ways: /// /// - If the method does not yet exist in the recieving class, it is added as if /// \c addMethod:typeEncoding:implementation were called. /// /// - If the method does exist, its \c IMP is replaced. /// @return The previous \c IMP of \e method. + (IMP)replaceImplementationOfMethod:(FLEXMethodBase *)method with:(IMP)implementation useInstance:(BOOL)instanceMethod; /// Swaps the implementations of the given methods. /// @discussion If one or neither of the given methods exist in the recieving class, /// they are added to the class with their implementations swapped as if each method did exist. /// This method will not fail if each \c FLEXSimpleMethod contains a valid selector. /// @param instanceMethod YES to swizzle the instance method, NO to swizzle the class method. + (void)swizzle:(FLEXMethodBase *)original with:(FLEXMethodBase *)other onInstance:(BOOL)instanceMethod; /// Swaps the implementations of the given methods. /// @param instanceMethod YES to swizzle the instance method, NO to swizzle the class method. /// @return \c YES if successful, and \c NO if selectors could not be retrieved from the given strings. + (BOOL)swizzleByName:(NSString *)original with:(NSString *)other onInstance:(BOOL)instanceMethod; /// Swaps the implementations of methods corresponding to the given selectors. + (void)swizzleBySelector:(SEL)original with:(SEL)other onInstance:(BOOL)instanceMethod; @end #pragma mark Properties @interface NSObject (Ivars) /// All of the instance variables specific to the recieving class. /// @discussion This method will only retrieve instance varibles specific to the recieving class. /// To retrieve instance variables on a parent class, simply call \c [[self superclass] allIvars]. /// @return An array of \c FLEXIvar objects. @property (nonatomic, readonly, class) NSArray *flex_allIvars; /// Retrieves an instance variable with the corresponding name. /// @return An initialized \c FLEXIvar object, or \c nil if the Ivar wasn't found. + (FLEXIvar *)flex_ivarNamed:(NSString *)name; /// @return The address of the given ivar in the recieving object in memory, /// or \c NULL if it could not be found. - (void *)flex_getIvarAddress:(FLEXIvar *)ivar; /// @return The address of the given ivar in the recieving object in memory, /// or \c NULL if it could not be found. - (void *)flex_getIvarAddressByName:(NSString *)name; /// @discussion This method faster than creating an \c FLEXIvar and calling /// \c -getIvarAddress: if you already have an \c Ivar on hand /// @return The address of the given ivar in the recieving object in memory, /// or \c NULL if it could not be found\. - (void *)flex_getObjcIvarAddress:(Ivar)ivar; /// Sets the value of the given instance variable on the recieving object. /// @discussion Use only when the target instance variable is an object. - (void)flex_setIvar:(FLEXIvar *)ivar object:(id)value; /// Sets the value of the given instance variable on the recieving object. /// @discussion Use only when the target instance variable is an object. /// @return \c YES if successful, or \c NO if the instance variable could not be found. - (BOOL)flex_setIvarByName:(NSString *)name object:(id)value; /// @discussion Use only when the target instance variable is an object. /// This method is faster than creating an \c FLEXIvar and calling /// \c -setIvar: if you already have an \c Ivar on hand. - (void)flex_setObjcIvar:(Ivar)ivar object:(id)value; /// Sets the value of the given instance variable on the recieving object to the /// \e size number of bytes of data at \e value. /// @discussion Use one of the other methods if you can help it. - (void)flex_setIvar:(FLEXIvar *)ivar value:(void *)value size:(size_t)size; /// Sets the value of the given instance variable on the recieving object to the /// \e size number of bytes of data at \e value. /// @discussion Use one of the other methods if you can help it /// @return \c YES if successful, or \c NO if the instance variable could not be found. - (BOOL)flex_setIvarByName:(NSString *)name value:(void *)value size:(size_t)size; /// Sets the value of the given instance variable on the recieving object to the /// \e size number of bytes of data at \e value. /// @discussion This is faster than creating an \c FLEXIvar and calling /// \c -setIvar:value:size if you already have an \c Ivar on hand. - (void)flex_setObjcIvar:(Ivar)ivar value:(void *)value size:(size_t)size; @end #pragma mark Properties @interface NSObject (Properties) /// All instance and class properties specific to the recieving class. /// @discussion This method will only retrieve properties specific to the recieving class. /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. /// @return An array of \c FLEXProperty objects. @property (nonatomic, readonly, class) NSArray *flex_allProperties; /// All instance properties specific to the recieving class. /// @discussion This method will only retrieve properties specific to the recieving class. /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. /// @return An array of \c FLEXProperty objects. @property (nonatomic, readonly, class) NSArray *flex_allInstanceProperties; /// All class properties specific to the recieving class. /// @discussion This method will only retrieve properties specific to the recieving class. /// To retrieve instance variables on a parent class, simply call this on \c [self superclass]. /// @return An array of \c FLEXProperty objects. @property (nonatomic, readonly, class) NSArray *flex_allClassProperties; /// Retrieves the class's property with the given name. /// @return An initialized \c FLEXProperty object, or \c nil if the property wasn't found. + (FLEXProperty *)flex_propertyNamed:(NSString *)name; /// @return An initialized \c FLEXProperty object, or \c nil if the property wasn't found. + (FLEXProperty *)flex_classPropertyNamed:(NSString *)name; /// Replaces the given property on the recieving class. + (void)flex_replaceProperty:(FLEXProperty *)property; /// Replaces the given property on the recieving class. Useful for changing a property's attributes. + (void)flex_replaceProperty:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Categories/NSObject+FLEX_Reflection.m ================================================ // // NSObject+FLEX_Reflection.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "NSObject+FLEX_Reflection.h" #import "FLEXClassBuilder.h" #import "FLEXMirror.h" #import "FLEXProperty.h" #import "FLEXMethod.h" #import "FLEXIvar.h" #import "FLEXProtocol.h" #import "FLEXPropertyAttributes.h" #import "NSArray+FLEX.h" #import "FLEXUtility.h" NSString * FLEXTypeEncodingString(const char *returnType, NSUInteger count, ...) { if (!returnType) return nil; NSMutableString *encoding = [NSMutableString new]; [encoding appendFormat:@"%s%s%s", returnType, @encode(id), @encode(SEL)]; va_list args; va_start(args, count); char *type = va_arg(args, char *); for (NSUInteger i = 0; i < count; i++, type = va_arg(args, char *)) { [encoding appendFormat:@"%s", type]; } va_end(args); return encoding.copy; } NSArray *FLEXGetAllSubclasses(Class cls, BOOL includeSelf) { if (!cls) return nil; Class *buffer = NULL; int count, size; do { count = objc_getClassList(NULL, 0); buffer = (Class *)realloc(buffer, count * sizeof(*buffer)); size = objc_getClassList(buffer, count); } while (size != count); NSMutableArray *classes = [NSMutableArray new]; if (includeSelf) { [classes addObject:cls]; } for (int i = 0; i < count; i++) { Class candidate = buffer[i]; Class superclass = candidate; while ((superclass = class_getSuperclass(superclass))) { if (superclass == cls) { [classes addObject:candidate]; break; } } } free(buffer); return classes.copy; } NSArray *FLEXGetClassHierarchy(Class cls, BOOL includeSelf) { if (!cls) return nil; NSMutableArray *classes = [NSMutableArray new]; if (includeSelf) { [classes addObject:cls]; } while ((cls = [cls superclass])) { [classes addObject:cls]; }; return classes.copy; } NSArray *FLEXGetConformedProtocols(Class cls) { if (!cls) return nil; unsigned int count = 0; Protocol *__unsafe_unretained *list = class_copyProtocolList(cls, &count); NSArray *protocols = [NSArray arrayWithObjects:list count:count]; free(list); return [protocols flex_mapped:^id(Protocol *pro, NSUInteger idx) { return [FLEXProtocol protocol:pro]; }]; } NSArray *FLEXGetAllIvars(_Nullable Class cls) { if (!cls) return nil; unsigned int ivcount; Ivar *objcivars = class_copyIvarList(cls, &ivcount); NSArray *ivars = [NSArray flex_forEachUpTo:ivcount map:^id(NSUInteger i) { return [FLEXIvar ivar:objcivars[i]]; }]; free(objcivars); return ivars; } NSArray *FLEXGetAllProperties(_Nullable Class cls) { if (!cls) return nil; unsigned int pcount; objc_property_t *objcproperties = class_copyPropertyList(cls, &pcount); NSArray *properties = [NSArray flex_forEachUpTo:pcount map:^id(NSUInteger i) { return [FLEXProperty property:objcproperties[i] onClass:cls]; }]; free(objcproperties); return properties; } NSArray *FLEXGetAllMethods(_Nullable Class cls, BOOL instance) { if (!cls) return nil; unsigned int mcount; Method *objcmethods = class_copyMethodList(cls, &mcount); NSArray *methods = [NSArray flex_forEachUpTo:mcount map:^id(NSUInteger i) { return [FLEXMethod method:objcmethods[i] isInstanceMethod:instance]; }]; free(objcmethods); return methods; } #pragma mark NSProxy @interface NSProxy (AnyObjectAdditions) @end @implementation NSProxy (AnyObjectAdditions) + (void)load { FLEX_EXIT_IF_NO_CTORS() // We need to get all of the methods in this file and add them to NSProxy. // To do this we we need the class itself and it's metaclass. // Edit: also add them to Swift._SwiftObject Class NSProxyClass = [NSProxy class]; Class NSProxy_meta = object_getClass(NSProxyClass); Class SwiftObjectClass = ( NSClassFromString(@"SwiftObject") ?: NSClassFromString(@"Swift._SwiftObject") ); // Copy all of the "flex_" methods from NSObject id filterFunc = ^BOOL(FLEXMethod *method, NSUInteger idx) { return [method.name hasPrefix:@"flex_"]; }; NSArray *instanceMethods = [NSObject.flex_allInstanceMethods flex_filtered:filterFunc]; NSArray *classMethods = [NSObject.flex_allClassMethods flex_filtered:filterFunc]; FLEXClassBuilder *proxy = [FLEXClassBuilder builderForClass:NSProxyClass]; FLEXClassBuilder *proxyMeta = [FLEXClassBuilder builderForClass:NSProxy_meta]; [proxy addMethods:instanceMethods]; [proxyMeta addMethods:classMethods]; if (SwiftObjectClass) { Class SwiftObject_meta = object_getClass(SwiftObjectClass); FLEXClassBuilder *swiftObject = [FLEXClassBuilder builderForClass:SwiftObjectClass]; FLEXClassBuilder *swiftObjectMeta = [FLEXClassBuilder builderForClass:SwiftObject_meta]; [swiftObject addMethods:instanceMethods]; [swiftObjectMeta addMethods:classMethods]; // So we can put Swift objects into dictionaries... [swiftObjectMeta addMethods:@[ [NSObject flex_classMethodNamed:@"copyWithZone:"]] ]; } } @end #pragma mark Reflection @implementation NSObject (Reflection) + (FLEXMirror *)flex_reflection { return [FLEXMirror reflect:self]; } - (FLEXMirror *)flex_reflection { return [FLEXMirror reflect:self]; } /// Code borrowed from MAObjCRuntime by Mike Ash + (NSArray *)flex_allSubclasses { return FLEXGetAllSubclasses(self, YES); } - (Class)flex_setClass:(Class)cls { return object_setClass(self, cls); } + (Class)flex_metaclass { return objc_getMetaClass(NSStringFromClass(self.class).UTF8String); } + (size_t)flex_instanceSize { return class_getInstanceSize(self.class); } + (Class)flex_setSuperclass:(Class)superclass { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" return class_setSuperclass(self, superclass); #pragma clang diagnostic pop } + (NSArray *)flex_classHierarchy { return FLEXGetClassHierarchy(self, YES); } + (NSArray *)flex_protocols { return FLEXGetConformedProtocols(self); } @end #pragma mark Methods @implementation NSObject (Methods) + (NSArray *)flex_allMethods { NSMutableArray *instanceMethods = self.flex_allInstanceMethods.mutableCopy; [instanceMethods addObjectsFromArray:self.flex_allClassMethods]; return instanceMethods; } + (NSArray *)flex_allInstanceMethods { return FLEXGetAllMethods(self, YES); } + (NSArray *)flex_allClassMethods { return FLEXGetAllMethods(self.flex_metaclass, NO) ?: @[]; } + (FLEXMethod *)flex_methodNamed:(NSString *)name { Method m = class_getInstanceMethod([self class], NSSelectorFromString(name)); if (m == NULL) { return nil; } return [FLEXMethod method:m isInstanceMethod:YES]; } + (FLEXMethod *)flex_classMethodNamed:(NSString *)name { Method m = class_getClassMethod([self class], NSSelectorFromString(name)); if (m == NULL) { return nil; } return [FLEXMethod method:m isInstanceMethod:NO]; } + (BOOL)addMethod:(SEL)selector typeEncoding:(NSString *)typeEncoding implementation:(IMP)implementaiton toInstances:(BOOL)instance { return class_addMethod(instance ? self.class : self.flex_metaclass, selector, implementaiton, typeEncoding.UTF8String); } + (IMP)replaceImplementationOfMethod:(FLEXMethodBase *)method with:(IMP)implementation useInstance:(BOOL)instance { return class_replaceMethod(instance ? self.class : self.flex_metaclass, method.selector, implementation, method.typeEncoding.UTF8String); } + (void)swizzle:(FLEXMethodBase *)original with:(FLEXMethodBase *)other onInstance:(BOOL)instance { [self swizzleBySelector:original.selector with:other.selector onInstance:instance]; } + (BOOL)swizzleByName:(NSString *)original with:(NSString *)other onInstance:(BOOL)instance { SEL originalMethod = NSSelectorFromString(original); SEL newMethod = NSSelectorFromString(other); if (originalMethod == 0 || newMethod == 0) { return NO; } [self swizzleBySelector:originalMethod with:newMethod onInstance:instance]; return YES; } + (void)swizzleBySelector:(SEL)original with:(SEL)other onInstance:(BOOL)instance { Class cls = instance ? self.class : self.flex_metaclass; Method originalMethod = class_getInstanceMethod(cls, original); Method newMethod = class_getInstanceMethod(cls, other); if (class_addMethod(cls, original, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { class_replaceMethod(cls, other, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, newMethod); } } @end #pragma mark Ivars @implementation NSObject (Ivars) + (NSArray *)flex_allIvars { return FLEXGetAllIvars(self); } + (FLEXIvar *)flex_ivarNamed:(NSString *)name { Ivar i = class_getInstanceVariable([self class], name.UTF8String); if (i == NULL) { return nil; } return [FLEXIvar ivar:i]; } #pragma mark Get address - (void *)flex_getIvarAddress:(FLEXIvar *)ivar { return (uint8_t *)(__bridge void *)self + ivar.offset; } - (void *)flex_getObjcIvarAddress:(Ivar)ivar { return (uint8_t *)(__bridge void *)self + ivar_getOffset(ivar); } - (void *)flex_getIvarAddressByName:(NSString *)name { Ivar ivar = class_getInstanceVariable(self.class, name.UTF8String); if (!ivar) return 0; return (uint8_t *)(__bridge void *)self + ivar_getOffset(ivar); } #pragma mark Set ivar object - (void)flex_setIvar:(FLEXIvar *)ivar object:(id)value { object_setIvar(self, ivar.objc_ivar, value); } - (BOOL)flex_setIvarByName:(NSString *)name object:(id)value { Ivar ivar = class_getInstanceVariable(self.class, name.UTF8String); if (!ivar) return NO; object_setIvar(self, ivar, value); return YES; } - (void)flex_setObjcIvar:(Ivar)ivar object:(id)value { object_setIvar(self, ivar, value); } #pragma mark Set ivar value - (void)flex_setIvar:(FLEXIvar *)ivar value:(void *)value size:(size_t)size { void *address = [self flex_getIvarAddress:ivar]; memcpy(address, value, size); } - (BOOL)flex_setIvarByName:(NSString *)name value:(void *)value size:(size_t)size { Ivar ivar = class_getInstanceVariable(self.class, name.UTF8String); if (!ivar) return NO; [self flex_setObjcIvar:ivar value:value size:size]; return YES; } - (void)flex_setObjcIvar:(Ivar)ivar value:(void *)value size:(size_t)size { void *address = [self flex_getObjcIvarAddress:ivar]; memcpy(address, value, size); } @end #pragma mark Properties @implementation NSObject (Properties) + (NSArray *)flex_allProperties { NSMutableArray *instanceProperties = self.flex_allInstanceProperties.mutableCopy; [instanceProperties addObjectsFromArray:self.flex_allClassProperties]; return instanceProperties; } + (NSArray *)flex_allInstanceProperties { return FLEXGetAllProperties(self); } + (NSArray *)flex_allClassProperties { return FLEXGetAllProperties(self.flex_metaclass) ?: @[]; } + (FLEXProperty *)flex_propertyNamed:(NSString *)name { objc_property_t p = class_getProperty([self class], name.UTF8String); if (p == NULL) { return nil; } return [FLEXProperty property:p onClass:self]; } + (FLEXProperty *)flex_classPropertyNamed:(NSString *)name { objc_property_t p = class_getProperty(object_getClass(self), name.UTF8String); if (p == NULL) { return nil; } return [FLEXProperty property:p onClass:object_getClass(self)]; } + (void)flex_replaceProperty:(FLEXProperty *)property { [self flex_replaceProperty:property.name attributes:property.attributes]; } + (void)flex_replaceProperty:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes { unsigned int count; objc_property_attribute_t *objc_attributes = [attributes copyAttributesList:&count]; class_replaceProperty([self class], name.UTF8String, objc_attributes, count); free(objc_attributes); } @end ================================================ FILE: Classes/Utility/Categories/NSTimer+FLEX.h ================================================ // // NSTimer+Blocks.h // FLEX // // Created by Tanner on 3/23/17. // #import typedef void (^VoidBlock)(void); @interface NSTimer (Blocks) + (instancetype)flex_fireSecondsFromNow:(NSTimeInterval)delay block:(VoidBlock)block; // Forward declaration //+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block; @end ================================================ FILE: Classes/Utility/Categories/NSTimer+FLEX.m ================================================ // // NSTimer+Blocks.m // FLEX // // Created by Tanner on 3/23/17. // #import "NSTimer+FLEX.h" @interface Block : NSObject - (void)invoke; @end #pragma clang diagnostic ignored "-Wincomplete-implementation" @implementation NSTimer (Blocks) + (instancetype)flex_fireSecondsFromNow:(NSTimeInterval)delay block:(VoidBlock)block { if (@available(iOS 10, *)) { return [self scheduledTimerWithTimeInterval:delay repeats:NO block:(id)block]; } else { return [self scheduledTimerWithTimeInterval:delay target:block selector:@selector(invoke) userInfo:nil repeats:NO]; } } @end ================================================ FILE: Classes/Utility/Categories/NSUserDefaults+FLEX.h ================================================ // // NSUserDefaults+FLEX.h // FLEX // // Created by Tanner on 3/10/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import // Only use these if the getters and setters aren't good enough for whatever reason extern NSString * const kFLEXDefaultsToolbarTopMarginKey; extern NSString * const kFLEXDefaultsiOSPersistentOSLogKey; extern NSString * const kFLEXDefaultsHidePropertyIvarsKey; extern NSString * const kFLEXDefaultsHidePropertyMethodsKey; extern NSString * const kFLEXDefaultsHidePrivateMethodsKey; extern NSString * const kFLEXDefaultsShowMethodOverridesKey; extern NSString * const kFLEXDefaultsHideVariablePreviewsKey; extern NSString * const kFLEXDefaultsNetworkObserverEnabledKey; extern NSString * const kFLEXDefaultsNetworkHostDenylistKey; extern NSString * const kFLEXDefaultsDisableOSLogForceASLKey; extern NSString * const kFLEXDefaultsAPNSCaptureEnabledKey; extern NSString * const kFLEXDefaultsRegisterJSONExplorerKey; /// All BOOL preferences are NO by default @interface NSUserDefaults (FLEX) - (void)flex_toggleBoolForKey:(NSString *)key; @property (nonatomic) double flex_toolbarTopMargin; @property (nonatomic) BOOL flex_networkObserverEnabled; // Not actually stored in defaults, but written to a file @property (nonatomic) NSArray *flex_networkHostDenylist; /// Whether or not to register the object explorer as a JSON viewer on launch @property (nonatomic) BOOL flex_registerDictionaryJSONViewerOnLaunch; /// The last selected screen in the network observer @property (nonatomic) NSInteger flex_lastNetworkObserverMode; /// Disable os_log and re-enable ASL. May break Console.app output. @property (nonatomic) BOOL flex_disableOSLog; @property (nonatomic) BOOL flex_cacheOSLogMessages; @property (nonatomic) BOOL flex_enableAPNSCapture; @property (nonatomic) BOOL flex_explorerHidesPropertyIvars; @property (nonatomic) BOOL flex_explorerHidesPropertyMethods; @property (nonatomic) BOOL flex_explorerHidesPrivateMethods; @property (nonatomic) BOOL flex_explorerShowsMethodOverrides; @property (nonatomic) BOOL flex_explorerHidesVariablePreviews; @end ================================================ FILE: Classes/Utility/Categories/NSUserDefaults+FLEX.m ================================================ // // NSUserDefaults+FLEX.m // FLEX // // Created by Tanner on 3/10/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "NSUserDefaults+FLEX.h" NSString * const kFLEXDefaultsToolbarTopMarginKey = @"com.flex.FLEXToolbar.topMargin"; NSString * const kFLEXDefaultsiOSPersistentOSLogKey = @"com.flipborad.flex.enable_persistent_os_log"; NSString * const kFLEXDefaultsHidePropertyIvarsKey = @"com.flipboard.FLEX.hide_property_ivars"; NSString * const kFLEXDefaultsHidePropertyMethodsKey = @"com.flipboard.FLEX.hide_property_methods"; NSString * const kFLEXDefaultsHidePrivateMethodsKey = @"com.flipboard.FLEX.hide_private_or_namespaced_methods"; NSString * const kFLEXDefaultsShowMethodOverridesKey = @"com.flipboard.FLEX.show_method_overrides"; NSString * const kFLEXDefaultsHideVariablePreviewsKey = @"com.flipboard.FLEX.hide_variable_previews"; NSString * const kFLEXDefaultsNetworkObserverEnabledKey = @"com.flex.FLEXNetworkObserver.enableOnLaunch"; NSString * const kFLEXDefaultsNetworkObserverLastModeKey = @"com.flex.FLEXNetworkObserver.lastMode"; NSString * const kFLEXDefaultsNetworkHostDenylistKey = @"com.flipboard.FLEX.network_host_denylist"; NSString * const kFLEXDefaultsDisableOSLogForceASLKey = @"com.flipboard.FLEX.try_disable_os_log"; NSString * const kFLEXDefaultsAPNSCaptureEnabledKey = @"com.flipboard.FLEX.capture_apns"; NSString * const kFLEXDefaultsRegisterJSONExplorerKey = @"com.flipboard.FLEX.view_json_as_object"; #define FLEXDefaultsPathForFile(name) ({ \ NSArray *paths = NSSearchPathForDirectoriesInDomains( \ NSLibraryDirectory, NSUserDomainMask, YES \ ); \ [paths[0] stringByAppendingPathComponent:@"Preferences"]; \ }) @implementation NSUserDefaults (FLEX) #pragma mark Internal /// @param filename the name of a plist file without any extension - (NSString *)flex_defaultsPathForFile:(NSString *)filename { filename = [filename stringByAppendingPathExtension:@"plist"]; NSArray *paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES ); NSString *preferences = [paths[0] stringByAppendingPathComponent:@"Preferences"]; return [preferences stringByAppendingPathComponent:filename]; } #pragma mark Helper - (void)flex_toggleBoolForKey:(NSString *)key { [self setBool:![self boolForKey:key] forKey:key]; [NSNotificationCenter.defaultCenter postNotificationName:key object:nil]; } #pragma mark Misc - (double)flex_toolbarTopMargin { if ([self objectForKey:kFLEXDefaultsToolbarTopMarginKey]) { return [self doubleForKey:kFLEXDefaultsToolbarTopMarginKey]; } return 100; } - (void)setFlex_toolbarTopMargin:(double)margin { [self setDouble:margin forKey:kFLEXDefaultsToolbarTopMarginKey]; } - (BOOL)flex_networkObserverEnabled { return [self boolForKey:kFLEXDefaultsNetworkObserverEnabledKey]; } - (void)setFlex_networkObserverEnabled:(BOOL)enabled { [self setBool:enabled forKey:kFLEXDefaultsNetworkObserverEnabledKey]; } - (NSArray *)flex_networkHostDenylist { return [NSArray arrayWithContentsOfFile:[ self flex_defaultsPathForFile:kFLEXDefaultsNetworkHostDenylistKey ]] ?: @[]; } - (void)setFlex_networkHostDenylist:(NSArray *)denylist { NSParameterAssert(denylist); [denylist writeToFile:[ self flex_defaultsPathForFile:kFLEXDefaultsNetworkHostDenylistKey ] atomically:YES]; } - (BOOL)flex_registerDictionaryJSONViewerOnLaunch { return [self boolForKey:kFLEXDefaultsRegisterJSONExplorerKey]; } - (void)setFlex_registerDictionaryJSONViewerOnLaunch:(BOOL)enable { [self setBool:enable forKey:kFLEXDefaultsRegisterJSONExplorerKey]; } - (NSInteger)flex_lastNetworkObserverMode { return [self integerForKey:kFLEXDefaultsNetworkObserverLastModeKey]; } - (void)setFlex_lastNetworkObserverMode:(NSInteger)mode { [self setInteger:mode forKey:kFLEXDefaultsNetworkObserverLastModeKey]; } #pragma mark System Log - (BOOL)flex_disableOSLog { return [self boolForKey:kFLEXDefaultsDisableOSLogForceASLKey]; } - (void)setFlex_disableOSLog:(BOOL)disable { [self setBool:disable forKey:kFLEXDefaultsDisableOSLogForceASLKey]; } - (BOOL)flex_cacheOSLogMessages { return [self boolForKey:kFLEXDefaultsiOSPersistentOSLogKey]; } - (void)setFlex_cacheOSLogMessages:(BOOL)cache { [self setBool:cache forKey:kFLEXDefaultsiOSPersistentOSLogKey]; [NSNotificationCenter.defaultCenter postNotificationName:kFLEXDefaultsiOSPersistentOSLogKey object:nil ]; } #pragma mark Push Notifications - (BOOL)flex_enableAPNSCapture { return [self boolForKey:kFLEXDefaultsAPNSCaptureEnabledKey]; } - (void)setFlex_enableAPNSCapture:(BOOL)enable { [self setBool:enable forKey:kFLEXDefaultsAPNSCaptureEnabledKey]; } #pragma mark Object Explorer - (BOOL)flex_explorerHidesPropertyIvars { return [self boolForKey:kFLEXDefaultsHidePropertyIvarsKey]; } - (void)setFlex_explorerHidesPropertyIvars:(BOOL)hide { [self setBool:hide forKey:kFLEXDefaultsHidePropertyIvarsKey]; [NSNotificationCenter.defaultCenter postNotificationName:kFLEXDefaultsHidePropertyIvarsKey object:nil ]; } - (BOOL)flex_explorerHidesPropertyMethods { return [self boolForKey:kFLEXDefaultsHidePropertyMethodsKey]; } - (void)setFlex_explorerHidesPropertyMethods:(BOOL)hide { [self setBool:hide forKey:kFLEXDefaultsHidePropertyMethodsKey]; [NSNotificationCenter.defaultCenter postNotificationName:kFLEXDefaultsHidePropertyMethodsKey object:nil ]; } - (BOOL)flex_explorerHidesPrivateMethods { return [self boolForKey:kFLEXDefaultsHidePrivateMethodsKey]; } - (void)setFlex_explorerHidesPrivateMethods:(BOOL)show { [self setBool:show forKey:kFLEXDefaultsHidePrivateMethodsKey]; [NSNotificationCenter.defaultCenter postNotificationName:kFLEXDefaultsHidePrivateMethodsKey object:nil ]; } - (BOOL)flex_explorerShowsMethodOverrides { return [self boolForKey:kFLEXDefaultsShowMethodOverridesKey]; } - (void)setFlex_explorerShowsMethodOverrides:(BOOL)show { [self setBool:show forKey:kFLEXDefaultsShowMethodOverridesKey]; [NSNotificationCenter.defaultCenter postNotificationName:kFLEXDefaultsShowMethodOverridesKey object:nil ]; } - (BOOL)flex_explorerHidesVariablePreviews { return [self boolForKey:kFLEXDefaultsHideVariablePreviewsKey]; } - (void)setFlex_explorerHidesVariablePreviews:(BOOL)hide { [self setBool:hide forKey:kFLEXDefaultsHideVariablePreviewsKey]; [NSNotificationCenter.defaultCenter postNotificationName:kFLEXDefaultsHideVariablePreviewsKey object:nil ]; } @end ================================================ FILE: Classes/Utility/Categories/Private/Cocoa+FLEXShortcuts.h ================================================ // // Cocoa+FLEXShortcuts.h // Pods // // Created by Tanner on 2/24/21. // // #import @interface UIAlertAction (FLEXShortcuts) @property (nonatomic, readonly) NSString *flex_styleName; @end ================================================ FILE: Classes/Utility/Categories/Private/Cocoa+FLEXShortcuts.m ================================================ // // Cocoa+FLEXShortcuts.m // Pods // // Created by Tanner on 2/24/21. // // #import "Cocoa+FLEXShortcuts.h" @implementation UIAlertAction (FLEXShortcuts) - (NSString *)flex_styleName { switch (self.style) { case UIAlertActionStyleDefault: return @"Default style"; case UIAlertActionStyleCancel: return @"Cancel style"; case UIAlertActionStyleDestructive: return @"Destructive style"; default: return [NSString stringWithFormat:@"Unknown (%@)", @(self.style)]; } } @end ================================================ FILE: Classes/Utility/Categories/Private/NSDictionary+ObjcRuntime.h ================================================ // // NSDictionary+ObjcRuntime.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import @interface NSDictionary (ObjcRuntime) /// \c kFLEXPropertyAttributeKeyTypeEncoding is the only required key. /// Keys representing a boolean value should have a value of \c YES instead of an empty string. - (NSString *)propertyAttributesString; + (instancetype)attributesDictionaryForProperty:(objc_property_t)property; @end ================================================ FILE: Classes/Utility/Categories/Private/NSDictionary+ObjcRuntime.m ================================================ // // NSDictionary+ObjcRuntime.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "NSDictionary+ObjcRuntime.h" #import "FLEXRuntimeUtility.h" @implementation NSDictionary (ObjcRuntime) /// See this link on how to construct a proper attributes string: /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html - (NSString *)propertyAttributesString { if (!self[kFLEXPropertyAttributeKeyTypeEncoding]) return nil; NSMutableString *attributes = [NSMutableString new]; [attributes appendFormat:@"T%@,", self[kFLEXPropertyAttributeKeyTypeEncoding]]; for (NSString *attribute in self.allKeys) { FLEXPropertyAttribute c = (FLEXPropertyAttribute)[attribute characterAtIndex:0]; switch (c) { case FLEXPropertyAttributeTypeEncoding: break; case FLEXPropertyAttributeBackingIvarName: [attributes appendFormat:@"%@%@,", kFLEXPropertyAttributeKeyBackingIvarName, self[kFLEXPropertyAttributeKeyBackingIvarName] ]; break; case FLEXPropertyAttributeCopy: if ([self[kFLEXPropertyAttributeKeyCopy] boolValue]) [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyCopy]; break; case FLEXPropertyAttributeCustomGetter: [attributes appendFormat:@"%@%@,", kFLEXPropertyAttributeKeyCustomGetter, self[kFLEXPropertyAttributeKeyCustomGetter] ]; break; case FLEXPropertyAttributeCustomSetter: [attributes appendFormat:@"%@%@,", kFLEXPropertyAttributeKeyCustomSetter, self[kFLEXPropertyAttributeKeyCustomSetter] ]; break; case FLEXPropertyAttributeDynamic: if ([self[kFLEXPropertyAttributeKeyDynamic] boolValue]) [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyDynamic]; break; case FLEXPropertyAttributeGarbageCollectible: [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyGarbageCollectable]; break; case FLEXPropertyAttributeNonAtomic: if ([self[kFLEXPropertyAttributeKeyNonAtomic] boolValue]) [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyNonAtomic]; break; case FLEXPropertyAttributeOldTypeEncoding: [attributes appendFormat:@"%@%@,", kFLEXPropertyAttributeKeyOldStyleTypeEncoding, self[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] ]; break; case FLEXPropertyAttributeReadOnly: if ([self[kFLEXPropertyAttributeKeyReadOnly] boolValue]) [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyReadOnly]; break; case FLEXPropertyAttributeRetain: if ([self[kFLEXPropertyAttributeKeyRetain] boolValue]) [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyRetain]; break; case FLEXPropertyAttributeWeak: if ([self[kFLEXPropertyAttributeKeyWeak] boolValue]) [attributes appendFormat:@"%@,", kFLEXPropertyAttributeKeyWeak]; break; default: return nil; break; } } [attributes deleteCharactersInRange:NSMakeRange(attributes.length-1, 1)]; return attributes.copy; } + (instancetype)attributesDictionaryForProperty:(objc_property_t)property { NSMutableDictionary *attrs = [NSMutableDictionary new]; for (NSString *key in FLEXRuntimeUtility.allPropertyAttributeKeys) { char *value = property_copyAttributeValue(property, key.UTF8String); if (value) { attrs[key] = [[NSString alloc] initWithBytesNoCopy:value length:strlen(value) encoding:NSUTF8StringEncoding freeWhenDone:YES ]; } } return attrs.copy; } @end ================================================ FILE: Classes/Utility/Categories/Private/NSMapTable+FLEX_Subscripting.h ================================================ // // NSMapTable+FLEX_Subscripting.h // FLEX // // Created by Tanner Bennett on 1/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface NSMapTable (FLEX_Subscripting) - (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; - (void)setObject:(nullable ObjectType)obj forKeyedSubscript:(KeyType )key; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Categories/Private/NSMapTable+FLEX_Subscripting.m ================================================ // // NSMapTable+FLEX_Subscripting.m // FLEX // // Created by Tanner Bennett on 1/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "NSMapTable+FLEX_Subscripting.h" @implementation NSMapTable (FLEX_Subscripting) - (id)objectForKeyedSubscript:(id)key { return [self objectForKey:key]; } - (void)setObject:(id)obj forKeyedSubscript:(id)key { [self setObject:obj forKey:key]; } @end ================================================ FILE: Classes/Utility/Categories/Private/NSString+FLEX.h ================================================ // // NSString+FLEX.h // FLEX // // Created by Tanner on 3/26/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "FLEXRuntimeConstants.h" @interface NSString (FLEXTypeEncoding) ///@return whether this type starts with the const specifier @property (nonatomic, readonly) BOOL flex_typeIsConst; /// @return the first char in the type encoding that is not the const specifier @property (nonatomic, readonly) FLEXTypeEncoding flex_firstNonConstType; /// @return the first char in the type encoding after the pointer specifier, if it is a pointer @property (nonatomic, readonly) FLEXTypeEncoding flex_pointeeType; /// @return whether this type is an objc object of any kind, even if it's const @property (nonatomic, readonly) BOOL flex_typeIsObjectOrClass; /// @return the class named in this type encoding if it is of the form \c @"MYClass" @property (nonatomic, readonly) Class flex_typeClass; /// Includes C strings and selectors as well as regular pointers @property (nonatomic, readonly) BOOL flex_typeIsNonObjcPointer; @end @interface NSString (KeyPaths) - (NSString *)flex_stringByRemovingLastKeyPathComponent; - (NSString *)flex_stringByReplacingLastKeyPathComponent:(NSString *)replacement; @end ================================================ FILE: Classes/Utility/Categories/Private/NSString+FLEX.m ================================================ // // NSString+FLEX.m // FLEX // // Created by Tanner on 3/26/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // #import "NSString+FLEX.h" @interface NSMutableString (Replacement) - (void)replaceOccurencesOfString:(NSString *)string with:(NSString *)replacement; - (void)removeLastKeyPathComponent; @end @implementation NSMutableString (Replacement) - (void)replaceOccurencesOfString:(NSString *)string with:(NSString *)replacement { [self replaceOccurrencesOfString:string withString:replacement options:0 range:NSMakeRange(0, self.length)]; } - (void)removeLastKeyPathComponent { if (![self containsString:@"."]) { [self deleteCharactersInRange:NSMakeRange(0, self.length)]; return; } BOOL putEscapesBack = NO; if ([self containsString:@"\\."]) { [self replaceOccurencesOfString:@"\\." with:@"\\~"]; // Case like "UIKit\.framework" if (![self containsString:@"."]) { [self deleteCharactersInRange:NSMakeRange(0, self.length)]; return; } putEscapesBack = YES; } // Case like "Bund" or "Bundle.cla" if (![self hasSuffix:@"."]) { NSUInteger len = self.pathExtension.length; [self deleteCharactersInRange:NSMakeRange(self.length-len, len)]; } if (putEscapesBack) { [self replaceOccurencesOfString:@"\\~" with:@"\\."]; } } @end @implementation NSString (FLEXTypeEncoding) - (NSCharacterSet *)flex_classNameAllowedCharactersSet { static NSCharacterSet *classNameAllowedCharactersSet = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSMutableCharacterSet *temp = NSMutableCharacterSet.alphanumericCharacterSet; [temp addCharactersInString:@"_"]; classNameAllowedCharactersSet = temp.copy; }); return classNameAllowedCharactersSet; } - (BOOL)flex_typeIsConst { if (!self.length) return NO; return [self characterAtIndex:0] == FLEXTypeEncodingConst; } - (FLEXTypeEncoding)flex_firstNonConstType { if (!self.length) return FLEXTypeEncodingNull; return [self characterAtIndex:(self.flex_typeIsConst ? 1 : 0)]; } - (FLEXTypeEncoding)flex_pointeeType { if (!self.length) return FLEXTypeEncodingNull; if (self.flex_firstNonConstType == FLEXTypeEncodingPointer) { return [self characterAtIndex:(self.flex_typeIsConst ? 2 : 1)]; } return FLEXTypeEncodingNull; } - (BOOL)flex_typeIsObjectOrClass { FLEXTypeEncoding type = self.flex_firstNonConstType; return type == FLEXTypeEncodingObjcObject || type == FLEXTypeEncodingObjcClass; } - (Class)flex_typeClass { if (!self.flex_typeIsObjectOrClass) { return nil; } NSScanner *scan = [NSScanner scannerWithString:self]; // Skip const [scan scanString:@"r" intoString:nil]; // Scan leading @" if (![scan scanString:@"@\"" intoString:nil]) { return nil; } // Scan class name NSString *name = nil; if (![scan scanCharactersFromSet:self.flex_classNameAllowedCharactersSet intoString:&name]) { return nil; } // Scan trailing quote if (![scan scanString:@"\"" intoString:nil]) { return nil; } // Return found class return NSClassFromString(name); } - (BOOL)flex_typeIsNonObjcPointer { FLEXTypeEncoding type = self.flex_firstNonConstType; return type == FLEXTypeEncodingPointer || type == FLEXTypeEncodingCString || type == FLEXTypeEncodingSelector; } @end @implementation NSString (KeyPaths) - (NSString *)flex_stringByRemovingLastKeyPathComponent { if (![self containsString:@"."]) { return @""; } NSMutableString *mself = self.mutableCopy; [mself removeLastKeyPathComponent]; return mself; } - (NSString *)flex_stringByReplacingLastKeyPathComponent:(NSString *)replacement { // replacement should not have any escaped '.' in it, // so we escape all '.' if ([replacement containsString:@"."]) { replacement = [replacement stringByReplacingOccurrencesOfString:@"." withString:@"\\."]; } // Case like "Foo" if (![self containsString:@"."]) { return [replacement stringByAppendingString:@"."]; } NSMutableString *mself = self.mutableCopy; [mself removeLastKeyPathComponent]; [mself appendString:replacement]; [mself appendString:@"."]; return mself; } @end ================================================ FILE: Classes/Utility/Categories/Private/NSString+ObjcRuntime.h ================================================ // // NSString+ObjcRuntime.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/1/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @interface NSString (Utilities) /// A dictionary of property attributes if the receiver is a valid property attributes string. /// Values are either a string or \c YES. Boolean attributes which are false will not be /// present in the dictionary. See this link on how to construct a proper attributes string: /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html /// /// Note: this method doesn't work properly for certain type encodings, and neither does /// the property_copyAttributeValue function in the runtime itself. Radar: FB7499230 - (NSDictionary *)propertyAttributes; @end ================================================ FILE: Classes/Utility/Categories/Private/NSString+ObjcRuntime.m ================================================ // // NSString+ObjcRuntime.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/1/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "NSString+ObjcRuntime.h" #import "FLEXRuntimeUtility.h" @implementation NSString (Utilities) - (NSString *)stringbyDeletingCharacterAtIndex:(NSUInteger)idx { NSMutableString *string = self.mutableCopy; [string replaceCharactersInRange:NSMakeRange(idx, 1) withString:@""]; return string; } /// See this link on how to construct a proper attributes string: /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html - (NSDictionary *)propertyAttributes { if (!self.length) return nil; NSMutableDictionary *attributes = [NSMutableDictionary new]; NSArray *components = [self componentsSeparatedByString:@","]; for (NSString *attribute in components) { FLEXPropertyAttribute c = (FLEXPropertyAttribute)[attribute characterAtIndex:0]; switch (c) { case FLEXPropertyAttributeTypeEncoding: // Note: the type encoding here is not always correct. Radar: FB7499230 attributes[kFLEXPropertyAttributeKeyTypeEncoding] = [attribute stringbyDeletingCharacterAtIndex:0]; break; case FLEXPropertyAttributeBackingIvarName: attributes[kFLEXPropertyAttributeKeyBackingIvarName] = [attribute stringbyDeletingCharacterAtIndex:0]; break; case FLEXPropertyAttributeCopy: attributes[kFLEXPropertyAttributeKeyCopy] = @YES; break; case FLEXPropertyAttributeCustomGetter: attributes[kFLEXPropertyAttributeKeyCustomGetter] = [attribute stringbyDeletingCharacterAtIndex:0]; break; case FLEXPropertyAttributeCustomSetter: attributes[kFLEXPropertyAttributeKeyCustomSetter] = [attribute stringbyDeletingCharacterAtIndex:0]; break; case FLEXPropertyAttributeDynamic: attributes[kFLEXPropertyAttributeKeyDynamic] = @YES; break; case FLEXPropertyAttributeGarbageCollectible: attributes[kFLEXPropertyAttributeKeyGarbageCollectable] = @YES; break; case FLEXPropertyAttributeNonAtomic: attributes[kFLEXPropertyAttributeKeyNonAtomic] = @YES; break; case FLEXPropertyAttributeOldTypeEncoding: attributes[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] = [attribute stringbyDeletingCharacterAtIndex:0]; break; case FLEXPropertyAttributeReadOnly: attributes[kFLEXPropertyAttributeKeyReadOnly] = @YES; break; case FLEXPropertyAttributeRetain: attributes[kFLEXPropertyAttributeKeyRetain] = @YES; break; case FLEXPropertyAttributeWeak: attributes[kFLEXPropertyAttributeKeyWeak] = @YES; break; } } return attributes; } @end ================================================ FILE: Classes/Utility/Categories/Private/UIView+FLEX_Layout.h ================================================ // // UIView+FLEX_Layout.h // FLEX // // Created by Tanner Bennett on 7/18/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import #define Padding(p) UIEdgeInsetsMake(p, p, p, p) @interface UIView (FLEX_Layout) - (void)flex_centerInView:(UIView *)view; - (void)flex_pinEdgesTo:(UIView *)view; - (void)flex_pinEdgesTo:(UIView *)view withInsets:(UIEdgeInsets)insets; - (void)flex_pinEdgesToSuperview; - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets; - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets aboveView:(UIView *)sibling; - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets belowView:(UIView *)sibling; @end ================================================ FILE: Classes/Utility/Categories/Private/UIView+FLEX_Layout.m ================================================ // // UIView+FLEX_Layout.m // FLEX // // Created by Tanner Bennett on 7/18/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIView+FLEX_Layout.h" @implementation UIView (FLEX_Layout) - (void)flex_centerInView:(UIView *)view { [NSLayoutConstraint activateConstraints:@[ [self.centerXAnchor constraintEqualToAnchor:view.centerXAnchor], [self.centerYAnchor constraintEqualToAnchor:view.centerYAnchor], ]]; } - (void)flex_pinEdgesTo:(UIView *)view { [NSLayoutConstraint activateConstraints:@[ [self.topAnchor constraintEqualToAnchor:view.topAnchor], [self.leftAnchor constraintEqualToAnchor:view.leftAnchor], [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor], [self.rightAnchor constraintEqualToAnchor:view.rightAnchor], ]]; } - (void)flex_pinEdgesTo:(UIView *)view withInsets:(UIEdgeInsets)i { [NSLayoutConstraint activateConstraints:@[ [self.topAnchor constraintEqualToAnchor:view.topAnchor constant:i.top], [self.leftAnchor constraintEqualToAnchor:view.leftAnchor constant:i.left], [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:-i.bottom], [self.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:-i.right], ]]; } - (void)flex_pinEdgesToSuperview { [self flex_pinEdgesTo:self.superview]; } - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)insets { [self flex_pinEdgesTo:self.superview withInsets:insets]; } - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)i aboveView:(UIView *)sibling { UIView *view = self.superview; [NSLayoutConstraint activateConstraints:@[ [self.topAnchor constraintEqualToAnchor:view.topAnchor constant:i.top], [self.leftAnchor constraintEqualToAnchor:view.leftAnchor constant:i.left], [self.bottomAnchor constraintEqualToAnchor:sibling.topAnchor constant:-i.bottom], [self.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:-i.right], ]]; } - (void)flex_pinEdgesToSuperviewWithInsets:(UIEdgeInsets)i belowView:(UIView *)sibling { UIView *view = self.superview; [NSLayoutConstraint activateConstraints:@[ [self.topAnchor constraintEqualToAnchor:sibling.bottomAnchor constant:i.top], [self.leftAnchor constraintEqualToAnchor:view.leftAnchor constant:i.left], [self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:-i.bottom], [self.rightAnchor constraintEqualToAnchor:view.rightAnchor constant:-i.right], ]]; } @end ================================================ FILE: Classes/Utility/Categories/UIBarButtonItem+FLEX.h ================================================ // // UIBarButtonItem+FLEX.h // FLEX // // Created by Tanner on 2/4/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import #define FLEXBarButtonItem(title, tgt, sel) \ [UIBarButtonItem flex_itemWithTitle:title target:tgt action:sel] #define FLEXBarButtonItemSystem(item, tgt, sel) \ [UIBarButtonItem flex_systemItem:UIBarButtonSystemItem##item target:tgt action:sel] @interface UIBarButtonItem (FLEX) @property (nonatomic, readonly, class) UIBarButtonItem *flex_flexibleSpace; @property (nonatomic, readonly, class) UIBarButtonItem *flex_fixedSpace; + (instancetype)flex_itemWithCustomView:(UIView *)customView; + (instancetype)flex_backItemWithTitle:(NSString *)title; + (instancetype)flex_systemItem:(UIBarButtonSystemItem)item target:(id)target action:(SEL)action; + (instancetype)flex_itemWithTitle:(NSString *)title target:(id)target action:(SEL)action; + (instancetype)flex_doneStyleitemWithTitle:(NSString *)title target:(id)target action:(SEL)action; + (instancetype)flex_itemWithImage:(UIImage *)image target:(id)target action:(SEL)action; + (instancetype)flex_disabledSystemItem:(UIBarButtonSystemItem)item; + (instancetype)flex_disabledItemWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style; + (instancetype)flex_disabledItemWithImage:(UIImage *)image; /// @return the receiver - (UIBarButtonItem *)flex_withTintColor:(UIColor *)tint; - (void)_setWidth:(CGFloat)width; @end ================================================ FILE: Classes/Utility/Categories/UIBarButtonItem+FLEX.m ================================================ // // UIBarButtonItem+FLEX.m // FLEX // // Created by Tanner on 2/4/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIBarButtonItem+FLEX.h" #pragma clang diagnostic ignored "-Wincomplete-implementation" @implementation UIBarButtonItem (FLEX) + (UIBarButtonItem *)flex_flexibleSpace { return [self flex_systemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; } + (UIBarButtonItem *)flex_fixedSpace { UIBarButtonItem *fixed = [self flex_systemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; fixed.width = 60; return fixed; } + (instancetype)flex_systemItem:(UIBarButtonSystemItem)item target:(id)target action:(SEL)action { return [[self alloc] initWithBarButtonSystemItem:item target:target action:action]; } + (instancetype)flex_itemWithCustomView:(UIView *)customView { return [[self alloc] initWithCustomView:customView]; } + (instancetype)flex_backItemWithTitle:(NSString *)title { return [self flex_itemWithTitle:title target:nil action:nil]; } + (instancetype)flex_itemWithTitle:(NSString *)title target:(id)target action:(SEL)action { return [[self alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:target action:action]; } + (instancetype)flex_doneStyleitemWithTitle:(NSString *)title target:(id)target action:(SEL)action { return [[self alloc] initWithTitle:title style:UIBarButtonItemStyleDone target:target action:action]; } + (instancetype)flex_itemWithImage:(UIImage *)image target:(id)target action:(SEL)action { return [[self alloc] initWithImage:image style:UIBarButtonItemStylePlain target:target action:action]; } + (instancetype)flex_disabledSystemItem:(UIBarButtonSystemItem)system { UIBarButtonItem *item = [self flex_systemItem:system target:nil action:nil]; item.enabled = NO; return item; } + (instancetype)flex_disabledItemWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style { UIBarButtonItem *item = [self flex_itemWithTitle:title target:nil action:nil]; item.enabled = NO; return item; } + (instancetype)flex_disabledItemWithImage:(UIImage *)image { UIBarButtonItem *item = [self flex_itemWithImage:image target:nil action:nil]; item.enabled = NO; return item; } - (UIBarButtonItem *)flex_withTintColor:(UIColor *)tint { self.tintColor = tint; return self; } @end ================================================ FILE: Classes/Utility/Categories/UIFont+FLEX.h ================================================ // // UIFont+FLEX.h // FLEX // // Created by Tanner Bennett on 12/20/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface UIFont (FLEX) @property (nonatomic, readonly, class) UIFont *flex_defaultTableCellFont; @property (nonatomic, readonly, class) UIFont *flex_codeFont; @property (nonatomic, readonly, class) UIFont *flex_smallCodeFont; @end ================================================ FILE: Classes/Utility/Categories/UIFont+FLEX.m ================================================ // // UIFont+FLEX.m // FLEX // // Created by Tanner Bennett on 12/20/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIFont+FLEX.h" #define kFLEXDefaultCellFontSize 12.0 @implementation UIFont (FLEX) + (UIFont *)flex_defaultTableCellFont { static UIFont *defaultTableCellFont = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ defaultTableCellFont = [UIFont systemFontOfSize:kFLEXDefaultCellFontSize]; }); return defaultTableCellFont; } + (UIFont *)flex_codeFont { // Actually only available in iOS 13, the SDK headers are wrong if (@available(iOS 13, *)) { return [self monospacedSystemFontOfSize:kFLEXDefaultCellFontSize weight:UIFontWeightRegular]; } else { return [self fontWithName:@"Menlo-Regular" size:kFLEXDefaultCellFontSize]; } } + (UIFont *)flex_smallCodeFont { // Actually only available in iOS 13, the SDK headers are wrong if (@available(iOS 13, *)) { return [self monospacedSystemFontOfSize:self.smallSystemFontSize weight:UIFontWeightRegular]; } else { return [self fontWithName:@"Menlo-Regular" size:self.smallSystemFontSize]; } } @end ================================================ FILE: Classes/Utility/Categories/UIGestureRecognizer+Blocks.h ================================================ // // UIGestureRecognizer+Blocks.h // FLEX // // Created by Tanner Bennett on 12/20/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import typedef void (^GestureBlock)(UIGestureRecognizer *gesture); @interface UIGestureRecognizer (Blocks) + (instancetype)flex_action:(GestureBlock)action; @property (nonatomic, setter=flex_setAction:) GestureBlock flex_action; @end ================================================ FILE: Classes/Utility/Categories/UIGestureRecognizer+Blocks.m ================================================ // // UIGestureRecognizer+Blocks.m // FLEX // // Created by Tanner Bennett on 12/20/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIGestureRecognizer+Blocks.h" #import @implementation UIGestureRecognizer (Blocks) static void * actionKey; + (instancetype)flex_action:(GestureBlock)action { UIGestureRecognizer *gesture = [[self alloc] initWithTarget:nil action:nil]; [gesture addTarget:gesture action:@selector(flex_invoke)]; gesture.flex_action = action; return gesture; } - (void)flex_invoke { self.flex_action(self); } - (GestureBlock)flex_action { return objc_getAssociatedObject(self, &actionKey); } - (void)flex_setAction:(GestureBlock)action { objc_setAssociatedObject(self, &actionKey, action, OBJC_ASSOCIATION_COPY); } @end ================================================ FILE: Classes/Utility/Categories/UIMenu+FLEX.h ================================================ // // UIMenu+FLEX.h // FLEX // // Created by Tanner on 1/28/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface UIMenu (FLEX) + (instancetype)flex_inlineMenuWithTitle:(NSString *)title image:(UIImage *)image children:(NSArray *)children; - (instancetype)flex_collapsed; @end ================================================ FILE: Classes/Utility/Categories/UIMenu+FLEX.m ================================================ // // UIMenu+FLEX.m // FLEX // // Created by Tanner on 1/28/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIMenu+FLEX.h" @implementation UIMenu (FLEX) + (instancetype)flex_inlineMenuWithTitle:(NSString *)title image:(UIImage *)image children:(NSArray *)children { return [UIMenu menuWithTitle:title image:image identifier:nil options:UIMenuOptionsDisplayInline children:children ]; } - (instancetype)flex_collapsed { return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[[UIMenu menuWithTitle:self.title image:self.image identifier:self.identifier options:0 children:self.children ]] ]; } @end ================================================ FILE: Classes/Utility/Categories/UIPasteboard+FLEX.h ================================================ // // UIPasteboard+FLEX.h // FLEX // // Created by Tanner Bennett on 12/9/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface UIPasteboard (FLEX) /// For copying an object which could be a string, data, or number - (void)flex_copy:(id)unknownType; @end ================================================ FILE: Classes/Utility/Categories/UIPasteboard+FLEX.m ================================================ // // UIPasteboard+FLEX.m // FLEX // // Created by Tanner Bennett on 12/9/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "UIPasteboard+FLEX.h" @implementation UIPasteboard (FLEX) - (void)flex_copy:(id)object { if (!object) { return; } if ([object isKindOfClass:[NSString class]]) { UIPasteboard.generalPasteboard.string = object; } else if([object isKindOfClass:[NSData class]]) { [UIPasteboard.generalPasteboard setData:object forPasteboardType:@"public.data"]; } else if ([object isKindOfClass:[NSNumber class]]) { UIPasteboard.generalPasteboard.string = [object stringValue]; } else { // TODO: make this an alert instead of an exception [NSException raise:NSInternalInconsistencyException format:@"Tried to copy unsupported type: %@", [object class]]; } } @end ================================================ FILE: Classes/Utility/Categories/UITextField+Range.h ================================================ // // UITextField+Range.h // FLEX // // Created by Tanner on 6/13/17. // #import @interface UITextField (Range) @property (nonatomic, readonly) NSRange flex_selectedRange; @end ================================================ FILE: Classes/Utility/Categories/UITextField+Range.m ================================================ // // UITextField+Range.m // FLEX // // Created by Tanner on 6/13/17. // #import "UITextField+Range.h" @implementation UITextField (Range) - (NSRange)flex_selectedRange { UITextRange *r = self.selectedTextRange; if (r) { NSInteger loc = [self offsetFromPosition:self.beginningOfDocument toPosition:r.start]; NSInteger len = [self offsetFromPosition:r.start toPosition:r.end]; return NSMakeRange(loc, len); } return NSMakeRange(NSNotFound, 0); } @end ================================================ FILE: Classes/Utility/FLEXAlert.h ================================================ // // FLEXAlert.h // FLEX // // Created by Tanner Bennett on 8/20/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @class FLEXAlert, FLEXAlertAction; typedef void (^FLEXAlertReveal)(void); typedef void (^FLEXAlertBuilder)(FLEXAlert *make); typedef FLEXAlert * _Nonnull (^FLEXAlertStringProperty)(NSString * _Nullable); typedef FLEXAlert * _Nonnull (^FLEXAlertStringArg)(NSString * _Nullable); typedef FLEXAlert * _Nonnull (^FLEXAlertTextField)(void(^configurationHandler)(UITextField *textField)); typedef FLEXAlertAction * _Nonnull (^FLEXAlertAddAction)(NSString *title); typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionStringProperty)(NSString * _Nullable); typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionProperty)(void); typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionBOOLProperty)(BOOL); typedef FLEXAlertAction * _Nonnull (^FLEXAlertActionHandler)(void(^handler)(NSArray *strings)); @interface FLEXAlert : NSObject /// Shows a simple alert with one button which says "Dismiss" + (void)showAlert:(NSString * _Nullable)title message:(NSString * _Nullable)message from:(UIViewController *)viewController; /// Shows a simple alert with no buttons and only a title, for half a second + (void)showQuickAlert:(NSString *)title from:(UIViewController *)viewController; /// Construct and display an alert + (void)makeAlert:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController; /// Construct and display an action sheet-style alert + (void)makeSheet:(FLEXAlertBuilder)block showFrom:(UIViewController *)viewController source:(id)viewOrBarItem; /// Construct an alert + (UIAlertController *)makeAlert:(FLEXAlertBuilder)block; /// Construct an action sheet-style alert + (UIAlertController *)makeSheet:(FLEXAlertBuilder)block; /// Set the alert's title. /// /// Call in succession to append strings to the title. @property (nonatomic, readonly) FLEXAlertStringProperty title; /// Set the alert's message. /// /// Call in succession to append strings to the message. @property (nonatomic, readonly) FLEXAlertStringProperty message; /// Add a button with a given title with the default style and no action. @property (nonatomic, readonly) FLEXAlertAddAction button; /// Add a text field with the given (optional) placeholder text. @property (nonatomic, readonly) FLEXAlertStringArg textField; /// Add and configure the given text field. /// /// Use this if you need to more than set the placeholder, such as /// supply a delegate, make it secure entry, or change other attributes. @property (nonatomic, readonly) FLEXAlertTextField configuredTextField; @end @interface FLEXAlertAction : NSObject /// Set the action's title. /// /// Call in succession to append strings to the title. @property (nonatomic, readonly) FLEXAlertActionStringProperty title; /// Make the action destructive. It appears with red text. @property (nonatomic, readonly) FLEXAlertActionProperty destructiveStyle; /// Make the action cancel-style. It sometimes appears with a bolder font. @property (nonatomic, readonly) FLEXAlertActionProperty cancelStyle; /// Make the action the preferred action. It appears with a bolder font. /// The first action that is set as preferred will be used as the preferred action. @property (nonatomic, readonly) FLEXAlertActionProperty preferred; /// Enable or disable the action. Enabled by default. @property (nonatomic, readonly) FLEXAlertActionBOOLProperty enabled; /// Give the button an action. The action takes an array of text field strings. @property (nonatomic, readonly) FLEXAlertActionHandler handler; /// Access the underlying UIAlertAction, should you need to change it while /// the encompassing alert is being displayed. For example, you may want to /// enable or disable a button based on the input of some text fields in the alert. /// Do not call this more than once per instance. @property (nonatomic, readonly) UIAlertAction *action; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/FLEXAlert.m ================================================ // // FLEXAlert.m // FLEX // // Created by Tanner Bennett on 8/20/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXAlert.h" #import "FLEXMacros.h" @interface FLEXAlert () @property (nonatomic, readonly) UIAlertController *_controller; @property (nonatomic, readonly) NSMutableArray *_actions; @end #define FLEXAlertActionMutationAssertion() \ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAlertAction"); @interface FLEXAlertAction () @property (nonatomic) UIAlertController *_controller; @property (nonatomic) NSString *_title; @property (nonatomic) UIAlertActionStyle _style; @property (nonatomic) BOOL _disable; @property (nonatomic) BOOL _isPreferred; @property (nonatomic) void(^_handler)(UIAlertAction *action); @property (nonatomic) UIAlertAction *_action; @end @implementation FLEXAlert + (void)showAlert:(NSString *)title message:(NSString *)message from:(UIViewController *)viewController { [self makeAlert:^(FLEXAlert *make) { make.title(title).message(message).button(@"Dismiss").cancelStyle(); } showFrom:viewController]; } + (void)showQuickAlert:(NSString *)title from:(UIViewController *)viewController { UIAlertController *alert = [self makeAlert:^(FLEXAlert *make) { make.title(title); }]; [viewController presentViewController:alert animated:YES completion:^{ flex_dispatch_after(0.5, dispatch_get_main_queue(), ^{ [alert dismissViewControllerAnimated:YES completion:nil]; }); }]; } #pragma mark Initialization - (instancetype)initWithController:(UIAlertController *)controller { self = [super init]; if (self) { __controller = controller; __actions = [NSMutableArray new]; } return self; } + (UIAlertController *)make:(FLEXAlertBuilder)block withStyle:(UIAlertControllerStyle)style { // Create alert builder FLEXAlert *alert = [[self alloc] initWithController: [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:style] ]; // Configure alert block(alert); // Add actions for (FLEXAlertAction *builder in alert._actions) { [alert._controller addAction:builder.action]; } UIAlertController *controller = alert._controller; // Set preferred action on alert controller for (FLEXAlertAction *builder in alert._actions) { UIAlertAction *action = builder.action; if (builder._isPreferred) { controller.preferredAction = action; break; } } return controller; } + (void)make:(FLEXAlertBuilder)block withStyle:(UIAlertControllerStyle)style showFrom:(UIViewController *)viewController source:(id)viewOrBarItem { UIAlertController *alert = [self make:block withStyle:style]; if ([viewOrBarItem isKindOfClass:[UIBarButtonItem class]]) { alert.popoverPresentationController.barButtonItem = viewOrBarItem; } else if ([viewOrBarItem isKindOfClass:[UIView class]]) { alert.popoverPresentationController.sourceView = viewOrBarItem; alert.popoverPresentationController.sourceRect = [viewOrBarItem bounds]; } else if (viewOrBarItem) { NSParameterAssert( [viewOrBarItem isKindOfClass:[UIBarButtonItem class]] || [viewOrBarItem isKindOfClass:[UIView class]] || !viewOrBarItem ); } [viewController presentViewController:alert animated:YES completion:nil]; } + (void)makeAlert:(FLEXAlertBuilder)block showFrom:(UIViewController *)controller { [self make:block withStyle:UIAlertControllerStyleAlert showFrom:controller source:nil]; } + (void)makeSheet:(FLEXAlertBuilder)block showFrom:(UIViewController *)controller { [self make:block withStyle:UIAlertControllerStyleActionSheet showFrom:controller source:nil]; } /// Construct and display an action sheet-style alert + (void)makeSheet:(FLEXAlertBuilder)block showFrom:(UIViewController *)controller source:(id)viewOrBarItem { [self make:block withStyle:UIAlertControllerStyleActionSheet showFrom:controller source:viewOrBarItem]; } + (UIAlertController *)makeAlert:(FLEXAlertBuilder)block { return [self make:block withStyle:UIAlertControllerStyleAlert]; } + (UIAlertController *)makeSheet:(FLEXAlertBuilder)block { return [self make:block withStyle:UIAlertControllerStyleActionSheet]; } #pragma mark Configuration - (FLEXAlertStringProperty)title { return ^FLEXAlert *(NSString *title) { if (self._controller.title) { self._controller.title = [self._controller.title stringByAppendingString:title ?: @""]; } else { self._controller.title = title; } return self; }; } - (FLEXAlertStringProperty)message { return ^FLEXAlert *(NSString *message) { if (self._controller.message) { self._controller.message = [self._controller.message stringByAppendingString:message ?: @""]; } else { self._controller.message = message; } return self; }; } - (FLEXAlertAddAction)button { return ^FLEXAlertAction *(NSString *title) { FLEXAlertAction *action = FLEXAlertAction.new.title(title); action._controller = self._controller; [self._actions addObject:action]; return action; }; } - (FLEXAlertStringArg)textField { return ^FLEXAlert *(NSString *placeholder) { [self._controller addTextFieldWithConfigurationHandler:^(UITextField *textField) { textField.placeholder = placeholder; }]; return self; }; } - (FLEXAlertTextField)configuredTextField { return ^FLEXAlert *(void(^configurationHandler)(UITextField *)) { [self._controller addTextFieldWithConfigurationHandler:configurationHandler]; return self; }; } @end @implementation FLEXAlertAction - (FLEXAlertActionStringProperty)title { return ^FLEXAlertAction *(NSString *title) { FLEXAlertActionMutationAssertion(); if (self._title) { self._title = [self._title stringByAppendingString:title ?: @""]; } else { self._title = title; } return self; }; } - (FLEXAlertActionProperty)destructiveStyle { return ^FLEXAlertAction *() { FLEXAlertActionMutationAssertion(); self._style = UIAlertActionStyleDestructive; return self; }; } - (FLEXAlertActionProperty)cancelStyle { return ^FLEXAlertAction *() { FLEXAlertActionMutationAssertion(); self._style = UIAlertActionStyleCancel; return self; }; } - (FLEXAlertActionProperty)preferred { return ^FLEXAlertAction *() { FLEXAlertActionMutationAssertion(); self._isPreferred = YES; return self; }; } - (FLEXAlertActionBOOLProperty)enabled { return ^FLEXAlertAction *(BOOL enabled) { FLEXAlertActionMutationAssertion(); self._disable = !enabled; return self; }; } - (FLEXAlertActionHandler)handler { return ^FLEXAlertAction *(void(^handler)(NSArray *)) { FLEXAlertActionMutationAssertion(); // Get weak reference to the alert to avoid block <--> alert retain cycle UIAlertController *controller = self._controller; weakify(controller) self._handler = ^(UIAlertAction *action) { strongify(controller) // Strongify that reference and pass the text field strings to the handler NSArray *strings = [controller.textFields valueForKeyPath:@"text"]; handler(strings); }; return self; }; } - (UIAlertAction *)action { if (self._action) { return self._action; } self._action = [UIAlertAction actionWithTitle:self._title style:self._style handler:self._handler ]; self._action.enabled = !self._disable; return self._action; } @end ================================================ FILE: Classes/Utility/FLEXColor.h ================================================ // // FLEXColor.h // FLEX // // Created by Benny Wong on 6/18/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import NS_ASSUME_NONNULL_BEGIN @interface FLEXColor : NSObject @property (readonly, class) UIColor *primaryBackgroundColor; + (UIColor *)primaryBackgroundColorWithAlpha:(CGFloat)alpha; @property (readonly, class) UIColor *secondaryBackgroundColor; + (UIColor *)secondaryBackgroundColorWithAlpha:(CGFloat)alpha; @property (readonly, class) UIColor *tertiaryBackgroundColor; + (UIColor *)tertiaryBackgroundColorWithAlpha:(CGFloat)alpha; @property (readonly, class) UIColor *groupedBackgroundColor; + (UIColor *)groupedBackgroundColorWithAlpha:(CGFloat)alpha; @property (readonly, class) UIColor *secondaryGroupedBackgroundColor; + (UIColor *)secondaryGroupedBackgroundColorWithAlpha:(CGFloat)alpha; // Text colors @property (readonly, class) UIColor *primaryTextColor; @property (readonly, class) UIColor *deemphasizedTextColor; // UI element colors @property (readonly, class) UIColor *tintColor; @property (readonly, class) UIColor *scrollViewBackgroundColor; @property (readonly, class) UIColor *iconColor; @property (readonly, class) UIColor *borderColor; @property (readonly, class) UIColor *toolbarItemHighlightedColor; @property (readonly, class) UIColor *toolbarItemSelectedColor; @property (readonly, class) UIColor *hairlineColor; @property (readonly, class) UIColor *destructiveColor; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/FLEXColor.m ================================================ // // FLEXColor.m // FLEX // // Created by Benny Wong on 6/18/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXUtility.h" #define FLEXDynamicColor(dynamic, static) ({ \ UIColor *c; \ if (@available(iOS 13.0, *)) { \ c = [UIColor dynamic]; \ } else { \ c = [UIColor static]; \ } \ c; \ }); @implementation FLEXColor #pragma mark - Background Colors + (UIColor *)primaryBackgroundColor { return FLEXDynamicColor(systemBackgroundColor, whiteColor); } + (UIColor *)primaryBackgroundColorWithAlpha:(CGFloat)alpha { return [[self primaryBackgroundColor] colorWithAlphaComponent:alpha]; } + (UIColor *)secondaryBackgroundColor { return FLEXDynamicColor( secondarySystemBackgroundColor, colorWithHue:2.0/3.0 saturation:0.02 brightness:0.97 alpha:1 ); } + (UIColor *)secondaryBackgroundColorWithAlpha:(CGFloat)alpha { return [[self secondaryBackgroundColor] colorWithAlphaComponent:alpha]; } + (UIColor *)tertiaryBackgroundColor { // All the background/fill colors are varying shades // of white and black with really low alpha levels. // We use systemGray4Color instead to avoid alpha issues. return FLEXDynamicColor(systemGray4Color, lightGrayColor); } + (UIColor *)tertiaryBackgroundColorWithAlpha:(CGFloat)alpha { return [[self tertiaryBackgroundColor] colorWithAlphaComponent:alpha]; } + (UIColor *)groupedBackgroundColor { return FLEXDynamicColor( systemGroupedBackgroundColor, colorWithHue:2.0/3.0 saturation:0.02 brightness:0.97 alpha:1 ); } + (UIColor *)groupedBackgroundColorWithAlpha:(CGFloat)alpha { return [[self groupedBackgroundColor] colorWithAlphaComponent:alpha]; } + (UIColor *)secondaryGroupedBackgroundColor { return FLEXDynamicColor(secondarySystemGroupedBackgroundColor, whiteColor); } + (UIColor *)secondaryGroupedBackgroundColorWithAlpha:(CGFloat)alpha { return [[self secondaryGroupedBackgroundColor] colorWithAlphaComponent:alpha]; } #pragma mark - Text colors + (UIColor *)primaryTextColor { return FLEXDynamicColor(labelColor, blackColor); } + (UIColor *)deemphasizedTextColor { return FLEXDynamicColor(secondaryLabelColor, lightGrayColor); } #pragma mark - UI Element Colors + (UIColor *)tintColor { #if FLEX_AT_LEAST_IOS13_SDK if (@available(iOS 13.0, *)) { return UIColor.systemBlueColor; } else { return UIApplication.sharedApplication.keyWindow.tintColor; } #else return UIApplication.sharedApplication.keyWindow.tintColor; #endif } + (UIColor *)scrollViewBackgroundColor { return FLEXDynamicColor( systemGroupedBackgroundColor, colorWithHue:2.0/3.0 saturation:0.02 brightness:0.95 alpha:1 ); } + (UIColor *)iconColor { return FLEXDynamicColor(labelColor, blackColor); } + (UIColor *)borderColor { return [self primaryBackgroundColor]; } + (UIColor *)toolbarItemHighlightedColor { return FLEXDynamicColor( quaternaryLabelColor, colorWithHue:2.0/3.0 saturation:0.1 brightness:0.25 alpha:0.6 ); } + (UIColor *)toolbarItemSelectedColor { return FLEXDynamicColor( secondaryLabelColor, colorWithHue:2.0/3.0 saturation:0.1 brightness:0.25 alpha:0.68 ); } + (UIColor *)hairlineColor { return FLEXDynamicColor(systemGray3Color, colorWithWhite:0.75 alpha:1); } + (UIColor *)destructiveColor { return FLEXDynamicColor(systemRedColor, redColor); } @end ================================================ FILE: Classes/Utility/FLEXHeapEnumerator.h ================================================ // // FLEXHeapEnumerator.h // Flipboard // // Created by Ryan Olson on 5/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXObjectRef; NS_ASSUME_NONNULL_BEGIN typedef void (^flex_object_enumeration_block_t)(__unsafe_unretained id object, __unsafe_unretained Class actualClass); /// Counts and identifies all class instances on the heap. @interface FLEXHeapSnapshot : NSObject /// The names of every class instance discovered on the heap. @property (nonatomic, readonly) NSArray *classNames; /// A mapping of instance counts to class names. @property (nonatomic, readonly) NSDictionary *instanceCountsForClassNames; /// A mapping of class instance size to class name. /// /// To roughly calculate the memory usage of an entire class, multiply this number by the instance count. @property (nonatomic, readonly) NSDictionary *instanceSizesForClassNames; @end @interface FLEXHeapEnumerator : NSObject /// Use carefully; this method puts a global lock on the heap in between callbacks. /// /// Inspired by: /// [heap_find.cpp](https://llvm.org/svn/llvm-project/lldb/tags/RELEASE_34/final/examples/darwin/heap_find/heap/heap_find.cpp) /// and [samdmarshall](https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396) + (void)enumerateLiveObjectsUsingBlock:(flex_object_enumeration_block_t)callback NS_SWIFT_UNAVAILABLE("Use one of the other methods instead."); /// Returned references are not validated beyond containing a valid isa. /// To validate them yourself, pass each reference's object to \c FLEXPointerIsValidObjcObject + (NSArray *)instancesOfClassWithName:(NSString *)className retained:(BOOL)retain; /// Returned references have been validated via \c FLEXPointerIsValidObjcObject /// @param object the object to find references to /// @param retain whether to retain the objects referencing \c object + (NSArray *)objectsWithReferencesToObject:(id)object retained:(BOOL)retain; /// Capture all live objects on the heap and do with this information what you will. + (FLEXHeapSnapshot *)generateHeapSnapshot; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/FLEXHeapEnumerator.m ================================================ // // FLEXHeapEnumerator.m // Flipboard // // Created by Ryan Olson on 5/28/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXHeapEnumerator.h" #import "FLEXObjcInternal.h" #import "FLEXObjectRef.h" #import "NSObject+FLEX_Reflection.h" #import "NSString+FLEX.h" #import #import #import static CFMutableSetRef registeredClasses; // Mimics the objective-c object structure for checking if a range of memory is an object. typedef struct { Class isa; } flex_maybe_object_t; @implementation FLEXHeapSnapshot + (instancetype)snapshotWithCounts:(NSDictionary *)counts sizes:(NSDictionary *)sizes { FLEXHeapSnapshot *snapshot = [FLEXHeapSnapshot new]; snapshot->_classNames = counts.allKeys; snapshot->_instanceCountsForClassNames = counts; snapshot->_instanceSizesForClassNames = sizes; return snapshot; } @end @implementation FLEXHeapEnumerator static void range_callback(task_t task, void *context, unsigned type, vm_range_t *ranges, unsigned rangeCount) { if (!context) { return; } for (unsigned int i = 0; i < rangeCount; i++) { vm_range_t range = ranges[i]; flex_maybe_object_t *tryObject = (flex_maybe_object_t *)range.address; Class tryClass = NULL; #ifdef __arm64__ // See http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html extern uint64_t objc_debug_isa_class_mask WEAK_IMPORT_ATTRIBUTE; tryClass = (__bridge Class)((void *)((uint64_t)tryObject->isa & objc_debug_isa_class_mask)); #else tryClass = tryObject->isa; #endif // If the class pointer matches one in our set of class pointers from the runtime, then we should have an object. if (CFSetContainsValue(registeredClasses, (__bridge const void *)(tryClass))) { (*(flex_object_enumeration_block_t __unsafe_unretained *)context)((__bridge id)tryObject, tryClass); } } } static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_address, __unused vm_size_t size, void **local_memory) { *local_memory = (void *)remote_address; return KERN_SUCCESS; } + (void)enumerateLiveObjectsUsingBlock:(flex_object_enumeration_block_t)block { if (!block) { return; } // Refresh the class list on every call in case classes are added to the runtime. [self updateRegisteredClasses]; // Inspired by: // https://llvm.org/svn/llvm-project/lldb/tags/RELEASE_34/final/examples/darwin/heap_find/heap/heap_find.cpp // https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396 vm_address_t *zones = NULL; unsigned int zoneCount = 0; kern_return_t result = malloc_get_all_zones(TASK_NULL, reader, &zones, &zoneCount); if (result == KERN_SUCCESS) { for (unsigned int i = 0; i < zoneCount; i++) { malloc_zone_t *zone = (malloc_zone_t *)zones[i]; malloc_introspection_t *introspection = zone->introspect; // This may explain why some zone functions are // sometimes invalid; perhaps not all zones support them? if (!introspection) { continue; } void (*lock_zone)(malloc_zone_t *zone) = introspection->force_lock; void (*unlock_zone)(malloc_zone_t *zone) = introspection->force_unlock; // Callback has to unlock the zone so we freely allocate memory inside the given block flex_object_enumeration_block_t callback = ^(__unsafe_unretained id object, __unsafe_unretained Class actualClass) { unlock_zone(zone); block(object, actualClass); lock_zone(zone); }; BOOL lockZoneValid = FLEXPointerIsReadable(lock_zone); BOOL unlockZoneValid = FLEXPointerIsReadable(unlock_zone); // There is little documentation on when and why // any of these function pointers might be NULL // or garbage, so we resort to checking for NULL // and whether the pointer is readable if (introspection->enumerator && lockZoneValid && unlockZoneValid) { lock_zone(zone); introspection->enumerator(TASK_NULL, (void *)&callback, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, reader, &range_callback); unlock_zone(zone); } } } } + (void)updateRegisteredClasses { if (!registeredClasses) { registeredClasses = CFSetCreateMutable(NULL, 0, NULL); } else { CFSetRemoveAllValues(registeredClasses); } unsigned int count = 0; Class *classes = objc_copyClassList(&count); for (unsigned int i = 0; i < count; i++) { CFSetAddValue(registeredClasses, (__bridge const void *)(classes[i])); } free(classes); } + (NSArray *)instancesOfClassWithName:(NSString *)className retained:(BOOL)retain { const char *classNameCString = className.UTF8String; NSMutableArray *instances = [NSMutableArray new]; [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class actualClass) { if (strcmp(classNameCString, class_getName(actualClass)) == 0) { // Note: objects of certain classes crash when retain is called. // It is up to the user to avoid tapping into instance lists for these classes. // Ex. OS_dispatch_queue_specific_queue // In the future, we could provide some kind of warning for classes that are known to be problematic. if (malloc_size((__bridge const void *)(object)) > 0) { [instances addObject:object]; } } }]; NSArray *references = [FLEXObjectRef referencingAll:instances retained:retain]; return references; } + (NSArray *)objectsWithReferencesToObject:(id)object retained:(BOOL)retain { NSMutableArray *instances = [NSMutableArray new]; [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id tryObject, __unsafe_unretained Class actualClass) { // Skip known-invalid objects if (!FLEXPointerIsValidObjcObject((__bridge void *)tryObject)) { return; } // Get all the ivars on the object. Start with the class and and travel up the // inheritance chain. Once we find a match, record it and move on to the next object. // There's no reason to find multiple matches within the same object. Class tryClass = actualClass; while (tryClass) { unsigned int ivarCount = 0; Ivar *ivars = class_copyIvarList(tryClass, &ivarCount); for (unsigned int ivarIndex = 0; ivarIndex < ivarCount; ivarIndex++) { Ivar ivar = ivars[ivarIndex]; NSString *typeEncoding = @(ivar_getTypeEncoding(ivar) ?: ""); if (typeEncoding.flex_typeIsObjectOrClass) { ptrdiff_t offset = ivar_getOffset(ivar); uintptr_t *fieldPointer = (__bridge void *)tryObject + offset; if (*fieldPointer == (uintptr_t)(__bridge void *)object) { NSString *ivarName = @(ivar_getName(ivar) ?: "???"); id ref = [FLEXObjectRef referencing:tryObject ivar:ivarName retained:retain]; [instances addObject:ref]; return; } } } free(ivars); tryClass = class_getSuperclass(tryClass); } }]; return instances; } + (FLEXHeapSnapshot *)generateHeapSnapshot { // Set up a CFMutableDictionary with class pointer keys and NSUInteger values. // We abuse CFMutableDictionary a little to have primitive keys through judicious casting, but it gets the job done. // The dictionary is intialized with a 0 count for each class so that it doesn't have to expand during enumeration. // While it might be a little cleaner to populate an NSMutableDictionary with class name string keys to NSNumber // counts, we choose the CF/primitives approach because it lets us enumerate the objects in the heap without // allocating any memory during enumeration. The alternative of creating one NSString/NSNumber per object // on the heap ends up polluting the count of live objects quite a bit. unsigned int classCount = 0; Class *classes = objc_copyClassList(&classCount); CFMutableDictionaryRef mutableCountsForClasses = CFDictionaryCreateMutable(NULL, classCount, NULL, NULL); for (unsigned int i = 0; i < classCount; i++) { CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)classes[i], (const void *)0); } // Enumerate all objects on the heap to build the counts of instances for each class [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class cls) { NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue( mutableCountsForClasses, (__bridge const void *)cls ); instanceCount++; CFDictionarySetValue( mutableCountsForClasses, (__bridge const void *)cls, (const void *)instanceCount ); }]; // Convert our CF primitive dictionary into a nicer mapping of class name strings to instance counts NSMutableDictionary *countsForClassNames = [NSMutableDictionary new]; NSMutableDictionary *sizesForClassNames = [NSMutableDictionary new]; for (unsigned int i = 0; i < classCount; i++) { Class class = classes[i]; NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)(class)); NSString *className = @(class_getName(class)); if (instanceCount > 0) { countsForClassNames[className] = @(instanceCount); sizesForClassNames[className] = @(class_getInstanceSize(class)); } } free(classes); return [FLEXHeapSnapshot snapshotWithCounts:countsForClassNames sizes:sizesForClassNames]; } @end ================================================ FILE: Classes/Utility/FLEXMacros.h ================================================ // // FLEXMacros.h // FLEX // // Created by Tanner on 3/12/20. // Copyright © 2020 FLEX Team. All rights reserved. // #ifndef FLEXMacros_h #define FLEXMacros_h #ifndef __cplusplus #ifndef auto #define auto __auto_type #endif #endif #define flex_keywordify class NSObject; #define ctor flex_keywordify __attribute__((constructor)) void __flex_ctor_##__LINE__() #define dtor flex_keywordify __attribute__((destructor)) void __flex_dtor_##__LINE__() #ifndef strongify #define weakify(var) __weak __typeof(var) __weak__##var = var; #define strongify(var) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wshadow\"") \ __strong typeof(var) var = __weak__##var; \ _Pragma("clang diagnostic pop") #endif // A macro to check if we are running in a test environment #define FLEX_IS_TESTING() (NSClassFromString(@"XCTest") != nil) /// Whether we want the majority of constructors to run upon load or not. extern BOOL FLEXConstructorsShouldRun(void); /// A macro to return from the current procedure if we don't want to run constructors #define FLEX_EXIT_IF_NO_CTORS() if (!FLEXConstructorsShouldRun()) return; /// Rounds down to the nearest "point" coordinate NS_INLINE CGFloat FLEXFloor(CGFloat x) { return floor(UIScreen.mainScreen.scale * (x)) / UIScreen.mainScreen.scale; } /// Returns the given number of points in pixels NS_INLINE CGFloat FLEXPointsToPixels(CGFloat points) { return points / UIScreen.mainScreen.scale; } /// Creates a CGRect with all members rounded down to the nearest "point" coordinate NS_INLINE CGRect FLEXRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) { return CGRectMake(FLEXFloor(x), FLEXFloor(y), FLEXFloor(width), FLEXFloor(height)); } /// Adjusts the origin of an existing rect NS_INLINE CGRect FLEXRectSetOrigin(CGRect r, CGPoint origin) { r.origin = origin; return r; } /// Adjusts the size of an existing rect NS_INLINE CGRect FLEXRectSetSize(CGRect r, CGSize size) { r.size = size; return r; } /// Adjusts the origin.x of an existing rect NS_INLINE CGRect FLEXRectSetX(CGRect r, CGFloat x) { r.origin.x = x; return r; } /// Adjusts the origin.y of an existing rect NS_INLINE CGRect FLEXRectSetY(CGRect r, CGFloat y) { r.origin.y = y ; return r; } /// Adjusts the size.width of an existing rect NS_INLINE CGRect FLEXRectSetWidth(CGRect r, CGFloat width) { r.size.width = width; return r; } /// Adjusts the size.height of an existing rect NS_INLINE CGRect FLEXRectSetHeight(CGRect r, CGFloat height) { r.size.height = height; return r; } #define FLEXPluralString(count, plural, singular) [NSString \ stringWithFormat:@"%@ %@", @(count), (count == 1 ? singular : plural) \ ] #define FLEXPluralFormatString(count, pluralFormat, singularFormat) [NSString \ stringWithFormat:(count == 1 ? singularFormat : pluralFormat), @(count) \ ] #define flex_dispatch_after(nSeconds, onQueue, block) \ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, \ (int64_t)(nSeconds * NSEC_PER_SEC)), onQueue, block) #endif /* FLEXMacros_h */ ================================================ FILE: Classes/Utility/FLEXResources.h ================================================ // // FLEXResources.h // FLEX // // Created by Ryan Olson on 6/8/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @interface FLEXResources : NSObject #pragma mark - FLEX Toolbar Icons @property (readonly, class) UIImage *closeIcon; @property (readonly, class) UIImage *dragHandle; @property (readonly, class) UIImage *globalsIcon; @property (readonly, class) UIImage *hierarchyIcon; @property (readonly, class) UIImage *recentIcon; @property (readonly, class) UIImage *moveIcon; @property (readonly, class) UIImage *selectIcon; #pragma mark - Toolbar Icons @property (readonly, class) UIImage *bookmarksIcon; @property (readonly, class) UIImage *openTabsIcon; @property (readonly, class) UIImage *moreIcon; @property (readonly, class) UIImage *gearIcon; @property (readonly, class) UIImage *scrollToBottomIcon; #pragma mark - Content Type Icons @property (readonly, class) UIImage *jsonIcon; @property (readonly, class) UIImage *textPlainIcon; @property (readonly, class) UIImage *htmlIcon; @property (readonly, class) UIImage *audioIcon; @property (readonly, class) UIImage *jsIcon; @property (readonly, class) UIImage *plistIcon; @property (readonly, class) UIImage *textIcon; @property (readonly, class) UIImage *videoIcon; @property (readonly, class) UIImage *xmlIcon; @property (readonly, class) UIImage *binaryIcon; #pragma mark - 3D Explorer Icons @property (readonly, class) UIImage *toggle2DIcon; @property (readonly, class) UIImage *toggle3DIcon; @property (readonly, class) UIImage *rangeSliderLeftHandle; @property (readonly, class) UIImage *rangeSliderRightHandle; @property (readonly, class) UIImage *rangeSliderTrack; @property (readonly, class) UIImage *rangeSliderFill; #pragma mark - Misc Icons @property (readonly, class) UIImage *checkerPattern; @property (readonly, class) UIColor *checkerPatternColor; @property (readonly, class) UIImage *hierarchyIndentPattern; @end ================================================ FILE: Classes/Utility/FLEXResources.m ================================================ // // FLEXResources.m // FLEX // // Created by Ryan Olson on 6/8/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXResources.h" // Useful regex for formatting pasted hex // // To replace something with a newline, you need to manually copy // a newline from a text editor and paste it into the replace box. // For the "find" box, you can simply copy and paste the regexes below. // // Split into rows of 16: // ((?:0x[a-f\d][a-f\d], ){16}) // Replace with: \n $1 // // Split last greater-than-16 row into two rows of 16 and less than 16: // ((?:0x[a-f\d][a-f\d], ){16})((?:0x[a-f\d][a-f\d](?:, )?)+) // Replace with: $1\n $2 // // Trim trailing spaces: // , \n // Replace with: ,\n // // Trim trailing newline after hex: // \n\n\} // Replace with: \n} // The 1x icons are unused now since some new icons don't have a 1x version at all #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused" #pragma mark - FLEX Toolbar Icons static const u_int8_t FLEXCloseIcon[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1e, 0x08, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x30, 0xae, 0xa2, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0xca, 0x41, 0x37, 0x05, 0x00, 0x00, 0x01, 0xd2, 0x49, 0x44, 0x41, 0x54, 0x48, 0x0d, 0xad, 0x96, 0x3b, 0x4e, 0xc3, 0x40, 0x10, 0x86, 0xe3, 0x08, 0x51, 0x84, 0x0a, 0x24, 0x1e, 0xa2, 0x41, 0xa2, 0x41, 0x9c, 0x82, 0x82, 0x82, 0x23, 0x20, 0x1a, 0xe0, 0x0c, 0xb4, 0x3c, 0x7c, 0x85, 0x9c, 0x80, 0x0b, 0xa4, 0x04, 0x51, 0x12, 0xe0, 0x04, 0x08, 0xf1, 0xa8, 0x29, 0x10, 0x52, 0x3a, 0x1a, 0x40, 0xe2, 0x61, 0xbe, 0x89, 0x32, 0x96, 0x33, 0xac, 0x37, 0xf6, 0x7a, 0x47, 0xfa, 0xb5, 0x8f, 0xf1, 0xfc, 0xdf, 0xec, 0x26, 0x8a, 0xd3, 0x6a, 0x8d, 0x22, 0xcb, 0xb2, 0x79, 0xd4, 0xd6, 0x75, 0xec, 0x11, 0xef, 0x69, 0x34, 0x9b, 0xfb, 0xb2, 0x48, 0x50, 0x0f, 0x7d, 0xa3, 0x67, 0xb4, 0x93, 0x27, 0x23, 0x4d, 0xf0, 0x3c, 0x40, 0x03, 0xf4, 0x89, 0xba, 0x43, 0x5b, 0x26, 0x9b, 0xa8, 0x18, 0x3f, 0x2c, 0xf6, 0x23, 0x31, 0x5b, 0x78, 0xa5, 0x45, 0xf3, 0xd1, 0x7c, 0x4d, 0x12, 0x5b, 0x8e, 0x44, 0x14, 0x38, 0xbe, 0xa9, 0xc3, 0x5b, 0xb6, 0xd6, 0x05, 0xdc, 0x46, 0xb7, 0xb2, 0x32, 0xd1, 0x08, 0x8e, 0xd7, 0x89, 0xf1, 0xd3, 0xe5, 0x79, 0x7e, 0x9b, 0xec, 0x2c, 0xa0, 0x7b, 0xcd, 0x14, 0xc6, 0x20, 0x38, 0xf5, 0x65, 0xd0, 0x6b, 0x72, 0x33, 0x39, 0x58, 0x26, 0x6c, 0x44, 0x81, 0xe3, 0x73, 0x8c, 0x5c, 0xf1, 0x1f, 0xaa, 0x1d, 0xf0, 0x74, 0x23, 0x38, 0xf5, 0x65, 0xd0, 0x2b, 0x72, 0xe3, 0x27, 0x55, 0xa8, 0x8e, 0xa1, 0x70, 0xea, 0x8e, 0x90, 0x2b, 0x26, 0x43, 0x43, 0xe1, 0xd0, 0x9a, 0x43, 0xeb, 0xc2, 0x81, 0x1e, 0xba, 0x8e, 0xc9, 0x5e, 0x1f, 0xf9, 0xaf, 0x57, 0x61, 0x76, 0xa4, 0xd0, 0xfb, 0x99, 0x93, 0xf7, 0x41, 0x3b, 0xd6, 0xaf, 0xd6, 0xda, 0x03, 0xff, 0x25, 0xe7, 0x8a, 0x3e, 0x9b, 0xcd, 0xa0, 0xda, 0x21, 0x46, 0x65, 0x27, 0xb7, 0xe0, 0xcb, 0x68, 0xd0, 0x1a, 0xf0, 0x5a, 0xd0, 0xca, 0xaf, 0xc1, 0x24, 0x49, 0x06, 0x34, 0x71, 0xa1, 0x8d, 0x38, 0xc6, 0x1e, 0xcf, 0xbc, 0x3b, 0xf6, 0x9b, 0x6d, 0x71, 0x85, 0xa9, 0xbd, 0x57, 0xb3, 0x0e, 0xfa, 0x79, 0xf5, 0x76, 0x55, 0x01, 0xaa, 0x3d, 0xc4, 0x83, 0x7b, 0xa0, 0x37, 0xe4, 0xa2, 0xbd, 0x58, 0xc6, 0x4e, 0xee, 0x81, 0x0e, 0x7f, 0xf0, 0xc9, 0x97, 0x7d, 0xdb, 0xc3, 0x4f, 0x3e, 0x09, 0xaa, 0x1d, 0x46, 0x85, 0x57, 0x85, 0x1a, 0xf8, 0x03, 0x75, 0x36, 0xaa, 0x9f, 0x9c, 0xca, 0xd4, 0x56, 0x8f, 0xd6, 0xe5, 0xef, 0x53, 0x3a, 0xe0, 0x19, 0xb9, 0xf6, 0x30, 0x38, 0x85, 0x29, 0x72, 0x85, 0x17, 0x5a, 0x38, 0xf9, 0x22, 0xc5, 0xf5, 0xe0, 0x14, 0xa4, 0x2e, 0x22, 0x7b, 0x95, 0xa0, 0x41, 0x70, 0xcc, 0xab, 0xff, 0x47, 0x52, 0x82, 0x67, 0xc4, 0x4f, 0x4e, 0xfe, 0x88, 0x6c, 0xc8, 0x67, 0xbe, 0x3b, 0x2c, 0x65, 0xb2, 0x82, 0x64, 0xc3, 0x46, 0xad, 0x93, 0xda, 0x3e, 0x30, 0x2b, 0x83, 0xbf, 0x91, 0xeb, 0xc8, 0x97, 0x62, 0x0f, 0xd9, 0x68, 0x04, 0xd5, 0x26, 0x30, 0x5d, 0x42, 0xae, 0x93, 0x6f, 0x08, 0x78, 0x0e, 0x7d, 0x20, 0x8d, 0x28, 0x50, 0x03, 0x7f, 0x52, 0x73, 0xc6, 0x17, 0x34, 0x95, 0xc8, 0x03, 0x4c, 0x56, 0x19, 0xe4, 0xee, 0xef, 0xd0, 0x19, 0x6f, 0x99, 0x2f, 0xc6, 0x68, 0x81, 0xbf, 0xfc, 0x31, 0xd8, 0x46, 0xcb, 0xe8, 0x14, 0xff, 0xd7, 0x3f, 0x44, 0x89, 0xd3, 0x05, 0x85, 0xe7, 0xc6, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXCloseIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1e, 0x08, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x30, 0xae, 0xa2, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0xca, 0x41, 0x37, 0x05, 0x00, 0x00, 0x01, 0xd2, 0x49, 0x44, 0x41, 0x54, 0x48, 0x0d, 0xad, 0x96, 0x3b, 0x4e, 0xc3, 0x40, 0x10, 0x86, 0xe3, 0x08, 0x51, 0x84, 0x0a, 0x24, 0x1e, 0xa2, 0x41, 0xa2, 0x41, 0x9c, 0x82, 0x82, 0x82, 0x23, 0x20, 0x1a, 0xe0, 0x0c, 0xb4, 0x3c, 0x7c, 0x85, 0x9c, 0x80, 0x0b, 0xa4, 0x04, 0x51, 0x12, 0xe0, 0x04, 0x08, 0xf1, 0xa8, 0x29, 0x10, 0x52, 0x3a, 0x1a, 0x40, 0xe2, 0x61, 0xbe, 0x89, 0x32, 0x96, 0x33, 0xac, 0x37, 0xf6, 0x7a, 0x47, 0xfa, 0xb5, 0x8f, 0xf1, 0xfc, 0xdf, 0xec, 0x26, 0x8a, 0xd3, 0x6a, 0x8d, 0x22, 0xcb, 0xb2, 0x79, 0xd4, 0xd6, 0x75, 0xec, 0x11, 0xef, 0x69, 0x34, 0x9b, 0xfb, 0xb2, 0x48, 0x50, 0x0f, 0x7d, 0xa3, 0x67, 0xb4, 0x93, 0x27, 0x23, 0x4d, 0xf0, 0x3c, 0x40, 0x03, 0xf4, 0x89, 0xba, 0x43, 0x5b, 0x26, 0x9b, 0xa8, 0x18, 0x3f, 0x2c, 0xf6, 0x23, 0x31, 0x5b, 0x78, 0xa5, 0x45, 0xf3, 0xd1, 0x7c, 0x4d, 0x12, 0x5b, 0x8e, 0x44, 0x14, 0x38, 0xbe, 0xa9, 0xc3, 0x5b, 0xb6, 0xd6, 0x05, 0xdc, 0x46, 0xb7, 0xb2, 0x32, 0xd1, 0x08, 0x8e, 0xd7, 0x89, 0xf1, 0xd3, 0xe5, 0x79, 0x7e, 0x9b, 0xec, 0x2c, 0xa0, 0x7b, 0xcd, 0x14, 0xc6, 0x20, 0x38, 0xf5, 0x65, 0xd0, 0x6b, 0x72, 0x33, 0x39, 0x58, 0x26, 0x6c, 0x44, 0x81, 0xe3, 0x73, 0x8c, 0x5c, 0xf1, 0x1f, 0xaa, 0x1d, 0xf0, 0x74, 0x23, 0x38, 0xf5, 0x65, 0xd0, 0x2b, 0x72, 0xe3, 0x27, 0x55, 0xa8, 0x8e, 0xa1, 0x70, 0xea, 0x8e, 0x90, 0x2b, 0x26, 0x43, 0x43, 0xe1, 0xd0, 0x9a, 0x43, 0xeb, 0xc2, 0x81, 0x1e, 0xba, 0x8e, 0xc9, 0x5e, 0x1f, 0xf9, 0xaf, 0x57, 0x61, 0x76, 0xa4, 0xd0, 0xfb, 0x99, 0x93, 0xf7, 0x41, 0x3b, 0xd6, 0xaf, 0xd6, 0xda, 0x03, 0xff, 0x25, 0xe7, 0x8a, 0x3e, 0x9b, 0xcd, 0xa0, 0xda, 0x21, 0x46, 0x65, 0x27, 0xb7, 0xe0, 0xcb, 0x68, 0xd0, 0x1a, 0xf0, 0x5a, 0xd0, 0xca, 0xaf, 0xc1, 0x24, 0x49, 0x06, 0x34, 0x71, 0xa1, 0x8d, 0x38, 0xc6, 0x1e, 0xcf, 0xbc, 0x3b, 0xf6, 0x9b, 0x6d, 0x71, 0x85, 0xa9, 0xbd, 0x57, 0xb3, 0x0e, 0xfa, 0x79, 0xf5, 0x76, 0x55, 0x01, 0xaa, 0x3d, 0xc4, 0x83, 0x7b, 0xa0, 0x37, 0xe4, 0xa2, 0xbd, 0x58, 0xc6, 0x4e, 0xee, 0x81, 0x0e, 0x7f, 0xf0, 0xc9, 0x97, 0x7d, 0xdb, 0xc3, 0x4f, 0x3e, 0x09, 0xaa, 0x1d, 0x46, 0x85, 0x57, 0x85, 0x1a, 0xf8, 0x03, 0x75, 0x36, 0xaa, 0x9f, 0x9c, 0xca, 0xd4, 0x56, 0x8f, 0xd6, 0xe5, 0xef, 0x53, 0x3a, 0xe0, 0x19, 0xb9, 0xf6, 0x30, 0x38, 0x85, 0x29, 0x72, 0x85, 0x17, 0x5a, 0x38, 0xf9, 0x22, 0xc5, 0xf5, 0xe0, 0x14, 0xa4, 0x2e, 0x22, 0x7b, 0x95, 0xa0, 0x41, 0x70, 0xcc, 0xab, 0xff, 0x47, 0x52, 0x82, 0x67, 0xc4, 0x4f, 0x4e, 0xfe, 0x88, 0x6c, 0xc8, 0x67, 0xbe, 0x3b, 0x2c, 0x65, 0xb2, 0x82, 0x64, 0xc3, 0x46, 0xad, 0x93, 0xda, 0x3e, 0x30, 0x2b, 0x83, 0xbf, 0x91, 0xeb, 0xc8, 0x97, 0x62, 0x0f, 0xd9, 0x68, 0x04, 0xd5, 0x26, 0x30, 0x5d, 0x42, 0xae, 0x93, 0x6f, 0x08, 0x78, 0x0e, 0x7d, 0x20, 0x8d, 0x28, 0x50, 0x03, 0x7f, 0x52, 0x73, 0xc6, 0x17, 0x34, 0x95, 0xc8, 0x03, 0x4c, 0x56, 0x19, 0xe4, 0xee, 0xef, 0xd0, 0x19, 0x6f, 0x99, 0x2f, 0xc6, 0x68, 0x81, 0xbf, 0xfc, 0x31, 0xd8, 0x46, 0xcb, 0xe8, 0x14, 0xff, 0xd7, 0x3f, 0x44, 0x89, 0xd3, 0x05, 0x85, 0xe7, 0xc6, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXCloseIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2d, 0x08, 0x06, 0x00, 0x00, 0x00, 0x3a, 0x1a, 0xe2, 0x9a, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2d, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x46, 0xa8, 0x05, 0x50, 0x00, 0x00, 0x02, 0xc0, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xd5, 0x99, 0xbf, 0x6b, 0x14, 0x41, 0x14, 0x80, 0x6f, 0x4f, 0x0b, 0x83, 0x20, 0xb6, 0x42, 0xe2, 0xd8, 0x08, 0xe9, 0x2c, 0x15, 0xec, 0x2c, 0xb5, 0x14, 0xff, 0x0a, 0xc1, 0xf8, 0x23, 0x88, 0x65, 0x2a, 0x3b, 0x3b, 0x5b, 0xe3, 0xaf, 0xc2, 0x4a, 0x4b, 0x41, 0x50, 0x50, 0xac, 0xec, 0x45, 0x2c, 0x34, 0x03, 0x56, 0xc1, 0xd6, 0x42, 0x82, 0x9a, 0xcb, 0x37, 0x61, 0xdf, 0xb2, 0xb7, 0x77, 0xb3, 0x6f, 0x76, 0x77, 0x66, 0x16, 0x1f, 0x7c, 0x79, 0xbb, 0xfb, 0x66, 0xe6, 0x7d, 0x3b, 0xec, 0x91, 0x5c, 0x76, 0x32, 0xf9, 0x9f, 0x63, 0x36, 0x9b, 0x5d, 0x83, 0x57, 0xf0, 0x1a, 0x36, 0xe0, 0xe8, 0x98, 0xf7, 0x43, 0xff, 0x13, 0x70, 0x1f, 0xde, 0xc2, 0x73, 0x38, 0x3f, 0xe7, 0xc3, 0x85, 0x7b, 0xd0, 0x8c, 0xf7, 0x5c, 0x38, 0x3e, 0x37, 0x30, 0xd3, 0x09, 0x7d, 0xcf, 0xc0, 0x4e, 0x43, 0xe8, 0x1f, 0xe7, 0x97, 0x0f, 0x15, 0x38, 0x38, 0x06, 0xbf, 0x1b, 0x03, 0xe4, 0xf4, 0x03, 0x07, 0x59, 0xc5, 0xe9, 0x67, 0xc0, 0xc2, 0xb2, 0xf8, 0x2c, 0xd2, 0xeb, 0xcb, 0xaa, 0xb5, 0x6b, 0xd9, 0xc4, 0xe9, 0x69, 0xc0, 0x82, 0x2f, 0xfe, 0x50, 0x98, 0x4e, 0xf8, 0xb1, 0x02, 0x7b, 0xbe, 0x51, 0xe5, 0xf5, 0xe4, 0xe2, 0xf4, 0x31, 0x60, 0xa1, 0x2d, 0xbe, 0x56, 0x4f, 0x28, 0xa3, 0xb6, 0xda, 0x46, 0x96, 0xb5, 0x64, 0xe2, 0xac, 0x6f, 0xa0, 0xf9, 0x0c, 0x97, 0x6d, 0xe7, 0xd2, 0xd5, 0xba, 0xf4, 0x94, 0xd2, 0xa3, 0xb9, 0xf2, 0xf2, 0x93, 0xe8, 0xe2, 0xb4, 0x39, 0x0d, 0x21, 0xc2, 0x9b, 0x95, 0xb0, 0x1c, 0x30, 0x31, 0xbb, 0x78, 0x07, 0xe1, 0x3b, 0xe2, 0xb9, 0x90, 0x73, 0x8a, 0x47, 0x11, 0x96, 0x3b, 0xc8, 0x21, 0x5e, 0x0a, 0x7f, 0x27, 0x6b, 0xe1, 0xdf, 0x61, 0x11, 0x96, 0xcc, 0x4a, 0xc9, 0x1e, 0x15, 0xd6, 0x5e, 0x83, 0x10, 0xe1, 0xdb, 0xe2, 0x13, 0x9c, 0x53, 0x88, 0x27, 0x15, 0x96, 0x3b, 0x8b, 0x29, 0xde, 0x41, 0xf8, 0x96, 0xf4, 0xef, 0x9d, 0x63, 0x88, 0x97, 0xc2, 0xdf, 0xc8, 0x5a, 0x0c, 0x17, 0x96, 0x3b, 0xa5, 0x53, 0xef, 0x67, 0x9c, 0xb9, 0xab, 0x10, 0x22, 0x7c, 0x53, 0xfa, 0x45, 0xcb, 0x7d, 0xc4, 0x47, 0x15, 0x96, 0x3b, 0xef, 0x28, 0xee, 0xfe, 0x10, 0x1b, 0x67, 0x87, 0x45, 0x58, 0x72, 0x07, 0xf1, 0xbf, 0x8c, 0xd5, 0x62, 0x43, 0xd6, 0x4d, 0x9e, 0x31, 0x09, 0x7d, 0xc6, 0xdb, 0xa4, 0xf3, 0x09, 0xcb, 0x8e, 0x94, 0xe2, 0xdb, 0x6d, 0x56, 0x2d, 0xb5, 0x1b, 0xb2, 0x4e, 0xf6, 0xdc, 0x53, 0x7c, 0x3c, 0x61, 0xd9, 0xa1, 0x52, 0xfc, 0x45, 0xcb, 0xae, 0xd6, 0x4b, 0x77, 0x65, 0xde, 0x90, 0x3c, 0x1d, 0x32, 0xb9, 0x9c, 0xbb, 0x46, 0xbe, 0x10, 0xb8, 0xce, 0x15, 0xee, 0x20, 0xeb, 0x77, 0xce, 0x05, 0x2f, 0x04, 0x0c, 0x58, 0xe8, 0x12, 0xd1, 0xbf, 0x48, 0x2c, 0x88, 0xf9, 0x2e, 0x60, 0x69, 0xc0, 0x42, 0x9f, 0xc8, 0x2f, 0x8e, 0xa5, 0x01, 0x0b, 0x43, 0x22, 0x9f, 0x38, 0x96, 0x06, 0x2c, 0x68, 0xf1, 0x49, 0x1b, 0x40, 0x3d, 0xbd, 0x38, 0x4d, 0x0c, 0x58, 0xd0, 0x62, 0x93, 0x01, 0xa1, 0xbf, 0x80, 0xd2, 0x89, 0x23, 0x61, 0xc0, 0x82, 0x16, 0xd5, 0xb7, 0x66, 0x06, 0x3a, 0xf1, 0x6d, 0x6d, 0x02, 0xf5, 0xf8, 0xe2, 0x2c, 0x6a, 0xc0, 0x82, 0x16, 0x95, 0xb0, 0x7c, 0x80, 0x99, 0x90, 0x5f, 0x9c, 0xa6, 0x06, 0x2c, 0x68, 0xb1, 0x20, 0xdc, 0x10, 0x7f, 0xac, 0x2d, 0x40, 0x7d, 0xf8, 0x8e, 0xb3, 0x88, 0x01, 0x0b, 0x5a, 0x78, 0x85, 0xb3, 0x8a, 0x63, 0x69, 0xc0, 0x82, 0x16, 0xaa, 0x70, 0x16, 0x71, 0x2c, 0x0d, 0x58, 0xd0, 0x22, 0x58, 0xb8, 0x21, 0xfe, 0x44, 0x5b, 0x98, 0x7a, 0xf8, 0xa3, 0xc2, 0x60, 0x03, 0x16, 0xb4, 0xe8, 0x2c, 0x9c, 0x44, 0x1c, 0x4b, 0x03, 0x16, 0xb4, 0xe8, 0x2d, 0xdc, 0x10, 0x7f, 0xaa, 0x35, 0xa2, 0xee, 0xdf, 0x71, 0x8a, 0x06, 0x2c, 0x68, 0x31, 0x58, 0x38, 0x8a, 0x38, 0x96, 0xee, 0x5f, 0x55, 0x56, 0xb3, 0xa5, 0x1e, 0x4d, 0xb8, 0x21, 0xfe, 0x2c, 0xa0, 0xb7, 0xdb, 0xf1, 0x15, 0x99, 0xe7, 0xde, 0x06, 0xbc, 0x0b, 0x98, 0x14, 0x5d, 0x58, 0x04, 0xe8, 0xed, 0x7e, 0x01, 0x85, 0x88, 0x3f, 0x38, 0x9c, 0xc3, 0xe0, 0x53, 0x63, 0x0a, 0x77, 0x14, 0xff, 0x29, 0xd2, 0x67, 0x15, 0xe9, 0x64, 0x3b, 0x2c, 0xc2, 0x92, 0xf1, 0x70, 0x3b, 0xee, 0xde, 0x19, 0xfa, 0xe2, 0x17, 0x85, 0xc2, 0x3d, 0x1a, 0x6e, 0xe0, 0x8e, 0x67, 0x54, 0x36, 0xe1, 0x9a, 0xf8, 0x11, 0x5c, 0x7c, 0xe2, 0x2f, 0x65, 0x9c, 0x13, 0xbf, 0x08, 0xbb, 0x35, 0x71, 0xf7, 0x5e, 0xf1, 0x7a, 0x35, 0x20, 0xf3, 0x01, 0xbd, 0x9d, 0xf8, 0x43, 0xd8, 0x07, 0x89, 0x2f, 0x1c, 0xac, 0x3a, 0x95, 0x42, 0x7c, 0xb8, 0x70, 0x92, 0xe3, 0x4b, 0xe0, 0x3e, 0xa1, 0x1f, 0x8b, 0xa2, 0xf8, 0x21, 0xb5, 0xb1, 0x32, 0x4e, 0xe7, 0xe8, 0xed, 0x5e, 0x2f, 0xef, 0xc2, 0x1b, 0x9c, 0xf6, 0x9c, 0xcb, 0x01, 0x48, 0xaa, 0xb6, 0x3e, 0x0f, 0xf9, 0xdb, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXDragHandle[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x14, 0x08, 0x06, 0x00, 0x00, 0x01, 0x6d, 0xdf, 0x8a, 0x41, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x5b, 0x90, 0xfc, 0x00, 0x00, 0x00, 0x95, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x96, 0x51, 0x12, 0x00, 0x11, 0x08, 0x86, 0x73, 0xff, 0xeb, 0xac, 0xf3, 0xd9, 0xf6, 0xa1, 0x17, 0x9b, 0xc9, 0x43, 0x46, 0xf4, 0x1b, 0xc6, 0x20, 0x93, 0x3e, 0x45, 0x85, 0x94, 0xd2, 0x88, 0xb8, 0xae, 0x2e, 0xa5, 0x57, 0xa0, 0xa9, 0x65, 0xa1, 0x9f, 0x5c, 0xbf, 0x2f, 0xe5, 0x98, 0x61, 0xd5, 0x0f, 0x98, 0xd5, 0x52, 0xc2, 0x81, 0xd1, 0x37, 0x13, 0x98, 0x75, 0x7d, 0x2b, 0x34, 0x64, 0xfd, 0x66, 0x56, 0xba, 0x6d, 0xe1, 0x09, 0xca, 0xd5, 0x38, 0xf5, 0x55, 0xc7, 0x80, 0x59, 0x10, 0x38, 0x98, 0x80, 0x53, 0x70, 0x48, 0x0a, 0xf1, 0x1c, 0x8c, 0x02, 0x47, 0xdf, 0x44, 0xc0, 0xd9, 0x07, 0x63, 0x3e, 0xd4, 0x6c, 0xe4, 0x54, 0xae, 0xed, 0x0c, 0x43, 0x02, 0xd3, 0xea, 0x11, 0xb8, 0x9b, 0x9c, 0x1f, 0x6a, 0x41, 0x20, 0x0b, 0x81, 0xe0, 0x6f, 0x60, 0xcc, 0x8f, 0x23, 0x8b, 0x73, 0xc0, 0x4e, 0x10, 0x00, 0x01, 0x10, 0xb0, 0x08, 0x04, 0xff, 0x44, 0x86, 0x89, 0xf4, 0x0b, 0xbf, 0x81, 0xca, 0xc9, 0x10, 0xe6, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXDragHandle2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x14, 0x08, 0x06, 0x00, 0x00, 0x01, 0x6d, 0xdf, 0x8a, 0x41, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x5b, 0x90, 0xfc, 0x00, 0x00, 0x00, 0x95, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x96, 0x51, 0x12, 0x00, 0x11, 0x08, 0x86, 0x73, 0xff, 0xeb, 0xac, 0xf3, 0xd9, 0xf6, 0xa1, 0x17, 0x9b, 0xc9, 0x43, 0x46, 0xf4, 0x1b, 0xc6, 0x20, 0x93, 0x3e, 0x45, 0x85, 0x94, 0xd2, 0x88, 0xb8, 0xae, 0x2e, 0xa5, 0x57, 0xa0, 0xa9, 0x65, 0xa1, 0x9f, 0x5c, 0xbf, 0x2f, 0xe5, 0x98, 0x61, 0xd5, 0x0f, 0x98, 0xd5, 0x52, 0xc2, 0x81, 0xd1, 0x37, 0x13, 0x98, 0x75, 0x7d, 0x2b, 0x34, 0x64, 0xfd, 0x66, 0x56, 0xba, 0x6d, 0xe1, 0x09, 0xca, 0xd5, 0x38, 0xf5, 0x55, 0xc7, 0x80, 0x59, 0x10, 0x38, 0x98, 0x80, 0x53, 0x70, 0x48, 0x0a, 0xf1, 0x1c, 0x8c, 0x02, 0x47, 0xdf, 0x44, 0xc0, 0xd9, 0x07, 0x63, 0x3e, 0xd4, 0x6c, 0xe4, 0x54, 0xae, 0xed, 0x0c, 0x43, 0x02, 0xd3, 0xea, 0x11, 0xb8, 0x9b, 0x9c, 0x1f, 0x6a, 0x41, 0x20, 0x0b, 0x81, 0xe0, 0x6f, 0x60, 0xcc, 0x8f, 0x23, 0x8b, 0x73, 0xc0, 0x4e, 0x10, 0x00, 0x01, 0x10, 0xb0, 0x08, 0x04, 0xff, 0x44, 0x86, 0x89, 0xf4, 0x0b, 0xbf, 0x81, 0xca, 0xc9, 0x10, 0xe6, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXDragHandle3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x1e, 0x08, 0x06, 0x00, 0x00, 0x01, 0x9d, 0xd0, 0x9a, 0x44, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x6c, 0x42, 0x31, 0x00, 0x00, 0x01, 0x8f, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9a, 0x4d, 0x4e, 0xc3, 0x30, 0x10, 0x46, 0x5d, 0x84, 0xca, 0x06, 0x4e, 0x01, 0xbd, 0x0b, 0x27, 0x60, 0xc7, 0x41, 0xd8, 0x65, 0xc3, 0x49, 0xd8, 0x71, 0x25, 0xe8, 0x29, 0xca, 0xa6, 0x2b, 0xf8, 0xbc, 0xa8, 0xd4, 0x4a, 0x1e, 0xb7, 0x8c, 0xd3, 0xb1, 0x62, 0x3d, 0x2b, 0x51, 0x5a, 0x3b, 0xdf, 0xfc, 0x3c, 0x3b, 0x71, 0x9a, 0x7a, 0x95, 0x2a, 0xe5, 0x37, 0x25, 0x6d, 0xe5, 0xb2, 0x4a, 0x49, 0xdb, 0x0c, 0xa5, 0x6a, 0x45, 0xee, 0x27, 0xcb, 0x87, 0x84, 0x66, 0x9b, 0xa5, 0x29, 0xd6, 0x9b, 0x11, 0xc8, 0xfb, 0x5e, 0x8a, 0x75, 0x51, 0xa5, 0x4a, 0x09, 0x4d, 0xad, 0xa5, 0x29, 0xd6, 0x9b, 0x56, 0x14, 0xc1, 0x9b, 0x14, 0x77, 0x45, 0x95, 0x2a, 0x25, 0x9c, 0xac, 0x36, 0xea, 0x97, 0x45, 0xc0, 0x1c, 0x04, 0xb5, 0x34, 0x34, 0x40, 0xb4, 0xf9, 0x8a, 0x1c, 0xba, 0x7c, 0x9e, 0x78, 0xcb, 0x01, 0x38, 0xf7, 0xf7, 0x13, 0x43, 0x7c, 0xe9, 0x41, 0xc0, 0x35, 0x00, 0xd4, 0xdf, 0x93, 0x33, 0xd8, 0x0f, 0x39, 0xdc, 0x3a, 0xb5, 0x03, 0xc9, 0xbc, 0xd8, 0xbd, 0xd7, 0xfa, 0x8f, 0x1c, 0x3e, 0x84, 0xf3, 0x53, 0xb4, 0x7b, 0xe7, 0xbd, 0xc1, 0x7b, 0x4f, 0xc9, 0x3a, 0xee, 0x2b, 0xe1, 0x3d, 0xbd, 0x04, 0x87, 0xae, 0x6b, 0xce, 0x93, 0x58, 0x1e, 0xf2, 0x1e, 0x5d, 0xa3, 0xe6, 0x5b, 0x09, 0x6e, 0x1a, 0x6d, 0x20, 0x87, 0x00, 0x04, 0xfe, 0x49, 0x20, 0x4f, 0x39, 0xf9, 0x9a, 0x0f, 0xde, 0xf3, 0x4f, 0xd3, 0x6a, 0xb9, 0xa9, 0xb6, 0xd2, 0x08, 0x01, 0x08, 0x40, 0x00, 0x02, 0x63, 0x10, 0x08, 0x79, 0xbc, 0xd5, 0x14, 0xf7, 0x25, 0x5c, 0x4f, 0xd1, 0xc8, 0x94, 0x5c, 0x35, 0xbf, 0xdb, 0xa0, 0x80, 0x9e, 0xe5, 0xe7, 0x35, 0xc8, 0xd7, 0xc1, 0xcd, 0xd9, 0x79, 0xfe, 0x70, 0x22, 0x47, 0x08, 0x40, 0x00, 0x02, 0xc3, 0x13, 0xa8, 0x4e, 0x05, 0x73, 0x65, 0xaf, 0xa9, 0x6e, 0x27, 0x5b, 0xf7, 0x73, 0xd9, 0xbb, 0xd4, 0xce, 0xb9, 0xa9, 0x2e, 0x24, 0xf9, 0x4b, 0x83, 0x6d, 0x3d, 0xaf, 0x17, 0xe4, 0xd6, 0xb8, 0x9d, 0xfa, 0x3e, 0xaf, 0xe1, 0x9d, 0xc1, 0x22, 0x83, 0x00, 0x04, 0x20, 0x00, 0x01, 0x08, 0x40, 0x00, 0x02, 0x10, 0xb8, 0x22, 0x81, 0xd1, 0x9e, 0xea, 0x1f, 0xc5, 0xea, 0x45, 0xbb, 0xb9, 0x72, 0xf1, 0x8a, 0x2c, 0x23, 0x4d, 0xe7, 0x97, 0x73, 0x9f, 0xea, 0x3c, 0xf7, 0x9a, 0xa6, 0xd1, 0x3a, 0xbe, 0xcb, 0xeb, 0xe1, 0xc8, 0x1e, 0x3f, 0xf2, 0xc5, 0x8a, 0x8b, 0x23, 0x18, 0x7c, 0x84, 0x00, 0x04, 0x20, 0x00, 0x01, 0x08, 0x40, 0x00, 0x02, 0x10, 0x18, 0x85, 0x80, 0xfe, 0xa4, 0xc9, 0xeb, 0x1b, 0x7b, 0x2c, 0xe5, 0x0f, 0x5f, 0x53, 0x99, 0x73, 0x6d, 0xe9, 0x37, 0xd6, 0x5c, 0xb6, 0xd0, 0x5b, 0xb0, 0xf6, 0x0f, 0x6b, 0x93, 0x2d, 0x3b, 0x32, 0x91, 0xf4, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXGlobalsIcon[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x26, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa8, 0x3d, 0xe9, 0xae, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x84, 0xf2, 0x20, 0x5b, 0x00, 0x00, 0x03, 0x06, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xcd, 0x98, 0xc9, 0x6b, 0x14, 0x41, 0x14, 0x87, 0x33, 0x31, 0x8a, 0x22, 0x44, 0x24, 0x0b, 0x6a, 0xc0, 0x10, 0x24, 0x2e, 0x88, 0x42, 0x84, 0x88, 0x10, 0x24, 0x44, 0x3d, 0x84, 0x78, 0x50, 0x10, 0xd4, 0xa3, 0x20, 0xb8, 0xfc, 0x05, 0x1e, 0x24, 0x10, 0x15, 0x0f, 0x41, 0x51, 0x24, 0x9e, 0x3d, 0xe8, 0xc1, 0x8b, 0x22, 0xb8, 0x5d, 0x3c, 0x0c, 0x2a, 0xc4, 0x25, 0x68, 0x04, 0x15, 0xcd, 0x41, 0xdc, 0x0e, 0x8a, 0x5b, 0xa2, 0x12, 0xa2, 0x66, 0x99, 0x7c, 0x6f, 0x98, 0x32, 0x8f, 0xb2, 0x6b, 0xba, 0x27, 0xa9, 0x6a, 0xfc, 0xc1, 0x47, 0xbd, 0xae, 0xaa, 0x79, 0xef, 0x37, 0xd3, 0xdd, 0xd5, 0xd5, 0x53, 0x56, 0xf6, 0x9f, 0x2a, 0x13, 0xda, 0x57, 0x2e, 0x97, 0xab, 0xa7, 0xc6, 0x36, 0x58, 0x05, 0x2b, 0x61, 0x3e, 0xf4, 0xc1, 0x03, 0xb8, 0x9d, 0xc9, 0x64, 0xde, 0xd2, 0xa6, 0x27, 0x0c, 0xcd, 0x86, 0x43, 0x30, 0x0c, 0x2e, 0xfd, 0x61, 0xa0, 0x13, 0x2a, 0x52, 0x71, 0x46, 0xa1, 0x3a, 0x78, 0x06, 0x49, 0xf5, 0x88, 0x89, 0x2b, 0x82, 0x9a, 0xa3, 0xc0, 0x3c, 0xe8, 0x4b, 0xea, 0x48, 0xcd, 0x7b, 0x43, 0xbc, 0x30, 0x98, 0x39, 0x92, 0x77, 0xab, 0x62, 0x71, 0xe1, 0x20, 0x13, 0x2e, 0xc3, 0x41, 0x68, 0x0c, 0x69, 0xaa, 0x92, 0x02, 0xdf, 0xa1, 0x98, 0xc6, 0x18, 0x3c, 0x0b, 0xeb, 0xa1, 0x3c, 0x98, 0x19, 0x9d, 0x98, 0x42, 0x3b, 0x21, 0x4e, 0x27, 0xf4, 0x67, 0x5c, 0xb1, 0x6f, 0xc7, 0xcd, 0xae, 0x42, 0xaa, 0xff, 0x9d, 0x8a, 0x9d, 0xa1, 0x6f, 0x63, 0x4b, 0x9d, 0x95, 0xa6, 0x06, 0xaa, 0xa6, 0x42, 0x77, 0xe4, 0xdb, 0x58, 0x75, 0x44, 0xa9, 0x51, 0xfa, 0x1e, 0xc2, 0x58, 0x61, 0xac, 0x3d, 0x62, 0x4e, 0xd8, 0x2e, 0x2e, 0xae, 0xe7, 0xea, 0x02, 0xbb, 0x44, 0xdc, 0x01, 0xab, 0x61, 0x2f, 0xfc, 0x04, 0xd1, 0x38, 0xfc, 0xf3, 0x05, 0xe8, 0x6b, 0x80, 0x7d, 0x70, 0x01, 0xca, 0x7d, 0xaf, 0xb8, 0x8b, 0xd5, 0x57, 0xaf, 0x23, 0x3e, 0x03, 0xf2, 0x08, 0xba, 0x05, 0xf2, 0x18, 0x6a, 0x03, 0x39, 0x4b, 0xed, 0x14, 0xff, 0x40, 0x2b, 0x8f, 0xa8, 0x35, 0xb0, 0x05, 0x96, 0x81, 0x28, 0xcb, 0x63, 0x6a, 0xc2, 0x9b, 0x31, 0x0a, 0xcd, 0x25, 0xa9, 0x5e, 0x20, 0xe7, 0x70, 0xbc, 0x1b, 0x16, 0xc0, 0x66, 0x68, 0x05, 0xa3, 0x0e, 0x82, 0x5e, 0xe8, 0x31, 0x1d, 0xaa, 0xcd, 0xaa, 0x78, 0xe6, 0x21, 0xc6, 0x5a, 0x41, 0x6b, 0x82, 0x83, 0x1f, 0xd0, 0x0b, 0x47, 0x61, 0x0f, 0x18, 0x7d, 0x25, 0x68, 0x34, 0x07, 0xaa, 0xfd, 0x48, 0x5c, 0x33, 0x73, 0x37, 0x2a, 0x03, 0x09, 0xcf, 0xab, 0x02, 0x26, 0xdc, 0x4e, 0x20, 0xd7, 0xd7, 0x45, 0xf8, 0x64, 0x3a, 0x0b, 0x6d, 0x0b, 0xed, 0x0b, 0xab, 0x4f, 0x7e, 0x49, 0x7f, 0x22, 0xf9, 0x56, 0x90, 0x5f, 0xa8, 0x14, 0x1d, 0x67, 0xf2, 0x49, 0xf5, 0x81, 0x63, 0xfe, 0x1c, 0x91, 0x89, 0xc4, 0x1b, 0x40, 0x4e, 0x4d, 0xa9, 0xea, 0xe7, 0x03, 0x6d, 0x30, 0x04, 0x3b, 0x7c, 0x9b, 0xda, 0x45, 0xd2, 0x11, 0x98, 0x8e, 0xe4, 0xcb, 0xd4, 0x40, 0x43, 0x94, 0xa9, 0x69, 0xef, 0x60, 0x49, 0xd8, 0x49, 0xc2, 0x23, 0x10, 0x97, 0xe3, 0x31, 0x73, 0xee, 0xc1, 0x53, 0x18, 0x84, 0x61, 0x18, 0x80, 0x57, 0x2c, 0x0b, 0x39, 0x5a, 0x3f, 0xc2, 0x50, 0x35, 0x5c, 0x81, 0x38, 0x7d, 0x61, 0x82, 0xdf, 0x8b, 0xd9, 0xf5, 0x15, 0x28, 0x94, 0x5f, 0x18, 0xe3, 0x1c, 0x31, 0xfe, 0x19, 0x96, 0xbb, 0xf2, 0x78, 0xeb, 0xa7, 0x88, 0xec, 0x4a, 0x7b, 0x20, 0x89, 0x7e, 0x31, 0xa9, 0xc5, 0x5b, 0x71, 0x57, 0x22, 0x8a, 0x34, 0xc3, 0xcb, 0x24, 0x8e, 0x98, 0x23, 0x4b, 0x86, 0xac, 0xf6, 0xe1, 0x44, 0x81, 0x0a, 0xe8, 0x82, 0x51, 0x48, 0xaa, 0xc3, 0xe1, 0x1c, 0x91, 0x19, 0x17, 0x8b, 0x40, 0x1e, 0x25, 0xa5, 0xe8, 0x7a, 0x68, 0x53, 0xeb, 0x70, 0xf3, 0xbe, 0x14, 0x47, 0xcc, 0x95, 0x67, 0x5c, 0x6d, 0x30, 0x63, 0x24, 0x97, 0xfd, 0x53, 0xb1, 0x17, 0x54, 0x86, 0x23, 0x15, 0x6e, 0xf3, 0x47, 0xb9, 0x7a, 0x90, 0xd7, 0x29, 0xd1, 0x6f, 0xb8, 0x0a, 0xe7, 0x60, 0x00, 0x8a, 0x49, 0xf6, 0x5c, 0xe1, 0x44, 0xe5, 0x6c, 0xa1, 0xba, 0xac, 0x41, 0xb2, 0x79, 0xcb, 0x8b, 0x58, 0x6e, 0x82, 0x53, 0x85, 0x31, 0xbb, 0x91, 0x1d, 0xab, 0xec, 0xc3, 0xc2, 0x88, 0xe4, 0x72, 0xb1, 0x1b, 0xed, 0xb7, 0xab, 0x30, 0x30, 0x0b, 0xec, 0x25, 0x43, 0xfe, 0x77, 0x68, 0xb2, 0xe7, 0xfa, 0x3a, 0x36, 0x2f, 0x23, 0x1b, 0x55, 0xc2, 0x3b, 0x2a, 0xce, 0x87, 0x3c, 0xd3, 0xc6, 0x09, 0xee, 0x5b, 0xfd, 0x5d, 0xf4, 0xf7, 0x5b, 0x7d, 0xde, 0x0e, 0x8d, 0xb1, 0x21, 0x95, 0xd1, 0x75, 0x77, 0xc9, 0x1e, 0xde, 0x48, 0xcc, 0x77, 0x9b, 0x83, 0x60, 0x2d, 0xa7, 0xa4, 0x16, 0xcc, 0x46, 0xef, 0x9a, 0x5d, 0x88, 0x31, 0x59, 0xfd, 0xe5, 0xed, 0x46, 0xf4, 0x1a, 0xfc, 0x6e, 0x7f, 0xed, 0x82, 0xfa, 0x98, 0x62, 0xa7, 0xc1, 0xe8, 0x06, 0xc1, 0x26, 0x90, 0x57, 0xaf, 0x03, 0x60, 0x36, 0x82, 0xf2, 0x0a, 0xf6, 0xf7, 0xc6, 0xd0, 0x9f, 0x0f, 0x16, 0x53, 0x50, 0x1e, 0xd4, 0x4f, 0xc0, 0x25, 0xb9, 0x5b, 0xe5, 0x35, 0x2b, 0x7d, 0x51, 0x58, 0xcc, 0xc9, 0x2f, 0x67, 0x4e, 0x2b, 0x61, 0xfe, 0x14, 0xde, 0xa4, 0x5d, 0x92, 0xa6, 0xa3, 0xc8, 0xdd, 0x27, 0x26, 0xaa, 0x30, 0xb1, 0x16, 0x2a, 0xe1, 0x2e, 0x77, 0xdf, 0xb7, 0x34, 0x4d, 0x49, 0xad, 0x49, 0x44, 0x7e, 0xb1, 0xbc, 0xea, 0x50, 0x60, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXGlobalsIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x26, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa8, 0x3d, 0xe9, 0xae, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x84, 0xf2, 0x20, 0x5b, 0x00, 0x00, 0x03, 0x06, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xcd, 0x98, 0xc9, 0x6b, 0x14, 0x41, 0x14, 0x87, 0x33, 0x31, 0x8a, 0x22, 0x44, 0x24, 0x0b, 0x6a, 0xc0, 0x10, 0x24, 0x2e, 0x88, 0x42, 0x84, 0x88, 0x10, 0x24, 0x44, 0x3d, 0x84, 0x78, 0x50, 0x10, 0xd4, 0xa3, 0x20, 0xb8, 0xfc, 0x05, 0x1e, 0x24, 0x10, 0x15, 0x0f, 0x41, 0x51, 0x24, 0x9e, 0x3d, 0xe8, 0xc1, 0x8b, 0x22, 0xb8, 0x5d, 0x3c, 0x0c, 0x2a, 0xc4, 0x25, 0x68, 0x04, 0x15, 0xcd, 0x41, 0xdc, 0x0e, 0x8a, 0x5b, 0xa2, 0x12, 0xa2, 0x66, 0x99, 0x7c, 0x6f, 0x98, 0x32, 0x8f, 0xb2, 0x6b, 0xba, 0x27, 0xa9, 0x6a, 0xfc, 0xc1, 0x47, 0xbd, 0xae, 0xaa, 0x79, 0xef, 0x37, 0xd3, 0xdd, 0xd5, 0xd5, 0x53, 0x56, 0xf6, 0x9f, 0x2a, 0x13, 0xda, 0x57, 0x2e, 0x97, 0xab, 0xa7, 0xc6, 0x36, 0x58, 0x05, 0x2b, 0x61, 0x3e, 0xf4, 0xc1, 0x03, 0xb8, 0x9d, 0xc9, 0x64, 0xde, 0xd2, 0xa6, 0x27, 0x0c, 0xcd, 0x86, 0x43, 0x30, 0x0c, 0x2e, 0xfd, 0x61, 0xa0, 0x13, 0x2a, 0x52, 0x71, 0x46, 0xa1, 0x3a, 0x78, 0x06, 0x49, 0xf5, 0x88, 0x89, 0x2b, 0x82, 0x9a, 0xa3, 0xc0, 0x3c, 0xe8, 0x4b, 0xea, 0x48, 0xcd, 0x7b, 0x43, 0xbc, 0x30, 0x98, 0x39, 0x92, 0x77, 0xab, 0x62, 0x71, 0xe1, 0x20, 0x13, 0x2e, 0xc3, 0x41, 0x68, 0x0c, 0x69, 0xaa, 0x92, 0x02, 0xdf, 0xa1, 0x98, 0xc6, 0x18, 0x3c, 0x0b, 0xeb, 0xa1, 0x3c, 0x98, 0x19, 0x9d, 0x98, 0x42, 0x3b, 0x21, 0x4e, 0x27, 0xf4, 0x67, 0x5c, 0xb1, 0x6f, 0xc7, 0xcd, 0xae, 0x42, 0xaa, 0xff, 0x9d, 0x8a, 0x9d, 0xa1, 0x6f, 0x63, 0x4b, 0x9d, 0x95, 0xa6, 0x06, 0xaa, 0xa6, 0x42, 0x77, 0xe4, 0xdb, 0x58, 0x75, 0x44, 0xa9, 0x51, 0xfa, 0x1e, 0xc2, 0x58, 0x61, 0xac, 0x3d, 0x62, 0x4e, 0xd8, 0x2e, 0x2e, 0xae, 0xe7, 0xea, 0x02, 0xbb, 0x44, 0xdc, 0x01, 0xab, 0x61, 0x2f, 0xfc, 0x04, 0xd1, 0x38, 0xfc, 0xf3, 0x05, 0xe8, 0x6b, 0x80, 0x7d, 0x70, 0x01, 0xca, 0x7d, 0xaf, 0xb8, 0x8b, 0xd5, 0x57, 0xaf, 0x23, 0x3e, 0x03, 0xf2, 0x08, 0xba, 0x05, 0xf2, 0x18, 0x6a, 0x03, 0x39, 0x4b, 0xed, 0x14, 0xff, 0x40, 0x2b, 0x8f, 0xa8, 0x35, 0xb0, 0x05, 0x96, 0x81, 0x28, 0xcb, 0x63, 0x6a, 0xc2, 0x9b, 0x31, 0x0a, 0xcd, 0x25, 0xa9, 0x5e, 0x20, 0xe7, 0x70, 0xbc, 0x1b, 0x16, 0xc0, 0x66, 0x68, 0x05, 0xa3, 0x0e, 0x82, 0x5e, 0xe8, 0x31, 0x1d, 0xaa, 0xcd, 0xaa, 0x78, 0xe6, 0x21, 0xc6, 0x5a, 0x41, 0x6b, 0x82, 0x83, 0x1f, 0xd0, 0x0b, 0x47, 0x61, 0x0f, 0x18, 0x7d, 0x25, 0x68, 0x34, 0x07, 0xaa, 0xfd, 0x48, 0x5c, 0x33, 0x73, 0x37, 0x2a, 0x03, 0x09, 0xcf, 0xab, 0x02, 0x26, 0xdc, 0x4e, 0x20, 0xd7, 0xd7, 0x45, 0xf8, 0x64, 0x3a, 0x0b, 0x6d, 0x0b, 0xed, 0x0b, 0xab, 0x4f, 0x7e, 0x49, 0x7f, 0x22, 0xf9, 0x56, 0x90, 0x5f, 0xa8, 0x14, 0x1d, 0x67, 0xf2, 0x49, 0xf5, 0x81, 0x63, 0xfe, 0x1c, 0x91, 0x89, 0xc4, 0x1b, 0x40, 0x4e, 0x4d, 0xa9, 0xea, 0xe7, 0x03, 0x6d, 0x30, 0x04, 0x3b, 0x7c, 0x9b, 0xda, 0x45, 0xd2, 0x11, 0x98, 0x8e, 0xe4, 0xcb, 0xd4, 0x40, 0x43, 0x94, 0xa9, 0x69, 0xef, 0x60, 0x49, 0xd8, 0x49, 0xc2, 0x23, 0x10, 0x97, 0xe3, 0x31, 0x73, 0xee, 0xc1, 0x53, 0x18, 0x84, 0x61, 0x18, 0x80, 0x57, 0x2c, 0x0b, 0x39, 0x5a, 0x3f, 0xc2, 0x50, 0x35, 0x5c, 0x81, 0x38, 0x7d, 0x61, 0x82, 0xdf, 0x8b, 0xd9, 0xf5, 0x15, 0x28, 0x94, 0x5f, 0x18, 0xe3, 0x1c, 0x31, 0xfe, 0x19, 0x96, 0xbb, 0xf2, 0x78, 0xeb, 0xa7, 0x88, 0xec, 0x4a, 0x7b, 0x20, 0x89, 0x7e, 0x31, 0xa9, 0xc5, 0x5b, 0x71, 0x57, 0x22, 0x8a, 0x34, 0xc3, 0xcb, 0x24, 0x8e, 0x98, 0x23, 0x4b, 0x86, 0xac, 0xf6, 0xe1, 0x44, 0x81, 0x0a, 0xe8, 0x82, 0x51, 0x48, 0xaa, 0xc3, 0xe1, 0x1c, 0x91, 0x19, 0x17, 0x8b, 0x40, 0x1e, 0x25, 0xa5, 0xe8, 0x7a, 0x68, 0x53, 0xeb, 0x70, 0xf3, 0xbe, 0x14, 0x47, 0xcc, 0x95, 0x67, 0x5c, 0x6d, 0x30, 0x63, 0x24, 0x97, 0xfd, 0x53, 0xb1, 0x17, 0x54, 0x86, 0x23, 0x15, 0x6e, 0xf3, 0x47, 0xb9, 0x7a, 0x90, 0xd7, 0x29, 0xd1, 0x6f, 0xb8, 0x0a, 0xe7, 0x60, 0x00, 0x8a, 0x49, 0xf6, 0x5c, 0xe1, 0x44, 0xe5, 0x6c, 0xa1, 0xba, 0xac, 0x41, 0xb2, 0x79, 0xcb, 0x8b, 0x58, 0x6e, 0x82, 0x53, 0x85, 0x31, 0xbb, 0x91, 0x1d, 0xab, 0xec, 0xc3, 0xc2, 0x88, 0xe4, 0x72, 0xb1, 0x1b, 0xed, 0xb7, 0xab, 0x30, 0x30, 0x0b, 0xec, 0x25, 0x43, 0xfe, 0x77, 0x68, 0xb2, 0xe7, 0xfa, 0x3a, 0x36, 0x2f, 0x23, 0x1b, 0x55, 0xc2, 0x3b, 0x2a, 0xce, 0x87, 0x3c, 0xd3, 0xc6, 0x09, 0xee, 0x5b, 0xfd, 0x5d, 0xf4, 0xf7, 0x5b, 0x7d, 0xde, 0x0e, 0x8d, 0xb1, 0x21, 0x95, 0xd1, 0x75, 0x77, 0xc9, 0x1e, 0xde, 0x48, 0xcc, 0x77, 0x9b, 0x83, 0x60, 0x2d, 0xa7, 0xa4, 0x16, 0xcc, 0x46, 0xef, 0x9a, 0x5d, 0x88, 0x31, 0x59, 0xfd, 0xe5, 0xed, 0x46, 0xf4, 0x1a, 0xfc, 0x6e, 0x7f, 0xed, 0x82, 0xfa, 0x98, 0x62, 0xa7, 0xc1, 0xe8, 0x06, 0xc1, 0x26, 0x90, 0x57, 0xaf, 0x03, 0x60, 0x36, 0x82, 0xf2, 0x0a, 0xf6, 0xf7, 0xc6, 0xd0, 0x9f, 0x0f, 0x16, 0x53, 0x50, 0x1e, 0xd4, 0x4f, 0xc0, 0x25, 0xb9, 0x5b, 0xe5, 0x35, 0x2b, 0x7d, 0x51, 0x58, 0xcc, 0xc9, 0x2f, 0x67, 0x4e, 0x2b, 0x61, 0xfe, 0x14, 0xde, 0xa4, 0x5d, 0x92, 0xa6, 0xa3, 0xc8, 0xdd, 0x27, 0x26, 0xaa, 0x30, 0xb1, 0x16, 0x2a, 0xe1, 0x2e, 0x77, 0xdf, 0xb7, 0x34, 0x4d, 0x49, 0xad, 0x49, 0x44, 0x7e, 0xb1, 0xbc, 0xea, 0x50, 0x60, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXGlobalsIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x08, 0x06, 0x00, 0x00, 0x00, 0x8c, 0x18, 0x83, 0x85, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x91, 0x8a, 0xa0, 0x00, 0x00, 0x04, 0xf1, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xe5, 0x9a, 0x6b, 0x88, 0x16, 0x55, 0x1c, 0xc6, 0xf7, 0xf5, 0xb2, 0xa2, 0x82, 0x4a, 0x79, 0x43, 0x51, 0x89, 0x4c, 0x45, 0x83, 0x0c, 0x51, 0x04, 0x2f, 0xa9, 0x6c, 0x45, 0x42, 0x5e, 0x82, 0x54, 0xf0, 0x83, 0x20, 0xa8, 0x81, 0x81, 0x10, 0x42, 0x20, 0x91, 0x90, 0x12, 0x7d, 0x49, 0x08, 0xfa, 0x20, 0xda, 0x07, 0x05, 0xf1, 0x82, 0x60, 0x18, 0x4a, 0x65, 0x42, 0xfa, 0x41, 0xb7, 0x22, 0x96, 0x50, 0x50, 0x5c, 0xf2, 0xb2, 0x25, 0xab, 0x99, 0xe5, 0xad, 0x52, 0x33, 0x77, 0xfb, 0x3d, 0xaf, 0x33, 0xcb, 0xd9, 0xd3, 0xcc, 0xbc, 0xaf, 0x67, 0xe6, 0x9d, 0x39, 0x2f, 0xfd, 0xe1, 0xc7, 0x9c, 0xf3, 0x9f, 0x73, 0xe6, 0x3c, 0xcf, 0xcc, 0xce, 0x7b, 0xce, 0xcc, 0x6c, 0x43, 0xc3, 0xff, 0x20, 0x4a, 0x3e, 0x7b, 0xec, 0xec, 0xec, 0x1c, 0x88, 0xbe, 0x51, 0x30, 0x08, 0x1a, 0xe1, 0x67, 0x51, 0x2a, 0x95, 0xee, 0xb1, 0xad, 0xdf, 0xc0, 0xd8, 0x28, 0x78, 0x1f, 0xbe, 0x87, 0x87, 0x60, 0x47, 0x07, 0x89, 0x66, 0x78, 0x1b, 0x46, 0xd7, 0x95, 0x53, 0x04, 0x3f, 0x09, 0x9f, 0xc0, 0xdf, 0x50, 0x6d, 0xdc, 0xa7, 0xe1, 0x47, 0x30, 0xc4, 0x7b, 0xb3, 0x88, 0x9c, 0x06, 0x6d, 0xe0, 0x1a, 0xd7, 0xe8, 0x38, 0xdb, 0x5b, 0xa3, 0x88, 0x9b, 0x05, 0xba, 0x22, 0x69, 0x43, 0x7f, 0x01, 0x2b, 0xbc, 0x33, 0x8a, 0xa8, 0x31, 0xa0, 0xab, 0x90, 0x55, 0x3c, 0xe0, 0x40, 0x73, 0x6c, 0xa3, 0x3d, 0xec, 0x44, 0x5e, 0x75, 0xc4, 0xf4, 0x65, 0xac, 0x83, 0x90, 0xd5, 0xfd, 0xf4, 0x90, 0x63, 0xb5, 0xc0, 0x64, 0xe8, 0x16, 0xbd, 0xba, 0xd5, 0xf2, 0xad, 0xac, 0x62, 0xb8, 0xe7, 0x52, 0x0e, 0xd9, 0x4a, 0xff, 0xa3, 0x01, 0x5f, 0x33, 0xb5, 0xdc, 0x4c, 0x79, 0xbc, 0xec, 0xba, 0x73, 0x15, 0x7b, 0xc2, 0x45, 0x70, 0x89, 0xe3, 0x74, 0x5a, 0x09, 0x7e, 0x4f, 0x1f, 0x08, 0x5c, 0xe4, 0xe0, 0x4e, 0x73, 0xe6, 0x62, 0x97, 0x53, 0x5d, 0xd4, 0x3d, 0xf9, 0x82, 0x83, 0xd8, 0x5d, 0xfc, 0x39, 0x7e, 0xea, 0xd0, 0xaf, 0xa1, 0x28, 0x93, 0x53, 0x1d, 0xc4, 0x9e, 0x76, 0xe8, 0x53, 0xee, 0x52, 0x94, 0xc9, 0x49, 0x0e, 0x82, 0xb5, 0x86, 0x75, 0x8a, 0xdc, 0x4d, 0x72, 0x5f, 0xe9, 0x17, 0x5d, 0x0b, 0xee, 0xc7, 0x0d, 0xe7, 0x15, 0x4d, 0xee, 0x26, 0x71, 0xf6, 0xc4, 0xe3, 0xba, 0x0b, 0xda, 0x4f, 0xe6, 0x04, 0x8d, 0x70, 0xe9, 0x5b, 0x84, 0xc9, 0xa4, 0xab, 0x78, 0x09, 0x13, 0xdb, 0x61, 0x29, 0xc8, 0xd0, 0x31, 0x30, 0x63, 0xbe, 0x59, 0x49, 0x2a, 0x73, 0x42, 0x7a, 0xc0, 0xb0, 0xa4, 0x36, 0x35, 0xdb, 0xc7, 0xc0, 0x53, 0xc0, 0x8e, 0x8f, 0x49, 0x3c, 0xad, 0x41, 0xd9, 0x8e, 0x06, 0xcd, 0x83, 0xbb, 0xc1, 0x7e, 0x22, 0x39, 0x90, 0x24, 0x8c, 0xf6, 0x4f, 0xc1, 0x6a, 0xd8, 0x0f, 0xbf, 0xc1, 0x7b, 0x6a, 0x5f, 0xc4, 0x8a, 0x27, 0xea, 0x4a, 0x4e, 0x44, 0xcb, 0x5b, 0x88, 0x6a, 0x62, 0x3b, 0x4e, 0xc2, 0x88, 0x36, 0xb8, 0x08, 0x61, 0x5d, 0xb9, 0x26, 0xda, 0xf4, 0x66, 0x2b, 0xee, 0xc2, 0x50, 0x98, 0x09, 0xea, 0xf7, 0x22, 0x94, 0x4f, 0x14, 0xdb, 0x30, 0xce, 0x85, 0x85, 0x5c, 0xb7, 0x88, 0x5c, 0x05, 0x51, 0x71, 0x8b, 0xe4, 0x41, 0x58, 0x0b, 0x65, 0x63, 0x6c, 0xa7, 0x83, 0x1d, 0xf3, 0x48, 0x2c, 0x87, 0x7f, 0xec, 0x1d, 0x56, 0x5d, 0xfb, 0x75, 0x12, 0x0a, 0x99, 0x27, 0x9f, 0x89, 0x38, 0xab, 0xbf, 0x92, 0x1b, 0x0e, 0x7a, 0x54, 0x6a, 0x87, 0x75, 0x08, 0xd4, 0x55, 0x68, 0x06, 0x3b, 0x74, 0x5f, 0x7e, 0x01, 0x95, 0x5e, 0xdd, 0x7c, 0xc5, 0xe2, 0xe1, 0x9a, 0xdd, 0x39, 0x97, 0x3a, 0xe2, 0xf5, 0xea, 0x22, 0x2a, 0x7e, 0x24, 0x19, 0x5e, 0x9d, 0xdb, 0x94, 0x3f, 0x03, 0x99, 0x3d, 0x0c, 0x66, 0x9c, 0x95, 0x50, 0x12, 0x27, 0xcc, 0x64, 0x44, 0x79, 0x56, 0x2e, 0x86, 0xec, 0x41, 0x10, 0x32, 0x2e, 0x42, 0x4c, 0x98, 0xfa, 0x89, 0xc2, 0x46, 0x98, 0x01, 0xbd, 0x60, 0x10, 0x2c, 0x86, 0x56, 0xb0, 0x43, 0x3f, 0x30, 0x1b, 0xec, 0xa4, 0x51, 0xdf, 0x67, 0x8f, 0x9d, 0x5b, 0x1d, 0x11, 0x5b, 0x0c, 0x21, 0x76, 0x71, 0x27, 0x89, 0xb9, 0xb0, 0x09, 0xbe, 0x81, 0xf0, 0xaa, 0x52, 0xfc, 0x4f, 0xe8, 0xbe, 0xd5, 0xbc, 0x19, 0x15, 0xe7, 0x49, 0x0e, 0xce, 0xcd, 0x94, 0x39, 0x10, 0x03, 0xcb, 0x40, 0xd4, 0xdb, 0xb7, 0x28, 0xa1, 0x95, 0x72, 0x87, 0x75, 0x6c, 0x1a, 0x5d, 0xb6, 0x1a, 0xfe, 0x4e, 0x7d, 0x82, 0x39, 0x6e, 0x6e, 0x65, 0x06, 0x1e, 0x06, 0x57, 0x2c, 0x41, 0x69, 0xaa, 0x7f, 0xd1, 0xb9, 0x2f, 0x6c, 0x33, 0x0e, 0x72, 0x81, 0x72, 0x61, 0x06, 0xc7, 0x32, 0xf8, 0x39, 0x43, 0x4c, 0x56, 0xc5, 0x57, 0x38, 0xd0, 0xc2, 0xe0, 0x60, 0x5f, 0xb2, 0x2d, 0x4f, 0x17, 0xb9, 0x5d, 0xb9, 0x70, 0x20, 0x06, 0x9e, 0x0d, 0xd7, 0x03, 0x21, 0x59, 0x6f, 0xb4, 0x4a, 0xea, 0x0f, 0xcb, 0xc2, 0xf1, 0xe2, 0xb6, 0x95, 0xe6, 0x9a, 0xb8, 0x7e, 0x15, 0xf3, 0x0c, 0xae, 0x39, 0x6f, 0x1b, 0x34, 0x56, 0x6c, 0x5c, 0xb9, 0x41, 0x07, 0x4d, 0xb4, 0xc2, 0xb9, 0x01, 0x5a, 0x05, 0x7d, 0x07, 0x07, 0x98, 0x07, 0x4f, 0xb2, 0xad, 0x18, 0x99, 0x9b, 0xc4, 0x9c, 0x8e, 0xb9, 0x19, 0x36, 0x54, 0x1c, 0x3d, 0xba, 0x81, 0x16, 0x00, 0x7a, 0x39, 0xf5, 0x43, 0x40, 0x3b, 0x66, 0xfc, 0xf9, 0xf6, 0x81, 0xc1, 0xa1, 0x70, 0x08, 0x5c, 0xa2, 0x8d, 0x4e, 0x2f, 0x45, 0xfb, 0xf6, 0x24, 0x8b, 0xc0, 0x57, 0xe1, 0x17, 0x17, 0x77, 0xf4, 0xd1, 0xea, 0xa5, 0x98, 0x1f, 0x8e, 0x6a, 0xce, 0x1f, 0xe2, 0x74, 0xf3, 0x6f, 0x05, 0xd7, 0x38, 0x45, 0xc7, 0x01, 0xd5, 0x8c, 0x55, 0x48, 0x1b, 0xc4, 0x4d, 0x85, 0x34, 0xd3, 0x43, 0x3b, 0xfd, 0x9d, 0xdf, 0xdd, 0xd4, 0xd4, 0x34, 0xc2, 0xb4, 0xb6, 0xdc, 0x08, 0x0f, 0xc0, 0x35, 0xfe, 0xa0, 0xe3, 0x94, 0x9a, 0x0a, 0x75, 0x3d, 0x38, 0xc2, 0xc6, 0xc3, 0xb7, 0xae, 0xce, 0x82, 0x7e, 0x5a, 0xde, 0x2d, 0x70, 0xd5, 0x50, 0xb3, 0x7e, 0x88, 0x2a, 0xc1, 0x9b, 0xa0, 0x25, 0x55, 0xda, 0x58, 0x57, 0x33, 0xa1, 0xae, 0x07, 0xc6, 0xd1, 0x48, 0x38, 0x92, 0xd6, 0x59, 0xd0, 0x5f, 0x0b, 0x04, 0xbf, 0x02, 0x61, 0x4d, 0x90, 0xd5, 0x37, 0xc4, 0xb3, 0x1c, 0xab, 0x9f, 0x57, 0x0e, 0x11, 0xa4, 0x07, 0xd3, 0xac, 0x1e, 0x8f, 0xf4, 0x35, 0xf9, 0x79, 0x6f, 0x0c, 0x22, 0x46, 0x9f, 0xd6, 0xcc, 0xc7, 0x18, 0xaa, 0xa9, 0x63, 0xbd, 0x37, 0x06, 0x25, 0x04, 0x3b, 0x7b, 0x52, 0x5b, 0xea, 0x7e, 0x80, 0xa3, 0x54, 0x33, 0x5f, 0x27, 0x3b, 0x9f, 0x34, 0xc4, 0xbc, 0xd1, 0x5d, 0x5f, 0x57, 0x4d, 0x0f, 0xbd, 0x77, 0xba, 0x6a, 0xd5, 0x17, 0xf4, 0xa8, 0xa5, 0xb7, 0xe1, 0x7e, 0x04, 0x62, 0x26, 0x81, 0x39, 0x45, 0xe8, 0x7e, 0xfc, 0x10, 0xf4, 0xba, 0x50, 0x57, 0x58, 0xd3, 0xc8, 0xb3, 0xf0, 0x39, 0x54, 0x1b, 0xaf, 0xf9, 0xe1, 0x2e, 0x50, 0x81, 0xea, 0x66, 0x4b, 0xf9, 0xf2, 0x38, 0x81, 0xb4, 0x4b, 0x7a, 0x29, 0x15, 0x1e, 0x66, 0x47, 0x5c, 0xff, 0x42, 0xf2, 0xa8, 0x1a, 0x0c, 0x1d, 0xa1, 0x3a, 0xb6, 0x87, 0x92, 0x84, 0xb0, 0xbf, 0x0f, 0x24, 0xad, 0x5b, 0x2f, 0xb1, 0xdf, 0x8b, 0x85, 0xb7, 0xf9, 0x55, 0x6b, 0x2e, 0xa6, 0xcc, 0x1f, 0x87, 0xbd, 0x49, 0x26, 0x79, 0x90, 0xbd, 0xcf, 0xfe, 0xf2, 0x5b, 0xb3, 0x88, 0x76, 0x7a, 0x92, 0x5f, 0x41, 0x9b, 0xdb, 0x11, 0xfb, 0x72, 0x4f, 0x99, 0x26, 0xa7, 0x5b, 0xa3, 0x97, 0xdf, 0x54, 0x5b, 0x39, 0xbb, 0xda, 0x6a, 0x27, 0x82, 0xfa, 0x16, 0x0c, 0x1e, 0x8f, 0xd9, 0x97, 0x7b, 0xda, 0x34, 0xa9, 0x6f, 0x10, 0x66, 0x8c, 0x31, 0x2b, 0x31, 0xe5, 0xb1, 0x11, 0xf9, 0x53, 0xe4, 0xde, 0x89, 0xc8, 0x17, 0x96, 0x32, 0x4d, 0x4a, 0x9c, 0x19, 0xfa, 0x14, 0x16, 0x1b, 0xdc, 0x6f, 0x3d, 0xd9, 0xf9, 0xb2, 0xd5, 0xe0, 0x0e, 0xf5, 0x25, 0xc1, 0x9f, 0xb2, 0xb5, 0xcb, 0x83, 0x2a, 0xa2, 0x87, 0x80, 0xf9, 0x6a, 0x5e, 0xd3, 0xc7, 0x8c, 0x38, 0x69, 0xec, 0x5b, 0x0f, 0x76, 0xbc, 0x1e, 0xd7, 0xde, 0x9b, 0x3c, 0x8a, 0xf5, 0x1d, 0xc2, 0x8c, 0x9b, 0x54, 0xd6, 0x40, 0xd7, 0x0f, 0x12, 0x65, 0xfd, 0xaa, 0x7e, 0x00, 0xe6, 0x09, 0xa1, 0xfa, 0xe8, 0xab, 0xae, 0x37, 0x66, 0xe2, 0x84, 0x20, 0xb4, 0x37, 0xb4, 0x48, 0xb1, 0x15, 0xfa, 0xc6, 0x70, 0x0c, 0x4e, 0xc2, 0x9f, 0xd6, 0x3e, 0x55, 0xf5, 0x9d, 0xbf, 0x7e, 0x02, 0xc1, 0x5a, 0xf5, 0x5c, 0x95, 0xf2, 0x2a, 0x42, 0x57, 0xf3, 0x5d, 0x30, 0xef, 0xed, 0xfa, 0x30, 0x8b, 0x68, 0x2d, 0x0c, 0xf6, 0x43, 0x52, 0x68, 0xb2, 0x8f, 0xbd, 0x67, 0xeb, 0xc3, 0x29, 0x2a, 0x31, 0xb1, 0x04, 0xf6, 0xc1, 0x19, 0xd0, 0x4b, 0x2b, 0x3d, 0x3c, 0xef, 0x05, 0xfd, 0x87, 0xc5, 0xc0, 0xba, 0x31, 0x52, 0xad, 0x50, 0x4c, 0x35, 0x42, 0xd7, 0x0f, 0x50, 0xb5, 0xfd, 0x7c, 0x68, 0xf7, 0x2f, 0xa6, 0x49, 0x6c, 0x47, 0x4c, 0x46, 0x1e, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHierarchyIcon[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x50, 0x21, 0x2b, 0x21, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xeb, 0x6b, 0x49, 0x00, 0x00, 0x01, 0xe9, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x98, 0xbf, 0x2f, 0x04, 0x41, 0x14, 0xc7, 0x77, 0xb9, 0x82, 0x88, 0x82, 0x56, 0x4f, 0x68, 0x88, 0x48, 0x88, 0x6b, 0x2e, 0x21, 0x41, 0xe9, 0x3f, 0x50, 0x88, 0x46, 0xc5, 0x3f, 0xa0, 0xd2, 0x8a, 0x44, 0xab, 0xf0, 0x0f, 0xe8, 0x28, 0x50, 0x9f, 0x46, 0xa3, 0x11, 0x09, 0x11, 0xc5, 0x35, 0x2a, 0x91, 0x08, 0x22, 0x7e, 0x9c, 0xcf, 0x3b, 0x7b, 0xc9, 0x4e, 0x76, 0x2e, 0xe6, 0xe6, 0x47, 0xf6, 0x24, 0xfb, 0x92, 0x6f, 0x76, 0xe6, 0xcd, 0x7b, 0xdf, 0x79, 0x6f, 0xde, 0xde, 0xcc, 0xec, 0xc5, 0xf5, 0x7a, 0x3d, 0x8e, 0xa2, 0x68, 0x0b, 0x2c, 0x83, 0x21, 0xe0, 0x2a, 0x6f, 0x10, 0x9c, 0x80, 0xed, 0x38, 0x8e, 0xef, 0x5d, 0xc9, 0x8c, 0xfd, 0x49, 0xa4, 0x0c, 0x42, 0xc8, 0x8e, 0x71, 0x10, 0x1e, 0x0c, 0x4b, 0x70, 0x2c, 0x79, 0xe0, 0xd1, 0x51, 0x2c, 0xa2, 0xdc, 0xd4, 0x0d, 0xa4, 0x75, 0xac, 0xe0, 0x00, 0xfd, 0xf1, 0xb4, 0xce, 0xb0, 0x5d, 0xa3, 0xe2, 0x77, 0x4d, 0x5b, 0x49, 0xa4, 0xa7, 0xd9, 0xf1, 0xfc, 0xfc, 0x93, 0x97, 0x24, 0x76, 0x99, 0x73, 0x1d, 0x48, 0x1c, 0x6d, 0x0b, 0xfe, 0x97, 0x38, 0xcd, 0x90, 0xd0, 0x7b, 0x57, 0xdb, 0xde, 0x9e, 0x1c, 0x08, 0xa2, 0x0f, 0xaa, 0x35, 0x60, 0x95, 0x44, 0x12, 0xc6, 0x04, 0xcf, 0x05, 0x69, 0xe7, 0x96, 0x08, 0x73, 0x8f, 0x81, 0x5e, 0x09, 0xc2, 0x51, 0xa6, 0xc4, 0x3f, 0xcf, 0x44, 0x1c, 0xe3, 0x57, 0xdd, 0x8b, 0x44, 0xd4, 0xf5, 0xc8, 0xbf, 0x57, 0x54, 0x24, 0xff, 0x1a, 0xa8, 0x11, 0x48, 0x45, 0x5e, 0x54, 0x95, 0xb7, 0x5e, 0x28, 0x5e, 0x6d, 0x80, 0x92, 0xc8, 0x91, 0x76, 0xc4, 0x5d, 0x79, 0xec, 0x4e, 0x61, 0xce, 0x50, 0xe2, 0x54, 0xbc, 0xe0, 0x70, 0xda, 0xc0, 0xc5, 0xd7, 0xa5, 0xf1, 0x15, 0x2e, 0xb9, 0x34, 0xee, 0x99, 0x87, 0xe1, 0x6e, 0xd9, 0x38, 0x55, 0x49, 0x46, 0xae, 0x0a, 0x82, 0x7f, 0x2b, 0xc5, 0xae, 0xd5, 0x69, 0xa5, 0x2b, 0x2a, 0xd2, 0x69, 0x15, 0xd1, 0x5e, 0xa1, 0xd9, 0xc5, 0x46, 0x09, 0x74, 0x15, 0x94, 0x41, 0xb7, 0x65, 0xd0, 0x5f, 0xf8, 0x55, 0xc1, 0x3e, 0x9b, 0xc9, 0xb5, 0x25, 0x87, 0xb1, 0x9b, 0x36, 0x11, 0xbc, 0x65, 0xeb, 0x9c, 0x33, 0x66, 0x69, 0x6d, 0x38, 0xcd, 0x90, 0x7c, 0xfd, 0xcd, 0xb7, 0x36, 0xf1, 0x33, 0x92, 0xf9, 0x8d, 0x50, 0x8d, 0x7e, 0xa8, 0x2b, 0x7e, 0xe8, 0x1b, 0x2c, 0x95, 0x84, 0xd3, 0x23, 0x65, 0x96, 0x2a, 0x93, 0x08, 0x26, 0xf2, 0xe5, 0x66, 0xfb, 0x3a, 0x65, 0x67, 0xf8, 0xe5, 0x12, 0xce, 0xa0, 0xa2, 0x4b, 0x24, 0xe8, 0x84, 0xa1, 0xc8, 0x8b, 0x44, 0x42, 0xad, 0xac, 0x2d, 0x6f, 0x9e, 0x15, 0xb9, 0x25, 0xe8, 0x0f, 0xdb, 0xc0, 0x53, 0x7e, 0x57, 0xd2, 0xce, 0x2d, 0x11, 0xce, 0x96, 0x27, 0xe6, 0x3f, 0x4c, 0x05, 0x64, 0xd3, 0x7c, 0xc0, 0xa9, 0xf1, 0x19, 0x22, 0xff, 0xfb, 0x2a, 0x92, 0x6c, 0x95, 0x8f, 0x28, 0x5b, 0x9d, 0x31, 0x8a, 0xbd, 0x41, 0xe7, 0x13, 0x9b, 0x41, 0x02, 0x7f, 0xd6, 0xd9, 0x32, 0xdf, 0x08, 0xfa, 0x49, 0x90, 0x89, 0x45, 0x67, 0x9f, 0xd2, 0xd5, 0x68, 0x57, 0xe1, 0xfd, 0x4e, 0xe9, 0xd4, 0x26, 0xe4, 0x67, 0xc0, 0x97, 0x9c, 0xaa, 0xec, 0x61, 0x7a, 0xda, 0x55, 0x20, 0x83, 0x61, 0xa6, 0x5b, 0x01, 0xb3, 0xc0, 0xf6, 0x4c, 0x91, 0x2b, 0xca, 0x39, 0x38, 0x60, 0xd5, 0x6e, 0x78, 0x16, 0x62, 0xb2, 0x02, 0x3f, 0xb2, 0x15, 0xf7, 0xb1, 0x2c, 0xc1, 0x90, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHierarchyIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x50, 0x21, 0x2b, 0x21, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xeb, 0x6b, 0x49, 0x00, 0x00, 0x01, 0xe9, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x98, 0xbf, 0x2f, 0x04, 0x41, 0x14, 0xc7, 0x77, 0xb9, 0x82, 0x88, 0x82, 0x56, 0x4f, 0x68, 0x88, 0x48, 0x88, 0x6b, 0x2e, 0x21, 0x41, 0xe9, 0x3f, 0x50, 0x88, 0x46, 0xc5, 0x3f, 0xa0, 0xd2, 0x8a, 0x44, 0xab, 0xf0, 0x0f, 0xe8, 0x28, 0x50, 0x9f, 0x46, 0xa3, 0x11, 0x09, 0x11, 0xc5, 0x35, 0x2a, 0x91, 0x08, 0x22, 0x7e, 0x9c, 0xcf, 0x3b, 0x7b, 0xc9, 0x4e, 0x76, 0x2e, 0xe6, 0xe6, 0x47, 0xf6, 0x24, 0xfb, 0x92, 0x6f, 0x76, 0xe6, 0xcd, 0x7b, 0xdf, 0x79, 0x6f, 0xde, 0xde, 0xcc, 0xec, 0xc5, 0xf5, 0x7a, 0x3d, 0x8e, 0xa2, 0x68, 0x0b, 0x2c, 0x83, 0x21, 0xe0, 0x2a, 0x6f, 0x10, 0x9c, 0x80, 0xed, 0x38, 0x8e, 0xef, 0x5d, 0xc9, 0x8c, 0xfd, 0x49, 0xa4, 0x0c, 0x42, 0xc8, 0x8e, 0x71, 0x10, 0x1e, 0x0c, 0x4b, 0x70, 0x2c, 0x79, 0xe0, 0xd1, 0x51, 0x2c, 0xa2, 0xdc, 0xd4, 0x0d, 0xa4, 0x75, 0xac, 0xe0, 0x00, 0xfd, 0xf1, 0xb4, 0xce, 0xb0, 0x5d, 0xa3, 0xe2, 0x77, 0x4d, 0x5b, 0x49, 0xa4, 0xa7, 0xd9, 0xf1, 0xfc, 0xfc, 0x93, 0x97, 0x24, 0x76, 0x99, 0x73, 0x1d, 0x48, 0x1c, 0x6d, 0x0b, 0xfe, 0x97, 0x38, 0xcd, 0x90, 0xd0, 0x7b, 0x57, 0xdb, 0xde, 0x9e, 0x1c, 0x08, 0xa2, 0x0f, 0xaa, 0x35, 0x60, 0x95, 0x44, 0x12, 0xc6, 0x04, 0xcf, 0x05, 0x69, 0xe7, 0x96, 0x08, 0x73, 0x8f, 0x81, 0x5e, 0x09, 0xc2, 0x51, 0xa6, 0xc4, 0x3f, 0xcf, 0x44, 0x1c, 0xe3, 0x57, 0xdd, 0x8b, 0x44, 0xd4, 0xf5, 0xc8, 0xbf, 0x57, 0x54, 0x24, 0xff, 0x1a, 0xa8, 0x11, 0x48, 0x45, 0x5e, 0x54, 0x95, 0xb7, 0x5e, 0x28, 0x5e, 0x6d, 0x80, 0x92, 0xc8, 0x91, 0x76, 0xc4, 0x5d, 0x79, 0xec, 0x4e, 0x61, 0xce, 0x50, 0xe2, 0x54, 0xbc, 0xe0, 0x70, 0xda, 0xc0, 0xc5, 0xd7, 0xa5, 0xf1, 0x15, 0x2e, 0xb9, 0x34, 0xee, 0x99, 0x87, 0xe1, 0x6e, 0xd9, 0x38, 0x55, 0x49, 0x46, 0xae, 0x0a, 0x82, 0x7f, 0x2b, 0xc5, 0xae, 0xd5, 0x69, 0xa5, 0x2b, 0x2a, 0xd2, 0x69, 0x15, 0xd1, 0x5e, 0xa1, 0xd9, 0xc5, 0x46, 0x09, 0x74, 0x15, 0x94, 0x41, 0xb7, 0x65, 0xd0, 0x5f, 0xf8, 0x55, 0xc1, 0x3e, 0x9b, 0xc9, 0xb5, 0x25, 0x87, 0xb1, 0x9b, 0x36, 0x11, 0xbc, 0x65, 0xeb, 0x9c, 0x33, 0x66, 0x69, 0x6d, 0x38, 0xcd, 0x90, 0x7c, 0xfd, 0xcd, 0xb7, 0x36, 0xf1, 0x33, 0x92, 0xf9, 0x8d, 0x50, 0x8d, 0x7e, 0xa8, 0x2b, 0x7e, 0xe8, 0x1b, 0x2c, 0x95, 0x84, 0xd3, 0x23, 0x65, 0x96, 0x2a, 0x93, 0x08, 0x26, 0xf2, 0xe5, 0x66, 0xfb, 0x3a, 0x65, 0x67, 0xf8, 0xe5, 0x12, 0xce, 0xa0, 0xa2, 0x4b, 0x24, 0xe8, 0x84, 0xa1, 0xc8, 0x8b, 0x44, 0x42, 0xad, 0xac, 0x2d, 0x6f, 0x9e, 0x15, 0xb9, 0x25, 0xe8, 0x0f, 0xdb, 0xc0, 0x53, 0x7e, 0x57, 0xd2, 0xce, 0x2d, 0x11, 0xce, 0x96, 0x27, 0xe6, 0x3f, 0x4c, 0x05, 0x64, 0xd3, 0x7c, 0xc0, 0xa9, 0xf1, 0x19, 0x22, 0xff, 0xfb, 0x2a, 0x92, 0x6c, 0x95, 0x8f, 0x28, 0x5b, 0x9d, 0x31, 0x8a, 0xbd, 0x41, 0xe7, 0x13, 0x9b, 0x41, 0x02, 0x7f, 0xd6, 0xd9, 0x32, 0xdf, 0x08, 0xfa, 0x49, 0x90, 0x89, 0x45, 0x67, 0x9f, 0xd2, 0xd5, 0x68, 0x57, 0xe1, 0xfd, 0x4e, 0xe9, 0xd4, 0x26, 0xe4, 0x67, 0xc0, 0x97, 0x9c, 0xaa, 0xec, 0x61, 0x7a, 0xda, 0x55, 0x20, 0x83, 0x61, 0xa6, 0x5b, 0x01, 0xb3, 0xc0, 0xf6, 0x4c, 0x91, 0x2b, 0xca, 0x39, 0x38, 0x60, 0xd5, 0x6e, 0x78, 0x16, 0x62, 0xb2, 0x02, 0x3f, 0xb2, 0x15, 0xf7, 0xb1, 0x2c, 0xc1, 0x90, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHierarchyIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x59, 0xab, 0x87, 0xe8, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4b, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x09, 0xb6, 0x65, 0x00, 0x00, 0x02, 0x81, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9b, 0x3d, 0x4b, 0xc4, 0x40, 0x10, 0x86, 0x8d, 0x1f, 0xa0, 0x88, 0x82, 0x95, 0x36, 0x5a, 0x9c, 0x82, 0xf8, 0x07, 0x2c, 0xc5, 0x4a, 0x2c, 0xac, 0x6c, 0xfc, 0x05, 0x5a, 0x9d, 0x56, 0x76, 0x36, 0xd7, 0x58, 0xc8, 0x29, 0x36, 0xfe, 0x03, 0x7b, 0xc5, 0x4a, 0x0b, 0x11, 0x3c, 0x6b, 0x41, 0xad, 0x04, 0x3f, 0x8a, 0x03, 0xf1, 0xaa, 0x43, 0x94, 0x13, 0x44, 0x24, 0xbe, 0x03, 0x6e, 0x8a, 0x80, 0xbb, 0x99, 0x4b, 0x76, 0xb3, 0xe1, 0x66, 0x60, 0x88, 0x31, 0xb3, 0xf3, 0xce, 0x3c, 0x6e, 0x72, 0xd9, 0xe4, 0x0c, 0xba, 0x60, 0x61, 0x18, 0x8e, 0x60, 0xb3, 0x05, 0x9f, 0x83, 0x8f, 0xc1, 0x03, 0xb8, 0x6d, 0x0b, 0x21, 0xd0, 0x80, 0x5f, 0xc2, 0xb7, 0x83, 0x20, 0x78, 0xb3, 0x2d, 0x98, 0x49, 0x7e, 0xc0, 0xba, 0x80, 0xe7, 0x69, 0xe7, 0x99, 0x34, 0x62, 0x39, 0x49, 0x00, 0x42, 0xd3, 0xd0, 0xb8, 0xb7, 0xac, 0x93, 0x24, 0xfd, 0x14, 0x66, 0xd7, 0x53, 0x92, 0xc0, 0xbc, 0x62, 0xba, 0x21, 0x3c, 0x99, 0x97, 0x78, 0x4c, 0xb7, 0x14, 0xdb, 0xf7, 0x6e, 0xb7, 0x17, 0x15, 0xf5, 0x7b, 0x52, 0x15, 0xbb, 0x0e, 0x9c, 0x15, 0x8b, 0xa8, 0x7d, 0x03, 0x4e, 0xa0, 0x07, 0x32, 0xec, 0xa3, 0x85, 0x5c, 0x0f, 0xf0, 0x3d, 0xcc, 0xf6, 0x9a, 0xca, 0x4b, 0xb0, 0x0a, 0x69, 0x00, 0x35, 0x8f, 0xc2, 0x4f, 0x2d, 0x16, 0x3f, 0x83, 0xdc, 0x4b, 0xd0, 0x99, 0x05, 0xb0, 0x6b, 0xd2, 0xa1, 0xd3, 0xb0, 0xa8, 0xb6, 0xe6, 0xa0, 0x70, 0xe2, 0xb3, 0xaa, 0x74, 0x8a, 0x0c, 0xab, 0xa4, 0x9a, 0xb0, 0xbc, 0x8d, 0xae, 0xe9, 0x45, 0x86, 0xc5, 0xbe, 0xc6, 0xb5, 0x09, 0x35, 0xd2, 0x29, 0x32, 0xac, 0x36, 0x7b, 0x6f, 0x7f, 0x98, 0xc0, 0x62, 0xb0, 0x13, 0x58, 0x02, 0x8b, 0x41, 0x80, 0x11, 0x2a, 0x33, 0x4b, 0x60, 0x31, 0x08, 0x30, 0x42, 0x65, 0x66, 0x09, 0x2c, 0x06, 0x01, 0x46, 0x28, 0xcd, 0x2c, 0x5a, 0x34, 0xfa, 0x60, 0xbe, 0xd4, 0xf1, 0x2f, 0x0b, 0x82, 0x45, 0xab, 0x6b, 0x1f, 0xec, 0xd1, 0x87, 0x22, 0x74, 0x35, 0x74, 0x63, 0x45, 0xfd, 0x8c, 0x80, 0x13, 0x5d, 0x90, 0x83, 0x63, 0x47, 0xa8, 0xa3, 0xee, 0x40, 0x27, 0x95, 0x84, 0x7a, 0x44, 0xb3, 0x82, 0x2c, 0xeb, 0x70, 0x7a, 0x06, 0x3f, 0x0a, 0x77, 0xf9, 0x0c, 0xbe, 0x06, 0xbd, 0x03, 0xb8, 0x98, 0x2d, 0x02, 0x78, 0xce, 0x74, 0x03, 0x77, 0x61, 0x57, 0xaa, 0x07, 0xba, 0x66, 0x89, 0x25, 0x24, 0x20, 0xb0, 0x12, 0x82, 0xa2, 0x30, 0x81, 0x25, 0xb0, 0x18, 0x04, 0x18, 0xa1, 0x32, 0xb3, 0x04, 0x16, 0x83, 0x00, 0x23, 0x54, 0xdd, 0x67, 0x69, 0x87, 0xe0, 0xf3, 0xb9, 0x0f, 0x01, 0x13, 0xf0, 0x61, 0x6d, 0x60, 0xba, 0x83, 0xef, 0x18, 0x5e, 0xc7, 0xcd, 0xe9, 0x77, 0xba, 0x34, 0xf6, 0x46, 0x1b, 0x61, 0x01, 0xd4, 0x26, 0xe4, 0x2b, 0xf0, 0x41, 0x7b, 0x65, 0x44, 0x99, 0x5b, 0xd0, 0xab, 0x00, 0xd8, 0x6e, 0xf4, 0x1b, 0x8f, 0x7e, 0xd0, 0x5e, 0xb3, 0x50, 0x38, 0xdd, 0xd1, 0x57, 0xe1, 0x2e, 0x40, 0x11, 0x16, 0xd2, 0xa9, 0xfe, 0xe9, 0xd2, 0xbe, 0x57, 0xa6, 0x85, 0x85, 0x4a, 0x17, 0x72, 0xaa, 0x36, 0x2f, 0x5d, 0x6d, 0xbb, 0x26, 0x58, 0xe3, 0xda, 0xd1, 0xf6, 0x0e, 0xe6, 0xa5, 0xab, 0xed, 0xc8, 0x04, 0xab, 0x47, 0x3b, 0xda, 0xde, 0xc1, 0xbc, 0x74, 0xb5, 0x1d, 0x99, 0x60, 0x69, 0x07, 0x77, 0xda, 0x41, 0x81, 0xc5, 0xf8, 0x8b, 0x0b, 0x2c, 0x81, 0xc5, 0x20, 0xc0, 0x08, 0x95, 0x99, 0xd5, 0x21, 0xb0, 0x9a, 0x8c, 0x3e, 0xd3, 0x84, 0x46, 0x3a, 0x45, 0x9e, 0x59, 0x77, 0x69, 0x08, 0x30, 0xc6, 0xde, 0xaa, 0xd8, 0x22, 0xc3, 0xda, 0x41, 0x13, 0x2f, 0xaa, 0x11, 0x4b, 0x5b, 0xfa, 0xaa, 0xf9, 0xbe, 0xca, 0x6d, 0x5a, 0x48, 0xff, 0xa8, 0x40, 0xc7, 0x5b, 0xa3, 0x2e, 0x16, 0xdb, 0xaf, 0x58, 0x43, 0xd2, 0x77, 0xf8, 0x97, 0xe1, 0x25, 0x78, 0xf4, 0x0d, 0xbd, 0x0c, 0x6a, 0xfd, 0x44, 0x0e, 0x7a, 0x9f, 0x7a, 0x0c, 0x9d, 0x2f, 0x95, 0xcf, 0x04, 0xab, 0xae, 0x02, 0x1d, 0x6f, 0x13, 0xe9, 0xa2, 0x11, 0x7a, 0x8b, 0x7d, 0xe8, 0xaa, 0x36, 0xd3, 0x69, 0x78, 0xe6, 0xaa, 0x90, 0x98, 0x4e, 0x5e, 0xba, 0xb1, 0x32, 0x98, 0xbb, 0x98, 0xea, 0x65, 0x78, 0x13, 0xee, 0xc2, 0x48, 0xa7, 0xcc, 0x2c, 0xd1, 0x59, 0x78, 0xe2, 0x37, 0xcf, 0x68, 0x82, 0xfe, 0x5b, 0x6c, 0xc8, 0x62, 0x65, 0x1f, 0x38, 0xad, 0x1a, 0x16, 0xf3, 0x4b, 0x6a, 0x97, 0x04, 0x7e, 0x01, 0x1e, 0xdc, 0xbb, 0x6b, 0x41, 0x9a, 0x28, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRecentTabIcon[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9b, 0xdc, 0x28, 0xa4, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x48, 0xca, 0xb9, 0xf5, 0x00, 0x00, 0x02, 0x99, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xdd, 0x98, 0xcf, 0x6f, 0x0d, 0x51, 0x14, 0xc7, 0x67, 0x8a, 0x60, 0x41, 0xfc, 0x4a, 0x10, 0x45, 0x35, 0x84, 0x26, 0x24, 0x62, 0x61, 0x41, 0xf0, 0x22, 0x12, 0x1b, 0x61, 0x53, 0xec, 0x24, 0x76, 0x36, 0xfe, 0x00, 0x6b, 0x89, 0xa5, 0x85, 0xb5, 0xb4, 0x76, 0x92, 0xb2, 0x22, 0xd6, 0x15, 0x0b, 0x56, 0x7e, 0x84, 0x1d, 0x41, 0xc2, 0x13, 0x21, 0x0d, 0x0b, 0x91, 0x94, 0xb4, 0x9e, 0xcf, 0xf7, 0x71, 0xd3, 0xd3, 0xd7, 0x3b, 0xbd, 0xf7, 0xcd, 0x9b, 0x3b, 0x1a, 0x27, 0xf9, 0x66, 0xce, 0x3d, 0x73, 0xee, 0xf7, 0x7c, 0xdf, 0xbd, 0xa7, 0x77, 0x66, 0x9a, 0x65, 0x7f, 0xad, 0xd5, 0x6a, 0x9d, 0x01, 0xf7, 0xc1, 0x47, 0xd0, 0x8b, 0x4d, 0x33, 0xf9, 0x2d, 0xb8, 0x07, 0x0e, 0x38, 0xfe, 0xa8, 0x2b, 0x13, 0xae, 0x82, 0x14, 0xf6, 0x0b, 0xd2, 0x73, 0x31, 0x22, 0x72, 0x12, 0x77, 0x93, 0xf8, 0x0c, 0x2c, 0x32, 0x13, 0xbe, 0xe0, 0x4f, 0x9b, 0x71, 0x37, 0xee, 0x52, 0x92, 0x57, 0x9a, 0x09, 0x13, 0xf8, 0x83, 0x79, 0x9e, 0x7f, 0x33, 0xb1, 0xb9, 0x2e, 0x42, 0x2e, 0x9a, 0xa5, 0x78, 0x82, 0xbf, 0x69, 0x6e, 0x56, 0x77, 0x11, 0x38, 0xf6, 0x82, 0x09, 0xc3, 0x7b, 0x28, 0xc4, 0xd0, 0x47, 0xc2, 0x76, 0x93, 0x74, 0x0d, 0xe5, 0x1f, 0xcc, 0xb8, 0x94, 0x0b, 0x87, 0x56, 0xf8, 0xa6, 0x99, 0x6c, 0x6b, 0x98, 0xf0, 0x8c, 0x2b, 0x21, 0x2b, 0x66, 0x86, 0x99, 0x96, 0xb1, 0x2a, 0xb3, 0x5c, 0xb6, 0x86, 0x97, 0x5f, 0x42, 0x16, 0x84, 0x2d, 0x8e, 0x55, 0xc1, 0x7e, 0xef, 0x27, 0x77, 0x08, 0xa8, 0x87, 0x62, 0x7e, 0xc0, 0x61, 0xc3, 0x7d, 0x9c, 0xf9, 0x6a, 0xe0, 0x16, 0xf8, 0x04, 0x5e, 0x82, 0x87, 0x6c, 0xe1, 0x14, 0xd7, 0x3f, 0x46, 0xc2, 0x88, 0x69, 0xaa, 0x13, 0x2e, 0xee, 0xae, 0xdc, 0x1b, 0x00, 0xe3, 0x26, 0xa7, 0x2a, 0xf7, 0x39, 0x44, 0xfb, 0x5c, 0x9d, 0x98, 0x5f, 0x76, 0x83, 0xe4, 0x86, 0x9b, 0x50, 0xe1, 0x75, 0x0f, 0x5c, 0xb7, 0x11, 0xb3, 0x4c, 0x9c, 0xf3, 0x0a, 0x21, 0x49, 0x27, 0xe3, 0x11, 0x25, 0x26, 0xb2, 0x6d, 0xf0, 0x9e, 0x15, 0xf7, 0xbc, 0x42, 0xb8, 0xaf, 0x9e, 0x48, 0x6d, 0xbb, 0x54, 0x20, 0x24, 0x64, 0x4b, 0x6a, 0x15, 0xf0, 0x6f, 0x8e, 0x11, 0x12, 0x12, 0x5a, 0x85, 0xce, 0xf6, 0xa3, 0x25, 0xfa, 0xcf, 0xd7, 0x53, 0xf1, 0x2e, 0xb1, 0xd8, 0x53, 0x78, 0x15, 0xb9, 0xc3, 0xa0, 0xb0, 0x5e, 0xe1, 0x0d, 0x4f, 0x61, 0x1b, 0x6a, 0x72, 0x06, 0x9c, 0xb4, 0x81, 0x90, 0x4f, 0xe3, 0x6f, 0x20, 0xa7, 0x51, 0x94, 0x57, 0x56, 0x48, 0x3f, 0xc4, 0x63, 0x90, 0xbe, 0x2b, 0x22, 0xee, 0x88, 0xaf, 0x61, 0x7c, 0xb0, 0x23, 0x36, 0x6b, 0x58, 0x56, 0x88, 0x48, 0x4e, 0xcf, 0x62, 0xea, 0x71, 0x50, 0x47, 0x33, 0x46, 0x49, 0xec, 0x65, 0x45, 0xee, 0x50, 0xa1, 0x19, 0x55, 0x25, 0xcb, 0x56, 0x93, 0xa7, 0x66, 0x5d, 0x52, 0x94, 0x5f, 0x56, 0x88, 0x9a, 0xf5, 0x54, 0x11, 0xa9, 0x2f, 0x4e, 0x4f, 0x6d, 0x24, 0xde, 0xf0, 0xdd, 0x53, 0xac, 0xac, 0x10, 0x35, 0xeb, 0x2d, 0xe6, 0xbf, 0x2f, 0x22, 0xee, 0x88, 0x27, 0x6d, 0x56, 0x2d, 0x75, 0x65, 0xf6, 0xdf, 0x34, 0xab, 0x6f, 0x6b, 0x8e, 0xb2, 0x4c, 0x5d, 0x3f, 0x2c, 0xcb, 0xf6, 0x48, 0x61, 0xb3, 0xd2, 0x3b, 0x0d, 0x84, 0x8c, 0x77, 0xbb, 0x67, 0x12, 0xa2, 0xd7, 0x37, 0x67, 0xb9, 0x73, 0x02, 0x57, 0xd7, 0xac, 0x4d, 0x4f, 0xde, 0x31, 0x4f, 0x2c, 0x18, 0x92, 0x90, 0xcf, 0x26, 0x6b, 0x87, 0xf1, 0x43, 0x6e, 0xa5, 0xcd, 0x2a, 0x21, 0xaf, 0x4c, 0xc5, 0x4b, 0x2c, 0xad, 0xbe, 0xf2, 0x5e, 0x00, 0xbd, 0xd8, 0xae, 0x37, 0xf7, 0x92, 0xba, 0x12, 0xa2, 0x13, 0xf2, 0x2b, 0xd0, 0xe9, 0xb7, 0x0e, 0x8c, 0x82, 0xda, 0xad, 0x8f, 0x13, 0x52, 0x1f, 0x42, 0x17, 0xc0, 0xf7, 0xda, 0xab, 0x9b, 0x82, 0x5a, 0x91, 0x0c, 0x31, 0x63, 0x6c, 0xc9, 0x23, 0xdc, 0xf3, 0x60, 0x27, 0xd0, 0xbb, 0x83, 0x1a, 0x77, 0x10, 0x6c, 0x05, 0xff, 0xd6, 0x10, 0x77, 0x19, 0xa4, 0xb6, 0xf6, 0x37, 0xf2, 0x82, 0x39, 0x59, 0x43, 0x42, 0x7e, 0xd6, 0xb0, 0x27, 0xed, 0x1a, 0x21, 0x21, 0x6f, 0x6a, 0x10, 0xd2, 0xae, 0x11, 0x12, 0xf2, 0x14, 0x21, 0xf6, 0xe4, 0x4d, 0xa1, 0xeb, 0x71, 0x14, 0x29, 0x9d, 0x3a, 0x9a, 0xb0, 0x5b, 0x1f, 0x44, 0x89, 0x50, 0x12, 0x22, 0x96, 0x83, 0x2b, 0x60, 0xb2, 0x42, 0x41, 0x53, 0x70, 0x5d, 0x07, 0x6b, 0x9d, 0x90, 0xd8, 0x87, 0x9c, 0x04, 0xe9, 0xcc, 0x19, 0x00, 0xfd, 0x20, 0xb4, 0xa5, 0xa4, 0x78, 0x4d, 0xdb, 0xac, 0xff, 0x8f, 0xbc, 0xe6, 0xec, 0xfa, 0x61, 0x33, 0x7e, 0x03, 0xf9, 0x2d, 0xce, 0xa7, 0xa9, 0x35, 0x85, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRecentTabIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9b, 0xdc, 0x28, 0xa4, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x48, 0xca, 0xb9, 0xf5, 0x00, 0x00, 0x02, 0x99, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xdd, 0x98, 0xcf, 0x6f, 0x0d, 0x51, 0x14, 0xc7, 0x67, 0x8a, 0x60, 0x41, 0xfc, 0x4a, 0x10, 0x45, 0x35, 0x84, 0x26, 0x24, 0x62, 0x61, 0x41, 0xf0, 0x22, 0x12, 0x1b, 0x61, 0x53, 0xec, 0x24, 0x76, 0x36, 0xfe, 0x00, 0x6b, 0x89, 0xa5, 0x85, 0xb5, 0xb4, 0x76, 0x92, 0xb2, 0x22, 0xd6, 0x15, 0x0b, 0x56, 0x7e, 0x84, 0x1d, 0x41, 0xc2, 0x13, 0x21, 0x0d, 0x0b, 0x91, 0x94, 0xb4, 0x9e, 0xcf, 0xf7, 0x71, 0xd3, 0xd3, 0xd7, 0x3b, 0xbd, 0xf7, 0xcd, 0x9b, 0x3b, 0x1a, 0x27, 0xf9, 0x66, 0xce, 0x3d, 0x73, 0xee, 0xf7, 0x7c, 0xdf, 0xbd, 0xa7, 0x77, 0x66, 0x9a, 0x65, 0x7f, 0xad, 0xd5, 0x6a, 0x9d, 0x01, 0xf7, 0xc1, 0x47, 0xd0, 0x8b, 0x4d, 0x33, 0xf9, 0x2d, 0xb8, 0x07, 0x0e, 0x38, 0xfe, 0xa8, 0x2b, 0x13, 0xae, 0x82, 0x14, 0xf6, 0x0b, 0xd2, 0x73, 0x31, 0x22, 0x72, 0x12, 0x77, 0x93, 0xf8, 0x0c, 0x2c, 0x32, 0x13, 0xbe, 0xe0, 0x4f, 0x9b, 0x71, 0x37, 0xee, 0x52, 0x92, 0x57, 0x9a, 0x09, 0x13, 0xf8, 0x83, 0x79, 0x9e, 0x7f, 0x33, 0xb1, 0xb9, 0x2e, 0x42, 0x2e, 0x9a, 0xa5, 0x78, 0x82, 0xbf, 0x69, 0x6e, 0x56, 0x77, 0x11, 0x38, 0xf6, 0x82, 0x09, 0xc3, 0x7b, 0x28, 0xc4, 0xd0, 0x47, 0xc2, 0x76, 0x93, 0x74, 0x0d, 0xe5, 0x1f, 0xcc, 0xb8, 0x94, 0x0b, 0x87, 0x56, 0xf8, 0xa6, 0x99, 0x6c, 0x6b, 0x98, 0xf0, 0x8c, 0x2b, 0x21, 0x2b, 0x66, 0x86, 0x99, 0x96, 0xb1, 0x2a, 0xb3, 0x5c, 0xb6, 0x86, 0x97, 0x5f, 0x42, 0x16, 0x84, 0x2d, 0x8e, 0x55, 0xc1, 0x7e, 0xef, 0x27, 0x77, 0x08, 0xa8, 0x87, 0x62, 0x7e, 0xc0, 0x61, 0xc3, 0x7d, 0x9c, 0xf9, 0x6a, 0xe0, 0x16, 0xf8, 0x04, 0x5e, 0x82, 0x87, 0x6c, 0xe1, 0x14, 0xd7, 0x3f, 0x46, 0xc2, 0x88, 0x69, 0xaa, 0x13, 0x2e, 0xee, 0xae, 0xdc, 0x1b, 0x00, 0xe3, 0x26, 0xa7, 0x2a, 0xf7, 0x39, 0x44, 0xfb, 0x5c, 0x9d, 0x98, 0x5f, 0x76, 0x83, 0xe4, 0x86, 0x9b, 0x50, 0xe1, 0x75, 0x0f, 0x5c, 0xb7, 0x11, 0xb3, 0x4c, 0x9c, 0xf3, 0x0a, 0x21, 0x49, 0x27, 0xe3, 0x11, 0x25, 0x26, 0xb2, 0x6d, 0xf0, 0x9e, 0x15, 0xf7, 0xbc, 0x42, 0xb8, 0xaf, 0x9e, 0x48, 0x6d, 0xbb, 0x54, 0x20, 0x24, 0x64, 0x4b, 0x6a, 0x15, 0xf0, 0x6f, 0x8e, 0x11, 0x12, 0x12, 0x5a, 0x85, 0xce, 0xf6, 0xa3, 0x25, 0xfa, 0xcf, 0xd7, 0x53, 0xf1, 0x2e, 0xb1, 0xd8, 0x53, 0x78, 0x15, 0xb9, 0xc3, 0xa0, 0xb0, 0x5e, 0xe1, 0x0d, 0x4f, 0x61, 0x1b, 0x6a, 0x72, 0x06, 0x9c, 0xb4, 0x81, 0x90, 0x4f, 0xe3, 0x6f, 0x20, 0xa7, 0x51, 0x94, 0x57, 0x56, 0x48, 0x3f, 0xc4, 0x63, 0x90, 0xbe, 0x2b, 0x22, 0xee, 0x88, 0xaf, 0x61, 0x7c, 0xb0, 0x23, 0x36, 0x6b, 0x58, 0x56, 0x88, 0x48, 0x4e, 0xcf, 0x62, 0xea, 0x71, 0x50, 0x47, 0x33, 0x46, 0x49, 0xec, 0x65, 0x45, 0xee, 0x50, 0xa1, 0x19, 0x55, 0x25, 0xcb, 0x56, 0x93, 0xa7, 0x66, 0x5d, 0x52, 0x94, 0x5f, 0x56, 0x88, 0x9a, 0xf5, 0x54, 0x11, 0xa9, 0x2f, 0x4e, 0x4f, 0x6d, 0x24, 0xde, 0xf0, 0xdd, 0x53, 0xac, 0xac, 0x10, 0x35, 0xeb, 0x2d, 0xe6, 0xbf, 0x2f, 0x22, 0xee, 0x88, 0x27, 0x6d, 0x56, 0x2d, 0x75, 0x65, 0xf6, 0xdf, 0x34, 0xab, 0x6f, 0x6b, 0x8e, 0xb2, 0x4c, 0x5d, 0x3f, 0x2c, 0xcb, 0xf6, 0x48, 0x61, 0xb3, 0xd2, 0x3b, 0x0d, 0x84, 0x8c, 0x77, 0xbb, 0x67, 0x12, 0xa2, 0xd7, 0x37, 0x67, 0xb9, 0x73, 0x02, 0x57, 0xd7, 0xac, 0x4d, 0x4f, 0xde, 0x31, 0x4f, 0x2c, 0x18, 0x92, 0x90, 0xcf, 0x26, 0x6b, 0x87, 0xf1, 0x43, 0x6e, 0xa5, 0xcd, 0x2a, 0x21, 0xaf, 0x4c, 0xc5, 0x4b, 0x2c, 0xad, 0xbe, 0xf2, 0x5e, 0x00, 0xbd, 0xd8, 0xae, 0x37, 0xf7, 0x92, 0xba, 0x12, 0xa2, 0x13, 0xf2, 0x2b, 0xd0, 0xe9, 0xb7, 0x0e, 0x8c, 0x82, 0xda, 0xad, 0x8f, 0x13, 0x52, 0x1f, 0x42, 0x17, 0xc0, 0xf7, 0xda, 0xab, 0x9b, 0x82, 0x5a, 0x91, 0x0c, 0x31, 0x63, 0x6c, 0xc9, 0x23, 0xdc, 0xf3, 0x60, 0x27, 0xd0, 0xbb, 0x83, 0x1a, 0x77, 0x10, 0x6c, 0x05, 0xff, 0xd6, 0x10, 0x77, 0x19, 0xa4, 0xb6, 0xf6, 0x37, 0xf2, 0x82, 0x39, 0x59, 0x43, 0x42, 0x7e, 0xd6, 0xb0, 0x27, 0xed, 0x1a, 0x21, 0x21, 0x6f, 0x6a, 0x10, 0xd2, 0xae, 0x11, 0x12, 0xf2, 0x14, 0x21, 0xf6, 0xe4, 0x4d, 0xa1, 0xeb, 0x71, 0x14, 0x29, 0x9d, 0x3a, 0x9a, 0xb0, 0x5b, 0x1f, 0x44, 0x89, 0x50, 0x12, 0x22, 0x96, 0x83, 0x2b, 0x60, 0xb2, 0x42, 0x41, 0x53, 0x70, 0x5d, 0x07, 0x6b, 0x9d, 0x90, 0xd8, 0x87, 0x9c, 0x04, 0xe9, 0xcc, 0x19, 0x00, 0xfd, 0x20, 0xb4, 0xa5, 0xa4, 0x78, 0x4d, 0xdb, 0xac, 0xff, 0x8f, 0xbc, 0xe6, 0xec, 0xfa, 0x61, 0x33, 0x7e, 0x03, 0xf9, 0x2d, 0xce, 0xa7, 0xa9, 0x35, 0x85, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRecentTabIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3c, 0x08, 0x06, 0x00, 0x00, 0x00, 0xcb, 0xf7, 0x82, 0xff, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x33, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x5d, 0xda, 0x89, 0x00, 0x00, 0x04, 0x11, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9a, 0xcb, 0x6f, 0x0d, 0x51, 0x1c, 0xc7, 0x5d, 0x55, 0xb4, 0x44, 0x3c, 0x42, 0xbc, 0x42, 0x51, 0x8f, 0xa8, 0x56, 0xda, 0x84, 0x90, 0xc6, 0x42, 0x54, 0x22, 0xc1, 0x86, 0x58, 0x49, 0x2c, 0x88, 0x34, 0x22, 0x65, 0xc3, 0xdf, 0x80, 0xa5, 0xc4, 0x63, 0xd9, 0x74, 0x69, 0x61, 0x63, 0xd3, 0x04, 0xf1, 0x08, 0xb1, 0x13, 0x89, 0x54, 0x82, 0xa6, 0x08, 0x8a, 0x8d, 0x44, 0x28, 0x25, 0x15, 0xd7, 0xe7, 0x7b, 0x73, 0x67, 0x32, 0x3d, 0x9d, 0xa9, 0x39, 0x73, 0xe7, 0xdc, 0x19, 0xe5, 0x97, 0x7c, 0x73, 0xe6, 0x9c, 0x39, 0xbf, 0xdf, 0xf9, 0x7e, 0xe7, 0xbc, 0xe6, 0xdc, 0xb9, 0x85, 0x49, 0x01, 0x2b, 0x16, 0x8b, 0xb5, 0x64, 0x8f, 0x81, 0x6d, 0x60, 0x0d, 0x68, 0x04, 0xf5, 0xc0, 0x95, 0x7d, 0x24, 0xf0, 0xf3, 0x32, 0xae, 0x14, 0x0a, 0x85, 0xde, 0x54, 0x1a, 0x42, 0x48, 0x3b, 0x78, 0x02, 0xb2, 0xb4, 0xab, 0x34, 0xbe, 0x28, 0xa9, 0xa0, 0x82, 0x1c, 0x09, 0x30, 0x9f, 0x44, 0x4f, 0x68, 0xb6, 0xf2, 0x19, 0xdb, 0x4d, 0x7a, 0x68, 0x67, 0x12, 0x0e, 0x93, 0xcb, 0x4e, 0xe7, 0x48, 0xf3, 0x20, 0x44, 0x74, 0x3a, 0x78, 0xb8, 0x07, 0xca, 0xbc, 0xac, 0x92, 0x02, 0x8e, 0x75, 0x78, 0x7c, 0x05, 0xa5, 0x5e, 0x0a, 0x78, 0xdf, 0xe2, 0xfa, 0x3a, 0xf8, 0x10, 0x28, 0x4b, 0xf3, 0xb2, 0x86, 0x60, 0x0d, 0xe0, 0x20, 0x58, 0x09, 0x82, 0x76, 0x87, 0xde, 0xd9, 0x1e, 0x2c, 0x88, 0x75, 0x8d, 0x98, 0x66, 0x60, 0xda, 0xf9, 0x58, 0xce, 0x29, 0x54, 0xa2, 0xe1, 0x3a, 0xd0, 0x67, 0x10, 0x18, 0x4c, 0x14, 0x9a, 0x20, 0xfb, 0x8c, 0x40, 0xca, 0x2e, 0x4e, 0x14, 0x2c, 0xa1, 0x13, 0xed, 0x75, 0x86, 0x70, 0x98, 0x61, 0x1b, 0x4e, 0x73, 0xc6, 0x24, 0x3e, 0x4c, 0x17, 0xbf, 0xb3, 0x0d, 0x54, 0x61, 0xfd, 0x81, 0x10, 0x7f, 0xeb, 0x55, 0x4d, 0x62, 0xcc, 0xb9, 0xf2, 0x33, 0x24, 0xb0, 0xeb, 0xa2, 0x91, 0x90, 0x06, 0x4c, 0x5e, 0x21, 0x55, 0x46, 0x17, 0x79, 0xab, 0xd9, 0xe8, 0xd2, 0xbf, 0x34, 0x37, 0xa1, 0xc4, 0x4c, 0x49, 0xda, 0x09, 0x4c, 0x58, 0x0d, 0x03, 0x6d, 0x6e, 0x1b, 0x80, 0x5e, 0x7d, 0x96, 0x03, 0x2d, 0xb7, 0x49, 0x6c, 0x4e, 0x88, 0x53, 0x37, 0x6d, 0x7c, 0x2b, 0x97, 0x17, 0x49, 0xdf, 0x03, 0x6d, 0xec, 0xcf, 0x40, 0x2f, 0xf3, 0xda, 0xbb, 0x47, 0xb6, 0x6c, 0x38, 0x74, 0x81, 0xa0, 0x7d, 0xf6, 0xee, 0x45, 0xa5, 0x54, 0x6e, 0x01, 0x0f, 0x82, 0x4e, 0x55, 0xbe, 0x7e, 0x45, 0x7b, 0x7b, 0x4d, 0x7e, 0xd6, 0xc3, 0x8c, 0x20, 0x6d, 0x04, 0x79, 0x08, 0xb6, 0x9a, 0xc1, 0xaa, 0x98, 0xd7, 0x28, 0xb8, 0x06, 0x97, 0xa3, 0xc1, 0x36, 0xad, 0xc4, 0xe0, 0xac, 0xa1, 0x75, 0x11, 0x24, 0x1e, 0x9e, 0xc1, 0xc6, 0x53, 0xb8, 0x3e, 0x03, 0xa7, 0x79, 0x5e, 0x1c, 0x2b, 0x31, 0x38, 0xed, 0x06, 0x5b, 0x3c, 0xe7, 0x1c, 0xa4, 0x12, 0x72, 0xd2, 0xe3, 0x61, 0x2b, 0xa6, 0xd9, 0x73, 0xcc, 0x51, 0xda, 0xe2, 0x71, 0xb1, 0x15, 0xb3, 0xda, 0x73, 0xcc, 0x51, 0xea, 0x73, 0xb2, 0x15, 0xb3, 0x22, 0x47, 0x22, 0x3c, 0x2a, 0x0d, 0xde, 0x85, 0xad, 0x98, 0x5a, 0xcf, 0x31, 0x47, 0xa9, 0xcf, 0xc9, 0x56, 0x4c, 0x8e, 0x34, 0x8c, 0xa5, 0xe2, 0x6a, 0x89, 0xd5, 0x8e, 0x3d, 0x08, 0x94, 0x26, 0x35, 0x1d, 0xe5, 0xa7, 0xdb, 0x38, 0xbb, 0x10, 0xa3, 0x37, 0x88, 0x56, 0x5e, 0x37, 0x5e, 0xd8, 0x10, 0x31, 0xeb, 0xb2, 0x7f, 0xe8, 0x18, 0xaf, 0x93, 0xee, 0x26, 0xf3, 0x5e, 0x54, 0xde, 0xc5, 0x30, 0x1b, 0xa8, 0x54, 0x88, 0xc8, 0x12, 0xe3, 0x13, 0x89, 0xde, 0x34, 0x62, 0x9b, 0x6d, 0xcf, 0xc4, 0x39, 0x63, 0xb4, 0xf2, 0x54, 0x2f, 0xc0, 0xe0, 0x76, 0x6c, 0x16, 0xe1, 0x15, 0xd7, 0x51, 0xac, 0xdf, 0x07, 0x62, 0x9b, 0xad, 0x98, 0xb8, 0x73, 0xe0, 0x38, 0x0c, 0x84, 0xaa, 0x9a, 0x8b, 0x61, 0x56, 0x55, 0x01, 0xc1, 0xc6, 0xfe, 0x8b, 0x09, 0x3e, 0x8d, 0x3c, 0x5d, 0xdb, 0xce, 0x99, 0xb8, 0xdc, 0xbb, 0xa9, 0x78, 0x0f, 0xc4, 0x9d, 0x63, 0x61, 0x71, 0x75, 0x7a, 0x3d, 0x01, 0x62, 0xff, 0xe4, 0xe4, 0x42, 0xcc, 0x23, 0x96, 0xd5, 0xc3, 0x61, 0xec, 0x6c, 0xcb, 0x58, 0x15, 0xe7, 0xe2, 0xd3, 0x19, 0xd7, 0xcf, 0xc5, 0x9c, 0x59, 0x05, 0x89, 0x25, 0x71, 0x09, 0x44, 0xd5, 0x23, 0x86, 0x7a, 0xa4, 0x35, 0xea, 0x7e, 0x58, 0xb9, 0x8b, 0x9e, 0x99, 0x45, 0x43, 0x3a, 0xa3, 0xeb, 0xc7, 0x87, 0x4a, 0x6c, 0x29, 0xce, 0x8a, 0x15, 0xdb, 0x5c, 0x88, 0x51, 0xe3, 0x8a, 0xbb, 0x3e, 0x36, 0x8b, 0x94, 0x2a, 0xba, 0x18, 0x66, 0x29, 0x51, 0xb3, 0x0f, 0x33, 0xa1, 0xc4, 0xb8, 0x1a, 0x66, 0xbf, 0x78, 0xae, 0xaf, 0x41, 0xd4, 0xd2, 0xac, 0x77, 0xbc, 0x65, 0x20, 0xd5, 0x87, 0xe9, 0x42, 0xcc, 0x17, 0x48, 0x6e, 0x64, 0x79, 0x7e, 0x49, 0x1a, 0x69, 0x2c, 0x10, 0x3a, 0xbb, 0x3f, 0x06, 0x56, 0x67, 0x96, 0xc8, 0x80, 0xdc, 0x48, 0xf5, 0xc9, 0x94, 0x1b, 0xea, 0xff, 0x93, 0x10, 0xd5, 0xa3, 0x4e, 0x3f, 0x89, 0x7a, 0x2f, 0x35, 0x73, 0xd1, 0x33, 0x6d, 0x3c, 0xf5, 0x4b, 0x30, 0xbc, 0x0b, 0xc6, 0x1b, 0x66, 0x1d, 0xdc, 0xd7, 0x2e, 0x9f, 0x9a, 0x49, 0xcc, 0x77, 0x23, 0x5a, 0x3d, 0x64, 0x6a, 0x79, 0x72, 0x95, 0x7c, 0x33, 0xd1, 0x7f, 0x09, 0x84, 0xaa, 0x9a, 0x86, 0x99, 0xf9, 0xd5, 0x4a, 0xbf, 0xe4, 0x6f, 0x8e, 0x60, 0x11, 0xf5, 0xa4, 0x23, 0xaa, 0x57, 0xb7, 0x58, 0x62, 0x34, 0x76, 0x4d, 0xbb, 0x4c, 0xef, 0xac, 0x35, 0x0b, 0xf3, 0x9e, 0xd7, 0xa7, 0x73, 0x2d, 0x93, 0xfa, 0x86, 0xb9, 0xd0, 0x20, 0xeb, 0x2d, 0xaf, 0xc1, 0x4f, 0xe7, 0xfa, 0x16, 0x33, 0xd3, 0xa8, 0x97, 0x75, 0x76, 0x84, 0x29, 0x31, 0x55, 0x24, 0x4a, 0x67, 0x7a, 0x04, 0x1d, 0xe2, 0xba, 0x27, 0x6b, 0x56, 0x09, 0xdb, 0x1f, 0x23, 0x46, 0xa2, 0x6e, 0x80, 0x1d, 0x09, 0x03, 0x66, 0xe9, 0xe6, 0x8b, 0x29, 0xed, 0x33, 0x74, 0x93, 0x26, 0xf6, 0x2e, 0x70, 0x0a, 0x0c, 0x65, 0xc9, 0xac, 0x92, 0xb6, 0x4b, 0xc3, 0x2c, 0x18, 0x80, 0x21, 0xa7, 0x5f, 0x12, 0xf5, 0x75, 0x4c, 0x7b, 0x80, 0x76, 0xe9, 0xe0, 0x49, 0x6f, 0x0f, 0xf9, 0x05, 0x20, 0x4f, 0xe6, 0xf7, 0xcc, 0x18, 0x31, 0xe3, 0xb1, 0x44, 0xe8, 0x7d, 0xee, 0xb7, 0x8f, 0x57, 0x27, 0x83, 0x7b, 0xbe, 0x98, 0xd2, 0x30, 0xcb, 0x80, 0x80, 0x93, 0x26, 0xff, 0x69, 0x31, 0xda, 0x7b, 0xf2, 0x66, 0x3e, 0x27, 0xdb, 0x9e, 0x79, 0x93, 0x37, 0x25, 0xf0, 0xf1, 0x39, 0xd9, 0x8a, 0xa9, 0xf4, 0x47, 0x0a, 0x17, 0xcf, 0xc2, 0xe7, 0x64, 0x2b, 0xe6, 0xa9, 0x0b, 0x36, 0x15, 0xc6, 0xf4, 0x39, 0xd9, 0x2e, 0xcd, 0xd3, 0x68, 0xb8, 0x0f, 0x34, 0x56, 0x48, 0x20, 0x2d, 0xf7, 0x61, 0x02, 0x35, 0xb1, 0xe9, 0x97, 0x4e, 0xb5, 0x56, 0x3d, 0x83, 0xd3, 0x0f, 0x9c, 0xbb, 0xd2, 0x62, 0x92, 0x42, 0x9c, 0xb3, 0x9e, 0x90, 0xc4, 0xb1, 0xd8, 0x3c, 0xf7, 0x83, 0xb7, 0x20, 0x2b, 0x1b, 0xa2, 0xe1, 0xd3, 0x40, 0x87, 0x4b, 0xdf, 0xac, 0x86, 0x99, 0xef, 0xc5, 0x05, 0x81, 0x74, 0x14, 0x38, 0x02, 0x9a, 0x80, 0x5e, 0x7d, 0x1a, 0x40, 0x0d, 0x70, 0x61, 0x7a, 0x77, 0xd4, 0x5f, 0xb4, 0xf4, 0xf7, 0x2c, 0x4d, 0xf8, 0x1e, 0x7a, 0xc4, 0x5f, 0xc5, 0xc8, 0x4f, 0x3c, 0xfb, 0x0d, 0x29, 0x80, 0xc0, 0x68, 0xf7, 0x99, 0xb2, 0x13, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXMoveIcon[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x7a, 0x91, 0xda, 0x8e, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xdf, 0x8e, 0x45, 0x3c, 0x00, 0x00, 0x02, 0xa9, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x97, 0x3b, 0x68, 0x14, 0x51, 0x14, 0x86, 0x77, 0x4c, 0x14, 0x1f, 0x60, 0x20, 0x22, 0x82, 0x8f, 0x80, 0x10, 0xd1, 0xa8, 0x08, 0x0a, 0x42, 0x8a, 0x58, 0x18, 0x11, 0x8c, 0xa0, 0x95, 0x85, 0x69, 0xd2, 0x89, 0xa0, 0x8d, 0xd8, 0x58, 0x58, 0x2b, 0xd8, 0x89, 0x95, 0x85, 0xd8, 0x68, 0x21, 0xd8, 0x88, 0x92, 0x20, 0x5a, 0xd9, 0x58, 0x85, 0x24, 0x45, 0x50, 0x41, 0xc4, 0x80, 0x0a, 0xd1, 0x90, 0xd7, 0x82, 0x12, 0x0c, 0x71, 0xfd, 0x4e, 0x9c, 0xbb, 0x9c, 0x3d, 0xf7, 0xcc, 0xce, 0xce, 0x82, 0x58, 0xb8, 0x17, 0x7e, 0xce, 0xeb, 0xff, 0xcf, 0xbd, 0x73, 0x77, 0x76, 0xe6, 0x4e, 0xa9, 0xf4, 0x0f, 0x46, 0xa5, 0x52, 0x59, 0x03, 0x8e, 0x8b, 0x6d, 0x7a, 0x7a, 0xc4, 0x9d, 0xe0, 0x04, 0x38, 0x07, 0xba, 0x41, 0xd2, 0x4c, 0x33, 0x74, 0xb2, 0x98, 0xe7, 0x40, 0xc6, 0x30, 0x28, 0xd6, 0x07, 0xc1, 0x25, 0x30, 0x05, 0xec, 0x58, 0x24, 0xf1, 0x00, 0x74, 0x16, 0x59, 0x18, 0xfc, 0x6b, 0xa6, 0xd1, 0xd5, 0x86, 0xf4, 0x88, 0xb6, 0x01, 0xb9, 0x82, 0xbc, 0xf1, 0x05, 0xc2, 0xc9, 0x46, 0x9a, 0xc2, 0x3b, 0x02, 0x7e, 0x9a, 0x86, 0x4b, 0xc4, 0x87, 0x72, 0xf5, 0x90, 0x5e, 0x19, 0x61, 0xbd, 0x50, 0x26, 0xd9, 0x57, 0xaf, 0x29, 0xf5, 0x8d, 0xe0, 0x5d, 0x46, 0x93, 0x49, 0xf2, 0xeb, 0x33, 0xf5, 0x14, 0x07, 0x8d, 0x70, 0x8e, 0x58, 0x7e, 0xba, 0x03, 0x60, 0x3b, 0x38, 0x0d, 0xec, 0x82, 0x5f, 0x66, 0x36, 0xa4, 0x00, 0xff, 0x2e, 0xa8, 0x37, 0xee, 0xb8, 0x7a, 0x14, 0x6b, 0xc1, 0x67, 0xa5, 0xfc, 0x86, 0xbf, 0xcb, 0x92, 0xc9, 0xc9, 0xcd, 0x79, 0x4f, 0xf1, 0xc4, 0x3d, 0x6b, 0x79, 0x12, 0x4b, 0xde, 0xf0, 0xb2, 0xc2, 0x53, 0x91, 0x1e, 0x66, 0x8f, 0x61, 0x5f, 0x88, 0x48, 0x69, 0x02, 0x5e, 0x07, 0xf8, 0xaa, 0xf8, 0xb7, 0x2d, 0x97, 0x5a, 0x3b, 0xf8, 0xa4, 0x38, 0xf5, 0xdc, 0x8f, 0xde, 0x73, 0x60, 0xbf, 0x69, 0xfa, 0xd8, 0xc4, 0xd5, 0x30, 0x49, 0x92, 0x45, 0x82, 0x17, 0xd5, 0x44, 0xa9, 0x64, 0xb5, 0x52, 0x92, 0xbf, 0xf5, 0xa4, 0xe2, 0xd4, 0x73, 0xdf, 0xb4, 0x3b, 0xd5, 0x1e, 0x95, 0x9b, 0x66, 0xd2, 0x05, 0x15, 0x7b, 0xee, 0x5b, 0x95, 0x8c, 0x16, 0x84, 0x7e, 0x99, 0xfa, 0x00, 0xdb, 0xb2, 0x19, 0xdb, 0x06, 0x7a, 0xc1, 0x08, 0x08, 0xa3, 0x1f, 0x67, 0x02, 0xac, 0xc0, 0x2d, 0x7b, 0x0b, 0xda, 0x19, 0x98, 0xd8, 0x39, 0xe5, 0x67, 0xb9, 0xb3, 0xaa, 0xb0, 0x83, 0x89, 0xe9, 0x9b, 0x54, 0x54, 0x6e, 0xd5, 0x95, 0xc9, 0xc4, 0xa1, 0xbe, 0x6a, 0x55, 0xbd, 0x4c, 0x6d, 0x3e, 0xc4, 0xde, 0x4f, 0x16, 0x6a, 0xcd, 0xda, 0x62, 0x4f, 0x5e, 0x33, 0xcb, 0xdf, 0x58, 0x90, 0x99, 0xa2, 0x58, 0xd8, 0x5a, 0x50, 0xde, 0x7e, 0xb5, 0x76, 0xa8, 0xb5, 0x43, 0x79, 0x3b, 0x90, 0x57, 0x6f, 0xdd, 0x43, 0xff, 0xe5, 0x0e, 0x55, 0xdf, 0x43, 0xe9, 0xd5, 0x6f, 0xc8, 0xd9, 0x05, 0x5b, 0xaf, 0x7d, 0x5f, 0xf2, 0xb2, 0xdb, 0x03, 0x9e, 0x80, 0x0f, 0x29, 0xca, 0xd8, 0x30, 0xe4, 0x68, 0x1a, 0xf2, 0xaf, 0xf1, 0xfb, 0xed, 0x64, 0xe4, 0x0e, 0x06, 0x72, 0x6a, 0x0f, 0x5b, 0x8e, 0x8e, 0xe1, 0xc8, 0xc9, 0x33, 0x8c, 0x1f, 0x38, 0xb5, 0xf7, 0x31, 0x89, 0xf3, 0xa1, 0xda, 0x80, 0x8d, 0x8e, 0x99, 0x68, 0xd6, 0x81, 0x65, 0xa5, 0xbd, 0xa2, 0x17, 0x60, 0x7d, 0x78, 0x8f, 0x14, 0x77, 0xdc, 0xd6, 0xe5, 0x38, 0x20, 0x47, 0xd6, 0x51, 0x45, 0xca, 0x72, 0x17, 0x28, 0x74, 0x45, 0x0d, 0x48, 0x90, 0x9f, 0x50, 0xa2, 0x59, 0xfc, 0x2d, 0x19, 0xbc, 0x5e, 0x6a, 0xbf, 0x14, 0xf7, 0xbe, 0xc7, 0x93, 0x86, 0x7b, 0xc1, 0x77, 0x45, 0xf4, 0xdc, 0x41, 0x57, 0xfc, 0x67, 0x41, 0x67, 0x8c, 0xe0, 0x3d, 0x71, 0x5f, 0xe0, 0xe3, 0xb7, 0x01, 0xf9, 0x25, 0xe6, 0x41, 0x18, 0x4b, 0x38, 0xdd, 0x81, 0x13, 0x59, 0x8a, 0x17, 0x03, 0xd3, 0xb1, 0x0f, 0x23, 0x81, 0x49, 0xa0, 0x79, 0xe6, 0xe8, 0xe4, 0x6b, 0x65, 0x0c, 0x78, 0x17, 0x7b, 0xc3, 0xb4, 0x88, 0x43, 0x84, 0x4f, 0x9d, 0xa6, 0x53, 0xe4, 0x3a, 0x62, 0x76, 0x6d, 0x06, 0x4e, 0x17, 0x18, 0x77, 0xf4, 0x5e, 0x6a, 0x84, 0xe4, 0xa6, 0xda, 0x0e, 0x4e, 0x04, 0x69, 0x2b, 0x98, 0x56, 0x1d, 0x56, 0xf0, 0x8f, 0x39, 0x54, 0x37, 0x05, 0x57, 0x6e, 0xf0, 0x5b, 0x40, 0x74, 0xde, 0x90, 0x7f, 0xd5, 0x65, 0x57, 0x9c, 0x26, 0xa3, 0xe3, 0x26, 0x82, 0x01, 0x6a, 0xc3, 0x40, 0x6a, 0x37, 0x39, 0xef, 0x5e, 0x4f, 0xb9, 0x0d, 0x1b, 0x7a, 0xc8, 0x77, 0xdc, 0x51, 0x20, 0x8f, 0x80, 0xdd, 0x40, 0x3e, 0x04, 0xc6, 0xc0, 0x28, 0xfd, 0x66, 0xb0, 0xc5, 0x06, 0x0d, 0xfb, 0xc0, 0x10, 0x88, 0x16, 0x5c, 0xac, 0x53, 0x71, 0xf6, 0x6f, 0x9b, 0x02, 0x54, 0xeb, 0x7d, 0xaa, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXMoveIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x7a, 0x91, 0xda, 0x8e, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xdf, 0x8e, 0x45, 0x3c, 0x00, 0x00, 0x02, 0xa9, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x97, 0x3b, 0x68, 0x14, 0x51, 0x14, 0x86, 0x77, 0x4c, 0x14, 0x1f, 0x60, 0x20, 0x22, 0x82, 0x8f, 0x80, 0x10, 0xd1, 0xa8, 0x08, 0x0a, 0x42, 0x8a, 0x58, 0x18, 0x11, 0x8c, 0xa0, 0x95, 0x85, 0x69, 0xd2, 0x89, 0xa0, 0x8d, 0xd8, 0x58, 0x58, 0x2b, 0xd8, 0x89, 0x95, 0x85, 0xd8, 0x68, 0x21, 0xd8, 0x88, 0x92, 0x20, 0x5a, 0xd9, 0x58, 0x85, 0x24, 0x45, 0x50, 0x41, 0xc4, 0x80, 0x0a, 0xd1, 0x90, 0xd7, 0x82, 0x12, 0x0c, 0x71, 0xfd, 0x4e, 0x9c, 0xbb, 0x9c, 0x3d, 0xf7, 0xcc, 0xce, 0xce, 0x82, 0x58, 0xb8, 0x17, 0x7e, 0xce, 0xeb, 0xff, 0xcf, 0xbd, 0x73, 0x77, 0x76, 0xe6, 0x4e, 0xa9, 0xf4, 0x0f, 0x46, 0xa5, 0x52, 0x59, 0x03, 0x8e, 0x8b, 0x6d, 0x7a, 0x7a, 0xc4, 0x9d, 0xe0, 0x04, 0x38, 0x07, 0xba, 0x41, 0xd2, 0x4c, 0x33, 0x74, 0xb2, 0x98, 0xe7, 0x40, 0xc6, 0x30, 0x28, 0xd6, 0x07, 0xc1, 0x25, 0x30, 0x05, 0xec, 0x58, 0x24, 0xf1, 0x00, 0x74, 0x16, 0x59, 0x18, 0xfc, 0x6b, 0xa6, 0xd1, 0xd5, 0x86, 0xf4, 0x88, 0xb6, 0x01, 0xb9, 0x82, 0xbc, 0xf1, 0x05, 0xc2, 0xc9, 0x46, 0x9a, 0xc2, 0x3b, 0x02, 0x7e, 0x9a, 0x86, 0x4b, 0xc4, 0x87, 0x72, 0xf5, 0x90, 0x5e, 0x19, 0x61, 0xbd, 0x50, 0x26, 0xd9, 0x57, 0xaf, 0x29, 0xf5, 0x8d, 0xe0, 0x5d, 0x46, 0x93, 0x49, 0xf2, 0xeb, 0x33, 0xf5, 0x14, 0x07, 0x8d, 0x70, 0x8e, 0x58, 0x7e, 0xba, 0x03, 0x60, 0x3b, 0x38, 0x0d, 0xec, 0x82, 0x5f, 0x66, 0x36, 0xa4, 0x00, 0xff, 0x2e, 0xa8, 0x37, 0xee, 0xb8, 0x7a, 0x14, 0x6b, 0xc1, 0x67, 0xa5, 0xfc, 0x86, 0xbf, 0xcb, 0x92, 0xc9, 0xc9, 0xcd, 0x79, 0x4f, 0xf1, 0xc4, 0x3d, 0x6b, 0x79, 0x12, 0x4b, 0xde, 0xf0, 0xb2, 0xc2, 0x53, 0x91, 0x1e, 0x66, 0x8f, 0x61, 0x5f, 0x88, 0x48, 0x69, 0x02, 0x5e, 0x07, 0xf8, 0xaa, 0xf8, 0xb7, 0x2d, 0x97, 0x5a, 0x3b, 0xf8, 0xa4, 0x38, 0xf5, 0xdc, 0x8f, 0xde, 0x73, 0x60, 0xbf, 0x69, 0xfa, 0xd8, 0xc4, 0xd5, 0x30, 0x49, 0x92, 0x45, 0x82, 0x17, 0xd5, 0x44, 0xa9, 0x64, 0xb5, 0x52, 0x92, 0xbf, 0xf5, 0xa4, 0xe2, 0xd4, 0x73, 0xdf, 0xb4, 0x3b, 0xd5, 0x1e, 0x95, 0x9b, 0x66, 0xd2, 0x05, 0x15, 0x7b, 0xee, 0x5b, 0x95, 0x8c, 0x16, 0x84, 0x7e, 0x99, 0xfa, 0x00, 0xdb, 0xb2, 0x19, 0xdb, 0x06, 0x7a, 0xc1, 0x08, 0x08, 0xa3, 0x1f, 0x67, 0x02, 0xac, 0xc0, 0x2d, 0x7b, 0x0b, 0xda, 0x19, 0x98, 0xd8, 0x39, 0xe5, 0x67, 0xb9, 0xb3, 0xaa, 0xb0, 0x83, 0x89, 0xe9, 0x9b, 0x54, 0x54, 0x6e, 0xd5, 0x95, 0xc9, 0xc4, 0xa1, 0xbe, 0x6a, 0x55, 0xbd, 0x4c, 0x6d, 0x3e, 0xc4, 0xde, 0x4f, 0x16, 0x6a, 0xcd, 0xda, 0x62, 0x4f, 0x5e, 0x33, 0xcb, 0xdf, 0x58, 0x90, 0x99, 0xa2, 0x58, 0xd8, 0x5a, 0x50, 0xde, 0x7e, 0xb5, 0x76, 0xa8, 0xb5, 0x43, 0x79, 0x3b, 0x90, 0x57, 0x6f, 0xdd, 0x43, 0xff, 0xe5, 0x0e, 0x55, 0xdf, 0x43, 0xe9, 0xd5, 0x6f, 0xc8, 0xd9, 0x05, 0x5b, 0xaf, 0x7d, 0x5f, 0xf2, 0xb2, 0xdb, 0x03, 0x9e, 0x80, 0x0f, 0x29, 0xca, 0xd8, 0x30, 0xe4, 0x68, 0x1a, 0xf2, 0xaf, 0xf1, 0xfb, 0xed, 0x64, 0xe4, 0x0e, 0x06, 0x72, 0x6a, 0x0f, 0x5b, 0x8e, 0x8e, 0xe1, 0xc8, 0xc9, 0x33, 0x8c, 0x1f, 0x38, 0xb5, 0xf7, 0x31, 0x89, 0xf3, 0xa1, 0xda, 0x80, 0x8d, 0x8e, 0x99, 0x68, 0xd6, 0x81, 0x65, 0xa5, 0xbd, 0xa2, 0x17, 0x60, 0x7d, 0x78, 0x8f, 0x14, 0x77, 0xdc, 0xd6, 0xe5, 0x38, 0x20, 0x47, 0xd6, 0x51, 0x45, 0xca, 0x72, 0x17, 0x28, 0x74, 0x45, 0x0d, 0x48, 0x90, 0x9f, 0x50, 0xa2, 0x59, 0xfc, 0x2d, 0x19, 0xbc, 0x5e, 0x6a, 0xbf, 0x14, 0xf7, 0xbe, 0xc7, 0x93, 0x86, 0x7b, 0xc1, 0x77, 0x45, 0xf4, 0xdc, 0x41, 0x57, 0xfc, 0x67, 0x41, 0x67, 0x8c, 0xe0, 0x3d, 0x71, 0x5f, 0xe0, 0xe3, 0xb7, 0x01, 0xf9, 0x25, 0xe6, 0x41, 0x18, 0x4b, 0x38, 0xdd, 0x81, 0x13, 0x59, 0x8a, 0x17, 0x03, 0xd3, 0xb1, 0x0f, 0x23, 0x81, 0x49, 0xa0, 0x79, 0xe6, 0xe8, 0xe4, 0x6b, 0x65, 0x0c, 0x78, 0x17, 0x7b, 0xc3, 0xb4, 0x88, 0x43, 0x84, 0x4f, 0x9d, 0xa6, 0x53, 0xe4, 0x3a, 0x62, 0x76, 0x6d, 0x06, 0x4e, 0x17, 0x18, 0x77, 0xf4, 0x5e, 0x6a, 0x84, 0xe4, 0xa6, 0xda, 0x0e, 0x4e, 0x04, 0x69, 0x2b, 0x98, 0x56, 0x1d, 0x56, 0xf0, 0x8f, 0x39, 0x54, 0x37, 0x05, 0x57, 0x6e, 0xf0, 0x5b, 0x40, 0x74, 0xde, 0x90, 0x7f, 0xd5, 0x65, 0x57, 0x9c, 0x26, 0xa3, 0xe3, 0x26, 0x82, 0x01, 0x6a, 0xc3, 0x40, 0x6a, 0x37, 0x39, 0xef, 0x5e, 0x4f, 0xb9, 0x0d, 0x1b, 0x7a, 0xc8, 0x77, 0xdc, 0x51, 0x20, 0x8f, 0x80, 0xdd, 0x40, 0x3e, 0x04, 0xc6, 0xc0, 0x28, 0xfd, 0x66, 0xb0, 0xc5, 0x06, 0x0d, 0xfb, 0xc0, 0x10, 0x88, 0x16, 0x5c, 0xac, 0x53, 0x71, 0xf6, 0x6f, 0x9b, 0x02, 0x54, 0xeb, 0x7d, 0xaa, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXMoveIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x5a, 0x1c, 0x89, 0xc0, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x03, 0xdb, 0x04, 0x00, 0x00, 0x04, 0x37, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9a, 0x5b, 0x88, 0x8d, 0x51, 0x14, 0xc7, 0xcf, 0x71, 0x8b, 0x71, 0x9b, 0xdc, 0x72, 0x8f, 0x22, 0x11, 0x73, 0x49, 0x4a, 0xb9, 0x24, 0x45, 0xd2, 0x24, 0x25, 0xc2, 0x8b, 0xbc, 0xe0, 0x49, 0x52, 0xca, 0x25, 0xa4, 0xa6, 0x14, 0x4f, 0xca, 0x93, 0xa4, 0x10, 0x29, 0xb7, 0x21, 0x2f, 0xf2, 0xe4, 0x49, 0x79, 0x10, 0xc6, 0x25, 0xa2, 0x91, 0x72, 0x1d, 0x63, 0x34, 0xc8, 0x8c, 0xdb, 0xf8, 0xad, 0xa9, 0x73, 0x7c, 0xdf, 0x3e, 0x6b, 0xed, 0xfd, 0x7d, 0xc7, 0xd0, 0x51, 0x67, 0xd5, 0xbf, 0xd9, 0xfb, 0xbf, 0xd6, 0xfa, 0xaf, 0xfd, 0x7d, 0xfb, 0x3b, 0xfb, 0x7c, 0xb3, 0xf7, 0xc9, 0x64, 0xfe, 0x43, 0xeb, 0xec, 0xec, 0xac, 0xf8, 0x0f, 0x87, 0xed, 0x1f, 0x32, 0x17, 0xb5, 0x13, 0x7c, 0x03, 0xf7, 0xc0, 0x18, 0x2b, 0x3a, 0x6b, 0x39, 0x42, 0x3c, 0xa2, 0x7d, 0x88, 0x99, 0x0f, 0xa6, 0x82, 0x91, 0x60, 0x00, 0x78, 0x03, 0x5e, 0x82, 0xeb, 0xd9, 0x6c, 0xb6, 0x89, 0xbf, 0xdd, 0x6a, 0xd4, 0x5c, 0x8e, 0xe0, 0xc5, 0x88, 0xe8, 0x0d, 0xda, 0xf3, 0xa8, 0xf5, 0x23, 0xc2, 0x15, 0xd7, 0x44, 0x7c, 0x22, 0x38, 0x0e, 0xda, 0x80, 0xcf, 0xe4, 0x8e, 0x6e, 0x00, 0x3d, 0x8b, 0xab, 0x14, 0xcf, 0x42, 0x67, 0x14, 0x78, 0x07, 0x5c, 0xdb, 0x13, 0x8f, 0x4c, 0xd9, 0x43, 0x2d, 0x0b, 0xf6, 0x82, 0x76, 0x90, 0xc6, 0xee, 0x13, 0x5c, 0x9d, 0xb2, 0x5c, 0x2c, 0x9c, 0x7c, 0xa9, 0x7d, 0xd5, 0x28, 0x2a, 0x8f, 0xe5, 0xec, 0x58, 0x42, 0xd2, 0x0e, 0x89, 0x7d, 0xc0, 0x29, 0x43, 0x38, 0x09, 0x2d, 0xb3, 0xbb, 0x38, 0x69, 0x3d, 0x37, 0x8e, 0xdc, 0x2d, 0x81, 0x22, 0x4f, 0xf0, 0xcb, 0x47, 0x21, 0x9d, 0x91, 0xb4, 0x3b, 0x20, 0x9c, 0xc4, 0x2d, 0x17, 0x37, 0x3a, 0x5d, 0xe5, 0x4c, 0x86, 0x9c, 0x2a, 0x90, 0xe4, 0x29, 0x39, 0x96, 0x4a, 0x1b, 0xd1, 0xf1, 0xe0, 0x33, 0xb0, 0xec, 0x15, 0x8e, 0x2b, 0xe0, 0x28, 0xb8, 0x09, 0xbe, 0x00, 0xcb, 0x4e, 0xa7, 0x29, 0x8e, 0x48, 0x5f, 0xd0, 0x68, 0x89, 0x29, 0xfc, 0x8a, 0xc4, 0xfa, 0x24, 0x9f, 0x55, 0x04, 0x84, 0x7a, 0x04, 0x66, 0xb9, 0x42, 0x70, 0xbd, 0xc0, 0x76, 0xf0, 0x15, 0x68, 0xb6, 0xc0, 0xcd, 0xb1, 0xfa, 0x24, 0x1f, 0xd2, 0x04, 0x3c, 0x5c, 0x0b, 0xbe, 0xf0, 0x53, 0x41, 0xd0, 0x42, 0x43, 0xa4, 0x01, 0xde, 0xfb, 0x25, 0x89, 0xbf, 0x16, 0x48, 0x21, 0xd7, 0x64, 0xb5, 0xec, 0x65, 0x5d, 0x4c, 0x8e, 0x27, 0x66, 0x09, 0xf8, 0xe9, 0x26, 0x27, 0xe8, 0x5f, 0x23, 0xc6, 0xff, 0x35, 0x46, 0xc0, 0x39, 0x45, 0xe8, 0x03, 0xdc, 0x88, 0xdc, 0x00, 0x7c, 0x7f, 0x89, 0xdb, 0xa4, 0xe4, 0x0b, 0xb5, 0x28, 0x90, 0x57, 0x49, 0x8c, 0x3c, 0xe2, 0xc5, 0xda, 0x66, 0x53, 0x1f, 0x45, 0x59, 0x62, 0x9b, 0x15, 0xe5, 0xad, 0x66, 0x92, 0xe3, 0x20, 0xb7, 0x07, 0xb8, 0xa5, 0x68, 0xec, 0x77, 0x42, 0x63, 0x5d, 0xe2, 0x77, 0x28, 0x39, 0x69, 0xa8, 0xb7, 0x3d, 0x62, 0x8a, 0xf1, 0xce, 0x14, 0xba, 0xc3, 0xe2, 0x54, 0x57, 0xaf, 0x41, 0xe1, 0x54, 0x8a, 0x37, 0x82, 0x9f, 0x38, 0x2e, 0x29, 0xce, 0x79, 0x0a, 0x17, 0xa5, 0xbc, 0x8f, 0x79, 0x34, 0xd0, 0x68, 0x57, 0xf8, 0x9e, 0xf5, 0x51, 0x4a, 0x52, 0x3b, 0xdc, 0x33, 0x85, 0xf7, 0x51, 0x0f, 0x15, 0x67, 0xe8, 0x03, 0x5e, 0x4f, 0x4e, 0x33, 0x90, 0xd7, 0xb5, 0xe8, 0x9b, 0xcb, 0x2a, 0xfa, 0x83, 0x41, 0xd4, 0xae, 0xd2, 0x79, 0x1e, 0x21, 0x5a, 0x68, 0x9f, 0x8b, 0xf4, 0xe3, 0x4d, 0xe6, 0x7d, 0xa5, 0x32, 0xf7, 0x77, 0xe2, 0x51, 0xe1, 0x1e, 0x1a, 0xd3, 0x15, 0x9d, 0xb6, 0x70, 0x66, 0x61, 0x04, 0x3a, 0xb2, 0x12, 0xbb, 0x56, 0x57, 0x18, 0x99, 0xc9, 0xf8, 0x1e, 0x45, 0xf7, 0xce, 0x48, 0x7e, 0xab, 0x26, 0x12, 0xe0, 0xb4, 0x9c, 0x81, 0x8c, 0xce, 0x57, 0x3b, 0x20, 0x19, 0x76, 0xfb, 0xc4, 0xfd, 0x4b, 0x66, 0x58, 0x3b, 0x14, 0xf1, 0x57, 0xf5, 0x7d, 0x17, 0x16, 0x1a, 0x58, 0x49, 0xfb, 0xcb, 0x17, 0x56, 0xd2, 0xd3, 0xa3, 0x0c, 0xae, 0x3c, 0x63, 0xca, 0x4d, 0x29, 0x69, 0xaa, 0x3c, 0x63, 0x25, 0x3d, 0x3d, 0xca, 0xe0, 0xca, 0x33, 0xa6, 0xdc, 0x94, 0x92, 0xa6, 0xca, 0x33, 0x56, 0xd2, 0xd3, 0xa3, 0x0c, 0xae, 0x3c, 0x63, 0xca, 0x4d, 0x29, 0x69, 0xaa, 0x3c, 0x63, 0x25, 0x3d, 0x3d, 0xca, 0xe0, 0xca, 0x33, 0xa6, 0xdc, 0x94, 0xa4, 0x54, 0x87, 0x11, 0xe8, 0xdb, 0x6f, 0x31, 0x52, 0x32, 0xbd, 0x15, 0x87, 0xec, 0xc3, 0x14, 0x58, 0x97, 0x38, 0xff, 0xa6, 0xf7, 0xc7, 0x53, 0x0b, 0x2a, 0x23, 0x11, 0x55, 0x91, 0x76, 0xae, 0x39, 0x94, 0xd8, 0xe8, 0x1e, 0xc3, 0x37, 0x1c, 0x8d, 0xec, 0x46, 0xc9, 0x99, 0x98, 0x65, 0xef, 0x71, 0xc8, 0x6e, 0x95, 0xfb, 0x74, 0x4c, 0x82, 0xbb, 0x6f, 0x25, 0xb9, 0x3c, 0x75, 0xe5, 0xa2, 0xc6, 0xb9, 0x3c, 0x7d, 0xd9, 0xbc, 0x29, 0x34, 0x12, 0x06, 0x80, 0x26, 0x50, 0xac, 0x7d, 0x27, 0x31, 0xb4, 0x01, 0xaa, 0xed, 0x4f, 0xae, 0x2c, 0x1c, 0x8d, 0xcd, 0x50, 0x63, 0x9a, 0x31, 0xc0, 0xb1, 0x5a, 0x96, 0xdc, 0x45, 0x99, 0xa5, 0x09, 0x9a, 0x33, 0x21, 0x27, 0xdb, 0x63, 0x33, 0x02, 0xb1, 0xda, 0xee, 0xd6, 0xcc, 0x40, 0x8e, 0xeb, 0xd6, 0xe2, 0xe5, 0x69, 0x78, 0xe1, 0x06, 0xe6, 0xfb, 0xdc, 0x09, 0x39, 0x2d, 0x29, 0xd6, 0xe4, 0x24, 0x46, 0xdb, 0x83, 0x8c, 0xea, 0xcb, 0xb9, 0xb1, 0x6b, 0x1f, 0x21, 0x42, 0xfb, 0x8b, 0x5d, 0x1a, 0xc4, 0xc9, 0xf9, 0xdc, 0x63, 0x57, 0x80, 0xfe, 0x85, 0x7c, 0x11, 0xad, 0x41, 0xc0, 0x38, 0xd0, 0xaa, 0x24, 0x26, 0xa1, 0x36, 0x69, 0x9a, 0x51, 0x0e, 0x11, 0xd9, 0x5b, 0xfc, 0xa1, 0x88, 0x9d, 0x89, 0xc6, 0x59, 0x6d, 0xf2, 0xe4, 0xf4, 0x46, 0xb3, 0x75, 0x56, 0x4e, 0x9e, 0x27, 0x6b, 0xb5, 0x96, 0x19, 0xe0, 0x2e, 0xe7, 0x05, 0x02, 0x0d, 0x74, 0x8e, 0x18, 0x5a, 0xe7, 0xe1, 0x87, 0x6a, 0xe9, 0xf0, 0x32, 0x53, 0x07, 0x80, 0x76, 0xea, 0x22, 0x67, 0x02, 0xee, 0x82, 0xa4, 0xc9, 0x74, 0x9d, 0x1e, 0x9e, 0x24, 0x38, 0xa9, 0xbd, 0x26, 0x70, 0xb8, 0xae, 0x54, 0xc8, 0x12, 0x2b, 0x2b, 0xaa, 0x76, 0xac, 0x24, 0xf5, 0xe4, 0x64, 0xe5, 0x20, 0x58, 0x0b, 0x6a, 0xc0, 0x32, 0xb0, 0x0f, 0xdc, 0x05, 0x96, 0xcd, 0x29, 0xac, 0x62, 0x30, 0x28, 0x0c, 0x02, 0x4d, 0x96, 0x92, 0xc3, 0x2f, 0x35, 0x64, 0x4c, 0x9a, 0xfc, 0x8d, 0x8e, 0x46, 0xb1, 0xdd, 0x13, 0x66, 0x11, 0xcb, 0x41, 0xa5, 0xb9, 0x40, 0x96, 0x70, 0x9f, 0x1d, 0xb6, 0xf2, 0x7d, 0x3c, 0x82, 0x72, 0xac, 0x24, 0x07, 0x73, 0x7f, 0x62, 0xcf, 0x49, 0xf6, 0x2e, 0x56, 0xe6, 0x18, 0x48, 0xac, 0xf7, 0x54, 0x7e, 0x80, 0xaf, 0x9f, 0x99, 0x1c, 0x70, 0x90, 0xfb, 0x27, 0xbf, 0x40, 0xb8, 0x4d, 0x7e, 0xa2, 0x95, 0x54, 0x1d, 0x06, 0xc9, 0x72, 0x8e, 0x2c, 0x07, 0xe5, 0xae, 0x75, 0x40, 0xd4, 0xa8, 0x49, 0x29, 0x49, 0x74, 0x56, 0x81, 0xa7, 0x6e, 0x01, 0xa3, 0x2f, 0xbf, 0x38, 0xd8, 0x05, 0x8a, 0xbe, 0xa1, 0xf9, 0xe1, 0x21, 0x32, 0x19, 0x7c, 0x02, 0x51, 0xdb, 0x96, 0x0f, 0xe8, 0x86, 0x06, 0xc2, 0x32, 0x7b, 0x75, 0x40, 0x56, 0x4c, 0xf9, 0xa1, 0x4b, 0xee, 0x2b, 0x47, 0xbe, 0x1b, 0xe5, 0xa2, 0x2f, 0x80, 0xf5, 0x60, 0x48, 0x37, 0x94, 0xfb, 0x2d, 0x81, 0xe0, 0x1a, 0x90, 0xfb, 0xbc, 0x35, 0xd0, 0xfe, 0xab, 0x27, 0x24, 0x52, 0x99, 0x1a, 0xc5, 0xbc, 0x1c, 0xff, 0x1e, 0x74, 0xd2, 0x16, 0x85, 0xaa, 0x81, 0x2c, 0xbf, 0xda, 0x9b, 0x75, 0x52, 0x99, 0x7f, 0x1e, 0xf7, 0x0b, 0x20, 0xe5, 0xc9, 0x35, 0x1c, 0xb8, 0x12, 0x47, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXSelectIcon[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x33, 0x17, 0x79, 0x08, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xd7, 0xce, 0x93, 0x00, 0x00, 0x04, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x99, 0x5b, 0x88, 0x0e, 0x61, 0x18, 0xc7, 0x7d, 0xd6, 0x3a, 0x6c, 0x12, 0x42, 0x24, 0x17, 0x48, 0xb1, 0x21, 0x09, 0x37, 0x5c, 0x2d, 0xe5, 0x74, 0x23, 0x11, 0x39, 0xad, 0xc3, 0x86, 0x12, 0x17, 0x8b, 0x52, 0xd2, 0xda, 0xa4, 0xb4, 0x29, 0xa7, 0x24, 0xe4, 0xc2, 0x31, 0x57, 0x12, 0x52, 0x88, 0x1b, 0x09, 0xed, 0x0d, 0xc9, 0x46, 0x88, 0x9c, 0x6e, 0x24, 0xe7, 0x8d, 0x65, 0xf7, 0xf3, 0x7b, 0x3e, 0x33, 0xdb, 0xe3, 0x6d, 0x66, 0xbe, 0x99, 0xf9, 0xde, 0xd9, 0xa1, 0xe6, 0xa9, 0xff, 0xf7, 0x3e, 0xef, 0x73, 0x7e, 0xde, 0x99, 0xf9, 0x66, 0xe6, 0x9d, 0x4e, 0x9d, 0x32, 0xca, 0x56, 0x20, 0x5b, 0x81, 0x6c, 0x05, 0xb2, 0x15, 0xc8, 0x56, 0x20, 0xd9, 0x15, 0xc8, 0xe7, 0xf3, 0xd3, 0xc1, 0x33, 0xb0, 0x3d, 0xd9, 0x4c, 0x29, 0x45, 0xa7, 0xb1, 0x8b, 0x40, 0xe8, 0x07, 0x28, 0x4b, 0xa9, 0x0c, 0xcf, 0xb4, 0x9d, 0x3d, 0xa5, 0xd1, 0x85, 0xdd, 0x1d, 0x97, 0xae, 0x8c, 0xb6, 0x62, 0x46, 0xaf, 0xc2, 0xc3, 0xe3, 0x9f, 0x2a, 0xc6, 0xa3, 0xbe, 0x92, 0x45, 0xbe, 0x0d, 0x72, 0xaa, 0x55, 0x80, 0x31, 0x25, 0x67, 0xf0, 0x08, 0x40, 0xdc, 0xc1, 0x60, 0x88, 0x87, 0xaa, 0x63, 0x44, 0x24, 0xcf, 0x81, 0xc7, 0x40, 0xe8, 0x04, 0x08, 0xbc, 0xae, 0xd0, 0x5f, 0x13, 0x43, 0x87, 0xca, 0x83, 0xaa, 0xc4, 0xa6, 0x1a, 0xfc, 0x04, 0xbf, 0xc0, 0xc8, 0x20, 0xdb, 0xc4, 0x74, 0x24, 0xee, 0x02, 0xde, 0x02, 0x97, 0x4e, 0xc3, 0xf8, 0x36, 0x89, 0xae, 0xce, 0x31, 0xbc, 0x1b, 0x54, 0x14, 0x36, 0xcb, 0x41, 0xab, 0x63, 0x2b, 0xc3, 0xc4, 0x20, 0xfb, 0x44, 0x75, 0x24, 0x9f, 0x06, 0x9a, 0xa5, 0x0a, 0x87, 0xce, 0x30, 0x7a, 0x36, 0x89, 0x5c, 0x8e, 0xf8, 0x44, 0xd0, 0xc3, 0xaf, 0x28, 0x74, 0x2b, 0x41, 0x1b, 0x70, 0x69, 0xa7, 0x9f, 0x6d, 0x87, 0xc9, 0xa9, 0x64, 0x2a, 0xd0, 0x4d, 0x36, 0xc4, 0x49, 0x4e, 0x8c, 0x2a, 0xa0, 0x9b, 0xdb, 0x11, 0x27, 0x4e, 0x22, 0x3e, 0x4e, 0x71, 0xdf, 0x18, 0x85, 0x6e, 0xc7, 0x49, 0x82, 0xdf, 0xb6, 0x82, 0xf7, 0x9f, 0x9f, 0xfa, 0x38, 0x31, 0x12, 0xf5, 0xa1, 0xae, 0xf1, 0x60, 0x8f, 0x8c, 0x71, 0x12, 0xe1, 0xd7, 0x0f, 0xec, 0x02, 0x0b, 0xe2, 0xf8, 0x67, 0x3e, 0xd9, 0x0a, 0x64, 0x2b, 0xf0, 0xff, 0xae, 0x40, 0x4e, 0x97, 0xce, 0x9f, 0xc0, 0x5f, 0xf3, 0x5c, 0x2e, 0x97, 0xd7, 0x7a, 0x3f, 0xde, 0xf1, 0x9b, 0x89, 0xbe, 0x15, 0x9f, 0x2b, 0x7e, 0x76, 0x5a, 0x6e, 0xe6, 0x12, 0x5d, 0xd8, 0x7c, 0x3a, 0x4e, 0x28, 0x9e, 0x64, 0x4b, 0xc0, 0x47, 0x60, 0x92, 0xbc, 0xe3, 0x55, 0x16, 0x0b, 0x82, 0xcd, 0x71, 0xe5, 0x78, 0x28, 0xc8, 0x1e, 0xbb, 0x32, 0x70, 0x55, 0xd9, 0xbb, 0xac, 0x3c, 0xba, 0x1d, 0x0e, 0xf2, 0x8d, 0xad, 0x23, 0xf0, 0x05, 0x37, 0x8b, 0xc7, 0xb8, 0xbe, 0x58, 0x60, 0x7c, 0xe4, 0x3d, 0x50, 0xd3, 0x58, 0x3f, 0x1f, 0x8c, 0x86, 0x6b, 0x43, 0x83, 0x97, 0x87, 0x0a, 0x79, 0xe5, 0xb2, 0x46, 0x5d, 0x9c, 0x48, 0x75, 0x8c, 0xdf, 0x41, 0x6f, 0x23, 0xf2, 0x53, 0xe6, 0xa7, 0x0d, 0x99, 0xd7, 0xb4, 0x09, 0xe1, 0x38, 0xa5, 0xa8, 0x85, 0x5f, 0xae, 0xe6, 0xed, 0x2c, 0xa7, 0xa1, 0x9c, 0x15, 0x9b, 0x10, 0x4c, 0x6f, 0x17, 0xfe, 0x61, 0x5a, 0x19, 0x4e, 0xa2, 0x6f, 0x31, 0xe4, 0xe9, 0x4f, 0x29, 0x78, 0xb5, 0x71, 0x24, 0xe4, 0x88, 0x0e, 0x4a, 0xbf, 0x32, 0x4b, 0x15, 0xd0, 0x4c, 0x77, 0xf0, 0xce, 0x68, 0x32, 0xfd, 0x87, 0x69, 0x4b, 0xfd, 0x15, 0xc2, 0xd0, 0xdc, 0x76, 0xa3, 0xc1, 0xf7, 0xcc, 0x2b, 0x6c, 0xe6, 0x48, 0x35, 0x16, 0xcd, 0x0c, 0x00, 0xdf, 0x8d, 0x26, 0xd7, 0x15, 0x2b, 0x0a, 0xfb, 0x51, 0x60, 0x26, 0xe8, 0xeb, 0xda, 0xc2, 0xcb, 0xdb, 0xc7, 0x26, 0x90, 0xde, 0xfb, 0xa2, 0x5b, 0x8c, 0x1e, 0x29, 0xe8, 0x28, 0xd0, 0xf4, 0x84, 0x49, 0x67, 0x50, 0x0e, 0xe4, 0x56, 0xf2, 0x09, 0x3c, 0x00, 0x53, 0xc4, 0x8f, 0x71, 0x3e, 0x70, 0x49, 0x8e, 0xb8, 0x2b, 0x77, 0x77, 0x13, 0x44, 0x77, 0x04, 0xf8, 0x6e, 0xad, 0xe8, 0xfc, 0xb1, 0x79, 0x12, 0x6c, 0x00, 0x4d, 0x60, 0x45, 0x50, 0x10, 0xf4, 0x95, 0x40, 0xbf, 0xf7, 0x31, 0xcd, 0xcf, 0x01, 0x3d, 0x81, 0xdc, 0xe7, 0x5c, 0xfa, 0x00, 0x33, 0x10, 0xc8, 0x91, 0xd3, 0xf4, 0x82, 0x89, 0x5c, 0xcf, 0x9b, 0xb5, 0x10, 0x7e, 0x77, 0x50, 0xde, 0x92, 0x74, 0x04, 0xaf, 0x53, 0xc9, 0x1a, 0x8b, 0x05, 0xc3, 0xf6, 0xb2, 0xb2, 0x17, 0xf6, 0xa6, 0xf8, 0x30, 0x9e, 0x35, 0xe4, 0xfb, 0x99, 0xcb, 0x4e, 0xc0, 0x23, 0x43, 0xbe, 0x91, 0xb9, 0x9c, 0xa2, 0x9a, 0x7e, 0x32, 0xb1, 0xbf, 0x01, 0x46, 0xd0, 0x7a, 0x95, 0x45, 0x8e, 0xcc, 0xa2, 0x10, 0x0d, 0xca, 0x2e, 0x80, 0x49, 0xb2, 0x9d, 0x31, 0xc9, 0x10, 0x7e, 0x61, 0x2e, 0xd7, 0x6d, 0x83, 0x21, 0x97, 0x3d, 0x1b, 0xf3, 0x5a, 0x16, 0x93, 0xf3, 0xc5, 0x72, 0x47, 0xd2, 0x13, 0x70, 0xab, 0x44, 0x75, 0x48, 0x9a, 0xab, 0x09, 0x1b, 0x00, 0xdb, 0x7b, 0xae, 0xa3, 0x33, 0x9e, 0x15, 0x5f, 0xf8, 0x9b, 0x86, 0xfc, 0x29, 0x73, 0xf3, 0xf6, 0x62, 0x98, 0xb4, 0x4f, 0xa5, 0xf1, 0x11, 0x61, 0x6b, 0x08, 0xb4, 0x23, 0x50, 0x57, 0xd0, 0xec, 0x84, 0x96, 0xe6, 0x56, 0x05, 0x3a, 0x18, 0x4a, 0xec, 0x97, 0x39, 0xbe, 0xee, 0x20, 0xd7, 0xdf, 0x50, 0xb0, 0xc6, 0x15, 0xc4, 0x1c, 0x0f, 0x1a, 0xa9, 0xe2, 0x4f, 0x29, 0xe0, 0x18, 0x90, 0xad, 0xc3, 0xa5, 0x51, 0xa3, 0xe0, 0x23, 0xff, 0x9a, 0x6f, 0x80, 0x26, 0xf9, 0xf7, 0x6c, 0xd4, 0x82, 0x18, 0xfc, 0x57, 0x7c, 0x7a, 0x46, 0xad, 0x27, 0x11, 0x7b, 0x0a, 0xd9, 0x12, 0xa3, 0x81, 0x30, 0x2e, 0xd5, 0x89, 0x14, 0xec, 0x15, 0x94, 0x6a, 0xe4, 0x1e, 0x57, 0x0b, 0xe4, 0x1e, 0x57, 0xa5, 0x6d, 0x98, 0xf7, 0x01, 0xb2, 0xe2, 0xb6, 0xe9, 0xba, 0xce, 0x93, 0x28, 0x4f, 0xe5, 0x35, 0xaa, 0x7a, 0xb9, 0x56, 0x37, 0xe8, 0x84, 0xcc, 0x0f, 0x28, 0xbd, 0x2d, 0xb6, 0x85, 0x40, 0xdd, 0x74, 0x9e, 0x20, 0xbe, 0xd4, 0x27, 0x84, 0xe1, 0x2a, 0xb8, 0xec, 0x06, 0xec, 0x23, 0x79, 0xbd, 0x92, 0xed, 0x85, 0x6f, 0x53, 0x73, 0x1b, 0x6c, 0x39, 0x41, 0x46, 0xdb, 0x08, 0x54, 0x34, 0x06, 0xcd, 0x8c, 0x01, 0xe6, 0xcb, 0x2e, 0xa2, 0xc2, 0x91, 0x2b, 0x6c, 0x7f, 0xc0, 0x9f, 0x13, 0x81, 0x65, 0xea, 0xd0, 0xeb, 0x70, 0x2e, 0xc5, 0xcb, 0x93, 0x86, 0x49, 0xf2, 0xc1, 0x46, 0x3e, 0xe2, 0x4c, 0x36, 0x15, 0x16, 0xe6, 0x6b, 0x8a, 0xae, 0xbe, 0x4d, 0x03, 0x0a, 0x96, 0x87, 0x66, 0xfd, 0xac, 0xe9, 0xf6, 0x70, 0x09, 0xa6, 0x07, 0x68, 0x72, 0x05, 0x96, 0xc6, 0xb5, 0x36, 0xeb, 0x0f, 0x15, 0x8b, 0xc2, 0x17, 0x02, 0xaf, 0x26, 0x5f, 0x22, 0x97, 0x3f, 0x06, 0x9b, 0x34, 0x2b, 0x54, 0x51, 0xb6, 0x8d, 0xe8, 0x60, 0x31, 0x90, 0x47, 0xaa, 0xa4, 0xa9, 0xbf, 0xed, 0xda, 0x43, 0xc7, 0xa3, 0xb3, 0x6a, 0x90, 0x64, 0x93, 0x77, 0x42, 0x17, 0x93, 0x94, 0x21, 0x0d, 0xae, 0x04, 0x72, 0x5f, 0x4c, 0x82, 0xe6, 0x26, 0x55, 0x77, 0xa4, 0xb8, 0x74, 0x26, 0x3b, 0x6d, 0xb6, 0x9b, 0xbc, 0x41, 0xcc, 0x52, 0xef, 0xdd, 0x91, 0xfa, 0x08, 0x34, 0xa6, 0x18, 0xb9, 0x26, 0xbd, 0x6e, 0x21, 0x88, 0x23, 0xd3, 0x2b, 0x3c, 0x06, 0x04, 0x26, 0x4c, 0x43, 0x49, 0x51, 0xb3, 0x81, 0xd7, 0x67, 0x81, 0x28, 0x1d, 0x3e, 0xc4, 0x58, 0x3f, 0x35, 0xa5, 0xd1, 0x8a, 0x7f, 0x4e, 0x8a, 0x93, 0x77, 0xc1, 0xbb, 0x51, 0x3a, 0x72, 0x6c, 0xe5, 0x14, 0x3f, 0x05, 0x7a, 0xf9, 0x47, 0xff, 0x47, 0x34, 0x14, 0x59, 0x06, 0x56, 0x81, 0xd7, 0xa0, 0x18, 0x49, 0x63, 0xd7, 0xc0, 0x84, 0x52, 0xcb, 0x2f, 0x3c, 0x2f, 0x96, 0x1a, 0x24, 0x8a, 0x3f, 0x45, 0xcb, 0xc7, 0x95, 0x19, 0x60, 0x1e, 0x90, 0xef, 0x19, 0xc3, 0x80, 0x7c, 0xa6, 0xfb, 0x0c, 0xee, 0x83, 0x5b, 0xe0, 0x0c, 0xdf, 0x28, 0x9e, 0x33, 0x66, 0x94, 0xad, 0x40, 0xb6, 0x02, 0xd9, 0x0a, 0x64, 0x2b, 0x90, 0xea, 0x0a, 0xfc, 0x06, 0x2b, 0x3f, 0x89, 0x0a, 0x68, 0xdb, 0xdf, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXSelectIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x33, 0x17, 0x79, 0x08, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xd7, 0xce, 0x93, 0x00, 0x00, 0x04, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x99, 0x5b, 0x88, 0x0e, 0x61, 0x18, 0xc7, 0x7d, 0xd6, 0x3a, 0x6c, 0x12, 0x42, 0x24, 0x17, 0x48, 0xb1, 0x21, 0x09, 0x37, 0x5c, 0x2d, 0xe5, 0x74, 0x23, 0x11, 0x39, 0xad, 0xc3, 0x86, 0x12, 0x17, 0x8b, 0x52, 0xd2, 0xda, 0xa4, 0xb4, 0x29, 0xa7, 0x24, 0xe4, 0xc2, 0x31, 0x57, 0x12, 0x52, 0x88, 0x1b, 0x09, 0xed, 0x0d, 0xc9, 0x46, 0x88, 0x9c, 0x6e, 0x24, 0xe7, 0x8d, 0x65, 0xf7, 0xf3, 0x7b, 0x3e, 0x33, 0xdb, 0xe3, 0x6d, 0x66, 0xbe, 0x99, 0xf9, 0xde, 0xd9, 0xa1, 0xe6, 0xa9, 0xff, 0xf7, 0x3e, 0xef, 0x73, 0x7e, 0xde, 0x99, 0xf9, 0x66, 0xe6, 0x9d, 0x4e, 0x9d, 0x32, 0xca, 0x56, 0x20, 0x5b, 0x81, 0x6c, 0x05, 0xb2, 0x15, 0xc8, 0x56, 0x20, 0xd9, 0x15, 0xc8, 0xe7, 0xf3, 0xd3, 0xc1, 0x33, 0xb0, 0x3d, 0xd9, 0x4c, 0x29, 0x45, 0xa7, 0xb1, 0x8b, 0x40, 0xe8, 0x07, 0x28, 0x4b, 0xa9, 0x0c, 0xcf, 0xb4, 0x9d, 0x3d, 0xa5, 0xd1, 0x85, 0xdd, 0x1d, 0x97, 0xae, 0x8c, 0xb6, 0x62, 0x46, 0xaf, 0xc2, 0xc3, 0xe3, 0x9f, 0x2a, 0xc6, 0xa3, 0xbe, 0x92, 0x45, 0xbe, 0x0d, 0x72, 0xaa, 0x55, 0x80, 0x31, 0x25, 0x67, 0xf0, 0x08, 0x40, 0xdc, 0xc1, 0x60, 0x88, 0x87, 0xaa, 0x63, 0x44, 0x24, 0xcf, 0x81, 0xc7, 0x40, 0xe8, 0x04, 0x08, 0xbc, 0xae, 0xd0, 0x5f, 0x13, 0x43, 0x87, 0xca, 0x83, 0xaa, 0xc4, 0xa6, 0x1a, 0xfc, 0x04, 0xbf, 0xc0, 0xc8, 0x20, 0xdb, 0xc4, 0x74, 0x24, 0xee, 0x02, 0xde, 0x02, 0x97, 0x4e, 0xc3, 0xf8, 0x36, 0x89, 0xae, 0xce, 0x31, 0xbc, 0x1b, 0x54, 0x14, 0x36, 0xcb, 0x41, 0xab, 0x63, 0x2b, 0xc3, 0xc4, 0x20, 0xfb, 0x44, 0x75, 0x24, 0x9f, 0x06, 0x9a, 0xa5, 0x0a, 0x87, 0xce, 0x30, 0x7a, 0x36, 0x89, 0x5c, 0x8e, 0xf8, 0x44, 0xd0, 0xc3, 0xaf, 0x28, 0x74, 0x2b, 0x41, 0x1b, 0x70, 0x69, 0xa7, 0x9f, 0x6d, 0x87, 0xc9, 0xa9, 0x64, 0x2a, 0xd0, 0x4d, 0x36, 0xc4, 0x49, 0x4e, 0x8c, 0x2a, 0xa0, 0x9b, 0xdb, 0x11, 0x27, 0x4e, 0x22, 0x3e, 0x4e, 0x71, 0xdf, 0x18, 0x85, 0x6e, 0xc7, 0x49, 0x82, 0xdf, 0xb6, 0x82, 0xf7, 0x9f, 0x9f, 0xfa, 0x38, 0x31, 0x12, 0xf5, 0xa1, 0xae, 0xf1, 0x60, 0x8f, 0x8c, 0x71, 0x12, 0xe1, 0xd7, 0x0f, 0xec, 0x02, 0x0b, 0xe2, 0xf8, 0x67, 0x3e, 0xd9, 0x0a, 0x64, 0x2b, 0xf0, 0xff, 0xae, 0x40, 0x4e, 0x97, 0xce, 0x9f, 0xc0, 0x5f, 0xf3, 0x5c, 0x2e, 0x97, 0xd7, 0x7a, 0x3f, 0xde, 0xf1, 0x9b, 0x89, 0xbe, 0x15, 0x9f, 0x2b, 0x7e, 0x76, 0x5a, 0x6e, 0xe6, 0x12, 0x5d, 0xd8, 0x7c, 0x3a, 0x4e, 0x28, 0x9e, 0x64, 0x4b, 0xc0, 0x47, 0x60, 0x92, 0xbc, 0xe3, 0x55, 0x16, 0x0b, 0x82, 0xcd, 0x71, 0xe5, 0x78, 0x28, 0xc8, 0x1e, 0xbb, 0x32, 0x70, 0x55, 0xd9, 0xbb, 0xac, 0x3c, 0xba, 0x1d, 0x0e, 0xf2, 0x8d, 0xad, 0x23, 0xf0, 0x05, 0x37, 0x8b, 0xc7, 0xb8, 0xbe, 0x58, 0x60, 0x7c, 0xe4, 0x3d, 0x50, 0xd3, 0x58, 0x3f, 0x1f, 0x8c, 0x86, 0x6b, 0x43, 0x83, 0x97, 0x87, 0x0a, 0x79, 0xe5, 0xb2, 0x46, 0x5d, 0x9c, 0x48, 0x75, 0x8c, 0xdf, 0x41, 0x6f, 0x23, 0xf2, 0x53, 0xe6, 0xa7, 0x0d, 0x99, 0xd7, 0xb4, 0x09, 0xe1, 0x38, 0xa5, 0xa8, 0x85, 0x5f, 0xae, 0xe6, 0xed, 0x2c, 0xa7, 0xa1, 0x9c, 0x15, 0x9b, 0x10, 0x4c, 0x6f, 0x17, 0xfe, 0x61, 0x5a, 0x19, 0x4e, 0xa2, 0x6f, 0x31, 0xe4, 0xe9, 0x4f, 0x29, 0x78, 0xb5, 0x71, 0x24, 0xe4, 0x88, 0x0e, 0x4a, 0xbf, 0x32, 0x4b, 0x15, 0xd0, 0x4c, 0x77, 0xf0, 0xce, 0x68, 0x32, 0xfd, 0x87, 0x69, 0x4b, 0xfd, 0x15, 0xc2, 0xd0, 0xdc, 0x76, 0xa3, 0xc1, 0xf7, 0xcc, 0x2b, 0x6c, 0xe6, 0x48, 0x35, 0x16, 0xcd, 0x0c, 0x00, 0xdf, 0x8d, 0x26, 0xd7, 0x15, 0x2b, 0x0a, 0xfb, 0x51, 0x60, 0x26, 0xe8, 0xeb, 0xda, 0xc2, 0xcb, 0xdb, 0xc7, 0x26, 0x90, 0xde, 0xfb, 0xa2, 0x5b, 0x8c, 0x1e, 0x29, 0xe8, 0x28, 0xd0, 0xf4, 0x84, 0x49, 0x67, 0x50, 0x0e, 0xe4, 0x56, 0xf2, 0x09, 0x3c, 0x00, 0x53, 0xc4, 0x8f, 0x71, 0x3e, 0x70, 0x49, 0x8e, 0xb8, 0x2b, 0x77, 0x77, 0x13, 0x44, 0x77, 0x04, 0xf8, 0x6e, 0xad, 0xe8, 0xfc, 0xb1, 0x79, 0x12, 0x6c, 0x00, 0x4d, 0x60, 0x45, 0x50, 0x10, 0xf4, 0x95, 0x40, 0xbf, 0xf7, 0x31, 0xcd, 0xcf, 0x01, 0x3d, 0x81, 0xdc, 0xe7, 0x5c, 0xfa, 0x00, 0x33, 0x10, 0xc8, 0x91, 0xd3, 0xf4, 0x82, 0x89, 0x5c, 0xcf, 0x9b, 0xb5, 0x10, 0x7e, 0x77, 0x50, 0xde, 0x92, 0x74, 0x04, 0xaf, 0x53, 0xc9, 0x1a, 0x8b, 0x05, 0xc3, 0xf6, 0xb2, 0xb2, 0x17, 0xf6, 0xa6, 0xf8, 0x30, 0x9e, 0x35, 0xe4, 0xfb, 0x99, 0xcb, 0x4e, 0xc0, 0x23, 0x43, 0xbe, 0x91, 0xb9, 0x9c, 0xa2, 0x9a, 0x7e, 0x32, 0xb1, 0xbf, 0x01, 0x46, 0xd0, 0x7a, 0x95, 0x45, 0x8e, 0xcc, 0xa2, 0x10, 0x0d, 0xca, 0x2e, 0x80, 0x49, 0xb2, 0x9d, 0x31, 0xc9, 0x10, 0x7e, 0x61, 0x2e, 0xd7, 0x6d, 0x83, 0x21, 0x97, 0x3d, 0x1b, 0xf3, 0x5a, 0x16, 0x93, 0xf3, 0xc5, 0x72, 0x47, 0xd2, 0x13, 0x70, 0xab, 0x44, 0x75, 0x48, 0x9a, 0xab, 0x09, 0x1b, 0x00, 0xdb, 0x7b, 0xae, 0xa3, 0x33, 0x9e, 0x15, 0x5f, 0xf8, 0x9b, 0x86, 0xfc, 0x29, 0x73, 0xf3, 0xf6, 0x62, 0x98, 0xb4, 0x4f, 0xa5, 0xf1, 0x11, 0x61, 0x6b, 0x08, 0xb4, 0x23, 0x50, 0x57, 0xd0, 0xec, 0x84, 0x96, 0xe6, 0x56, 0x05, 0x3a, 0x18, 0x4a, 0xec, 0x97, 0x39, 0xbe, 0xee, 0x20, 0xd7, 0xdf, 0x50, 0xb0, 0xc6, 0x15, 0xc4, 0x1c, 0x0f, 0x1a, 0xa9, 0xe2, 0x4f, 0x29, 0xe0, 0x18, 0x90, 0xad, 0xc3, 0xa5, 0x51, 0xa3, 0xe0, 0x23, 0xff, 0x9a, 0x6f, 0x80, 0x26, 0xf9, 0xf7, 0x6c, 0xd4, 0x82, 0x18, 0xfc, 0x57, 0x7c, 0x7a, 0x46, 0xad, 0x27, 0x11, 0x7b, 0x0a, 0xd9, 0x12, 0xa3, 0x81, 0x30, 0x2e, 0xd5, 0x89, 0x14, 0xec, 0x15, 0x94, 0x6a, 0xe4, 0x1e, 0x57, 0x0b, 0xe4, 0x1e, 0x57, 0xa5, 0x6d, 0x98, 0xf7, 0x01, 0xb2, 0xe2, 0xb6, 0xe9, 0xba, 0xce, 0x93, 0x28, 0x4f, 0xe5, 0x35, 0xaa, 0x7a, 0xb9, 0x56, 0x37, 0xe8, 0x84, 0xcc, 0x0f, 0x28, 0xbd, 0x2d, 0xb6, 0x85, 0x40, 0xdd, 0x74, 0x9e, 0x20, 0xbe, 0xd4, 0x27, 0x84, 0xe1, 0x2a, 0xb8, 0xec, 0x06, 0xec, 0x23, 0x79, 0xbd, 0x92, 0xed, 0x85, 0x6f, 0x53, 0x73, 0x1b, 0x6c, 0x39, 0x41, 0x46, 0xdb, 0x08, 0x54, 0x34, 0x06, 0xcd, 0x8c, 0x01, 0xe6, 0xcb, 0x2e, 0xa2, 0xc2, 0x91, 0x2b, 0x6c, 0x7f, 0xc0, 0x9f, 0x13, 0x81, 0x65, 0xea, 0xd0, 0xeb, 0x70, 0x2e, 0xc5, 0xcb, 0x93, 0x86, 0x49, 0xf2, 0xc1, 0x46, 0x3e, 0xe2, 0x4c, 0x36, 0x15, 0x16, 0xe6, 0x6b, 0x8a, 0xae, 0xbe, 0x4d, 0x03, 0x0a, 0x96, 0x87, 0x66, 0xfd, 0xac, 0xe9, 0xf6, 0x70, 0x09, 0xa6, 0x07, 0x68, 0x72, 0x05, 0x96, 0xc6, 0xb5, 0x36, 0xeb, 0x0f, 0x15, 0x8b, 0xc2, 0x17, 0x02, 0xaf, 0x26, 0x5f, 0x22, 0x97, 0x3f, 0x06, 0x9b, 0x34, 0x2b, 0x54, 0x51, 0xb6, 0x8d, 0xe8, 0x60, 0x31, 0x90, 0x47, 0xaa, 0xa4, 0xa9, 0xbf, 0xed, 0xda, 0x43, 0xc7, 0xa3, 0xb3, 0x6a, 0x90, 0x64, 0x93, 0x77, 0x42, 0x17, 0x93, 0x94, 0x21, 0x0d, 0xae, 0x04, 0x72, 0x5f, 0x4c, 0x82, 0xe6, 0x26, 0x55, 0x77, 0xa4, 0xb8, 0x74, 0x26, 0x3b, 0x6d, 0xb6, 0x9b, 0xbc, 0x41, 0xcc, 0x52, 0xef, 0xdd, 0x91, 0xfa, 0x08, 0x34, 0xa6, 0x18, 0xb9, 0x26, 0xbd, 0x6e, 0x21, 0x88, 0x23, 0xd3, 0x2b, 0x3c, 0x06, 0x04, 0x26, 0x4c, 0x43, 0x49, 0x51, 0xb3, 0x81, 0xd7, 0x67, 0x81, 0x28, 0x1d, 0x3e, 0xc4, 0x58, 0x3f, 0x35, 0xa5, 0xd1, 0x8a, 0x7f, 0x4e, 0x8a, 0x93, 0x77, 0xc1, 0xbb, 0x51, 0x3a, 0x72, 0x6c, 0xe5, 0x14, 0x3f, 0x05, 0x7a, 0xf9, 0x47, 0xff, 0x47, 0x34, 0x14, 0x59, 0x06, 0x56, 0x81, 0xd7, 0xa0, 0x18, 0x49, 0x63, 0xd7, 0xc0, 0x84, 0x52, 0xcb, 0x2f, 0x3c, 0x2f, 0x96, 0x1a, 0x24, 0x8a, 0x3f, 0x45, 0xcb, 0xc7, 0x95, 0x19, 0x60, 0x1e, 0x90, 0xef, 0x19, 0xc3, 0x80, 0x7c, 0xa6, 0xfb, 0x0c, 0xee, 0x83, 0x5b, 0xe0, 0x0c, 0xdf, 0x28, 0x9e, 0x33, 0x66, 0x94, 0xad, 0x40, 0xb6, 0x02, 0xd9, 0x0a, 0x64, 0x2b, 0x90, 0xea, 0x0a, 0xfc, 0x06, 0x2b, 0x3f, 0x89, 0x0a, 0x68, 0xdb, 0xdf, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXSelectIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5a, 0x08, 0x06, 0x00, 0x00, 0x00, 0x26, 0x61, 0x71, 0xb1, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x08, 0x58, 0x73, 0x21, 0x00, 0x00, 0x07, 0x8e, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9c, 0x6b, 0xa8, 0x55, 0x45, 0x14, 0xc7, 0xbd, 0xbe, 0x25, 0x95, 0xac, 0x30, 0x0d, 0xba, 0xbe, 0xa2, 0xf2, 0x55, 0x64, 0xd0, 0x4b, 0x0a, 0xa2, 0xd0, 0xa2, 0x07, 0x81, 0x2f, 0x34, 0x84, 0x08, 0xb5, 0x8c, 0x88, 0x50, 0xa8, 0x88, 0xca, 0x0f, 0x41, 0x51, 0x91, 0x19, 0x29, 0x96, 0x50, 0xe0, 0x87, 0x50, 0x22, 0xf1, 0x59, 0x4a, 0x85, 0xa9, 0x65, 0x65, 0x24, 0xa6, 0xe0, 0x07, 0x2b, 0xbc, 0x5a, 0x99, 0xa8, 0x44, 0xa5, 0x99, 0xe5, 0xeb, 0xf6, 0xfb, 0x5f, 0xce, 0x3e, 0xcc, 0x9d, 0x3b, 0xfb, 0x38, 0xfb, 0x9c, 0xfd, 0xb8, 0xf7, 0x38, 0x0b, 0xfe, 0xcc, 0xcc, 0x9a, 0x35, 0x6b, 0xd6, 0xfa, 0x9f, 0x73, 0xe6, 0xce, 0x9e, 0xbd, 0xf7, 0xed, 0xd4, 0x29, 0x48, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x81, 0x81, 0xc0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x0c, 0xb4, 0x66, 0xa0, 0xa1, 0x75, 0x33, 0xbb, 0x56, 0x73, 0x73, 0x73, 0x0f, 0xbc, 0x4f, 0x06, 0xa7, 0xc0, 0x87, 0x0d, 0x0d, 0x0d, 0xa7, 0xb3, 0x9b, 0xed, 0x3c, 0xf0, 0x0c, 0xa1, 0x1b, 0x40, 0x24, 0xcb, 0xce, 0x83, 0x94, 0xb3, 0x4b, 0x11, 0x16, 0x2f, 0x8c, 0x98, 0x2c, 0x95, 0xa7, 0x28, 0xbb, 0x66, 0x37, 0x63, 0x71, 0x9e, 0x3b, 0xe7, 0x34, 0x75, 0x2f, 0x6b, 0x1e, 0x91, 0x19, 0x08, 0xb5, 0x48, 0x09, 0x4d, 0x07, 0x03, 0x79, 0x7d, 0x43, 0x1d, 0x53, 0xd7, 0xa7, 0x2a, 0x10, 0x9a, 0xf2, 0xe7, 0xea, 0xb5, 0x8e, 0xf1, 0x07, 0xa4, 0x27, 0xf3, 0x4e, 0x02, 0xc7, 0xc0, 0x6a, 0xb6, 0x3c, 0xcd, 0x29, 0xc7, 0xe1, 0xe5, 0x8e, 0x38, 0xba, 0x60, 0x38, 0x01, 0xa8, 0xd4, 0xd6, 0x4b, 0x5b, 0xb0, 0x8e, 0x27, 0x24, 0xb2, 0x05, 0x44, 0xb2, 0x9a, 0x4a, 0xf7, 0x24, 0x59, 0x60, 0x3f, 0x30, 0x1a, 0x6c, 0x94, 0xfa, 0x90, 0xbc, 0x85, 0x71, 0x17, 0x80, 0x8d, 0xc6, 0xf8, 0x35, 0xde, 0x83, 0xdb, 0x93, 0x21, 0x09, 0xb8, 0xc8, 0x58, 0x87, 0x5e, 0x1b, 0x75, 0x2f, 0xc1, 0xb6, 0x9f, 0x41, 0x84, 0xaa, 0x89, 0xb6, 0x4d, 0xd8, 0x8b, 0xcc, 0xcd, 0x1a, 0x68, 0x89, 0xbd, 0x7b, 0xf0, 0x8a, 0xa7, 0x50, 0x23, 0x12, 0xe8, 0x02, 0xf6, 0x5a, 0x89, 0xa8, 0xf9, 0x31, 0x48, 0x42, 0xea, 0xa7, 0x86, 0x8f, 0x0f, 0x7c, 0x93, 0x62, 0x4c, 0x6f, 0x60, 0xfe, 0x42, 0x22, 0x37, 0x3b, 0x7d, 0x7d, 0xb4, 0x3b, 0x3b, 0x32, 0xb8, 0x06, 0x1c, 0x89, 0x32, 0x31, 0xca, 0xf5, 0xd4, 0xbd, 0x48, 0xc5, 0xae, 0x27, 0x78, 0x18, 0x4c, 0x07, 0xdd, 0x7c, 0x92, 0xc4, 0x4e, 0x64, 0x7e, 0x01, 0x6c, 0xf9, 0x05, 0xc5, 0x15, 0x3e, 0x3e, 0xda, 0xad, 0x0d, 0x09, 0x8c, 0x06, 0x87, 0xed, 0xcc, 0x68, 0xeb, 0x92, 0x32, 0xd1, 0x7a, 0xe8, 0x93, 0x24, 0x3e, 0xfb, 0x80, 0x2f, 0x81, 0x2d, 0x22, 0x73, 0x98, 0x8f, 0x8f, 0x76, 0x6f, 0x43, 0x22, 0xa3, 0xc0, 0x21, 0x3b, 0x43, 0xda, 0x0b, 0xd2, 0x0e, 0x1e, 0x9f, 0x4b, 0x1d, 0xf3, 0xfc, 0x8c, 0x6e, 0x68, 0xda, 0x73, 0x15, 0xea, 0x8f, 0x84, 0x46, 0x02, 0x9b, 0xd4, 0xed, 0x69, 0x07, 0xc5, 0x1c, 0xf6, 0xba, 0xbd, 0x1f, 0xdd, 0x90, 0xb4, 0xe7, 0x69, 0x17, 0xfe, 0x48, 0x6c, 0x04, 0x38, 0x08, 0x22, 0x99, 0x97, 0x76, 0x60, 0x38, 0x5e, 0x10, 0x39, 0xa7, 0x6c, 0x02, 0xf5, 0x49, 0x66, 0x44, 0x1c, 0x09, 0xf6, 0x07, 0x4f, 0x80, 0x07, 0x22, 0x5d, 0x9a, 0x25, 0x7e, 0x3b, 0x83, 0x29, 0xe0, 0x71, 0xd0, 0x2f, 0x4d, 0xdf, 0xc1, 0x57, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x81, 0x81, 0x0e, 0xc0, 0x00, 0x5b, 0xa0, 0x3e, 0x1d, 0x20, 0xcc, 0xaa, 0x43, 0x2c, 0xdf, 0x97, 0x27, 0xd1, 0x8b, 0xf1, 0x32, 0x1d, 0x5c, 0x14, 0xe3, 0x4d, 0x87, 0xca, 0x3b, 0x38, 0xd4, 0x5d, 0x15, 0xd3, 0x5f, 0x51, 0x8d, 0xff, 0x7b, 0x31, 0x58, 0x08, 0x06, 0x81, 0x9f, 0xc0, 0x4c, 0x7c, 0x6d, 0xa2, 0xf4, 0x16, 0x7c, 0x28, 0x5e, 0xdd, 0xdb, 0x1f, 0x59, 0x61, 0xd0, 0x11, 0xfa, 0x96, 0xe2, 0xfb, 0x68, 0x05, 0x9b, 0x6c, 0xbb, 0x08, 0xb4, 0x1b, 0xd8, 0x03, 0x7c, 0xe4, 0xe9, 0xa4, 0xd1, 0xe0, 0xf4, 0x6a, 0x70, 0xd2, 0x72, 0xfe, 0x37, 0xed, 0x44, 0x57, 0x3f, 0xd8, 0xbf, 0x6c, 0xf9, 0x88, 0x6b, 0x6e, 0xa7, 0xa3, 0xfc, 0x65, 0x49, 0x1a, 0x6f, 0xcd, 0xf6, 0x4c, 0xae, 0x4b, 0x49, 0x5f, 0xd9, 0x92, 0x74, 0x42, 0x1c, 0xcf, 0x89, 0x71, 0xbe, 0x2c, 0x89, 0x2f, 0x7c, 0xec, 0x8a, 0xf1, 0xe3, 0x52, 0x0f, 0x4c, 0xe2, 0x3b, 0x2d, 0xdb, 0xe8, 0x26, 0xdd, 0x5e, 0x1c, 0xfe, 0xea, 0xe9, 0x74, 0xb3, 0xa7, 0x9d, 0x69, 0x76, 0xd2, 0x6c, 0x18, 0xf5, 0x89, 0x30, 0xd1, 0x68, 0xb4, 0xcf, 0x55, 0xf5, 0xfd, 0x30, 0xf7, 0xe0, 0xe8, 0xd0, 0xb9, 0x9c, 0x65, 0xd1, 0x5f, 0xfe, 0x59, 0x90, 0x98, 0xd6, 0xb6, 0x47, 0x40, 0xdc, 0x1a, 0x7a, 0x96, 0xbe, 0x1d, 0xe0, 0x3d, 0xd6, 0xa7, 0x33, 0x94, 0xde, 0x82, 0x6f, 0x1d, 0x06, 0xff, 0x00, 0xca, 0xf3, 0x19, 0x83, 0xe7, 0xe3, 0x6f, 0xae, 0xd1, 0x8e, 0xad, 0xe2, 0x47, 0xf7, 0xb2, 0x66, 0x01, 0xad, 0xa1, 0x2e, 0x5f, 0x1a, 0x7b, 0x18, 0x2c, 0xc6, 0xe7, 0x41, 0x35, 0xea, 0x56, 0x20, 0x43, 0xb7, 0x4c, 0x5c, 0xf2, 0x17, 0xca, 0xbe, 0x75, 0x9b, 0x78, 0x56, 0x89, 0x41, 0xda, 0x1d, 0x2e, 0x36, 0x4b, 0xba, 0x39, 0x59, 0xcd, 0x5b, 0xd7, 0x7e, 0x21, 0xef, 0xfb, 0x18, 0x52, 0xf7, 0xa1, 0xd7, 0xbd, 0xf6, 0x20, 0x49, 0x18, 0x80, 0x34, 0xdd, 0xa0, 0x8b, 0x93, 0x29, 0x49, 0x7c, 0x05, 0x5b, 0x18, 0x80, 0xc9, 0xee, 0xe0, 0x40, 0x0c, 0xa3, 0xdf, 0x06, 0x92, 0xaa, 0x60, 0x00, 0x32, 0x9f, 0x89, 0x21, 0x54, 0xea, 0x5b, 0x93, 0xba, 0x64, 0xcc, 0xb5, 0x40, 0xa7, 0xfa, 0xb3, 0xc1, 0x70, 0x73, 0x3c, 0xed, 0xbe, 0x60, 0x21, 0xd0, 0x7d, 0xfd, 0x65, 0xe0, 0x1e, 0xb3, 0xbf, 0x2e, 0xea, 0x24, 0xa5, 0xa7, 0x48, 0x74, 0x95, 0xe4, 0x92, 0x95, 0x66, 0x92, 0x18, 0xe8, 0x3e, 0xbe, 0x6e, 0x25, 0xcb, 0x7e, 0x27, 0x98, 0x61, 0xf5, 0xcf, 0x43, 0x67, 0xca, 0x69, 0x1a, 0x2f, 0x46, 0x36, 0xd4, 0x75, 0x0b, 0xc5, 0x96, 0x95, 0x28, 0xda, 0xdf, 0x79, 0x02, 0x41, 0xe9, 0x21, 0x84, 0xa9, 0x60, 0x6c, 0x94, 0x80, 0x6f, 0xc9, 0x98, 0xb7, 0x80, 0x4b, 0xce, 0xa0, 0x6c, 0x79, 0x80, 0x81, 0x32, 0xee, 0xea, 0xed, 0x95, 0x68, 0x1e, 0x6c, 0xd6, 0xb9, 0x9c, 0xa0, 0x9b, 0x29, 0x1b, 0x4a, 0x3d, 0x39, 0x7d, 0xcc, 0x61, 0xb3, 0x09, 0x5d, 0x8f, 0xc8, 0x4f, 0xe1, 0x25, 0xc1, 0x34, 0x82, 0x1f, 0x8d, 0x40, 0x9f, 0x4c, 0x12, 0x14, 0xe3, 0x86, 0x01, 0x91, 0xe7, 0x92, 0x45, 0xf2, 0x45, 0x87, 0xc8, 0x38, 0xe1, 0x30, 0xd0, 0xb8, 0x1b, 0x4b, 0x36, 0x93, 0x1d, 0xfd, 0x52, 0x69, 0x6f, 0xab, 0xc3, 0x1e, 0xf9, 0x89, 0xfb, 0xf0, 0x5e, 0x50, 0x7f, 0xe1, 0x42, 0x80, 0x83, 0x40, 0x13, 0x30, 0xe5, 0xbb, 0xa4, 0x81, 0x31, 0x78, 0x85, 0xe9, 0xc0, 0xa8, 0x1f, 0xa7, 0xde, 0x72, 0xb5, 0x46, 0xf9, 0x8e, 0xa1, 0x37, 0xab, 0x1b, 0x35, 0x1f, 0x8a, 0x2e, 0xa0, 0xc9, 0xec, 0x30, 0xea, 0xcf, 0x97, 0x6c, 0x26, 0x1a, 0x3a, 0xb3, 0xfa, 0x0f, 0x8d, 0x21, 0x49, 0xe3, 0x4e, 0xd5, 0x9e, 0x00, 0x06, 0x83, 0x26, 0x60, 0xcb, 0x92, 0xa4, 0x13, 0xe1, 0x60, 0xac, 0xed, 0xc4, 0x68, 0x3f, 0x5b, 0x22, 0x43, 0xa7, 0x54, 0x67, 0x0d, 0xbd, 0x59, 0xbd, 0xae, 0x64, 0xf3, 0x94, 0xa9, 0x34, 0xea, 0x5a, 0x77, 0xe7, 0x82, 0xcf, 0x0c, 0x9d, 0x5d, 0x5d, 0x91, 0x34, 0xee, 0xd4, 0xec, 0x89, 0x64, 0x08, 0xd8, 0x67, 0x47, 0x44, 0xfb, 0x6b, 0x50, 0xd5, 0xa5, 0x23, 0xe3, 0xbe, 0x71, 0xf8, 0x93, 0xea, 0x37, 0xd0, 0xf2, 0x0c, 0x2a, 0x65, 0xdc, 0x3a, 0xd9, 0x72, 0x2e, 0x4b, 0xff, 0x38, 0x50, 0xad, 0xe8, 0xc3, 0xba, 0x2a, 0x35, 0x92, 0x7c, 0x1d, 0x31, 0xe9, 0x50, 0xa0, 0x47, 0x61, 0x6c, 0xf9, 0x0a, 0x45, 0x55, 0x64, 0x6a, 0x6e, 0xc6, 0x4e, 0xb2, 0x1d, 0x1a, 0xed, 0x87, 0x4a, 0x36, 0xb7, 0x1b, 0x3a, 0xbb, 0xba, 0x0a, 0x45, 0x92, 0x23, 0x3d, 0x7b, 0xbc, 0xda, 0x6f, 0x6b, 0x9e, 0xdc, 0x84, 0x09, 0xf5, 0x04, 0xc7, 0x6e, 0xcd, 0x6c, 0xc9, 0x56, 0xda, 0x35, 0x6d, 0x3f, 0x18, 0x5f, 0x69, 0x0d, 0xdc, 0x15, 0x25, 0x89, 0xdd, 0x0e, 0x6b, 0xee, 0x34, 0x9b, 0x5a, 0x4b, 0x2f, 0x89, 0xe6, 0xaa, 0xb5, 0x8c, 0xce, 0x43, 0x2b, 0xf9, 0x19, 0x40, 0xe7, 0x08, 0xcb, 0x60, 0x2b, 0xed, 0xbb, 0x38, 0x22, 0x3b, 0x66, 0xe9, 0x13, 0x35, 0x4b, 0xc7, 0x80, 0x6f, 0xc6, 0x0c, 0xd2, 0xe3, 0x93, 0x77, 0x97, 0xfa, 0x3e, 0x8f, 0xb1, 0x49, 0x43, 0xdd, 0x0b, 0x27, 0xb3, 0xd3, 0x70, 0xe4, 0xe5, 0x83, 0xa4, 0x1a, 0x80, 0x79, 0xa8, 0xa1, 0xab, 0x8e, 0xde, 0x5e, 0x83, 0x3d, 0x8c, 0xf0, 0xa5, 0xe7, 0x40, 0xff, 0x04, 0x2e, 0xd1, 0x03, 0x69, 0x33, 0x80, 0xef, 0xed, 0x19, 0x97, 0x0f, 0x1f, 0x9d, 0xd6, 0xec, 0xfc, 0x0e, 0x67, 0x98, 0x4c, 0x7b, 0x42, 0xdd, 0xc6, 0x98, 0x05, 0x12, 0xbd, 0xb0, 0xe0, 0xc1, 0xa9, 0xd6, 0xd2, 0xd7, 0x40, 0xd1, 0x12, 0xfd, 0x1a, 0x7c, 0x42, 0x8e, 0xb5, 0x89, 0x3b, 0xf5, 0x8e, 0x1d, 0x90, 0x45, 0x07, 0x4c, 0x5e, 0x8e, 0xdf, 0xbd, 0xa0, 0x6b, 0x16, 0xfe, 0x3d, 0x7d, 0x2e, 0x67, 0x09, 0x9a, 0xea, 0x69, 0x1b, 0x6b, 0x96, 0x39, 0xa1, 0x90, 0xd5, 0xc8, 0xec, 0x13, 0x80, 0xde, 0x29, 0x5a, 0x49, 0xd0, 0x07, 0x5c, 0xd1, 0x60, 0xf7, 0x3e, 0xfa, 0x69, 0xae, 0xbe, 0x9c, 0x74, 0x27, 0x98, 0x67, 0x00, 0xf1, 0x1d, 0xcd, 0x69, 0xbe, 0xe4, 0xd3, 0x40, 0xd2, 0x18, 0xa0, 0xcb, 0xc0, 0x48, 0x54, 0xd7, 0x7d, 0xf5, 0x36, 0x82, 0xfe, 0xfa, 0xc8, 0xa8, 0xc0, 0xb2, 0xc8, 0x0f, 0xb4, 0x0d, 0x27, 0x6d, 0x14, 0x10, 0xf3, 0x91, 0x83, 0x1c, 0x5d, 0x8b, 0x3f, 0xd6, 0xc6, 0x18, 0x05, 0xfa, 0x4d, 0x0e, 0xfb, 0x3c, 0x55, 0xf3, 0x5d, 0x71, 0x25, 0xd1, 0xf9, 0x6c, 0x9b, 0x92, 0xf8, 0xb3, 0x6d, 0x5d, 0xd7, 0xca, 0x9a, 0x73, 0x11, 0x2c, 0x3d, 0x67, 0x1b, 0xd3, 0x7e, 0xdd, 0xa1, 0xcb, 0x53, 0x35, 0x26, 0xcf, 0xc9, 0x12, 0xcf, 0x05, 0x69, 0x4b, 0xcf, 0xf1, 0xf5, 0x7a, 0x83, 0xfe, 0xf2, 0x3a, 0xae, 0x3a, 0xc8, 0x7a, 0x8b, 0x54, 0x29, 0x24, 0x2d, 0x49, 0xe5, 0x78, 0x12, 0x27, 0x9c, 0xf5, 0x00, 0x82, 0x6b, 0x04, 0x71, 0x7b, 0xcc, 0x28, 0x31, 0x91, 0x5e, 0xfe, 0xeb, 0x4e, 0xfd, 0xd1, 0xa8, 0xa3, 0xa0, 0xf2, 0xd2, 0xac, 0x79, 0xa9, 0xc9, 0x3f, 0xa4, 0xdc, 0x06, 0x74, 0xf2, 0x53, 0x49, 0x56, 0xd3, 0xd9, 0xf2, 0xf2, 0x18, 0x65, 0x2f, 0xe0, 0x7a, 0x6b, 0xaf, 0xd2, 0xf8, 0x34, 0xfb, 0x2e, 0xab, 0x25, 0xe1, 0xac, 0xd7, 0xd0, 0x4e, 0x6c, 0x43, 0xb6, 0x10, 0xe0, 0x7d, 0x40, 0xdb, 0x92, 0x38, 0xb9, 0x9f, 0x0e, 0xbd, 0x91, 0xd7, 0x17, 0x7b, 0xd9, 0x2d, 0x8e, 0x33, 0xcc, 0x41, 0x9f, 0x39, 0x27, 0xa9, 0xe4, 0x00, 0x59, 0x77, 0x02, 0xd7, 0x09, 0xbc, 0xf9, 0xed, 0xd2, 0x53, 0x73, 0xfd, 0x81, 0xde, 0x2d, 0x2d, 0x4a, 0x74, 0x91, 0x51, 0xb5, 0xe4, 0xba, 0x00, 0xc3, 0xd0, 0x38, 0x22, 0x5d, 0x03, 0x2a, 0xdd, 0xcf, 0x39, 0x44, 0xff, 0x7e, 0x70, 0x03, 0xc8, 0x5b, 0xfe, 0x65, 0x42, 0xfd, 0x4a, 0xaa, 0xfe, 0xc7, 0x06, 0xb9, 0x12, 0x2a, 0x76, 0x20, 0x55, 0xd7, 0xcc, 0x3a, 0x1c, 0x4e, 0xfd, 0x4c, 0x40, 0xfe, 0x6b, 0x94, 0x6d, 0x90, 0x79, 0x53, 0x2d, 0x3e, 0x72, 0x5f, 0x2f, 0x08, 0x78, 0x3d, 0x01, 0x4f, 0x04, 0x27, 0x6b, 0x09, 0x3c, 0xa3, 0xb1, 0x35, 0x3f, 0x6c, 0x91, 0x3b, 0xa1, 0x22, 0x02, 0x52, 0xd7, 0x52, 0x4c, 0x01, 0x55, 0xff, 0xb4, 0xe4, 0x27, 0x03, 0xd9, 0x50, 0xab, 0xcf, 0xdc, 0x7f, 0xf2, 0x66, 0xc0, 0xfc, 0xfc, 0x75, 0x68, 0xb2, 0x1c, 0x94, 0xf7, 0xa1, 0x66, 0x7f, 0xce, 0xf5, 0xdd, 0xcc, 0x37, 0x9a, 0x0f, 0x5b, 0xef, 0x12, 0x54, 0x2d, 0x85, 0x7c, 0x43, 0xa3, 0x68, 0x09, 0x5e, 0x77, 0x1d, 0x1f, 0x04, 0x67, 0x22, 0x5d, 0x81, 0xe5, 0xab, 0xb5, 0x92, 0x59, 0x60, 0xec, 0xad, 0xa7, 0xe6, 0x9b, 0x3a, 0x0d, 0xe8, 0x31, 0x9a, 0xa2, 0x44, 0xdb, 0xb5, 0x6e, 0xad, 0xa3, 0xea, 0xe0, 0x2d, 0x12, 0x9a, 0x0c, 0xec, 0x37, 0x45, 0xf2, 0x20, 0xf8, 0x77, 0x26, 0x19, 0xdc, 0xc1, 0xe9, 0x73, 0x87, 0x4f, 0x62, 0xe3, 0xc1, 0xf1, 0x3c, 0x58, 0x2c, 0xcd, 0xa1, 0xa3, 0xc4, 0xf1, 0xee, 0x68, 0xea, 0x44, 0x4b, 0x82, 0xb7, 0x00, 0x7d, 0x6b, 0xb2, 0x16, 0x3d, 0x44, 0xa6, 0x4b, 0xde, 0xfa, 0x17, 0x12, 0xd5, 0x29, 0x95, 0xee, 0xfb, 0x67, 0x25, 0x4d, 0x38, 0x1e, 0x5d, 0xff, 0x4c, 0x1a, 0x19, 0x92, 0x70, 0x57, 0xf0, 0x12, 0x88, 0x7b, 0x4a, 0x8f, 0xae, 0xc4, 0x22, 0x5f, 0xef, 0x82, 0xd4, 0x1e, 0x6c, 0x30, 0x42, 0xee, 0x18, 0x55, 0x92, 0xd7, 0x13, 0xca, 0x3a, 0x89, 0xaa, 0x55, 0x74, 0x3b, 0x66, 0x54, 0xc7, 0xc8, 0x3a, 0x87, 0x28, 0x21, 0x43, 0xaf, 0xe5, 0xac, 0x01, 0x49, 0x76, 0x02, 0x7f, 0x60, 0xbf, 0x04, 0xdc, 0x9c, 0x43, 0x88, 0x2d, 0x53, 0x14, 0x7a, 0xa5, 0x54, 0x4d, 0x92, 0x90, 0xa3, 0xff, 0x90, 0xa3, 0x2b, 0x2c, 0x91, 0x34, 0x1c, 0x5c, 0x09, 0x74, 0xd0, 0xf2, 0x1f, 0xd0, 0x59, 0xaa, 0xae, 0x78, 0xb6, 0x83, 0x6d, 0xe0, 0x13, 0x36, 0xeb, 0xd2, 0x07, 0x09, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x81, 0x81, 0xc0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03, 0xfe, 0x0c, 0xfc, 0x0f, 0x72, 0x11, 0x0f, 0x81, 0x42, 0xe0, 0xea, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; #pragma mark - Toolbar Icons static const u_int8_t FLEXBookmarksIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x79, 0x47, 0xd8, 0x15, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x6e, 0xd2, 0xbe, 0x00, 0x00, 0x03, 0x25, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x99, 0xdb, 0x8b, 0x4d, 0x71, 0x14, 0xc7, 0xcf, 0x76, 0xab, 0x51, 0x4a, 0x52, 0x2e, 0x45, 0xa9, 0xe3, 0xf2, 0x40, 0x8a, 0x29, 0x3c, 0x32, 0x63, 0xf2, 0x36, 0x4a, 0x22, 0x49, 0x4a, 0x4a, 0x09, 0x85, 0x47, 0x5e, 0xa6, 0x3c, 0x69, 0x22, 0xb9, 0x65, 0x5c, 0x1a, 0xe1, 0xd1, 0xcb, 0x78, 0x51, 0xfe, 0x01, 0xf7, 0x4b, 0x29, 0x22, 0x79, 0xa3, 0x63, 0xcc, 0x18, 0x42, 0x53, 0x72, 0x7c, 0xd6, 0x99, 0xdf, 0xda, 0x2d, 0xbf, 0xdf, 0xde, 0x67, 0x66, 0xef, 0x39, 0x73, 0x92, 0xf6, 0xaa, 0xef, 0xac, 0xcb, 0x5e, 0xbf, 0xb5, 0xbe, 0x7b, 0xfd, 0x7e, 0xe7, 0x9c, 0xf6, 0x9e, 0x52, 0xe9, 0x1f, 0x93, 0x28, 0x89, 0x4f, 0xb5, 0x5a, 0x5d, 0x42, 0x7c, 0x19, 0x58, 0x0c, 0xe6, 0x80, 0xc4, 0x3c, 0xe2, 0x79, 0xa4, 0xca, 0xa2, 0x0a, 0x78, 0x03, 0x5e, 0x47, 0x51, 0xf4, 0x2a, 0xb5, 0x08, 0x44, 0x16, 0x82, 0xdb, 0xa0, 0x99, 0xd2, 0x47, 0xb3, 0x45, 0x4a, 0x2a, 0xbe, 0x73, 0x82, 0x0b, 0x08, 0xbe, 0x04, 0x33, 0xf4, 0x62, 0x13, 0xf5, 0x0f, 0x7a, 0xad, 0x60, 0x5a, 0xef, 0xa6, 0x98, 0xa6, 0xdd, 0xd8, 0x3e, 0x19, 0x19, 0xef, 0x00, 0xf8, 0x6d, 0xf2, 0xc6, 0x6b, 0x4e, 0xa2, 0xc0, 0x2c, 0x10, 0x0f, 0x03, 0x7b, 0x3a, 0x38, 0x05, 0x3a, 0x41, 0xa9, 0x24, 0x23, 0xf3, 0xf6, 0xa8, 0x1f, 0x7f, 0x3b, 0x90, 0xc4, 0x86, 0x0b, 0x75, 0x5b, 0xc0, 0x56, 0x50, 0x01, 0x56, 0xe4, 0xdc, 0xd6, 0x08, 0x6d, 0xb2, 0x51, 0xec, 0x1d, 0x0d, 0x67, 0x91, 0x50, 0x90, 0x3e, 0x5b, 0xbc, 0xbe, 0x9b, 0x65, 0x7c, 0x22, 0xe5, 0x11, 0x15, 0xff, 0xed, 0x8b, 0xad, 0x89, 0x35, 0xee, 0x50, 0xde, 0x1e, 0x87, 0xb2, 0x12, 0x9a, 0x67, 0xfa, 0x7e, 0xe5, 0x70, 0x7d, 0x33, 0xfe, 0x84, 0x99, 0xf4, 0x19, 0xa6, 0x78, 0xbf, 0x69, 0x30, 0x57, 0x09, 0x99, 0x58, 0x49, 0x0e, 0x72, 0x33, 0xc5, 0xf6, 0x8b, 0x92, 0x08, 0x35, 0x93, 0x4c, 0xd0, 0xab, 0x20, 0x14, 0x8c, 0xc4, 0x0b, 0x14, 0x13, 0xf2, 0x06, 0x12, 0xb8, 0xc5, 0x84, 0x82, 0x91, 0x78, 0x81, 0x62, 0x42, 0xde, 0x40, 0x02, 0xb7, 0x98, 0x50, 0x30, 0x12, 0x2f, 0x50, 0x4c, 0xc8, 0x1b, 0x48, 0xe0, 0x16, 0x13, 0x0a, 0x46, 0xe2, 0x05, 0x8a, 0x09, 0x79, 0x03, 0x09, 0xdc, 0x62, 0x42, 0xc1, 0x48, 0xbc, 0xc0, 0xff, 0x39, 0x21, 0x9e, 0xad, 0x8e, 0x81, 0x4f, 0xa0, 0xcb, 0xbb, 0xe1, 0xcc, 0xae, 0x7d, 0x94, 0xce, 0xbc, 0x58, 0x16, 0x40, 0xe2, 0x24, 0xea, 0xa8, 0x5b, 0x7c, 0x1c, 0x7f, 0x26, 0x8f, 0x37, 0x07, 0x9d, 0x9f, 0x59, 0x8d, 0x6b, 0xcb, 0x68, 0x2e, 0xef, 0x03, 0x94, 0x8c, 0x36, 0x3f, 0x40, 0xfc, 0x2c, 0xb0, 0xcf, 0xee, 0x7a, 0x6d, 0x54, 0x9d, 0x9b, 0x10, 0x0d, 0x4f, 0x53, 0xfd, 0x70, 0x4a, 0x87, 0xfd, 0xc4, 0xcf, 0xe7, 0x21, 0x95, 0x8b, 0x10, 0x8d, 0xce, 0xd0, 0xf0, 0x90, 0x21, 0x23, 0x4f, 0xba, 0x47, 0xc0, 0x17, 0x13, 0xdb, 0x87, 0x7d, 0x31, 0x0f, 0x29, 0x39, 0x07, 0x27, 0x80, 0x8a, 0x2d, 0x6a, 0xea, 0xd7, 0xce, 0x4b, 0x44, 0xd2, 0x39, 0x4d, 0x74, 0x7a, 0x08, 0xbd, 0x4e, 0x12, 0xd1, 0xad, 0x60, 0xc0, 0xc5, 0x55, 0x5d, 0xc6, 0x48, 0xdd, 0x3e, 0xae, 0x7d, 0xd4, 0x44, 0xb4, 0x1c, 0x81, 0x5a, 0xa1, 0x51, 0x09, 0x49, 0x51, 0x70, 0xc1, 0x2c, 0x16, 0x53, 0xc8, 0xac, 0xad, 0x15, 0x71, 0x7f, 0xf0, 0x57, 0x03, 0x9f, 0xd4, 0x55, 0x62, 0x89, 0xbb, 0x41, 0x3c, 0x3b, 0x21, 0x16, 0x09, 0x99, 0x4b, 0xc0, 0x8a, 0x90, 0x59, 0x63, 0xc9, 0xa8, 0x4d, 0x7c, 0x15, 0xf0, 0x49, 0xf5, 0x12, 0x0b, 0x48, 0x11, 0xfb, 0x8b, 0x50, 0x90, 0xa0, 0x45, 0x55, 0xbb, 0x22, 0x3d, 0xf8, 0x7b, 0x35, 0x86, 0x1e, 0x02, 0x1d, 0x7c, 0xbc, 0xef, 0x9b, 0x58, 0x6c, 0x12, 0x7f, 0x82, 0xd3, 0x0e, 0xe4, 0xed, 0x9b, 0xca, 0x2e, 0x0c, 0x21, 0x35, 0x59, 0x03, 0x49, 0xba, 0x2e, 0x21, 0x47, 0xe6, 0x0a, 0x0b, 0xf7, 0x98, 0xc5, 0x75, 0xc9, 0x68, 0x5e, 0x0a, 0xa9, 0x9d, 0x5c, 0xbf, 0x5e, 0x8f, 0x54, 0x2a, 0x21, 0x47, 0xe6, 0x1a, 0x05, 0x76, 0x6b, 0x13, 0xb4, 0x1c, 0xf8, 0x8d, 0x34, 0x7b, 0x60, 0x62, 0xa9, 0x26, 0x79, 0x4f, 0xb9, 0xd8, 0x06, 0x3e, 0x9b, 0x24, 0x79, 0x3b, 0x77, 0x93, 0xfa, 0x89, 0x5f, 0xca, 0x89, 0x84, 0xdc, 0x1d, 0xf4, 0xb2, 0x50, 0xc6, 0xac, 0xa2, 0x64, 0x1e, 0x6a, 0x60, 0x2c, 0x1a, 0x52, 0xcf, 0xc8, 0x93, 0xed, 0xb3, 0xa4, 0xb6, 0xe1, 0xdf, 0x4a, 0x23, 0xe5, 0x7f, 0xec, 0x7f, 0x91, 0x78, 0x17, 0x58, 0x19, 0xc4, 0x69, 0x1d, 0x0b, 0x81, 0xb4, 0x1c, 0xd6, 0xaf, 0x04, 0xf2, 0x32, 0xd5, 0xca, 0x3d, 0x9c, 0x61, 0x13, 0xe8, 0x4e, 0x9a, 0x90, 0x1c, 0xba, 0x0e, 0x53, 0x78, 0x10, 0xbb, 0x9d, 0x3b, 0x7d, 0x64, 0x62, 0x99, 0x4d, 0xd6, 0x3f, 0x67, 0xd1, 0x06, 0x60, 0x5f, 0xe1, 0xc9, 0x76, 0x4e, 0xb3, 0xc5, 0x94, 0x90, 0x7d, 0xad, 0x66, 0xaf, 0x2b, 0x99, 0xc7, 0x36, 0x98, 0xd7, 0x86, 0xd4, 0x0b, 0xd6, 0x0a, 0x09, 0x4b, 0xca, 0x96, 0x8b, 0xbf, 0x17, 0x3e, 0xd8, 0xa8, 0xb3, 0x65, 0xcf, 0xdb, 0x28, 0x22, 0x1f, 0xe1, 0x86, 0x89, 0x23, 0xb5, 0x9e, 0x82, 0xf2, 0xff, 0x0e, 0x5f, 0x46, 0x78, 0xb0, 0x87, 0xf2, 0x3f, 0x8e, 0xef, 0x66, 0x2f, 0xdf, 0x63, 0x2f, 0xf7, 0xb3, 0x1b, 0xe9, 0x53, 0x7f, 0x29, 0x78, 0x6b, 0x7a, 0xfe, 0xc4, 0x2e, 0xc7, 0xbf, 0x31, 0x38, 0xb3, 0x69, 0x28, 0xdf, 0x13, 0x53, 0x41, 0x0f, 0x77, 0x92, 0xfa, 0x9b, 0xd6, 0x28, 0x62, 0xf4, 0x6c, 0xa1, 0x56, 0x27, 0x98, 0x0f, 0x6e, 0xd0, 0xb3, 0xf2, 0x07, 0x6f, 0x8f, 0xd6, 0x17, 0xd8, 0x01, 0xed, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXBookmarksIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x48, 0x08, 0x06, 0x00, 0x00, 0x00, 0xbd, 0x6d, 0x06, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xa1, 0x70, 0x0a, 0x00, 0x00, 0x04, 0xb4, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9b, 0x7b, 0x48, 0x64, 0x55, 0x1c, 0xc7, 0x1d, 0xd3, 0xde, 0x9b, 0xab, 0x59, 0xd0, 0x03, 0x42, 0xd6, 0x30, 0xf6, 0x8f, 0x36, 0x42, 0x7a, 0x10, 0x25, 0x15, 0x12, 0x11, 0x49, 0x84, 0x2c, 0x44, 0xd4, 0x2e, 0x19, 0x44, 0xd0, 0x2e, 0x58, 0x4a, 0xd9, 0x53, 0x2b, 0xcb, 0xa0, 0xa4, 0x88, 0x10, 0xa2, 0xa2, 0x07, 0x14, 0xc5, 0xe2, 0xb2, 0x1a, 0xed, 0x56, 0xbb, 0x20, 0xd5, 0x1f, 0x51, 0x54, 0x94, 0xb4, 0x3d, 0x76, 0x33, 0x22, 0x7a, 0x48, 0x41, 0x14, 0xd1, 0xc3, 0x34, 0xfb, 0xfc, 0xe4, 0xce, 0xcc, 0x99, 0xd3, 0x39, 0x73, 0xef, 0x3d, 0x33, 0xe3, 0xb9, 0xc2, 0x39, 0xf0, 0x65, 0xce, 0xf9, 0x3d, 0xbf, 0xdf, 0x73, 0xee, 0x0c, 0x83, 0x73, 0xac, 0xab, 0x0b, 0x63, 0x6d, 0xed, 0x40, 0xae, 0x1c, 0xdd, 0xe5, 0xe5, 0xe5, 0x26, 0xfc, 0xe7, 0x80, 0x53, 0x15, 0x9c, 0xc0, 0xbc, 0x6c, 0x1e, 0xfe, 0x5a, 0x8d, 0xbf, 0x28, 0xfc, 0x15, 0x38, 0x10, 0x61, 0x7f, 0x2e, 0x97, 0xfb, 0x28, 0x71, 0x33, 0x04, 0xe5, 0xc0, 0x16, 0x30, 0x0f, 0xb2, 0x3e, 0x5e, 0x85, 0x60, 0x5b, 0xac, 0x38, 0x09, 0x02, 0x6f, 0x65, 0x5d, 0x8d, 0xc6, 0xef, 0x0f, 0xd6, 0x43, 0xaa, 0xb8, 0xff, 0x3d, 0x52, 0x04, 0xcc, 0x10, 0xd0, 0xa5, 0x06, 0xad, 0xa1, 0x79, 0x0f, 0x8f, 0xe6, 0xb4, 0xf0, 0x2d, 0x11, 0x86, 0xa8, 0xab, 0xb0, 0xbd, 0xb8, 0x86, 0x84, 0xe8, 0x54, 0xe7, 0x30, 0x6c, 0x44, 0xdc, 0xdf, 0x05, 0x61, 0x88, 0x3a, 0x0a, 0xe3, 0x17, 0xe0, 0x24, 0x3d, 0x9a, 0xf5, 0x22, 0x90, 0x9d, 0xd8, 0x0f, 0xbe, 0x8e, 0xd6, 0xbc, 0xac, 0xfa, 0x58, 0x4f, 0xc7, 0x0e, 0x70, 0x21, 0x38, 0xcd, 0xd2, 0xfd, 0x4e, 0x84, 0x8d, 0x16, 0x7c, 0x08, 0xeb, 0x02, 0xa6, 0x31, 0x8b, 0xf1, 0xf4, 0x42, 0x60, 0x06, 0x26, 0xf0, 0x69, 0x00, 0x83, 0x26, 0xb2, 0xd8, 0xde, 0x2f, 0xa1, 0x88, 0xa1, 0xcf, 0x10, 0xb8, 0x88, 0x6d, 0x53, 0x49, 0x60, 0x86, 0x16, 0x70, 0x7b, 0xce, 0xc0, 0xf9, 0x97, 0x12, 0x8a, 0x04, 0x8c, 0x19, 0x82, 0xa6, 0x4a, 0x82, 0x32, 0xb6, 0x80, 0x6f, 0x87, 0x81, 0xb3, 0x98, 0x5a, 0xeb, 0x15, 0xae, 0xed, 0xca, 0x3c, 0x3f, 0xfd, 0x2c, 0x3f, 0xc9, 0xe8, 0xeb, 0x41, 0x78, 0x2d, 0x18, 0xb8, 0xb5, 0xab, 0xc2, 0x4c, 0x1f, 0x1a, 0xdf, 0x18, 0x92, 0x32, 0x63, 0xe2, 0x43, 0x62, 0x09, 0x32, 0xdf, 0x19, 0x08, 0x9d, 0xa8, 0x0a, 0x2b, 0x7c, 0x42, 0x2a, 0x81, 0xff, 0x2a, 0xf3, 0xac, 0x4e, 0x4d, 0x1c, 0xeb, 0x55, 0x61, 0x59, 0x25, 0xee, 0xc4, 0x2b, 0x08, 0x73, 0xda, 0x36, 0x8f, 0x49, 0xe1, 0xc4, 0x3c, 0x6e, 0xbe, 0x53, 0xeb, 0x70, 0x62, 0x4e, 0xdb, 0xe6, 0x31, 0x29, 0x9c, 0x98, 0xc7, 0xcd, 0x77, 0x6a, 0x1d, 0x4e, 0xcc, 0x69, 0xdb, 0x3c, 0x26, 0x85, 0x13, 0xf3, 0xb8, 0xf9, 0x4e, 0xad, 0xc3, 0x89, 0x39, 0x6d, 0x9b, 0xc7, 0xa4, 0x70, 0x62, 0x1e, 0x37, 0xdf, 0xa9, 0x75, 0x38, 0x31, 0xa7, 0x6d, 0xf3, 0x98, 0x14, 0x4e, 0xcc, 0xe3, 0xe6, 0x3b, 0xb5, 0x0e, 0x27, 0xe6, 0xb4, 0x6d, 0x1e, 0x93, 0xc2, 0x89, 0x79, 0xdc, 0x7c, 0xa7, 0xd6, 0xe1, 0xc4, 0x9c, 0xb6, 0xcd, 0x63, 0x52, 0x38, 0x31, 0x8f, 0x9b, 0xef, 0xd4, 0x3a, 0x9c, 0x98, 0xd3, 0xb6, 0x79, 0x4c, 0x5a, 0xb5, 0x13, 0xe3, 0xe7, 0xd3, 0x7e, 0xf0, 0x2d, 0x98, 0x01, 0xf2, 0xcb, 0x7f, 0x4d, 0xc7, 0xaa, 0x08, 0x43, 0xc8, 0x38, 0x2a, 0x04, 0x27, 0x03, 0xb9, 0x1c, 0xf3, 0x0e, 0xb6, 0x9a, 0xfe, 0x68, 0x5f, 0x73, 0x61, 0x08, 0x78, 0x0c, 0x21, 0xfd, 0x40, 0x1d, 0xad, 0x2c, 0xf6, 0xe1, 0x3b, 0x43, 0x35, 0x56, 0x73, 0x5e, 0x33, 0x61, 0x90, 0x96, 0x8b, 0x66, 0x8f, 0x43, 0x76, 0xbb, 0x85, 0xf0, 0xb1, 0xd8, 0x45, 0xdc, 0x99, 0x16, 0x7f, 0x45, 0xe6, 0x9a, 0x08, 0x13, 0x51, 0xb0, 0x7a, 0x02, 0xdc, 0x14, 0xc3, 0xae, 0x05, 0xff, 0x5e, 0xe2, 0x3b, 0x63, 0xe2, 0x52, 0xbb, 0xab, 0x2e, 0x2c, 0x12, 0x35, 0x01, 0x93, 0x1b, 0x13, 0xb2, 0x69, 0x26, 0xee, 0x4d, 0xf2, 0xce, 0x4a, 0x18, 0x9f, 0x28, 0xac, 0xaa, 0xc2, 0x22, 0x51, 0x4f, 0xd2, 0xf9, 0x06, 0x4b, 0xf7, 0x17, 0xb0, 0xcb, 0x45, 0x2e, 0x7d, 0xc8, 0x1d, 0xa9, 0x37, 0xc8, 0x3f, 0x5b, 0x77, 0xb8, 0xae, 0xab, 0x26, 0x0c, 0x52, 0x52, 0xeb, 0x29, 0x70, 0xbd, 0x85, 0xcc, 0x08, 0xf7, 0x32, 0xae, 0xc5, 0x77, 0x01, 0xf8, 0xd2, 0x10, 0xd3, 0x84, 0x4d, 0xc4, 0x9d, 0x6b, 0xf0, 0xa5, 0x36, 0x55, 0x45, 0x58, 0x24, 0xea, 0x69, 0xba, 0x5f, 0x67, 0x61, 0x30, 0x8c, 0xa8, 0x61, 0xf1, 0xf1, 0x2a, 0x17, 0x4e, 0xe4, 0xf6, 0x9a, 0x49, 0xdc, 0x31, 0xd8, 0x5f, 0xa7, 0xde, 0x79, 0xbc, 0x56, 0x34, 0x2a, 0x16, 0x16, 0x89, 0x7a, 0x16, 0x16, 0x5b, 0x2d, 0x4c, 0xee, 0x41, 0xcc, 0x88, 0xea, 0x63, 0xfd, 0x3d, 0x6b, 0x11, 0x27, 0xd7, 0x08, 0xf5, 0xb1, 0x0e, 0xc3, 0x1e, 0xea, 0x9e, 0xaf, 0x3b, 0xd2, 0xac, 0x2b, 0x12, 0x46, 0xf3, 0x43, 0x68, 0xf6, 0x3c, 0xb8, 0xc6, 0xd2, 0xf4, 0x6e, 0x44, 0xdc, 0x6b, 0xf2, 0xc5, 0x88, 0x3b, 0x9a, 0x9c, 0xdd, 0xd4, 0x77, 0xbe, 0xe9, 0xea, 0x2c, 0x2c, 0x12, 0x25, 0x1f, 0x06, 0x57, 0x9b, 0x88, 0x63, 0xbb, 0x0b, 0xf2, 0xf7, 0x59, 0x7c, 0x2b, 0x66, 0xfc, 0x3f, 0x30, 0x91, 0x93, 0xfb, 0xdc, 0x10, 0x27, 0x17, 0x43, 0x5f, 0xa3, 0x8f, 0xf8, 0x53, 0x0f, 0x27, 0x61, 0x34, 0x6b, 0xa0, 0x93, 0x5c, 0xb1, 0x95, 0xab, 0xb6, 0xa6, 0x21, 0xb7, 0x3c, 0xef, 0x37, 0x39, 0x74, 0x5b, 0x8c, 0xb8, 0x23, 0x89, 0x97, 0x5b, 0xda, 0x17, 0xeb, 0x79, 0x71, 0xeb, 0xd4, 0xc2, 0x22, 0x51, 0x2f, 0x51, 0x78, 0xb3, 0xa5, 0xf8, 0x1d, 0x90, 0x2d, 0x5e, 0x5d, 0xb5, 0x04, 0xa9, 0x66, 0xe2, 0x7f, 0x64, 0x2d, 0x27, 0x63, 0xba, 0x46, 0x28, 0xe2, 0xa6, 0xe9, 0xdb, 0xad, 0xe6, 0xc4, 0xcd, 0x53, 0x09, 0xa3, 0x78, 0x23, 0x05, 0x5f, 0x06, 0xbd, 0x96, 0xc2, 0xb7, 0x43, 0xf2, 0x01, 0x8b, 0xaf, 0xac, 0x39, 0x46, 0xdc, 0x11, 0x24, 0x4f, 0xd1, 0xff, 0x92, 0xb2, 0x45, 0x14, 0x67, 0x62, 0x61, 0x91, 0xa8, 0x57, 0xc8, 0xbd, 0x52, 0xc9, 0x57, 0xa7, 0x43, 0x90, 0x7b, 0x50, 0x35, 0xa4, 0x9d, 0x93, 0x3f, 0x4f, 0x8e, 0x9c, 0x9c, 0x5c, 0xaa, 0xd6, 0xc7, 0xe1, 0x18, 0x76, 0xc1, 0xe3, 0x52, 0xdd, 0x61, 0x5a, 0x27, 0x12, 0x46, 0xb1, 0x43, 0x49, 0xde, 0x01, 0xae, 0x30, 0x15, 0xc1, 0x76, 0x1b, 0xa4, 0xc6, 0x2c, 0xbe, 0x54, 0xe6, 0x48, 0xdc, 0x45, 0x24, 0x7d, 0x6a, 0x48, 0x3c, 0x0c, 0xdb, 0x4e, 0xf8, 0x5c, 0x66, 0xf0, 0x95, 0x98, 0x62, 0x85, 0x51, 0x44, 0x8a, 0x4d, 0x82, 0x9e, 0x92, 0xcc, 0xe2, 0xe2, 0x56, 0xc8, 0x3c, 0x54, 0x5c, 0x56, 0x3e, 0x4b, 0x20, 0x6e, 0x12, 0x5e, 0x97, 0x27, 0xea, 0x44, 0xe0, 0xbb, 0x40, 0x1f, 0xcf, 0x60, 0x90, 0x6f, 0x02, 0xb6, 0x31, 0x98, 0xa8, 0xb8, 0x63, 0x10, 0x4d, 0x8f, 0x07, 0x72, 0x3d, 0xde, 0x34, 0x16, 0x30, 0x6e, 0x03, 0xbf, 0x1a, 0x9c, 0xc5, 0xcf, 0x00, 0x9c, 0x26, 0x61, 0x86, 0x9c, 0x82, 0x69, 0xc0, 0x91, 0x6f, 0xaa, 0x34, 0xba, 0x1d, 0x07, 0x6c, 0xe2, 0x0a, 0x64, 0xb4, 0x49, 0x6f, 0xec, 0xa3, 0x68, 0x61, 0x31, 0xc0, 0xe3, 0xf2, 0xb0, 0xc5, 0x57, 0x55, 0x33, 0x7d, 0x7e, 0xa2, 0xa0, 0xbc, 0xe7, 0x66, 0xd3, 0x14, 0x76, 0x11, 0x76, 0x0b, 0xcd, 0x1e, 0x49, 0xd3, 0xa4, 0xd2, 0x58, 0x45, 0xdc, 0x27, 0x49, 0x6b, 0xa9, 0xc2, 0x7e, 0x4f, 0x90, 0x74, 0x33, 0x4d, 0xc6, 0x13, 0xc4, 0x55, 0x3d, 0x84, 0xbe, 0x3f, 0x53, 0x54, 0xbe, 0x81, 0x7c, 0x9c, 0xa0, 0x78, 0x51, 0x0b, 0xcf, 0xe8, 0x84, 0xf6, 0x9c, 0xea, 0xcb, 0x6d, 0x09, 0x0a, 0xd6, 0x3c, 0x04, 0x52, 0x2d, 0xe0, 0x03, 0x9d, 0x9c, 0xb6, 0xde, 0x50, 0x20, 0x82, 0xa3, 0x5b, 0x73, 0xe6, 0x97, 0xff, 0x30, 0xe9, 0x2b, 0x04, 0x66, 0x60, 0x02, 0x9f, 0x66, 0x20, 0x7f, 0x9f, 0x34, 0x8d, 0x0f, 0x31, 0xaa, 0x4f, 0x62, 0x5d, 0x1d, 0x86, 0x51, 0xb0, 0xa4, 0x44, 0xcf, 0x31, 0x97, 0x37, 0x6e, 0xe6, 0x06, 0xbc, 0x1a, 0xc1, 0xa3, 0x40, 0x36, 0x3e, 0x3f, 0x84, 0xef, 0xca, 0x9f, 0xf4, 0x72, 0x3a, 0x63, 0x1c, 0x6d, 0xd8, 0xc4, 0xf9, 0x1b, 0x78, 0x9b, 0x67, 0xdb, 0xf4, 0xbf, 0x23, 0x7a, 0x9a, 0xb7, 0x35, 0x7c, 0x4f, 0xa1, 0x79, 0x27, 0x90, 0xf7, 0xe0, 0x7b, 0xf0, 0xfd, 0x53, 0xc8, 0xfc, 0x07, 0x76, 0x76, 0x93, 0xea, 0x76, 0xb7, 0x97, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXOpenTabsIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x53, 0xf7, 0x29, 0xba, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0xfc, 0xcb, 0x00, 0x00, 0x03, 0x6e, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9a, 0xcb, 0x6b, 0x53, 0x41, 0x14, 0xc6, 0x13, 0xab, 0xf8, 0x40, 0xeb, 0x03, 0xc4, 0x17, 0x5a, 0xc5, 0xfa, 0x40, 0x4a, 0xbb, 0x71, 0x55, 0x50, 0xa4, 0x05, 0xa1, 0x6e, 0xdd, 0xd5, 0x22, 0x2e, 0xdc, 0xb8, 0xf3, 0x0f, 0x10, 0x77, 0x5d, 0x09, 0x2e, 0x2c, 0x28, 0xb8, 0x75, 0x21, 0xba, 0xb3, 0x08, 0x42, 0xf1, 0x01, 0xae, 0x74, 0x21, 0x56, 0xb1, 0x52, 0xad, 0x20, 0x54, 0xad, 0xf8, 0x02, 0x4b, 0x11, 0xd4, 0xda, 0xc6, 0xdf, 0xb9, 0xcd, 0xa4, 0x5f, 0x6f, 0xd2, 0x90, 0xcc, 0xcd, 0xcd, 0x4d, 0x4a, 0x0e, 0x7c, 0xdc, 0x73, 0x26, 0x33, 0xdf, 0x77, 0xce, 0x9d, 0xb9, 0x93, 0xe9, 0x4d, 0x53, 0xa9, 0x25, 0x62, 0xe9, 0x70, 0x1d, 0x99, 0x4c, 0x66, 0x0d, 0x6d, 0x7d, 0xe0, 0x20, 0x68, 0x05, 0xdb, 0x41, 0x5e, 0x3f, 0xda, 0x2a, 0x61, 0x53, 0x90, 0xbc, 0xcb, 0x62, 0x30, 0x9d, 0x4e, 0xbf, 0xae, 0x04, 0x69, 0x8a, 0x22, 0x4e, 0x82, 0x71, 0x90, 0x84, 0x4d, 0x23, 0x7a, 0x19, 0xac, 0x8d, 0x54, 0x0c, 0x04, 0x9d, 0x60, 0x16, 0x24, 0x6d, 0xd7, 0x7d, 0x0a, 0x09, 0x96, 0x0c, 0x99, 0x37, 0x31, 0xf8, 0x19, 0xe8, 0x10, 0x92, 0xdf, 0xf8, 0xcf, 0xc1, 0x07, 0x90, 0x91, 0xf6, 0x4a, 0xba, 0x1b, 0x20, 0x6b, 0x07, 0xdb, 0x84, 0xd4, 0xb4, 0x3a, 0x59, 0x66, 0x4f, 0xa4, 0xad, 0x34, 0x97, 0x42, 0xda, 0x43, 0xd3, 0xf0, 0x82, 0xb8, 0xa5, 0xb4, 0xd1, 0xd1, 0x7a, 0xa1, 0xb3, 0x02, 0x5c, 0x0a, 0xe9, 0x5f, 0xf1, 0x62, 0x85, 0xc4, 0x9e, 0x0d, 0xb5, 0xa3, 0x5e, 0x44, 0x9e, 0x83, 0x10, 0x6e, 0x02, 0xa3, 0x92, 0xc0, 0xbd, 0x72, 0xa9, 0x96, 0x65, 0x07, 0xd8, 0xee, 0xe4, 0x6c, 0x16, 0xe7, 0xa9, 0x0b, 0xaa, 0x71, 0x65, 0x19, 0xcd, 0xa0, 0xa3, 0x4b, 0x69, 0x5f, 0xb9, 0xba, 0xae, 0x90, 0x66, 0x19, 0x38, 0x05, 0xf1, 0x1f, 0x89, 0xab, 0xe5, 0x7e, 0x13, 0x21, 0xcd, 0x47, 0x9a, 0x17, 0x77, 0x5d, 0x21, 0x8b, 0xf7, 0xa8, 0x93, 0x4f, 0x96, 0x4c, 0x21, 0xcb, 0xa3, 0xdc, 0x70, 0x1e, 0xce, 0x75, 0x8c, 0xef, 0x06, 0xf6, 0x8c, 0x19, 0x36, 0x02, 0x5f, 0xd3, 0xad, 0xbf, 0x19, 0xee, 0x5b, 0x42, 0x64, 0x5b, 0xf2, 0x57, 0x60, 0xa7, 0x80, 0xb7, 0xe0, 0x3e, 0xcb, 0xff, 0x1f, 0xd7, 0x85, 0xc6, 0xa0, 0x7e, 0xd9, 0x31, 0x7e, 0x2e, 0xfc, 0x34, 0x3f, 0xa2, 0x6f, 0x1a, 0xf4, 0x81, 0x09, 0x19, 0x57, 0x4d, 0x77, 0x04, 0xb1, 0x2e, 0xcd, 0xcc, 0x77, 0x69, 0x9d, 0x83, 0xe4, 0x06, 0xd0, 0x2f, 0x32, 0xe5, 0x8d, 0xdb, 0x3f, 0x84, 0xc0, 0x03, 0x8a, 0x39, 0xe6, 0x84, 0xca, 0x5e, 0x5a, 0x0c, 0xde, 0xc2, 0xe0, 0x7e, 0x47, 0x90, 0xbd, 0xda, 0x96, 0x6d, 0x27, 0x80, 0xef, 0xa1, 0xf6, 0x4a, 0x85, 0x76, 0x02, 0xd9, 0x0a, 0xec, 0x00, 0xab, 0x76, 0x95, 0x7c, 0x3a, 0x58, 0x66, 0xd3, 0x41, 0x23, 0x41, 0xc9, 0x4b, 0x8b, 0xbe, 0xe7, 0x81, 0xda, 0x10, 0xc1, 0x0e, 0x65, 0x8f, 0xcb, 0x47, 0xa7, 0x0d, 0xbc, 0x54, 0x71, 0xfc, 0xe3, 0xa6, 0xe7, 0xb3, 0xb4, 0xf4, 0xcb, 0xca, 0x8e, 0xe1, 0xbd, 0xdc, 0x91, 0x4f, 0x71, 0x25, 0xaf, 0xbc, 0xe8, 0xbc, 0x22, 0x3e, 0xad, 0x6d, 0xf8, 0x41, 0x3e, 0x3e, 0x85, 0xec, 0x17, 0xa2, 0x61, 0xc8, 0xe3, 0x5a, 0x4e, 0x22, 0x33, 0xef, 0xa2, 0x37, 0x4c, 0xf4, 0x63, 0xbe, 0x25, 0x15, 0xe4, 0xe3, 0x53, 0xc8, 0x26, 0x21, 0xf9, 0x2c, 0x7e, 0x35, 0x5d, 0xd5, 0x0d, 0xf2, 0xf1, 0x29, 0x44, 0x13, 0x8e, 0xeb, 0x78, 0xaf, 0x1a, 0x85, 0xfc, 0x3c, 0xdd, 0xa8, 0x85, 0x14, 0x12, 0x49, 0xa4, 0xad, 0x51, 0x48, 0x22, 0xb7, 0xbd, 0x88, 0x68, 0x63, 0x46, 0x8a, 0xdc, 0x9c, 0x44, 0x3e, 0x6a, 0xcc, 0x48, 0x22, 0xb7, 0xbd, 0x88, 0x68, 0x63, 0x46, 0x8a, 0xdc, 0x9c, 0x44, 0x3e, 0x5a, 0x72, 0x33, 0x32, 0x77, 0x9e, 0x9f, 0xbb, 0x97, 0xab, 0x38, 0x1a, 0xc7, 0xf5, 0xd2, 0x3a, 0xb6, 0xd9, 0x72, 0x33, 0xf2, 0x5e, 0x14, 0x56, 0xe2, 0xef, 0x96, 0xb8, 0x2e, 0x5c, 0x57, 0xc8, 0x58, 0x28, 0xdb, 0x8b, 0xa1, 0xb8, 0xe6, 0x43, 0xf7, 0xa7, 0xee, 0x08, 0x99, 0x4e, 0x82, 0xf5, 0xd9, 0x8c, 0xcf, 0xb0, 0xbc, 0xf6, 0xe2, 0xdf, 0x01, 0x1f, 0x81, 0x9e, 0x36, 0xa3, 0xbc, 0x29, 0xc9, 0xd2, 0x57, 0xfe, 0x12, 0x14, 0xc2, 0x1f, 0x2b, 0x93, 0x24, 0x7e, 0x01, 0xfa, 0x01, 0x91, 0x38, 0x82, 0x6f, 0xa8, 0x0b, 0x73, 0x4b, 0xcb, 0x92, 0xbd, 0x06, 0x6e, 0xd6, 0x45, 0xd6, 0x05, 0x92, 0xcc, 0x15, 0xc2, 0xac, 0xcc, 0x80, 0x5e, 0xfa, 0x74, 0x81, 0x21, 0x30, 0x0e, 0xec, 0xed, 0x48, 0x5d, 0x98, 0x7b, 0x46, 0x72, 0xc9, 0x52, 0xcc, 0x23, 0x02, 0x83, 0xfd, 0x14, 0x67, 0x3b, 0x98, 0xbd, 0xfe, 0xd1, 0xed, 0xf8, 0x2e, 0x71, 0x1b, 0xa8, 0x29, 0xcb, 0x2b, 0x44, 0xb3, 0xa3, 0x28, 0x7b, 0x2b, 0x6f, 0x33, 0x93, 0x33, 0x8a, 0xfb, 0x9b, 0x0b, 0x6a, 0xc8, 0xc9, 0x2d, 0xad, 0x1a, 0xca, 0xc9, 0x2b, 0x15, 0x9f, 0x42, 0x74, 0x2b, 0x6e, 0xf2, 0x52, 0x8d, 0x3e, 0x48, 0x75, 0x83, 0x7c, 0x7c, 0x0a, 0xd1, 0x57, 0x31, 0x2d, 0xd1, 0x73, 0xf2, 0x62, 0xd8, 0x25, 0xa3, 0x82, 0x7c, 0x7c, 0x0a, 0xd1, 0x53, 0x40, 0x07, 0xcf, 0xcc, 0x01, 0x21, 0x8d, 0xdd, 0x45, 0xaf, 0x07, 0x11, 0xfd, 0x2d, 0x5e, 0xf3, 0x29, 0x5d, 0x1f, 0xa2, 0xb3, 0x40, 0xed, 0x0d, 0x41, 0x0f, 0x70, 0xa7, 0x82, 0xd2, 0xc9, 0xca, 0xe8, 0x09, 0xff, 0x66, 0x70, 0x0a, 0x7c, 0x01, 0x6a, 0x9d, 0x46, 0xa3, 0xdb, 0x6a, 0x49, 0xb4, 0x30, 0xac, 0xa6, 0xa3, 0x1d, 0x69, 0xf6, 0x14, 0x18, 0x90, 0xff, 0xe3, 0x4b, 0x81, 0x4e, 0x1e, 0x4d, 0x96, 0xa7, 0x3e, 0x17, 0x8e, 0xe2, 0x21, 0x3b, 0x6b, 0xb7, 0x05, 0x65, 0x17, 0x62, 0x83, 0x28, 0xe6, 0x04, 0x97, 0x41, 0x50, 0x88, 0xdc, 0xba, 0x54, 0xc3, 0x7e, 0x21, 0x72, 0x98, 0x42, 0x46, 0x23, 0x89, 0x51, 0x8c, 0xfd, 0x93, 0xc1, 0x63, 0x90, 0x84, 0xdd, 0x46, 0x74, 0xa7, 0x16, 0xe0, 0x35, 0x23, 0x4a, 0x00, 0xa1, 0x2d, 0xb1, 0xd6, 0x2c, 0xec, 0x5f, 0x32, 0xe2, 0x32, 0xf7, 0x1b, 0xe2, 0x18, 0xb3, 0x30, 0x11, 0x16, 0xf9, 0x0f, 0xa4, 0x99, 0x6b, 0x25, 0x92, 0x4d, 0xbc, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXOpenTabsIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x48, 0x08, 0x06, 0x00, 0x00, 0x00, 0xbe, 0xda, 0x08, 0x44, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4b, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xab, 0x1d, 0x6b, 0x00, 0x00, 0x05, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9c, 0x4d, 0x68, 0x15, 0x57, 0x14, 0xc7, 0xf3, 0xaa, 0x6d, 0xb0, 0x9f, 0xba, 0x88, 0xa8, 0xad, 0xb6, 0x09, 0x7e, 0xa1, 0x50, 0x50, 0xbb, 0xb1, 0x10, 0x25, 0xdd, 0x14, 0xd2, 0xd2, 0x2c, 0xda, 0x2e, 0x0a, 0x0d, 0x85, 0xfa, 0x81, 0x0a, 0xe2, 0x22, 0x94, 0xe2, 0xc2, 0x4d, 0x05, 0x45, 0xe8, 0xa2, 0x50, 0xaa, 0x60, 0x57, 0x51, 0xdc, 0xb8, 0x71, 0x91, 0xda, 0x95, 0xe9, 0xa6, 0x1f, 0xf4, 0x0b, 0x1a, 0xda, 0xb4, 0xd0, 0x54, 0xda, 0x2e, 0x8c, 0x69, 0xd1, 0x95, 0x28, 0x56, 0xa2, 0x8d, 0xbf, 0x23, 0x93, 0x77, 0x6f, 0xee, 0x3b, 0xf3, 0x6e, 0xe6, 0x71, 0x27, 0x99, 0xc9, 0xdc, 0x03, 0x7f, 0xe6, 0xde, 0x33, 0xe7, 0xde, 0x73, 0xce, 0x7f, 0xe6, 0xde, 0x99, 0xb9, 0x6f, 0xe6, 0xb5, 0xb5, 0x45, 0x99, 0x35, 0x03, 0xb5, 0x59, 0x5b, 0x62, 0x38, 0x35, 0x35, 0xd5, 0xc1, 0xe6, 0x19, 0xf0, 0x50, 0x96, 0x76, 0x39, 0xd9, 0x4e, 0xd1, 0xef, 0xd5, 0x5a, 0xad, 0xf6, 0x4f, 0x4e, 0xfd, 0x37, 0x74, 0xdb, 0x94, 0x2c, 0xc8, 0x79, 0x93, 0x16, 0xaf, 0x83, 0xb5, 0x09, 0x9e, 0x6a, 0xe8, 0x61, 0xfe, 0x15, 0x37, 0x09, 0xe1, 0x72, 0x82, 0x21, 0xb6, 0x67, 0x21, 0x50, 0x88, 0x9c, 0x1b, 0x81, 0xa4, 0x4d, 0x60, 0x18, 0x94, 0x51, 0xbe, 0x21, 0xe8, 0xad, 0x73, 0xc2, 0x14, 0x8e, 0xde, 0x02, 0x93, 0x65, 0x64, 0xc9, 0x8a, 0xf9, 0x1e, 0xe5, 0x7d, 0xa1, 0x09, 0x9b, 0x31, 0x0c, 0x71, 0xb0, 0x0c, 0x07, 0xbf, 0x03, 0x99, 0x9b, 0xca, 0x2e, 0x32, 0x3c, 0x37, 0x32, 0x24, 0xc7, 0x43, 0x25, 0xb2, 0xd8, 0xe9, 0xe8, 0x18, 0x75, 0x1f, 0x51, 0xff, 0x63, 0x33, 0x77, 0x73, 0x82, 0x13, 0xa0, 0x55, 0x95, 0x03, 0xdd, 0xec, 0x42, 0xf3, 0x38, 0xfb, 0x3f, 0x02, 0x32, 0xef, 0x86, 0x15, 0xce, 0xaa, 0x4e, 0x20, 0xa7, 0xaf, 0x26, 0x32, 0x0f, 0xf4, 0x82, 0x67, 0x41, 0xb3, 0x00, 0xc3, 0x06, 0xd5, 0xa4, 0x37, 0xe2, 0xa8, 0x81, 0xd5, 0xe0, 0x65, 0xf0, 0x05, 0x48, 0x93, 0x17, 0x9a, 0x74, 0xd3, 0xda, 0x2e, 0x3c, 0xbd, 0x96, 0xe2, 0xed, 0x14, 0xfa, 0x19, 0xc3, 0xb5, 0x35, 0x0f, 0xf9, 0xb6, 0x22, 0xc6, 0xe3, 0x29, 0xf1, 0xef, 0x0e, 0xee, 0x19, 0x47, 0x03, 0x8a, 0xb3, 0x09, 0x74, 0x4f, 0x06, 0x77, 0x96, 0x43, 0x87, 0xc4, 0xd9, 0x0e, 0xc6, 0x94, 0x1c, 0x4e, 0x84, 0x72, 0x67, 0x0f, 0xa9, 0x75, 0x4a, 0xa7, 0x97, 0x98, 0x20, 0x6f, 0x28, 0xfa, 0xc2, 0xa9, 0x88, 0xf3, 0x0e, 0x41, 0x7d, 0xa6, 0x04, 0xb6, 0x5e, 0xd1, 0xb5, 0xa4, 0xb2, 0xc9, 0x92, 0x1b, 0x4f, 0x57, 0xc6, 0x5c, 0x45, 0xc1, 0xeb, 0x72, 0x25, 0x77, 0x45, 0x3b, 0x09, 0x5c, 0x9b, 0x59, 0xd5, 0x6d, 0xb2, 0xe4, 0xea, 0xe1, 0xca, 0x35, 0x57, 0x51, 0xf0, 0xfa, 0xbf, 0x4a, 0x7c, 0x8f, 0x29, 0xba, 0x96, 0x54, 0x36, 0x59, 0x2d, 0x75, 0x50, 0xa5, 0x46, 0x91, 0xac, 0x0c, 0x47, 0x3b, 0x92, 0x15, 0xc9, 0xca, 0xc0, 0x40, 0x06, 0x53, 0xf7, 0x71, 0x27, 0x43, 0x53, 0xbf, 0x29, 0xf7, 0x3c, 0xab, 0xb1, 0xb2, 0x97, 0x78, 0xe4, 0x8a, 0xbb, 0x06, 0x2c, 0xf2, 0xb7, 0x6e, 0xc9, 0x42, 0xbb, 0x79, 0x7e, 0x8e, 0x38, 0xee, 0xa5, 0xf4, 0x26, 0x6b, 0x61, 0xb2, 0xbc, 0xf3, 0x47, 0xb2, 0x1d, 0xe6, 0x16, 0xe4, 0x87, 0x14, 0xdb, 0xb6, 0x5c, 0xc8, 0x22, 0xb8, 0x76, 0x1c, 0xbe, 0x07, 0x0e, 0x83, 0x47, 0xd3, 0x9c, 0xcf, 0xa1, 0x3e, 0x6d, 0xba, 0x59, 0x45, 0x0c, 0x82, 0x1d, 0x49, 0x2c, 0x72, 0x4f, 0x7b, 0x86, 0xf2, 0xfb, 0x90, 0xd6, 0x70, 0x65, 0x4d, 0xeb, 0x24, 0x69, 0x9b, 0x7d, 0x83, 0xb3, 0xcd, 0xb4, 0xfa, 0x0d, 0x1c, 0x05, 0x45, 0x20, 0x2a, 0x4b, 0x12, 0x72, 0x66, 0xbe, 0x03, 0xe4, 0x49, 0xe0, 0x0d, 0xb7, 0x61, 0x50, 0xb2, 0x70, 0x20, 0xce, 0x4e, 0x83, 0x2e, 0xd7, 0x51, 0xc9, 0xea, 0xf2, 0x88, 0xf7, 0x29, 0xf9, 0x2c, 0xb7, 0xe3, 0x0e, 0x4a, 0x16, 0x1d, 0xbf, 0x0b, 0x5e, 0xb4, 0x1d, 0x94, 0xb8, 0xbc, 0x94, 0xd8, 0x3f, 0xb4, 0xe3, 0x0f, 0x36, 0x67, 0x71, 0x14, 0x64, 0xc8, 0xa5, 0x3d, 0xb4, 0xca, 0xf3, 0xe5, 0x49, 0xf0, 0x33, 0xf8, 0x13, 0x4c, 0x82, 0xf9, 0x16, 0x19, 0x05, 0x4f, 0x83, 0x0d, 0x60, 0x0f, 0xd0, 0x1e, 0x8b, 0xfa, 0xc9, 0xeb, 0x13, 0xe6, 0xaf, 0xef, 0xd8, 0x6f, 0x04, 0xe5, 0xb7, 0xc0, 0x95, 0x59, 0x2f, 0xcd, 0xd2, 0x70, 0xab, 0xdb, 0x38, 0xa9, 0x7f, 0xcf, 0x76, 0xa5, 0xf1, 0x54, 0xbc, 0x12, 0xf1, 0xc9, 0x8a, 0xc5, 0xe9, 0x24, 0x5e, 0x77, 0x33, 0x30, 0x1d, 0x71, 0xc8, 0x61, 0xa8, 0x1d, 0x99, 0xbb, 0x38, 0xda, 0xc5, 0x91, 0x99, 0x98, 0x76, 0x58, 0xc4, 0x2d, 0xf1, 0xc9, 0x8a, 0xc5, 0x21, 0xf0, 0x97, 0x12, 0x5f, 0x3d, 0xaf, 0xbc, 0xc9, 0xfa, 0x9a, 0x40, 0x7e, 0x51, 0x02, 0x28, 0x9c, 0x8a, 0x38, 0x6f, 0x13, 0xd4, 0xa0, 0x12, 0x58, 0x2e, 0x64, 0x69, 0xeb, 0x46, 0x63, 0x8a, 0xf3, 0x22, 0xab, 0xb4, 0x78, 0xeb, 0x79, 0x85, 0x3c, 0xb3, 0xe4, 0x97, 0x21, 0x57, 0xae, 0xb8, 0x8a, 0x82, 0xd7, 0xb5, 0x78, 0xeb, 0x79, 0x85, 0x24, 0x4b, 0xe3, 0x41, 0x7e, 0x09, 0x2a, 0x93, 0x34, 0x8d, 0x37, 0x6f, 0xb2, 0xca, 0x44, 0x94, 0x37, 0xd6, 0x48, 0x96, 0x97, 0x22, 0x63, 0x10, 0xc9, 0x32, 0x5c, 0x78, 0x4b, 0x91, 0x2c, 0x2f, 0x45, 0xc6, 0x20, 0x92, 0x65, 0xb8, 0xf0, 0x96, 0x22, 0x59, 0x5e, 0x8a, 0x8c, 0x41, 0x24, 0xcb, 0x70, 0xe1, 0x2d, 0x45, 0xb2, 0xbc, 0x14, 0x19, 0x83, 0x48, 0x96, 0xe1, 0xc2, 0x5b, 0x8a, 0x64, 0x79, 0x29, 0x32, 0x06, 0x91, 0x2c, 0xc3, 0x85, 0xb7, 0x14, 0xc9, 0xf2, 0x52, 0x64, 0x0c, 0x22, 0x59, 0x86, 0x0b, 0x6f, 0x29, 0x92, 0xe5, 0xa5, 0xc8, 0x18, 0x44, 0xb2, 0x0c, 0x17, 0xde, 0x52, 0x24, 0xcb, 0x4b, 0x91, 0x31, 0xb0, 0xc9, 0xd2, 0x16, 0xbe, 0x1e, 0x31, 0xa6, 0xb1, 0x64, 0x93, 0x35, 0xae, 0xd0, 0xd1, 0xa5, 0xe8, 0x2a, 0xab, 0xb2, 0xc9, 0x92, 0x37, 0x49, 0x5c, 0x79, 0xde, 0x55, 0x54, 0xb9, 0xee, 0x23, 0xab, 0x87, 0x5f, 0x1c, 0x5f, 0xaa, 0x32, 0x41, 0x76, 0xee, 0x3e, 0xb2, 0xc4, 0x76, 0x10, 0xc2, 0x7a, 0xed, 0x46, 0x55, 0x2d, 0xdb, 0xef, 0x3a, 0xfc, 0x08, 0x09, 0xf2, 0x1e, 0x42, 0x97, 0x43, 0x86, 0x7c, 0x8c, 0x79, 0x11, 0xc2, 0x7e, 0x62, 0x2b, 0xaf, 0x12, 0xfd, 0x0d, 0xb4, 0x97, 0xc3, 0xe4, 0x9d, 0x81, 0x05, 0x2d, 0x75, 0xb2, 0xf8, 0x45, 0xf6, 0x3f, 0x08, 0x39, 0x48, 0xb6, 0x17, 0x53, 0x32, 0xde, 0x82, 0x5e, 0x50, 0x59, 0xb1, 0x87, 0x61, 0x1b, 0x84, 0x7d, 0x0e, 0x13, 0x17, 0x2a, 0xcb, 0x86, 0x27, 0xf1, 0x19, 0x64, 0x25, 0xb6, 0x7b, 0xd9, 0x9e, 0xf3, 0xb4, 0xab, 0xe4, 0xee, 0x06, 0xb2, 0x38, 0xbb, 0xae, 0x83, 0xb7, 0x61, 0xa3, 0x1b, 0x8c, 0x54, 0x92, 0x95, 0x94, 0xa4, 0xeb, 0x73, 0x96, 0xbb, 0x1f, 0xc2, 0xbe, 0x62, 0x0e, 0xdb, 0x86, 0x5e, 0x26, 0xfc, 0xb5, 0x16, 0x64, 0xc2, 0x6f, 0x20, 0x19, 0xdd, 0x76, 0xb0, 0x02, 0x2c, 0x58, 0x49, 0x25, 0x4b, 0x32, 0x86, 0x30, 0x79, 0x04, 0xba, 0x9c, 0x40, 0x54, 0xa9, 0x02, 0xb1, 0x43, 0xec, 0x7c, 0x35, 0xd5, 0x60, 0x01, 0xec, 0xd0, 0xce, 0x90, 0x05, 0x90, 0x56, 0x3e, 0x29, 0x44, 0xb2, 0x32, 0xf0, 0x1a, 0xc9, 0x8a, 0x64, 0x65, 0x60, 0x20, 0x83, 0x69, 0xc8, 0x33, 0xeb, 0x96, 0xe2, 0x77, 0xc6, 0x4b, 0xf7, 0xca, 0xfe, 0xa2, 0xa9, 0x3a, 0x94, 0x80, 0xea, 0x79, 0x85, 0x24, 0x4b, 0xae, 0x9a, 0xae, 0x94, 0xed, 0x79, 0x51, 0x8b, 0xb7, 0x9e, 0x57, 0x48, 0xb2, 0xb4, 0x97, 0x57, 0xb7, 0x73, 0x4b, 0x51, 0x8a, 0x7b, 0x2f, 0xe2, 0x94, 0x8f, 0x08, 0xfa, 0xdc, 0xa3, 0x4d, 0xbd, 0x9e, 0x57, 0x48, 0xb2, 0xb4, 0xc5, 0xc3, 0x27, 0x70, 0xf6, 0x31, 0x81, 0xe4, 0xf5, 0xc9, 0x9c, 0x92, 0x5b, 0xcb, 0xaa, 0x03, 0xb4, 0x94, 0x1b, 0x6b, 0x57, 0xb4, 0xbc, 0x5c, 0x9b, 0x6c, 0x75, 0x08, 0x59, 0x06, 0x64, 0xe5, 0x42, 0x93, 0x11, 0x94, 0x7d, 0x40, 0xbe, 0xfd, 0x0b, 0x79, 0x80, 0xb2, 0x05, 0xe9, 0x58, 0x13, 0x4b, 0x07, 0xe8, 0x06, 0xe7, 0x41, 0x9a, 0xbc, 0x32, 0xdd, 0x4c, 0x4e, 0xbd, 0x60, 0x82, 0xb7, 0x0f, 0xe8, 0xec, 0x88, 0xa7, 0x43, 0x59, 0x0b, 0xd3, 0xd6, 0xc3, 0x3c, 0xcd, 0x82, 0xef, 0x96, 0xdc, 0x1f, 0xf6, 0xf4, 0xfa, 0x25, 0xfb, 0x77, 0xf2, 0x24, 0xf3, 0xe0, 0xbf, 0x77, 0x42, 0x93, 0xb5, 0x84, 0xce, 0x7f, 0x05, 0x9d, 0x9e, 0x20, 0xca, 0xb0, 0xfb, 0x2e, 0x41, 0x6e, 0x81, 0xa8, 0xd1, 0xe9, 0x60, 0x83, 0x0e, 0x09, 0x3a, 0x96, 0x4f, 0x3a, 0xf6, 0x03, 0xf9, 0x16, 0xa6, 0xec, 0x72, 0xcc, 0x26, 0x2a, 0xb7, 0x64, 0x18, 0x8e, 0x5d, 0x60, 0x28, 0x6d, 0x12, 0x28, 0xb8, 0x7e, 0x94, 0xf8, 0x7a, 0x34, 0x72, 0x82, 0x0e, 0x43, 0xd7, 0x01, 0x4e, 0x65, 0x72, 0xec, 0x07, 0xf2, 0xb1, 0x90, 0x2c, 0xf3, 0x14, 0xf1, 0x4f, 0x80, 0x64, 0x14, 0xc8, 0x6f, 0x0f, 0x72, 0xd5, 0xbb, 0x04, 0x4e, 0x71, 0x46, 0xc9, 0x10, 0x6c, 0x90, 0x5c, 0xc9, 0x72, 0xbd, 0x41, 0x9e, 0xdc, 0x21, 0xaf, 0x01, 0x45, 0xb9, 0x95, 0x90, 0xaf, 0xef, 0xaf, 0x40, 0x8e, 0x2c, 0x45, 0x79, 0xe5, 0x3e, 0xde, 0xb6, 0x6e, 0x62, 0xf2, 0xf1, 0x89, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXMoreIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x20, 0x4f, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x6b, 0xda, 0xa9, 0x4c, 0x00, 0x00, 0x01, 0xc4, 0x49, 0x44, 0x41, 0x54, 0x48, 0x0d, 0xdd, 0x95, 0x4f, 0x2b, 0x44, 0x51, 0x18, 0xc6, 0xe7, 0xfa, 0x5b, 0x14, 0x4d, 0xc4, 0x6a, 0xec, 0xd4, 0xa4, 0xd8, 0x68, 0x90, 0x1a, 0x5f, 0x60, 0x0a, 0x65, 0xef, 0x23, 0x8c, 0x6c, 0x94, 0xad, 0x9d, 0x95, 0x2f, 0x80, 0xa5, 0x05, 0x45, 0x29, 0x35, 0x62, 0x41, 0x4a, 0x22, 0xa9, 0x61, 0x35, 0x8c, 0x42, 0xca, 0x9f, 0xd8, 0xa9, 0x99, 0x05, 0xe3, 0xf7, 0x4e, 0x33, 0x67, 0xee, 0x4c, 0xf7, 0x9c, 0x79, 0x6d, 0xbd, 0xf5, 0x73, 0xcf, 0x79, 0xdf, 0xe7, 0x79, 0x8e, 0x3b, 0xdd, 0x99, 0x1b, 0x0a, 0xf9, 0xaa, 0x50, 0x28, 0x24, 0x60, 0x07, 0x1e, 0xe1, 0x01, 0xb6, 0x21, 0xe1, 0x93, 0xa8, 0x97, 0xf8, 0xfa, 0x61, 0x1d, 0x6e, 0xe0, 0x1d, 0x0e, 0x61, 0x0e, 0x1a, 0xd5, 0x21, 0x25, 0x21, 0x9e, 0x4e, 0x58, 0x86, 0x53, 0xf8, 0x84, 0x73, 0x58, 0x81, 0xae, 0xaa, 0x2c, 0x1a, 0x1e, 0xac, 0x82, 0xad, 0xd6, 0x18, 0x78, 0x55, 0x26, 0xc7, 0x06, 0xed, 0x2c, 0x7c, 0x59, 0xc2, 0xce, 0xe8, 0x77, 0x3b, 0xec, 0x55, 0x23, 0xb4, 0x31, 0x78, 0xb2, 0x64, 0xbd, 0xd2, 0x9f, 0x30, 0x06, 0x36, 0x49, 0x8b, 0xd0, 0xdf, 0x4e, 0x1a, 0x83, 0x63, 0x81, 0x61, 0x00, 0x72, 0x7e, 0x63, 0xc0, 0x7a, 0xd3, 0x11, 0x61, 0x46, 0xf8, 0xda, 0x21, 0x1b, 0xe0, 0xf7, 0xb7, 0x9e, 0xd9, 0x84, 0x43, 0xfc, 0x69, 0x01, 0xdb, 0xa7, 0xe7, 0x37, 0x88, 0xa6, 0xd9, 0x9c, 0x62, 0x59, 0xa0, 0x91, 0x47, 0x53, 0x53, 0x31, 0x4b, 0x84, 0x69, 0x13, 0xb2, 0xa0, 0x09, 0x42, 0xb3, 0xd4, 0x80, 0x6b, 0x08, 0xda, 0x8c, 0xdb, 0xbe, 0x10, 0xcd, 0xa0, 0x7d, 0x6c, 0x26, 0x63, 0x66, 0xe5, 0x5e, 0x8c, 0xb8, 0xc7, 0xc5, 0xa9, 0x36, 0x6b, 0x54, 0x6e, 0xa4, 0x4f, 0x11, 0x58, 0x96, 0x44, 0xca, 0x8b, 0xa0, 0x2b, 0x9f, 0x4c, 0x2b, 0xfd, 0x9e, 0xa0, 0x59, 0x40, 0x4f, 0x73, 0xae, 0x46, 0x23, 0xd1, 0x11, 0xb9, 0x91, 0xeb, 0x80, 0x43, 0x6c, 0x2d, 0xa7, 0xd6, 0xf3, 0xbc, 0x3c, 0xc6, 0x8c, 0xcd, 0x5c, 0xd3, 0x4f, 0xd7, 0xec, 0x83, 0xb6, 0x1a, 0x8d, 0xf8, 0xd2, 0x72, 0x23, 0x77, 0x90, 0x95, 0x5d, 0x9d, 0xca, 0xf0, 0x8f, 0xde, 0xd7, 0xd1, 0xc8, 0x78, 0x5f, 0xa1, 0xc9, 0xa1, 0x39, 0x56, 0xe8, 0x34, 0x59, 0x12, 0x93, 0x2a, 0x66, 0xf1, 0x48, 0xc4, 0xe1, 0x1b, 0x6c, 0x25, 0xb3, 0x71, 0xc5, 0xc1, 0xf2, 0xe3, 0xd1, 0x01, 0xf2, 0x0e, 0x72, 0xd5, 0xbc, 0x26, 0x4b, 0x34, 0x84, 0xec, 0xb9, 0x82, 0x98, 0x1d, 0x40, 0xe5, 0xd5, 0xc0, 0x66, 0x06, 0xde, 0xa0, 0xb6, 0x5e, 0x68, 0x4c, 0x6b, 0x0f, 0x2e, 0x1d, 0x1e, 0xc5, 0x73, 0x51, 0x1b, 0xc4, 0x3e, 0x0f, 0x8b, 0x20, 0x4f, 0x82, 0xaa, 0xd0, 0x86, 0x61, 0x03, 0x82, 0x6a, 0x8b, 0x66, 0xf1, 0xa5, 0x58, 0xb9, 0x13, 0x62, 0x69, 0xca, 0x8b, 0x6a, 0x0a, 0x86, 0xe1, 0x07, 0x2e, 0x61, 0x97, 0x47, 0xea, 0x83, 0xeb, 0x9f, 0x8a, 0xac, 0x26, 0x0c, 0x93, 0x20, 0xbf, 0x4e, 0xbd, 0x70, 0x05, 0x29, 0xb2, 0xb4, 0xdf, 0x21, 0xe4, 0x95, 0x22, 0x2f, 0xce, 0x4e, 0x88, 0xc2, 0x2d, 0x9c, 0x90, 0x75, 0xc4, 0xf5, 0x7f, 0xd5, 0x2f, 0x30, 0x5e, 0xa3, 0xc0, 0xef, 0xd7, 0xce, 0x11, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXMoreIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x12, 0x08, 0x06, 0x00, 0x00, 0x00, 0x13, 0xcf, 0x23, 0xd5, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4b, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x08, 0xca, 0x01, 0x00, 0x00, 0x02, 0xc6, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x98, 0x3d, 0x68, 0x15, 0x41, 0x14, 0x85, 0xb3, 0x2f, 0xd1, 0x48, 0x10, 0x02, 0xa2, 0x21, 0x85, 0x76, 0x16, 0x16, 0x1a, 0x34, 0x2f, 0xa0, 0xd8, 0xd8, 0x99, 0x68, 0x11, 0x14, 0xb4, 0x10, 0x24, 0xa8, 0x85, 0xd6, 0x5a, 0x06, 0xd4, 0xc6, 0x46, 0x14, 0x0b, 0x8b, 0x34, 0x4a, 0x8a, 0xa0, 0x36, 0xa9, 0xd2, 0xaa, 0x8d, 0x8d, 0x29, 0x12, 0x83, 0x29, 0xb4, 0x12, 0x9b, 0x88, 0x8a, 0x81, 0x60, 0x63, 0xfc, 0x21, 0xc9, 0xf3, 0xbb, 0x92, 0x59, 0x76, 0x33, 0x33, 0x99, 0xbd, 0xf7, 0x3d, 0xbb, 0x5c, 0x38, 0xec, 0xcc, 0xfd, 0x39, 0x67, 0x77, 0x76, 0x77, 0x66, 0x76, 0xb3, 0xb6, 0x80, 0x35, 0x1a, 0x8d, 0xfd, 0xb8, 0xcf, 0x83, 0x01, 0x50, 0x07, 0x6b, 0xe0, 0x0d, 0x98, 0x03, 0x93, 0x59, 0x96, 0x7d, 0xe0, 0xd8, 0x52, 0x43, 0xf3, 0x20, 0x84, 0x67, 0x81, 0x68, 0xf6, 0x83, 0x65, 0xe0, 0x34, 0x9f, 0xa2, 0xf9, 0x85, 0x7e, 0x4b, 0x0d, 0xcd, 0xe3, 0x10, 0x9e, 0x02, 0x72, 0x8d, 0x47, 0xc0, 0x12, 0x70, 0x9a, 0x13, 0x68, 0x4a, 0x3f, 0x6c, 0x14, 0xd7, 0xc0, 0x75, 0xb0, 0x0c, 0x62, 0x26, 0xb1, 0x1b, 0xa0, 0x16, 0x66, 0xd1, 0x79, 0xe1, 0xe9, 0x00, 0x37, 0xc1, 0x1f, 0x10, 0xb3, 0x25, 0x02, 0x17, 0x75, 0xcc, 0xf1, 0x6c, 0xb8, 0x76, 0x82, 0x31, 0xb0, 0x06, 0x62, 0xf6, 0x95, 0xc0, 0x99, 0x20, 0x0b, 0x81, 0x76, 0xf0, 0x22, 0x56, 0x19, 0xf0, 0xbf, 0x94, 0x9a, 0x20, 0x59, 0x45, 0x27, 0xf5, 0x9d, 0x60, 0x3a, 0xc0, 0x1d, 0x73, 0x3d, 0xae, 0x48, 0x1d, 0x4d, 0x83, 0xb8, 0x17, 0x7c, 0x8c, 0x09, 0x04, 0xfc, 0xf7, 0x3d, 0x32, 0x92, 0x46, 0x03, 0x89, 0x29, 0xd7, 0xa8, 0x47, 0xa4, 0x70, 0x40, 0x7e, 0x37, 0x25, 0x10, 0x88, 0x9f, 0x53, 0x48, 0x78, 0xa9, 0xf0, 0x4d, 0x05, 0x38, 0x53, 0xae, 0x93, 0x39, 0x11, 0x99, 0x7d, 0xe0, 0x77, 0xaa, 0x22, 0x10, 0x97, 0x9a, 0x43, 0x39, 0x91, 0xa2, 0x41, 0xdd, 0x31, 0xb0, 0x12, 0xe0, 0x4c, 0xb9, 0xbe, 0x91, 0xb0, 0x5b, 0x21, 0x95, 0xa7, 0x52, 0x37, 0x92, 0x22, 0x8f, 0xc4, 0x17, 0xf0, 0x77, 0xbb, 0x79, 0xe7, 0x12, 0x8c, 0xdb, 0x73, 0xd6, 0xea, 0x0d, 0xa9, 0x19, 0xa9, 0x9e, 0x5e, 0xca, 0xbc, 0x42, 0xcf, 0xf2, 0x1a, 0xef, 0xa1, 0x6e, 0xb8, 0xc4, 0x54, 0xbd, 0x73, 0xb5, 0x7a, 0x6a, 0x29, 0x73, 0x2f, 0xbd, 0x41, 0x37, 0x58, 0xb2, 0x02, 0x59, 0x4d, 0x56, 0x12, 0x8b, 0x59, 0xeb, 0x44, 0x4b, 0x5d, 0xcb, 0x93, 0x21, 0x37, 0x46, 0x56, 0x3c, 0xab, 0xd5, 0x65, 0xf5, 0x93, 0x01, 0x6b, 0x86, 0xa4, 0x1f, 0x8e, 0x4c, 0x73, 0x06, 0xe4, 0x77, 0x92, 0x6f, 0x7a, 0x7d, 0xd7, 0x75, 0x64, 0x6b, 0xa1, 0xb5, 0x03, 0x14, 0x74, 0x69, 0x8b, 0x0a, 0xf9, 0x75, 0x19, 0x28, 0xb9, 0x50, 0xf7, 0x84, 0x15, 0x62, 0x95, 0x9b, 0x96, 0x5a, 0xd1, 0x54, 0x0d, 0xf0, 0x86, 0xb3, 0xb1, 0x68, 0x5a, 0x5e, 0xf9, 0xa2, 0x6c, 0xad, 0xc6, 0xc6, 0x6b, 0x15, 0xcf, 0xdb, 0xa2, 0x57, 0xd9, 0x9e, 0x83, 0xa3, 0xa1, 0xa9, 0x21, 0xff, 0x17, 0xf9, 0xef, 0x34, 0x35, 0x1b, 0x72, 0x65, 0xe3, 0xa8, 0xb5, 0xf7, 0x14, 0xfc, 0xd4, 0x16, 0x15, 0xf2, 0x67, 0xdd, 0x1d, 0x9a, 0x2d, 0x38, 0xb5, 0x4d, 0x6b, 0xad, 0xe5, 0x82, 0xdd, 0xb9, 0xa9, 0x35, 0xb9, 0x41, 0x2b, 0x14, 0xcf, 0x3b, 0x02, 0xc3, 0x71, 0xe6, 0x5f, 0x0d, 0x73, 0xc8, 0x00, 0xb0, 0x2c, 0xe3, 0xb2, 0xeb, 0x3e, 0x6c, 0x10, 0x6e, 0xa3, 0xee, 0x04, 0xd8, 0x6c, 0x07, 0x4d, 0x38, 0x68, 0x8b, 0x78, 0x7b, 0x8c, 0x9a, 0xd7, 0x82, 0x8c, 0x69, 0xe7, 0x27, 0x52, 0xba, 0x73, 0x4d, 0x3a, 0x77, 0xd2, 0x35, 0x5e, 0xc6, 0xad, 0x9c, 0xc0, 0xd0, 0x80, 0xed, 0xa1, 0xc7, 0x98, 0x76, 0xc8, 0x37, 0xab, 0xc9, 0xa0, 0xce, 0xc0, 0xf3, 0xb4, 0x84, 0x97, 0x31, 0x54, 0x12, 0x24, 0xbc, 0x0d, 0xbc, 0xf6, 0xd2, 0xe2, 0x8e, 0x57, 0x84, 0x3a, 0x4a, 0x24, 0xca, 0x0e, 0xf5, 0x5d, 0x60, 0x3e, 0x2e, 0xe1, 0x45, 0xc6, 0x95, 0x12, 0x5e, 0x3a, 0x8c, 0xfb, 0xc0, 0x67, 0x8f, 0x39, 0xee, 0x78, 0xe0, 0x91, 0x88, 0x83, 0x7c, 0x19, 0xb0, 0xdb, 0x60, 0xb3, 0x8f, 0x5a, 0xd9, 0xb5, 0xcb, 0xa7, 0x51, 0x53, 0x03, 0xe5, 0x4e, 0x00, 0x9e, 0x1d, 0xe0, 0x1e, 0x58, 0x05, 0x31, 0xfb, 0x4e, 0xe0, 0xb2, 0xab, 0x69, 0xf6, 0x08, 0xd7, 0x2e, 0xf0, 0x24, 0x26, 0xb6, 0xee, 0x97, 0xd7, 0xbd, 0xf4, 0x14, 0x07, 0x97, 0x6f, 0x92, 0xfa, 0x38, 0xa1, 0x0b, 0xc0, 0xfd, 0x2e, 0x71, 0xbf, 0x68, 0x64, 0x52, 0x7e, 0xc6, 0x64, 0xd9, 0xcc, 0x4a, 0x16, 0xbc, 0x56, 0x34, 0x8f, 0x12, 0x90, 0xef, 0x3e, 0xa7, 0xf9, 0x83, 0xb6, 0xe8, 0xc9, 0x64, 0x3e, 0x8e, 0xe6, 0x02, 0xc7, 0x96, 0x1a, 0x9a, 0x83, 0x10, 0x9e, 0x06, 0xb2, 0xc9, 0x95, 0xb9, 0x57, 0x7e, 0xc9, 0x88, 0x9e, 0xe0, 0x11, 0x9a, 0x8b, 0x1c, 0xb7, 0x6c, 0x6b, 0x04, 0xfe, 0xf3, 0x08, 0xfc, 0x05, 0x85, 0x24, 0x43, 0xa4, 0x3d, 0xeb, 0x6a, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXGearIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x57, 0x02, 0xf9, 0x87, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x37, 0x6c, 0x0c, 0x00, 0x00, 0x06, 0x6a, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xd5, 0x98, 0x7b, 0x88, 0x55, 0x55, 0x14, 0x87, 0x67, 0xd4, 0xd1, 0xc9, 0x4c, 0x2d, 0xcb, 0x34, 0x8a, 0x91, 0x1c, 0x2b, 0x31, 0xc9, 0x71, 0x8a, 0x4c, 0x1c, 0x2a, 0xc8, 0x30, 0xc5, 0xcc, 0xc2, 0x12, 0x13, 0x24, 0xe9, 0x45, 0xd0, 0x04, 0xa6, 0xf5, 0xc7, 0x64, 0x91, 0x14, 0x61, 0x6f, 0xb4, 0x18, 0x2a, 0x04, 0x4b, 0x7b, 0x3f, 0xa0, 0x07, 0x21, 0xf6, 0x18, 0x32, 0x8a, 0x2c, 0x19, 0x4b, 0x53, 0x62, 0x6a, 0xd0, 0x2c, 0x1b, 0xf3, 0x91, 0x39, 0x96, 0xe9, 0xa4, 0x4d, 0xdf, 0x6f, 0xe6, 0x9e, 0x7b, 0xd7, 0xdd, 0x77, 0xdf, 0x73, 0xcf, 0x9e, 0x07, 0x53, 0x0b, 0xbe, 0x7b, 0xd6, 0x5a, 0x7b, 0xad, 0xb5, 0xf7, 0x3d, 0x8f, 0xbd, 0xf7, 0x39, 0x45, 0x45, 0x5d, 0x28, 0xad, 0xad, 0xad, 0x63, 0xa0, 0xcc, 0x57, 0x12, 0x7f, 0x29, 0x54, 0x41, 0x89, 0xaf, 0xbd, 0xc7, 0x7d, 0x0c, 0xac, 0x02, 0x24, 0xcd, 0x30, 0xc2, 0x1d, 0x10, 0xbe, 0x57, 0xd5, 0x88, 0xd4, 0xba, 0x6d, 0xff, 0x09, 0x9b, 0x81, 0xdd, 0xde, 0x36, 0xbc, 0xf6, 0x9f, 0xe7, 0xed, 0xa0, 0x70, 0x4d, 0x30, 0x6d, 0x5b, 0x6c, 0x5b, 0x67, 0xf5, 0x3e, 0x21, 0x05, 0x18, 0xc4, 0x60, 0xe2, 0x1f, 0x85, 0xaf, 0x8b, 0x8b, 0x8b, 0x9f, 0x72, 0x72, 0xcb, 0x8d, 0x3d, 0x97, 0xd8, 0xed, 0xd8, 0xbd, 0xa0, 0x05, 0x66, 0x40, 0x24, 0x67, 0xd2, 0x46, 0x7a, 0x71, 0x6b, 0xe4, 0xc0, 0x3e, 0x0f, 0x7d, 0x11, 0xac, 0xc6, 0xbf, 0x26, 0xf2, 0x77, 0xf9, 0x91, 0x8e, 0xe6, 0x43, 0x24, 0x2b, 0x50, 0xfa, 0xc0, 0x28, 0x58, 0x02, 0xbb, 0x21, 0xa9, 0xbc, 0x4c, 0xe0, 0x15, 0xa0, 0xfc, 0xc9, 0xa0, 0xdb, 0x4e, 0xf2, 0x55, 0x97, 0x0f, 0xda, 0x16, 0xa4, 0x03, 0x75, 0x66, 0x65, 0xa7, 0x35, 0x3a, 0xa0, 0xff, 0x4a, 0x4e, 0x8b, 0xc9, 0x7b, 0xcb, 0xf6, 0x97, 0x44, 0xd7, 0x25, 0x0e, 0x91, 0x0d, 0x4e, 0xf0, 0x69, 0x8e, 0x6d, 0xcd, 0x63, 0x18, 0x4d, 0xd0, 0x6c, 0x9d, 0x8e, 0x3e, 0x14, 0xdb, 0xce, 0x4a, 0xc1, 0x57, 0x20, 0xe8, 0x19, 0xa0, 0xb3, 0x8b, 0x41, 0x03, 0xeb, 0x0d, 0xae, 0xd4, 0xe3, 0x78, 0x09, 0x3e, 0x83, 0x9f, 0xa1, 0x89, 0xfb, 0x59, 0xb1, 0x45, 0x9c, 0xe1, 0xe3, 0x38, 0x0c, 0x87, 0x0b, 0x61, 0x1a, 0x4c, 0x81, 0x21, 0xe0, 0x4a, 0x25, 0xb1, 0xa5, 0xe4, 0x1d, 0x76, 0x1b, 0x82, 0x6c, 0x8a, 0x94, 0x80, 0xa6, 0xbd, 0x35, 0x30, 0x07, 0x34, 0x87, 0xd7, 0xc0, 0x3f, 0xe0, 0x4a, 0x03, 0x8e, 0xb3, 0x42, 0x3a, 0x20, 0xbe, 0x37, 0xcc, 0x83, 0x46, 0x70, 0x65, 0x3d, 0x8e, 0xe1, 0x30, 0x0e, 0x6a, 0xe1, 0x0b, 0xb8, 0x28, 0xa4, 0xbe, 0xce, 0xd8, 0x78, 0xb0, 0xf2, 0xa7, 0x35, 0x3c, 0xfa, 0xc4, 0xa0, 0x0e, 0x52, 0xc1, 0xd4, 0xd1, 0x89, 0xaa, 0x86, 0xc3, 0x4e, 0xcd, 0x43, 0x8e, 0xfd, 0x5c, 0x50, 0x7d, 0x92, 0x7b, 0xc1, 0x97, 0x4e, 0x11, 0x6b, 0x2e, 0xc6, 0x78, 0xdb, 0x38, 0x36, 0xa0, 0x87, 0x3e, 0x4f, 0xe9, 0x31, 0x91, 0x7b, 0x3e, 0x6c, 0x37, 0xf5, 0xac, 0x7a, 0x0c, 0x43, 0xb7, 0x5e, 0x98, 0x90, 0xa4, 0x4b, 0xf8, 0xb7, 0xad, 0x84, 0xae, 0x5b, 0xa8, 0x5a, 0x95, 0x38, 0x6a, 0x3e, 0xb7, 0x67, 0xee, 0xc6, 0xb0, 0x1e, 0xb2, 0xa3, 0xa9, 0x75, 0x32, 0x6c, 0x05, 0x57, 0xdc, 0xf5, 0x26, 0x3b, 0x31, 0x9f, 0x45, 0x95, 0x41, 0xf0, 0x9b, 0x53, 0xed, 0x26, 0x1b, 0x4f, 0xdb, 0x03, 0xa6, 0x5d, 0xeb, 0xc0, 0x20, 0xdb, 0x1e, 0xaa, 0x93, 0x5f, 0x06, 0xbf, 0x80, 0x95, 0x9b, 0x43, 0xeb, 0xb4, 0xc5, 0x53, 0xe1, 0x11, 0x5b, 0x05, 0xfd, 0x75, 0xb7, 0x10, 0xbe, 0xfe, 0xb0, 0xc3, 0xc4, 0x3d, 0xe1, 0xc6, 0x84, 0xda, 0xd4, 0xd2, 0x4c, 0x64, 0xaf, 0x7c, 0x13, 0xf6, 0x80, 0x82, 0x75, 0x08, 0x3a, 0x15, 0xa6, 0xc2, 0xa5, 0x30, 0x05, 0x8e, 0x40, 0x24, 0x7b, 0x51, 0x34, 0x67, 0xe7, 0x08, 0xfe, 0xeb, 0xa2, 0x20, 0x8e, 0xea, 0x78, 0x74, 0x4e, 0x50, 0xa0, 0x83, 0x1a, 0x8f, 0x99, 0x9a, 0x52, 0x97, 0x41, 0x15, 0x68, 0x5c, 0x17, 0x78, 0xcb, 0xd1, 0xb0, 0x0d, 0xf2, 0xc9, 0xf5, 0xde, 0xa4, 0x94, 0x93, 0xa4, 0x3a, 0x93, 0xb8, 0x36, 0x2e, 0x36, 0x49, 0x1b, 0xb5, 0x4e, 0x80, 0xb8, 0x55, 0x5e, 0xeb, 0x48, 0xb6, 0x90, 0xb0, 0xc9, 0x0c, 0xc2, 0xaa, 0x2a, 0x14, 0x3b, 0xc3, 0xd0, 0x3e, 0x16, 0x8e, 0x9a, 0xa4, 0xab, 0xb2, 0xab, 0x87, 0x5b, 0xd4, 0x5a, 0x64, 0xea, 0x59, 0x55, 0xb3, 0xd2, 0xa4, 0x9c, 0x8a, 0x38, 0x35, 0x0b, 0xd4, 0x81, 0x2b, 0x0f, 0xe7, 0x04, 0x7b, 0x1c, 0x24, 0x2d, 0x37, 0x89, 0x8d, 0xe8, 0xfd, 0xc0, 0xb7, 0x62, 0x7b, 0xb2, 0x73, 0x5d, 0xe4, 0x6a, 0x31, 0xb3, 0x27, 0x05, 0xb3, 0xf5, 0x77, 0xd0, 0x4a, 0xee, 0x17, 0x1a, 0xb5, 0x3b, 0xfc, 0x1c, 0xac, 0x8c, 0xf5, 0x47, 0x67, 0x7b, 0x49, 0x38, 0x11, 0xf6, 0x98, 0xc4, 0xfd, 0xe8, 0x9a, 0x76, 0x77, 0xc1, 0x6b, 0x70, 0x6e, 0x76, 0x46, 0x61, 0x8b, 0x9c, 0xf7, 0xc1, 0x4a, 0x45, 0xc1, 0x2c, 0xa2, 0xeb, 0x4d, 0xc6, 0xf7, 0x05, 0x13, 0x4c, 0x00, 0x79, 0x0b, 0x4c, 0xae, 0xab, 0xea, 0x01, 0x9f, 0x69, 0xc2, 0x0b, 0xaa, 0xc4, 0xdf, 0xe5, 0x14, 0xc9, 0x59, 0xf1, 0xb3, 0xee, 0x6d, 0x82, 0x75, 0xc9, 0xed, 0x2c, 0xf2, 0x43, 0xc1, 0x5e, 0x52, 0x01, 0xe4, 0xaa, 0xd6, 0xac, 0x98, 0x78, 0x6d, 0x1c, 0xb5, 0xbf, 0x1a, 0x13, 0x13, 0xe3, 0x36, 0x6d, 0x76, 0x1c, 0x39, 0x77, 0x43, 0xfa, 0x0f, 0x50, 0x58, 0x5b, 0xe3, 0x05, 0x50, 0x6a, 0x92, 0x7e, 0x32, 0x7a, 0x21, 0x55, 0x8b, 0xdc, 0x84, 0x54, 0xd0, 0x21, 0x8e, 0xf3, 0x41, 0x53, 0xaf, 0x36, 0x62, 0x1b, 0x41, 0xa2, 0xad, 0xf3, 0x83, 0xf4, 0x35, 0x20, 0x09, 0xc4, 0x6e, 0x53, 0x92, 0x91, 0xd9, 0xe4, 0x4d, 0x84, 0xf4, 0xb8, 0xb5, 0x25, 0xd0, 0xbd, 0xbb, 0x16, 0x7c, 0x3b, 0xcd, 0x7b, 0x4c, 0x72, 0xac, 0x4a, 0xfe, 0x9b, 0x10, 0x89, 0x5e, 0x0f, 0xd3, 0x82, 0xf3, 0x0c, 0xd0, 0xec, 0xd1, 0x55, 0xa2, 0xd5, 0x7a, 0xba, 0x3a, 0xd0, 0x3f, 0xd1, 0x59, 0x9b, 0x0c, 0xc5, 0xe0, 0xca, 0x5e, 0xd7, 0x11, 0x63, 0x9f, 0x6d, 0xda, 0xd6, 0x1b, 0xbd, 0x88, 0xfd, 0xbd, 0xae, 0xe4, 0x2e, 0xeb, 0xeb, 0xa4, 0xae, 0x77, 0x8b, 0xb6, 0x6d, 0x8d, 0xfe, 0x40, 0x1d, 0x3c, 0x0e, 0xbb, 0xc1, 0x95, 0x61, 0xae, 0x23, 0xc6, 0xfe, 0xce, 0xb4, 0x55, 0x19, 0x5d, 0x57, 0x79, 0x24, 0xb6, 0x3a, 0x95, 0xe8, 0x65, 0x7e, 0x4f, 0x42, 0x7c, 0x27, 0xf0, 0x28, 0xb9, 0x1f, 0xc2, 0x7d, 0x90, 0x11, 0x3a, 0xd1, 0x16, 0x5a, 0x5b, 0x09, 0x2b, 0x2b, 0x32, 0x11, 0xf1, 0x1a, 0x49, 0x37, 0x98, 0xc4, 0xbf, 0xd0, 0x6f, 0x83, 0xd3, 0x41, 0x5b, 0x93, 0x6f, 0x21, 0x92, 0xac, 0x4f, 0x2e, 0x71, 0x55, 0x49, 0xd0, 0x8e, 0xd7, 0xca, 0x2b, 0x18, 0x27, 0xc5, 0xe5, 0xe8, 0x6c, 0x1d, 0x30, 0x19, 0x1f, 0xc4, 0x06, 0x9b, 0x46, 0x72, 0x8a, 0xe1, 0x63, 0x93, 0xeb, 0x53, 0xf5, 0x62, 0xa4, 0xab, 0x91, 0x48, 0x88, 0x9d, 0xe1, 0x14, 0x99, 0xe7, 0x26, 0x66, 0x9e, 0xe6, 0x4c, 0xcb, 0xd6, 0x8c, 0x5a, 0x74, 0x0e, 0x05, 0x7c, 0xcf, 0x86, 0x09, 0x69, 0x57, 0xb9, 0xcf, 0x75, 0x6b, 0xac, 0xcb, 0x69, 0xc8, 0x38, 0x8e, 0xa0, 0xce, 0x24, 0xae, 0x31, 0xe3, 0x2a, 0xa8, 0xb9, 0xd3, 0xe6, 0xa6, 0xd8, 0x0c, 0x06, 0x3b, 0x04, 0x36, 0x3b, 0xff, 0x7a, 0x52, 0x6c, 0x52, 0xaa, 0x91, 0x9c, 0x11, 0x60, 0x5f, 0x05, 0xf5, 0xc9, 0x44, 0x8b, 0xd7, 0x8f, 0xf0, 0x02, 0x94, 0x27, 0xa9, 0x63, 0x63, 0xc8, 0xf9, 0x14, 0xac, 0xe4, 0xdf, 0x63, 0x11, 0xa5, 0x37, 0xb0, 0xed, 0x36, 0x3a, 0xa5, 0x3f, 0x63, 0x8b, 0xe6, 0xd3, 0x89, 0x7d, 0xc7, 0xe4, 0xea, 0x24, 0x68, 0xe1, 0xea, 0xb0, 0x90, 0x5f, 0x6e, 0xea, 0x45, 0xaa, 0xa6, 0xe2, 0x1a, 0x6f, 0x51, 0x1a, 0xf2, 0x6d, 0x5f, 0xf5, 0x56, 0xd6, 0xd7, 0x9b, 0x94, 0x72, 0xd2, 0xee, 0xde, 0xab, 0x59, 0xb3, 0x50, 0x5c, 0x6e, 0xbe, 0x36, 0x6a, 0xda, 0xb7, 0x3d, 0xcc, 0x2c, 0xb9, 0x32, 0x27, 0x8f, 0x66, 0xbd, 0x98, 0xe7, 0x93, 0xbb, 0x73, 0x12, 0x52, 0x0e, 0x12, 0xfa, 0x83, 0xbd, 0x72, 0x2b, 0xf3, 0xc5, 0x26, 0xf5, 0x53, 0x6f, 0x18, 0xd8, 0xc9, 0x04, 0x33, 0x2d, 0xda, 0xa1, 0x46, 0x2b, 0x7e, 0xa6, 0x24, 0x4e, 0x7d, 0xab, 0xd1, 0xb4, 0xa5, 0x37, 0x33, 0x4d, 0x7f, 0xda, 0x4d, 0x46, 0xa2, 0x69, 0xd1, 0xfb, 0xed, 0x07, 0xff, 0x43, 0x51, 0x10, 0x47, 0x5d, 0xad, 0x53, 0x32, 0x55, 0x3b, 0xa6, 0x51, 0x63, 0x95, 0xa9, 0x29, 0xb5, 0x1a, 0xb4, 0x63, 0x28, 0xd3, 0x31, 0x51, 0x55, 0x02, 0xef, 0x04, 0x2b, 0xeb, 0x30, 0xb2, 0x66, 0x24, 0xec, 0xd1, 0xd0, 0x62, 0x82, 0x6e, 0x4d, 0x54, 0x3c, 0x26, 0x88, 0x5a, 0xd3, 0x4c, 0x3d, 0xa9, 0x5b, 0x20, 0xfc, 0x79, 0x22, 0xa9, 0x2f, 0xec, 0x03, 0x2b, 0x5a, 0xb1, 0xd3, 0x42, 0x43, 0x9d, 0x69, 0xd4, 0x17, 0x35, 0xdf, 0xb4, 0x9c, 0x8e, 0x2f, 0xa4, 0x90, 0x5f, 0x09, 0x7f, 0x98, 0x9a, 0x52, 0x67, 0x17, 0xca, 0xf3, 0xb6, 0x93, 0x38, 0xdd, 0x29, 0x14, 0x99, 0xb5, 0x28, 0x5a, 0xb4, 0xe6, 0x46, 0x0e, 0x8e, 0x9a, 0x1d, 0xc6, 0x7b, 0x0b, 0x25, 0x74, 0x92, 0xaf, 0xab, 0xa9, 0x97, 0x1f, 0x57, 0xde, 0x4b, 0x58, 0x22, 0x13, 0x46, 0x85, 0x52, 0xb0, 0x0f, 0xa6, 0x5b, 0xf4, 0x45, 0x1c, 0xb6, 0xb3, 0xe5, 0x99, 0xec, 0x70, 0x8d, 0x5a, 0xb3, 0xa0, 0xd9, 0xed, 0xc4, 0xd8, 0x57, 0x07, 0x55, 0x25, 0xb1, 0xc2, 0x24, 0x4b, 0xfd, 0x06, 0x7c, 0xdb, 0x6d, 0xb5, 0xe9, 0xbb, 0x4d, 0x87, 0x3e, 0x68, 0x91, 0x37, 0x14, 0x9e, 0x06, 0x57, 0x1a, 0x70, 0x68, 0xdb, 0x11, 0xc9, 0xb3, 0xa1, 0x7f, 0x40, 0xb7, 0xc8, 0xfd, 0xf0, 0x24, 0x8c, 0x53, 0x32, 0x47, 0xcd, 0xf5, 0x07, 0xc1, 0x95, 0x8d, 0x38, 0xd4, 0x16, 0xbb, 0x56, 0xd8, 0x01, 0x10, 0xab, 0x69, 0x52, 0xb3, 0x97, 0x7b, 0xbf, 0xe3, 0x6a, 0x5d, 0x0d, 0xba, 0x03, 0x06, 0x83, 0x66, 0x9f, 0x95, 0x90, 0x77, 0x15, 0xcf, 0x9a, 0x55, 0x6c, 0x27, 0x3e, 0x9d, 0x42, 0xda, 0x9b, 0xd4, 0x83, 0x6f, 0x56, 0xd8, 0x8f, 0xff, 0x0d, 0xd0, 0xbb, 0x80, 0xf6, 0xff, 0x62, 0x27, 0x1c, 0x0f, 0xda, 0x96, 0x0b, 0xcd, 0xdf, 0x53, 0xa1, 0x12, 0x7c, 0x7d, 0x2f, 0x63, 0xaf, 0x74, 0x07, 0x6d, 0xdd, 0x23, 0xfc, 0x01, 0x7d, 0xea, 0xe8, 0x4e, 0x59, 0x12, 0x3a, 0xf2, 0xd0, 0x69, 0xcf, 0xfd, 0xac, 0xf7, 0x11, 0x1d, 0x1e, 0x08, 0xed, 0x34, 0x15, 0xaf, 0xdd, 0xeb, 0x27, 0xb0, 0xcf, 0xe4, 0xbb, 0xf5, 0x4d, 0x93, 0x5f, 0x0d, 0xfd, 0x03, 0x03, 0x53, 0x65, 0xd4, 0xf9, 0x42, 0x2e, 0xf7, 0x65, 0x1c, 0x75, 0x6b, 0x5c, 0x0b, 0x0d, 0x90, 0x54, 0x16, 0x13, 0x58, 0x46, 0xfe, 0x25, 0x1c, 0xb5, 0xdb, 0xdd, 0x01, 0x92, 0xa8, 0x7e, 0xbb, 0xd5, 0xd5, 0xbf, 0xdc, 0x3b, 0x25, 0xb0, 0x10, 0x2e, 0x77, 0x6b, 0xe3, 0xb3, 0x5f, 0xb3, 0xb5, 0xad, 0xd6, 0xd4, 0x38, 0x07, 0xae, 0x81, 0x77, 0x21, 0x92, 0x83, 0x9e, 0x5c, 0x3d, 0xd4, 0x35, 0xe0, 0xee, 0xff, 0xdd, 0xd0, 0xee, 0xb3, 0xe9, 0xfc, 0x96, 0x68, 0x84, 0x1c, 0x97, 0xda, 0x9e, 0xb0, 0x47, 0x81, 0xde, 0x0d, 0x24, 0xd1, 0x27, 0x16, 0x1b, 0xd2, 0xf3, 0x3a, 0x03, 0x1b, 0x09, 0x9a, 0x16, 0xb5, 0x00, 0xe6, 0x6c, 0xb8, 0xf0, 0x2d, 0x05, 0xc9, 0xbd, 0x3d, 0x3f, 0xda, 0x3c, 0x23, 0x60, 0x70, 0x03, 0xa1, 0x5f, 0x9e, 0x66, 0xad, 0x25, 0x9d, 0xde, 0xa9, 0xe6, 0xab, 0xfd, 0xbf, 0xf5, 0xff, 0x0b, 0x9a, 0x0a, 0x94, 0x47, 0x1c, 0x29, 0x6a, 0x39, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXGearIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x08, 0x06, 0x00, 0x00, 0x00, 0x55, 0xed, 0xb3, 0x47, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x90, 0x31, 0x46, 0xef, 0x00, 0x00, 0x0a, 0x22, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9a, 0x7b, 0xf0, 0x55, 0x55, 0x15, 0xc7, 0xf9, 0xf9, 0x00, 0xd1, 0x02, 0x05, 0x8c, 0x50, 0x12, 0x30, 0x34, 0x53, 0xb0, 0x86, 0x81, 0xc6, 0x47, 0x45, 0x8e, 0x8a, 0xf9, 0x47, 0x93, 0x4d, 0x5a, 0x99, 0x3d, 0x06, 0x7b, 0xd1, 0x64, 0x8d, 0x8e, 0x36, 0x9a, 0x5a, 0x43, 0x9a, 0xce, 0x68, 0x35, 0x66, 0x52, 0x3e, 0xd2, 0x8a, 0x64, 0x1c, 0x99, 0xd2, 0x9e, 0x0a, 0x3d, 0xc8, 0x34, 0x67, 0x22, 0x29, 0x41, 0x14, 0xc9, 0xc9, 0xfa, 0xa5, 0xf8, 0xa0, 0xa2, 0xd4, 0xd1, 0x44, 0x25, 0xa4, 0xcf, 0xf7, 0xfe, 0xee, 0xb9, 0xb3, 0x7f, 0xeb, 0xae, 0xbd, 0xcf, 0x3e, 0xf7, 0x9c, 0x7e, 0x3c, 0x86, 0x35, 0xf3, 0xbd, 0x7b, 0xef, 0xb5, 0xd7, 0xeb, 0xec, 0x73, 0xce, 0x7e, 0xac, 0x73, 0x87, 0x0d, 0xdb, 0x49, 0xdb, 0xce, 0x08, 0x6c, 0xd9, 0xb2, 0x65, 0x24, 0xf8, 0x3e, 0x78, 0x04, 0x5c, 0x58, 0x16, 0x19, 0x32, 0x1f, 0x03, 0x7f, 0x01, 0x4b, 0xc1, 0xb8, 0x32, 0xf9, 0xed, 0xbe, 0x9f, 0x8b, 0xfc, 0x28, 0x08, 0xe9, 0xed, 0xb1, 0x8b, 0x42, 0x68, 0x3a, 0xd8, 0x1c, 0x08, 0xcf, 0x8f, 0xc9, 0xee, 0x30, 0x7c, 0x2e, 0xf6, 0xba, 0xe0, 0x82, 0x55, 0xbd, 0x17, 0xf4, 0x79, 0x17, 0x08, 0xff, 0x27, 0x12, 0x08, 0x68, 0x89, 0x27, 0xb7, 0xdd, 0xf0, 0xb8, 0x90, 0xdd, 0xca, 0x82, 0x45, 0xe6, 0xd7, 0xc1, 0x05, 0x17, 0xd5, 0xf7, 0x5a, 0x3d, 0x3a, 0x8e, 0x2a, 0x3a, 0x83, 0xf2, 0xcf, 0x56, 0xce, 0xb6, 0x91, 0x1d, 0x6e, 0x79, 0xdb, 0x44, 0x9b, 0xc0, 0x2e, 0x03, 0x1b, 0xc1, 0x0a, 0xf0, 0xfa, 0x58, 0x50, 0xf4, 0xad, 0x03, 0x96, 0x9e, 0x84, 0xa1, 0x39, 0xe6, 0xb7, 0xe0, 0x6e, 0xf0, 0x0b, 0xf0, 0x57, 0x60, 0x69, 0x13, 0x0c, 0xf7, 0x26, 0xc0, 0xdf, 0x1f, 0x2c, 0x03, 0x92, 0x59, 0x08, 0x76, 0x89, 0xc5, 0x30, 0xe4, 0x7c, 0x82, 0x19, 0x0d, 0x42, 0xfa, 0x37, 0x8d, 0x37, 0x87, 0x81, 0xd0, 0xd6, 0x7c, 0x72, 0x39, 0x78, 0x19, 0xd4, 0xa1, 0x05, 0x28, 0xcf, 0x30, 0xb6, 0x0f, 0x85, 0xf7, 0xa8, 0x31, 0x3a, 0x3d, 0x94, 0xa9, 0x53, 0x77, 0xdf, 0xff, 0x2a, 0x06, 0x09, 0x6c, 0x34, 0xf2, 0x4f, 0x1b, 0x9d, 0x17, 0x68, 0xcf, 0x03, 0x63, 0xc0, 0x87, 0xc0, 0x1b, 0x41, 0x93, 0xb4, 0x16, 0x63, 0x8b, 0xc0, 0x9f, 0xc0, 0xf5, 0x60, 0x1f, 0x10, 0xd2, 0xb4, 0xbe, 0xbe, 0xbe, 0x35, 0x21, 0xa3, 0xd7, 0x7a, 0xed, 0x01, 0x92, 0x63, 0x06, 0x49, 0xf3, 0xc3, 0xd4, 0x5e, 0x83, 0x68, 0x58, 0xef, 0x79, 0xec, 0x8d, 0x62, 0x80, 0x36, 0x37, 0x61, 0xb7, 0xa9, 0x77, 0x75, 0x45, 0x13, 0xc1, 0x34, 0x64, 0x63, 0x65, 0x53, 0x83, 0xa3, 0x78, 0xdc, 0x49, 0xaf, 0x87, 0x40, 0xef, 0x43, 0xe7, 0xd4, 0x0a, 0x7a, 0x7a, 0x25, 0xfb, 0xc1, 0x63, 0x60, 0x5d, 0xbb, 0x54, 0x7d, 0x13, 0x78, 0x75, 0x80, 0x09, 0xd4, 0xdf, 0x04, 0xf6, 0x06, 0xb9, 0xa4, 0x58, 0xb6, 0x1d, 0xe2, 0xf5, 0x3a, 0x0e, 0x68, 0x62, 0x2e, 0xa3, 0xa7, 0x10, 0xb8, 0x01, 0x1c, 0x0b, 0x76, 0xcd, 0xbd, 0x02, 0x64, 0x77, 0x03, 0x6f, 0x05, 0x5a, 0x29, 0xef, 0x07, 0x65, 0xa4, 0xd5, 0xb4, 0xca, 0xcd, 0xca, 0x0d, 0xc5, 0x97, 0xc3, 0xd9, 0x04, 0x70, 0x23, 0xb8, 0x09, 0x9c, 0x02, 0x76, 0x2f, 0x24, 0xa9, 0x7f, 0x1a, 0xfc, 0x17, 0xa4, 0xe8, 0x1e, 0x3a, 0x4f, 0x02, 0x23, 0x0a, 0xbd, 0x3a, 0x25, 0x76, 0xde, 0x00, 0x7e, 0x04, 0xca, 0xe8, 0x12, 0x04, 0x5a, 0x73, 0x2c, 0xe5, 0x2e, 0xe0, 0x44, 0xa0, 0x2d, 0x80, 0x8e, 0x3a, 0x07, 0xd5, 0x89, 0x61, 0x90, 0x2e, 0xc6, 0x6e, 0x03, 0x21, 0xad, 0xa7, 0x71, 0x29, 0xb8, 0x3e, 0x64, 0x26, 0xea, 0x3a, 0x4b, 0x35, 0x32, 0x38, 0x61, 0x60, 0xd8, 0x9c, 0x09, 0x96, 0x24, 0xfc, 0xaa, 0x4b, 0x03, 0x79, 0x3e, 0xe8, 0x07, 0x21, 0xad, 0x0e, 0x6d, 0xd5, 0xaa, 0x63, 0x75, 0x79, 0x68, 0xb9, 0xc7, 0xfa, 0x05, 0xb5, 0x82, 0x48, 0x28, 0x13, 0xcf, 0x3b, 0x80, 0x5e, 0xdf, 0x2a, 0xf4, 0x78, 0xc2, 0x64, 0xb5, 0x2e, 0xbc, 0x9e, 0x56, 0xc1, 0xb3, 0x76, 0xc0, 0x27, 0x83, 0xf0, 0x90, 0x29, 0xf5, 0xe7, 0xc0, 0xc4, 0x6a, 0x9e, 0xf3, 0xa5, 0xb1, 0x7d, 0x20, 0x58, 0x09, 0x72, 0xe9, 0xbc, 0x7c, 0xeb, 0x19, 0x92, 0x78, 0xd5, 0xf6, 0xbf, 0x8c, 0xd6, 0x20, 0xb0, 0x9f, 0xcc, 0x51, 0x5e, 0xeb, 0x08, 0xdf, 0x94, 0xe1, 0xaa, 0x67, 0x11, 0xfc, 0x29, 0x95, 0xa2, 0x39, 0xa6, 0x8c, 0x56, 0x23, 0xd0, 0x99, 0x47, 0x7b, 0x76, 0x18, 0x2a, 0x62, 0xf0, 0xb5, 0xe0, 0xf9, 0x84, 0xe7, 0x3f, 0xd0, 0x37, 0xb6, 0xd0, 0xa1, 0x3e, 0x0e, 0x78, 0x2b, 0xdb, 0xa0, 0x23, 0x48, 0x21, 0xdf, 0x54, 0x89, 0xcf, 0x3e, 0xa0, 0x05, 0x25, 0x46, 0x3a, 0xea, 0x1c, 0xd9, 0x94, 0xbf, 0x8e, 0x1d, 0x8c, 0x6a, 0x15, 0x58, 0x1b, 0xf1, 0x7a, 0x17, 0xfc, 0x51, 0x1d, 0xe1, 0x76, 0x05, 0xde, 0x19, 0x8e, 0xbc, 0xd2, 0x1b, 0x4d, 0x6d, 0x4e, 0xad, 0xcb, 0x56, 0x1b, 0xfb, 0xbb, 0x83, 0x5f, 0x3a, 0xbe, 0xc5, 0xda, 0x00, 0xf6, 0x72, 0x15, 0xeb, 0x30, 0x31, 0xaa, 0xcc, 0x9e, 0x47, 0xff, 0x80, 0xe9, 0x66, 0xfa, 0xe0, 0xef, 0x0a, 0xee, 0x73, 0x94, 0x3e, 0x51, 0x27, 0x96, 0x1c, 0x5d, 0x7c, 0x8e, 0x02, 0xab, 0x1c, 0xdf, 0x62, 0x5d, 0x94, 0x63, 0x23, 0x5b, 0x06, 0x83, 0xaf, 0x04, 0x5a, 0xda, 0x3d, 0x7a, 0x4f, 0xca, 0x10, 0x0a, 0xb3, 0x1d, 0xa5, 0x7f, 0xc2, 0xb3, 0x87, 0xcb, 0x94, 0x99, 0x9e, 0xfa, 0xf0, 0x31, 0x09, 0xfc, 0x07, 0x58, 0xd2, 0x54, 0xd1, 0xdc, 0x82, 0x81, 0x31, 0xed, 0x79, 0x3c, 0xba, 0x25, 0x27, 0x72, 0x14, 0x6f, 0x76, 0x94, 0xbf, 0x9e, 0xa3, 0x5b, 0x57, 0x06, 0xbf, 0xda, 0x03, 0x79, 0x74, 0x63, 0x65, 0xdb, 0x58, 0xd9, 0x13, 0xfc, 0x18, 0x3c, 0x0d, 0xf4, 0xea, 0xf4, 0x83, 0x07, 0xc0, 0x8b, 0xc0, 0xd2, 0xbf, 0x60, 0x8c, 0xcf, 0x71, 0x82, 0xdc, 0x44, 0xa0, 0x65, 0x3e, 0x24, 0x25, 0xb6, 0xa6, 0xe5, 0xe8, 0xd7, 0x91, 0xc1, 0xc7, 0x70, 0xf0, 0x50, 0xe8, 0xb8, 0x5d, 0xd7, 0x64, 0xad, 0x63, 0x8b, 0xb6, 0x25, 0x7f, 0x07, 0xcf, 0x80, 0x3b, 0xc0, 0xab, 0xa2, 0xfe, 0xe8, 0x3c, 0x13, 0xe4, 0xd2, 0xdc, 0xa8, 0x21, 0xa7, 0x03, 0xa3, 0xde, 0x9d, 0x5c, 0xe6, 0x88, 0x36, 0xce, 0xc2, 0xf7, 0x9c, 0xdc, 0x8b, 0x42, 0xee, 0xab, 0xd1, 0x00, 0xe8, 0x8c, 0x4d, 0xc4, 0xd6, 0xbe, 0x9e, 0x9e, 0x4a, 0xb9, 0x5f, 0xe4, 0x47, 0x80, 0x87, 0xad, 0x21, 0xda, 0xef, 0x8e, 0x06, 0xd4, 0x60, 0x07, 0x7e, 0x72, 0x4f, 0x03, 0xf3, 0xa3, 0x6e, 0x31, 0xa2, 0x3d, 0x44, 0xce, 0x46, 0xeb, 0x9b, 0x51, 0x23, 0x89, 0x0e, 0x6c, 0xeb, 0x48, 0x60, 0xa9, 0x1f, 0xc6, 0x1e, 0x09, 0xb5, 0x46, 0xba, 0xf0, 0x31, 0xcf, 0x3a, 0x76, 0xda, 0x3a, 0xd7, 0x95, 0x9f, 0x19, 0x11, 0x3a, 0x1b, 0xa4, 0x4e, 0xe8, 0x3d, 0x6f, 0xb4, 0xb0, 0x7b, 0x3b, 0xb0, 0xf4, 0x05, 0x8d, 0x02, 0xcc, 0xbd, 0xc0, 0xff, 0x65, 0x8f, 0x84, 0xdd, 0xbd, 0xc1, 0x0b, 0x20, 0x46, 0x5a, 0x88, 0xf2, 0x7d, 0x23, 0x7c, 0x3c, 0xb0, 0xe7, 0x29, 0x19, 0x2f, 0xfd, 0xf4, 0x92, 0xba, 0xe5, 0xe8, 0x1f, 0x0c, 0xec, 0xa4, 0xaf, 0xc0, 0xb5, 0xf4, 0x8b, 0x54, 0xd7, 0xe4, 0xf9, 0x6d, 0xf0, 0xba, 0x94, 0xad, 0xaa, 0x7d, 0xd8, 0x5b, 0x0c, 0x3c, 0x3a, 0xad, 0xaa, 0x2d, 0xdd, 0xcd, 0x7d, 0x3d, 0x4b, 0xf0, 0x5a, 0x77, 0xbb, 0xb2, 0xc1, 0x40, 0x01, 0x1b, 0x97, 0x45, 0x6c, 0x5b, 0xb6, 0x9e, 0xe2, 0x6b, 0x41, 0x23, 0xe7, 0x26, 0xec, 0x9c, 0x62, 0x1d, 0xb4, 0xdb, 0xb3, 0x82, 0xf0, 0xf2, 0xaa, 0x28, 0x1e, 0x13, 0x31, 0xd6, 0xf3, 0xeb, 0x55, 0x78, 0xc6, 0xee, 0x58, 0x90, 0x7a, 0x85, 0xad, 0xeb, 0x1f, 0xc0, 0xa8, 0x9d, 0x1a, 0xc6, 0x86, 0x9e, 0x5e, 0x8f, 0x4e, 0x2f, 0x62, 0xf3, 0xca, 0xd8, 0x7b, 0x77, 0x98, 0x27, 0x0c, 0xef, 0x91, 0x08, 0xbf, 0x0a, 0xfb, 0x2c, 0x84, 0xb3, 0x53, 0xae, 0xc8, 0x6a, 0xa5, 0xbb, 0xa2, 0x8a, 0x83, 0x88, 0xec, 0xc3, 0xf0, 0x37, 0x3a, 0x7d, 0x87, 0x3b, 0xbc, 0x34, 0x8b, 0x61, 0xbe, 0xc6, 0x19, 0xea, 0x97, 0xe0, 0xc5, 0x06, 0x34, 0x6d, 0xb0, 0xdd, 0x8b, 0xfe, 0x0c, 0xa0, 0x4d, 0xa2, 0xa5, 0x75, 0x30, 0x16, 0x01, 0x9d, 0xc4, 0xff, 0x66, 0x3b, 0x69, 0x4b, 0xe7, 0xe0, 0x2c, 0x27, 0x09, 0x21, 0x6c, 0x28, 0xeb, 0x60, 0x29, 0x6f, 0x3f, 0x86, 0x96, 0x96, 0xf9, 0x23, 0x80, 0xe6, 0x08, 0x9d, 0x7a, 0x2d, 0xe9, 0x2b, 0x44, 0x2d, 0xc2, 0xe0, 0x32, 0x6b, 0x94, 0xf6, 0xd5, 0xa0, 0xb3, 0xd4, 0x53, 0x1f, 0x0e, 0xae, 0x00, 0x96, 0x94, 0x4b, 0x1e, 0x53, 0x13, 0xde, 0xb1, 0x47, 0x67, 0xb3, 0x05, 0xe0, 0x58, 0xd0, 0xfd, 0x2a, 0xc3, 0xd4, 0xa9, 0xfb, 0x73, 0xe0, 0x09, 0x90, 0xa2, 0x3b, 0xeb, 0x8c, 0x0e, 0x86, 0xe5, 0xc7, 0x1e, 0x39, 0x7e, 0x07, 0xaf, 0xeb, 0xa9, 0x84, 0xa7, 0x9b, 0x75, 0x27, 0x18, 0x6a, 0xd2, 0x26, 0xf8, 0x72, 0x30, 0xb2, 0xb8, 0x56, 0x05, 0xa7, 0x4f, 0x24, 0x97, 0x82, 0x09, 0x05, 0x33, 0x52, 0xea, 0xfb, 0x55, 0x1d, 0x3a, 0x04, 0x65, 0x9b, 0x8b, 0xb9, 0x99, 0x8f, 0x7c, 0x2f, 0x5b, 0xa3, 0xf0, 0xb6, 0xc0, 0x5b, 0x6c, 0xf9, 0x43, 0xd0, 0xd6, 0xa7, 0xf2, 0xcf, 0x82, 0x33, 0x0b, 0x5f, 0x1a, 0x20, 0x31, 0x73, 0xc8, 0x9b, 0xe0, 0x72, 0xf4, 0x0a, 0x19, 0xef, 0x20, 0xb8, 0xa1, 0xe8, 0x74, 0xca, 0x54, 0x9f, 0x23, 0xde, 0x28, 0xab, 0x33, 0x26, 0x1a, 0xa0, 0x45, 0xe0, 0xf6, 0x0c, 0xf3, 0xaf, 0xc9, 0x90, 0x49, 0x89, 0xac, 0x72, 0x3a, 0x8f, 0x76, 0x78, 0x05, 0xab, 0xf6, 0x96, 0xa2, 0x30, 0x54, 0xb1, 0x5c, 0x8e, 0xfc, 0x55, 0x85, 0x4e, 0xeb, 0xc3, 0x9a, 0x1a, 0xbc, 0x77, 0x53, 0x29, 0xde, 0x05, 0x4e, 0x02, 0x0a, 0xae, 0xd3, 0x47, 0x5d, 0xb4, 0x96, 0x47, 0xff, 0xd0, 0x81, 0x6a, 0x6f, 0xbf, 0xf8, 0xd0, 0x4e, 0x5c, 0x7e, 0x0a, 0xda, 0x44, 0xe5, 0x68, 0xec, 0x0e, 0xfa, 0xb6, 0x8f, 0x9c, 0x96, 0xde, 0x7b, 0x40, 0x78, 0x36, 0xd2, 0xbf, 0x35, 0xbe, 0x0c, 0xea, 0xd0, 0x5c, 0x94, 0x67, 0x3b, 0x06, 0x56, 0xc3, 0xfb, 0x21, 0xb8, 0x95, 0x58, 0x54, 0x4f, 0x13, 0x01, 0x6a, 0xb9, 0xb5, 0xf4, 0x6c, 0x5a, 0xab, 0xbc, 0x17, 0x83, 0x3a, 0xe7, 0x59, 0x52, 0xd6, 0xef, 0x3c, 0x70, 0x78, 0x1b, 0xe7, 0x50, 0x3e, 0x0b, 0x2c, 0xe9, 0xc6, 0xd5, 0x22, 0x0c, 0xde, 0x61, 0x8d, 0xd2, 0xd6, 0x13, 0x53, 0x8d, 0x50, 0x3a, 0xd7, 0x31, 0x24, 0x56, 0xad, 0x54, 0x29, 0xfa, 0x4a, 0xfe, 0x6b, 0xe5, 0xaa, 0x4a, 0x7f, 0xac, 0x76, 0x05, 0xbe, 0x34, 0x4e, 0xbd, 0xed, 0xcb, 0x75, 0xbe, 0xf4, 0x00, 0xb7, 0x6b, 0x89, 0x6d, 0x0b, 0x3f, 0x10, 0x51, 0x3a, 0x20, 0xc2, 0xcf, 0x62, 0xf3, 0xf8, 0x6a, 0xc5, 0x3a, 0x1d, 0x68, 0x95, 0xca, 0x25, 0x4d, 0xd6, 0x1f, 0xc8, 0x15, 0x8e, 0xc9, 0x31, 0x38, 0x5a, 0xa5, 0xc7, 0x3a, 0xfd, 0xc9, 0x57, 0xaa, 0xea, 0x00, 0x1d, 0xe1, 0x38, 0xa8, 0xca, 0xd2, 0xe1, 0xd0, 0xce, 0x6f, 0x31, 0x1b, 0xcf, 0xd0, 0x31, 0x87, 0x81, 0xd5, 0x3f, 0xca, 0xea, 0x52, 0xec, 0x6f, 0x79, 0x3d, 0x0d, 0x90, 0xee, 0x70, 0xd7, 0xfe, 0x04, 0x5e, 0xad, 0x3b, 0xc9, 0x5d, 0xd4, 0x2b, 0x9a, 0x33, 0xd1, 0x6a, 0xf2, 0x56, 0x62, 0xee, 0x10, 0x06, 0x67, 0x25, 0x65, 0x13, 0x74, 0x42, 0xc4, 0x88, 0x7c, 0xe5, 0x13, 0x17, 0x31, 0x1b, 0x28, 0x91, 0xed, 0x91, 0x92, 0xdd, 0x53, 0xf2, 0xad, 0x0d, 0x96, 0x44, 0x57, 0xc7, 0x0a, 0x4b, 0x3a, 0x7e, 0xcc, 0x05, 0xf3, 0x81, 0xf2, 0xe2, 0x27, 0x00, 0xfd, 0x89, 0xaa, 0x31, 0xc2, 0x9e, 0xfe, 0x63, 0xb4, 0x1e, 0x78, 0xa4, 0x05, 0x41, 0x07, 0xe2, 0x72, 0x42, 0xf0, 0x0c, 0xe0, 0x1d, 0x26, 0x43, 0xc3, 0x17, 0x96, 0x5b, 0xea, 0x96, 0xc0, 0xc0, 0x2c, 0x60, 0x93, 0x70, 0x4a, 0x90, 0x35, 0xf7, 0x5f, 0x9d, 0x6e, 0xb7, 0x2d, 0x0e, 0x3e, 0xbc, 0x74, 0x2f, 0xec, 0x41, 0x74, 0x49, 0x44, 0x7d, 0x80, 0x8d, 0xa8, 0xee, 0x5c, 0x0e, 0x3d, 0x94, 0x34, 0xe4, 0x74, 0x62, 0x54, 0xab, 0x97, 0x77, 0x92, 0xfe, 0xa2, 0x23, 0xde, 0x38, 0x0b, 0xdf, 0xb7, 0xe4, 0x5c, 0x18, 0x32, 0x1f, 0x89, 0x3a, 0xa7, 0x53, 0x7b, 0x91, 0x5c, 0xf2, 0x36, 0x5b, 0x29, 0xdb, 0x7a, 0x32, 0x2d, 0xe9, 0x2b, 0x47, 0xe7, 0x14, 0x1f, 0x55, 0xae, 0xd9, 0x81, 0x8f, 0x03, 0x81, 0x4d, 0xf3, 0xda, 0x58, 0x8a, 0xf6, 0xd5, 0x51, 0x77, 0x48, 0x4c, 0x01, 0xde, 0x3f, 0x32, 0x0a, 0xe5, 0xb0, 0x5c, 0x43, 0x23, 0xeb, 0xd3, 0x0f, 0x72, 0xe3, 0x81, 0x3e, 0x46, 0x5a, 0x3a, 0x31, 0x1a, 0x4c, 0x83, 0x1d, 0x38, 0xfd, 0x99, 0x75, 0x1c, 0x69, 0x6f, 0x84, 0x9f, 0x4e, 0xbf, 0x22, 0x30, 0x02, 0xcc, 0x04, 0xd3, 0xc1, 0x24, 0xa0, 0xfc, 0x4b, 0x2c, 0xd9, 0x7d, 0x71, 0xce, 0x75, 0xa0, 0xaf, 0x44, 0x98, 0xa5, 0xac, 0xcf, 0xd6, 0x39, 0xf6, 0x53, 0x32, 0x38, 0x7d, 0xa7, 0x75, 0xdc, 0x6e, 0xdf, 0x4d, 0xb9, 0x0f, 0x38, 0x00, 0x1c, 0x06, 0x34, 0x3f, 0xbe, 0x22, 0x65, 0x2b, 0xda, 0x87, 0xe2, 0x64, 0xa0, 0xc9, 0xd4, 0x92, 0x32, 0x8c, 0xc9, 0x74, 0x25, 0xfd, 0xc7, 0x58, 0x25, 0xda, 0xca, 0x09, 0x4d, 0x8c, 0x3a, 0x6c, 0xa8, 0x03, 0x1f, 0x23, 0x41, 0x3f, 0xb0, 0xa4, 0x95, 0x78, 0x66, 0x43, 0x6e, 0x06, 0xcc, 0x60, 0x50, 0x59, 0x46, 0x8f, 0x34, 0xf1, 0xba, 0xf9, 0x65, 0xf8, 0xca, 0x0c, 0xae, 0x75, 0x94, 0xce, 0x69, 0x34, 0xb8, 0x88, 0x31, 0xfc, 0x7e, 0xc7, 0xf1, 0x2d, 0xd6, 0xc2, 0x88, 0x4a, 0xef, 0x6c, 0x8c, 0x8e, 0x06, 0xfa, 0x43, 0x83, 0x47, 0x0b, 0x60, 0x76, 0xed, 0x8c, 0xe1, 0x29, 0x4b, 0x69, 0xe9, 0x7e, 0x18, 0xdd, 0x69, 0xcd, 0xde, 0x43, 0x73, 0x35, 0xf1, 0x71, 0xb1, 0x75, 0xdc, 0x6e, 0xeb, 0xe9, 0xdd, 0xdf, 0x55, 0xaa, 0xc3, 0xc4, 0xe8, 0x58, 0x50, 0x7c, 0xdc, 0x6b, 0xfb, 0x1a, 0x54, 0x7c, 0x8b, 0x56, 0xe7, 0xd8, 0x42, 0x7d, 0x12, 0xd0, 0x29, 0xdd, 0xd2, 0x5b, 0xea, 0xc4, 0x91, 0xa3, 0x8b, 0xc3, 0x8f, 0x5b, 0xa7, 0x41, 0x5b, 0x31, 0x4d, 0xce, 0xb1, 0x53, 0x49, 0x06, 0xa3, 0x1a, 0x80, 0x32, 0xd2, 0x64, 0xdc, 0x7a, 0x3a, 0x28, 0xf5, 0x37, 0x1a, 0x4b, 0xdf, 0xad, 0xe4, 0xb4, 0x07, 0x61, 0x1c, 0x7e, 0x10, 0x94, 0x7d, 0x73, 0x5b, 0xd2, 0x83, 0xe9, 0xb8, 0x0a, 0x0e, 0x8f, 0x04, 0x9a, 0xd8, 0x72, 0xe8, 0x56, 0x84, 0x4e, 0x76, 0x04, 0xb5, 0x7d, 0xd8, 0x37, 0xee, 0xa5, 0x5e, 0x0f, 0xb6, 0xf5, 0xbf, 0xc4, 0xab, 0x1c, 0xbf, 0x31, 0xd6, 0xfb, 0xea, 0x79, 0x0c, 0xb4, 0xf1, 0x70, 0x5b, 0xcc, 0x4b, 0x84, 0xef, 0x0d, 0xe6, 0xbc, 0xc0, 0x64, 0xa3, 0x55, 0x62, 0x98, 0x08, 0xaa, 0xe6, 0x98, 0x94, 0x9d, 0x6c, 0x86, 0x70, 0xbe, 0xc2, 0x19, 0x08, 0xed, 0x23, 0xee, 0x75, 0xf8, 0x1e, 0xeb, 0xf7, 0x30, 0x3b, 0xf3, 0x53, 0x33, 0x51, 0xb5, 0x52, 0xc4, 0x7b, 0x60, 0xf7, 0x2c, 0xb0, 0xc1, 0x73, 0xda, 0xe6, 0x69, 0xb7, 0xbe, 0x14, 0xd8, 0x9b, 0xb6, 0xbe, 0xa9, 0x38, 0x94, 0xab, 0x9e, 0x0d, 0x9e, 0x04, 0x9a, 0xa4, 0xaf, 0x01, 0xad, 0xbd, 0x0f, 0xa5, 0xf6, 0x19, 0x8b, 0x41, 0x19, 0x5d, 0x89, 0x80, 0x97, 0xa8, 0xea, 0x29, 0x46, 0x6c, 0x69, 0xfb, 0xf0, 0x29, 0xf0, 0x38, 0x48, 0x91, 0xb2, 0x04, 0x63, 0xe4, 0x84, 0x72, 0x2a, 0xf8, 0x0a, 0x90, 0xce, 0x53, 0x20, 0xef, 0xf4, 0xde, 0x53, 0x84, 0x46, 0x09, 0x67, 0x9f, 0x07, 0xf6, 0x0e, 0xc1, 0x1a, 0x44, 0xda, 0x54, 0xfe, 0x14, 0x9c, 0x0a, 0xf6, 0x34, 0x26, 0x4a, 0x9b, 0xe8, 0x68, 0x50, 0x8e, 0x03, 0xfa, 0xe2, 0xba, 0x0e, 0x94, 0xd1, 0x37, 0x10, 0x68, 0x64, 0x3b, 0xd1, 0xb5, 0x7f, 0x29, 0x8d, 0xd6, 0x11, 0x20, 0x98, 0x2f, 0xc1, 0xbe, 0xc0, 0xe9, 0xf2, 0x58, 0xcf, 0xc1, 0xfc, 0x15, 0xe8, 0x07, 0xfa, 0x18, 0xf9, 0x68, 0xbb, 0x54, 0x5d, 0xc9, 0xab, 0xf1, 0x40, 0xf9, 0x20, 0x41, 0x69, 0xd2, 0xa3, 0xc0, 0xf1, 0x20, 0xf7, 0x28, 0xf0, 0x3d, 0x92, 0x6c, 0x1f, 0x46, 0xbe, 0x11, 0x6a, 0x64, 0x94, 0x89, 0xa4, 0xca, 0x0a, 0xa5, 0x0b, 0xad, 0xfd, 0x85, 0x22, 0x71, 0xf5, 0x55, 0x62, 0x49, 0x98, 0x19, 0xe8, 0x6a, 0x6a, 0xf2, 0x9c, 0x55, 0xea, 0x69, 0xe8, 0x04, 0x1a, 0x8d, 0xa5, 0xf6, 0x00, 0xf1, 0x7a, 0x29, 0xa7, 0x33, 0xcd, 0xb9, 0xfe, 0x1b, 0xe0, 0xe9, 0x63, 0xdc, 0x4b, 0x4e, 0x5f, 0x5d, 0xd6, 0x66, 0x0c, 0x2c, 0x05, 0x5f, 0x03, 0xca, 0x9f, 0x87, 0x34, 0x8e, 0x98, 0x26, 0x87, 0x8c, 0xad, 0x5a, 0x27, 0x18, 0x05, 0x14, 0x92, 0x26, 0xec, 0xb3, 0x8b, 0xa0, 0xa8, 0x2b, 0x65, 0xf2, 0x49, 0x50, 0x75, 0xaf, 0x82, 0x4a, 0x17, 0x69, 0xcb, 0xf1, 0x19, 0xd0, 0xf9, 0xce, 0x4f, 0xfd, 0xfd, 0x40, 0x8b, 0x40, 0x48, 0x33, 0x0a, 0xff, 0x5b, 0xbd, 0x24, 0x2a, 0xa5, 0x53, 0xef, 0x6a, 0x47, 0xa7, 0xa4, 0x53, 0xf4, 0xcb, 0x07, 0x7d, 0xde, 0x9e, 0x45, 0x39, 0xf0, 0x30, 0x9d, 0xf2, 0x22, 0x6d, 0x7b, 0xc1, 0xb0, 0x5a, 0x2b, 0x65, 0xe7, 0x6f, 0x29, 0xe1, 0x85, 0xd3, 0x37, 0x07, 0x14, 0x49, 0xb9, 0x07, 0xa9, 0xdb, 0x7f, 0x91, 0x84, 0xe2, 0x43, 0x5f, 0x27, 0x20, 0xed, 0x89, 0xde, 0x06, 0xb4, 0xea, 0x44, 0x89, 0xfe, 0xe5, 0xc0, 0xd2, 0xb9, 0x52, 0x80, 0xa9, 0xa5, 0xbc, 0x95, 0x82, 0xa5, 0xd4, 0x76, 0xc0, 0xd2, 0x63, 0x51, 0xc3, 0x03, 0xfa, 0x4a, 0x80, 0x69, 0x2b, 0x30, 0x2a, 0x25, 0xb7, 0x4d, 0xf7, 0x11, 0xbc, 0xfd, 0xe6, 0xff, 0x04, 0xbc, 0xae, 0xa7, 0x02, 0x5e, 0x1f, 0x58, 0x05, 0x42, 0xfa, 0xcd, 0xd6, 0xb8, 0xb8, 0xda, 0x93, 0x74, 0xc5, 0xa0, 0x1f, 0x34, 0xf2, 0x17, 0xb1, 0x67, 0xd9, 0x68, 0x78, 0xc3, 0xe0, 0x69, 0xe2, 0x3d, 0xdf, 0xf0, 0xad, 0xae, 0xe9, 0xde, 0x01, 0x9a, 0x3c, 0x0e, 0xfb, 0x01, 0x3d, 0x35, 0xa2, 0x9f, 0x83, 0xe4, 0x3e, 0x8c, 0xfe, 0x85, 0x12, 0x84, 0xf4, 0xd7, 0x38, 0x6f, 0xa5, 0xdc, 0x01, 0x46, 0xc5, 0x5c, 0x02, 0x17, 0xaa, 0xf9, 0xea, 0x20, 0xe0, 0xa6, 0x6a, 0x43, 0x71, 0x64, 0xf4, 0xaa, 0x4d, 0x01, 0xa3, 0x43, 0xfe, 0xce, 0xfa, 0xce, 0x11, 0xd8, 0x7e, 0x46, 0xe0, 0x7f, 0xd1, 0xc6, 0xfb, 0xa0, 0x6e, 0xf6, 0xa7, 0x73, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXCircleDownArrowIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2e, 0x08, 0x06, 0x00, 0x00, 0x00, 0x57, 0xb9, 0x2b, 0x37, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x52, 0x92, 0x24, 0x04, 0x00, 0x00, 0x05, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xe5, 0x99, 0x5d, 0x88, 0x55, 0x55, 0x14, 0xc7, 0xbd, 0xe6, 0x4c, 0x90, 0xa9, 0x59, 0xca, 0x64, 0x56, 0x68, 0x18, 0x0a, 0x21, 0x44, 0x90, 0x41, 0x06, 0xe5, 0x54, 0x50, 0xbd, 0x54, 0xcf, 0x9a, 0x45, 0x11, 0x69, 0x61, 0xd6, 0x93, 0x0f, 0x41, 0xa4, 0x49, 0x5a, 0xa2, 0x11, 0x46, 0x14, 0xf4, 0x52, 0x50, 0x4f, 0x81, 0x31, 0x84, 0x4a, 0xa6, 0x06, 0x95, 0x58, 0xf4, 0x01, 0x8a, 0x7d, 0x3a, 0xf6, 0x92, 0x5f, 0xf9, 0x11, 0x15, 0x36, 0x39, 0x3a, 0x4e, 0xbf, 0xff, 0xf1, 0xae, 0x3b, 0xeb, 0xec, 0x7b, 0xce, 0xb9, 0xfb, 0x5c, 0xef, 0xcc, 0x1d, 0x68, 0xc1, 0x9f, 0xbd, 0xbe, 0xd7, 0x3a, 0xe7, 0x9e, 0x7d, 0xce, 0xde, 0xfb, 0x56, 0xc6, 0xb4, 0x88, 0x06, 0x07, 0x07, 0xc7, 0x92, 0x6a, 0x2a, 0xb8, 0x0a, 0x4c, 0x07, 0x57, 0x82, 0x53, 0xe0, 0x20, 0x38, 0x24, 0x54, 0x2a, 0x95, 0x7f, 0x18, 0xdb, 0x4f, 0x34, 0x3b, 0x15, 0x3c, 0x06, 0x3e, 0x02, 0x7d, 0xa0, 0x11, 0xfd, 0x8c, 0xc3, 0x2b, 0xe0, 0x56, 0xa0, 0x0b, 0x1d, 0x59, 0xa2, 0xe8, 0x9d, 0x60, 0x27, 0x18, 0x00, 0xcd, 0xd2, 0x61, 0x02, 0xd7, 0x80, 0x49, 0xc3, 0xde, 0x3d, 0x45, 0xe6, 0x82, 0x2d, 0xa0, 0x95, 0x74, 0x9c, 0x64, 0xcf, 0x80, 0xce, 0x32, 0x17, 0x50, 0x89, 0x71, 0x26, 0x69, 0x07, 0x7e, 0x1b, 0xc0, 0x93, 0x20, 0xeb, 0x27, 0x3e, 0x87, 0x7e, 0x17, 0xf8, 0x0a, 0x24, 0xcf, 0x33, 0xe3, 0x51, 0x30, 0x1e, 0xe8, 0x99, 0x9f, 0x06, 0xae, 0x03, 0xf7, 0x00, 0xcd, 0x83, 0x2c, 0x3a, 0x80, 0x72, 0x21, 0xf3, 0x60, 0x77, 0x96, 0xb1, 0xb4, 0x8e, 0xa6, 0xa7, 0x80, 0x4f, 0x41, 0x16, 0x7d, 0x82, 0xf2, 0x51, 0x90, 0xd7, 0x4c, 0xaa, 0x1e, 0x7e, 0x63, 0xc1, 0x7c, 0xb0, 0x0e, 0x9c, 0x04, 0x21, 0xfd, 0x8b, 0xe2, 0xa1, 0x54, 0x50, 0x33, 0x02, 0x49, 0x6e, 0x00, 0x07, 0xc2, 0xec, 0xc8, 0x5f, 0x83, 0xee, 0x66, 0x72, 0x5a, 0x0c, 0xf1, 0x93, 0x81, 0x26, 0x6a, 0xd6, 0xa4, 0x7e, 0x19, 0x7d, 0xd6, 0x2f, 0x6b, 0xe1, 0xf9, 0x23, 0x81, 0xb3, 0xc0, 0x09, 0xe0, 0x49, 0x45, 0x74, 0x87, 0xa3, 0x1e, 0xb1, 0xfc, 0xec, 0x43, 0x16, 0x72, 0x5d, 0x03, 0xb2, 0x7e, 0xd1, 0xd7, 0x87, 0xbc, 0x22, 0x39, 0x12, 0x4d, 0x02, 0x3f, 0x00, 0x4f, 0x87, 0x10, 0xe6, 0x45, 0xa6, 0x28, 0xe5, 0x46, 0xde, 0x0e, 0xf0, 0xa6, 0x2f, 0x56, 0xe5, 0x97, 0x44, 0x27, 0x22, 0xe0, 0x22, 0xb0, 0x39, 0x48, 0xb2, 0x0f, 0x59, 0x1f, 0x95, 0x61, 0x25, 0x6a, 0xe8, 0xed, 0xe2, 0xe9, 0x0c, 0xc2, 0x82, 0xa8, 0xa2, 0x38, 0x3e, 0xe7, 0x23, 0xe1, 0xf5, 0xba, 0xd2, 0x1b, 0x61, 0x44, 0x88, 0x5a, 0xeb, 0x33, 0xea, 0x5f, 0x5e, 0x58, 0x9c, 0x80, 0x2e, 0xf0, 0xb7, 0x0b, 0x8c, 0xbf, 0x62, 0x97, 0x99, 0x78, 0x4d, 0xbc, 0x65, 0x20, 0xee, 0x6e, 0xa5, 0x63, 0xf5, 0x8b, 0x6f, 0x01, 0x9e, 0xd6, 0x3b, 0x97, 0x7a, 0x16, 0xcf, 0x37, 0xbc, 0x37, 0xfc, 0xf3, 0xf5, 0x5e, 0x8d, 0x35, 0xc4, 0xbd, 0xe3, 0xf2, 0xcc, 0x6e, 0x1c, 0x91, 0xf6, 0x20, 0xf6, 0x32, 0xf0, 0xbb, 0xcb, 0x71, 0x1a, 0x7e, 0x46, 0xda, 0xab, 0x2a, 0x61, 0x98, 0x0d, 0x74, 0x87, 0x8d, 0x34, 0x19, 0x2f, 0xc9, 0x74, 0x6e, 0xa0, 0x24, 0xee, 0x27, 0x4b, 0xc2, 0xb8, 0xb8, 0x81, 0x7b, 0xa6, 0x99, 0x38, 0xfd, 0x62, 0x9e, 0xde, 0xcf, 0x73, 0x7c, 0xcd, 0x7b, 0xc1, 0xc7, 0xcf, 0xe8, 0x20, 0x23, 0xb1, 0x5a, 0x4c, 0x19, 0x3d, 0x1c, 0x98, 0xa3, 0x44, 0x82, 0x3b, 0x41, 0xaf, 0x25, 0x61, 0xd4, 0xba, 0xa8, 0xcb, 0x82, 0xfd, 0x4b, 0xfe, 0x01, 0x53, 0x32, 0xea, 0xb3, 0xfd, 0xb6, 0x93, 0x47, 0x9c, 0xe5, 0xd3, 0xdf, 0x4f, 0xd1, 0x35, 0xae, 0xb0, 0x7a, 0xbd, 0xdf, 0xe4, 0xa4, 0x71, 0xae, 0xe4, 0x26, 0x14, 0xd7, 0x9a, 0x92, 0xb1, 0x87, 0xc0, 0xb3, 0x4e, 0x6e, 0x17, 0xfb, 0x21, 0x85, 0x07, 0x5c, 0xf1, 0x07, 0x8d, 0xb7, 0x3b, 0x5e, 0x53, 0x54, 0x0d, 0x3d, 0xe6, 0xd0, 0xce, 0x91, 0x9b, 0x77, 0x9c, 0xfa, 0x5f, 0xb8, 0x1e, 0xba, 0xb9, 0xc9, 0x13, 0x25, 0x5b, 0xe3, 0xf3, 0x9d, 0x51, 0xbb, 0x96, 0x1d, 0x4e, 0x6e, 0x37, 0xeb, 0x6f, 0x62, 0x27, 0xcd, 0xdc, 0xac, 0x86, 0xac, 0x71, 0x2d, 0x3d, 0x8d, 0x7a, 0xb9, 0xd2, 0xd3, 0x26, 0x8c, 0x82, 0x71, 0x5f, 0xd0, 0x43, 0xd2, 0xab, 0x35, 0xee, 0x3f, 0xe7, 0x9a, 0x98, 0xa3, 0x89, 0xc2, 0x7e, 0x92, 0x5e, 0xb5, 0x3e, 0x9e, 0x40, 0x97, 0x97, 0xba, 0x4e, 0x0f, 0x3b, 0x7e, 0x34, 0xb0, 0x61, 0xe3, 0xb5, 0x3b, 0x3e, 0x25, 0xe8, 0xee, 0x44, 0x20, 0xb7, 0x5b, 0x3c, 0x49, 0x03, 0x83, 0xae, 0x89, 0x2b, 0xc4, 0xeb, 0x51, 0x39, 0xe6, 0x94, 0x62, 0x13, 0x43, 0xa0, 0xab, 0x13, 0xf9, 0xa5, 0xb4, 0x18, 0xd2, 0xd2, 0xf7, 0xf1, 0x3a, 0x63, 0x81, 0x02, 0xff, 0x69, 0xe0, 0x03, 0xf0, 0x1d, 0xb8, 0xa5, 0xc0, 0xd5, 0x4c, 0x5a, 0x60, 0xf9, 0xf5, 0xbf, 0xde, 0x34, 0xe7, 0x89, 0x04, 0x7f, 0x01, 0xa3, 0xad, 0xa6, 0xcf, 0x1b, 0x71, 0x54, 0x71, 0x4f, 0x2b, 0xbc, 0x2f, 0x06, 0xbf, 0x96, 0x5f, 0x64, 0x36, 0xf4, 0x5a, 0xc4, 0x79, 0xdb, 0xbb, 0x66, 0xcb, 0x1b, 0xf1, 0x9f, 0x0b, 0x3c, 0x25, 0xb5, 0x6c, 0x72, 0xfa, 0xe7, 0x48, 0x1b, 0xdb, 0x42, 0xe2, 0xad, 0xa3, 0x79, 0xb0, 0xd7, 0x39, 0xad, 0x25, 0xb3, 0x6f, 0xbe, 0xcf, 0xd9, 0x12, 0x1e, 0xbb, 0xf6, 0xa5, 0xdb, 0xc1, 0x1c, 0x67, 0xdb, 0xe6, 0xf8, 0x3c, 0x36, 0xec, 0x67, 0xa8, 0x57, 0x92, 0x6e, 0x77, 0x97, 0x74, 0x0a, 0xfe, 0xe2, 0xbc, 0x2c, 0xa6, 0xc7, 0x47, 0x5b, 0x2e, 0xbf, 0x96, 0x50, 0x0a, 0x9d, 0x02, 0x8c, 0x61, 0xfc, 0x5c, 0x42, 0x95, 0xee, 0x65, 0x1c, 0x0f, 0xbe, 0x35, 0x45, 0x75, 0x5c, 0x6d, 0xb9, 0x8a, 0x46, 0x7c, 0x9f, 0x0d, 0xe2, 0xba, 0x6b, 0xfe, 0x18, 0x56, 0x06, 0xc6, 0xfb, 0x6a, 0xc6, 0x02, 0x86, 0x18, 0x35, 0xbf, 0xdf, 0xc5, 0x6a, 0x75, 0x79, 0x17, 0xf8, 0xd8, 0xe9, 0x16, 0xc0, 0x6f, 0x72, 0xb2, 0xd8, 0xa8, 0xa6, 0x55, 0x1a, 0xdf, 0x1d, 0x2e, 0x56, 0xcb, 0x5b, 0xbd, 0x05, 0xcf, 0x13, 0xc2, 0x8d, 0xce, 0x28, 0xf6, 0x2d, 0xb3, 0x35, 0x1a, 0xf1, 0xbd, 0x1a, 0xf8, 0xe6, 0xff, 0x40, 0xde, 0x03, 0x8c, 0xfc, 0x45, 0x48, 0xf7, 0x62, 0xa3, 0x9c, 0x66, 0xc7, 0x57, 0x1b, 0x12, 0xbf, 0xd4, 0xde, 0x6c, 0xb6, 0xda, 0x88, 0xc3, 0xaf, 0xc0, 0x48, 0xc7, 0x63, 0x3a, 0x04, 0x8a, 0x22, 0x7c, 0xd5, 0xfc, 0x2f, 0x16, 0x5c, 0x30, 0xae, 0x8a, 0x4a, 0x58, 0x75, 0x22, 0xcf, 0x23, 0x41, 0xae, 0xfa, 0x37, 0x18, 0x0e, 0xaf, 0x06, 0x4e, 0x4f, 0x95, 0x2c, 0xd2, 0xa8, 0xf9, 0x95, 0x25, 0xf3, 0x8d, 0xa3, 0x1f, 0xbf, 0xae, 0x1f, 0x40, 0xee, 0xaa, 0xcb, 0x81, 0x52, 0xe7, 0x28, 0xfd, 0xc0, 0xe8, 0x08, 0x8c, 0xff, 0xa2, 0xd6, 0xc5, 0x84, 0x0a, 0xfc, 0xa7, 0x83, 0xac, 0x3b, 0x5f, 0xaa, 0x69, 0xe5, 0x25, 0xcf, 0x52, 0xe0, 0xe9, 0xbd, 0xb0, 0x5e, 0x4d, 0xc6, 0x6b, 0xa3, 0xf7, 0x84, 0x2f, 0xf5, 0xd3, 0x56, 0x0b, 0xaa, 0x79, 0x7f, 0xa7, 0x5e, 0xa8, 0x15, 0x88, 0x64, 0x88, 0x9f, 0x08, 0x74, 0xe3, 0x8c, 0x74, 0x34, 0x37, 0x23, 0x37, 0x1c, 0xa3, 0xce, 0xbb, 0xff, 0x34, 0x6f, 0xc6, 0xb3, 0xe0, 0xee, 0xdc, 0x80, 0x1c, 0x03, 0x31, 0x7a, 0xfd, 0x2d, 0x07, 0x7e, 0xb9, 0x9c, 0xe3, 0x9d, 0x56, 0x13, 0xa3, 0xf5, 0x93, 0xce, 0xdb, 0x3d, 0x15, 0xef, 0xf2, 0x95, 0x02, 0xef, 0x15, 0x3e, 0x02, 0x5e, 0x87, 0x93, 0xd7, 0xa7, 0xd3, 0x0f, 0x9f, 0x44, 0x2d, 0x9d, 0x1b, 0x7a, 0x3a, 0x86, 0xa0, 0xcf, 0x7e, 0x31, 0xe1, 0xa4, 0x2b, 0xee, 0xf1, 0x91, 0xf0, 0x3f, 0x02, 0xbf, 0xb5, 0x2b, 0x4e, 0xd2, 0xa4, 0x95, 0x1a, 0x4f, 0x07, 0x75, 0x35, 0xe7, 0xee, 0x88, 0x4e, 0x87, 0xf3, 0x04, 0xb0, 0x37, 0x48, 0x72, 0x14, 0xb9, 0xf4, 0x4f, 0x1f, 0x53, 0x94, 0xbc, 0x7a, 0x83, 0x84, 0x67, 0x3a, 0x2a, 0xff, 0x44, 0x4c, 0x7c, 0xca, 0x87, 0xa0, 0x99, 0x40, 0x3f, 0x93, 0x27, 0x7d, 0xb9, 0x34, 0xdb, 0x6d, 0x8d, 0x93, 0x8a, 0x69, 0x46, 0x20, 0x97, 0xbe, 0xbe, 0x3b, 0x41, 0x48, 0x1b, 0x9b, 0xc9, 0x97, 0xc4, 0x90, 0x49, 0x87, 0x44, 0xfe, 0x0d, 0x61, 0xc9, 0xf5, 0x65, 0x8c, 0x5a, 0x16, 0xe4, 0x15, 0x27, 0x5e, 0x5f, 0xc5, 0x75, 0xa0, 0x0f, 0x84, 0xa4, 0xff, 0x86, 0x2e, 0xec, 0xe6, 0x90, 0x40, 0x05, 0xb6, 0x85, 0x99, 0xab, 0xf2, 0x67, 0x8c, 0x4b, 0x40, 0xb2, 0x9d, 0xca, 0x6b, 0xd2, 0xf4, 0xf8, 0xe9, 0x5c, 0xf0, 0x76, 0xb0, 0x01, 0x68, 0x69, 0x10, 0x92, 0x2e, 0x62, 0xa1, 0xf9, 0x17, 0x8d, 0x7e, 0x81, 0x9e, 0xeb, 0x47, 0xb2, 0x71, 0x18, 0xd7, 0x82, 0xe5, 0x40, 0x7c, 0x48, 0xda, 0xa1, 0x7c, 0x03, 0x76, 0x03, 0x2d, 0x3b, 0xb5, 0xec, 0x3d, 0x02, 0xf4, 0x1f, 0x90, 0x96, 0xa5, 0xda, 0x6e, 0xcd, 0x04, 0x7a, 0xb5, 0xe6, 0x6d, 0x54, 0x7a, 0xb1, 0xe9, 0x3f, 0xa0, 0x2f, 0x19, 0x5b, 0x4b, 0x5c, 0xc0, 0x1c, 0xb0, 0x09, 0xb4, 0x92, 0x34, 0x8f, 0x74, 0x4e, 0xd8, 0xd1, 0xda, 0x6e, 0x33, 0xb2, 0x51, 0xe4, 0x36, 0xb0, 0x15, 0xf8, 0x25, 0x02, 0x62, 0x29, 0xfa, 0x0d, 0xef, 0xd5, 0x20, 0x39, 0xe0, 0xc9, 0x28, 0x33, 0x7c, 0x2a, 0x8a, 0xea, 0x38, 0x78, 0x11, 0xd0, 0x1e, 0xd2, 0x6f, 0xff, 0x10, 0xeb, 0xe8, 0x1c, 0x9a, 0xef, 0xc1, 0x4b, 0x60, 0x1e, 0x88, 0x7a, 0x4c, 0xf3, 0xba, 0xbf, 0xa0, 0xe0, 0x30, 0x29, 0xcd, 0x4c, 0x46, 0xa7, 0xe7, 0x59, 0xd0, 0xb3, 0xad, 0x53, 0x31, 0x3d, 0xf3, 0xc9, 0x73, 0xcf, 0xf3, 0xdb, 0x0f, 0xff, 0xff, 0xa6, 0xff, 0x00, 0x60, 0x58, 0x79, 0x12, 0xc5, 0x84, 0x2f, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXCircleDownArrowIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x45, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x8d, 0x2b, 0x29, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x1f, 0x21, 0x4c, 0x00, 0x00, 0x08, 0x52, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9b, 0x79, 0xa8, 0x55, 0x55, 0x14, 0xc6, 0x7d, 0x6a, 0x5a, 0x62, 0xe5, 0xac, 0x95, 0x60, 0x65, 0x96, 0x59, 0x96, 0x69, 0xda, 0x68, 0x96, 0x46, 0x36, 0xa1, 0x26, 0x92, 0xd1, 0x3f, 0x15, 0x41, 0xa2, 0x66, 0x11, 0x12, 0x25, 0x28, 0x99, 0x95, 0x15, 0x44, 0x54, 0x52, 0x50, 0x34, 0x10, 0x1a, 0x0d, 0xfa, 0x87, 0x65, 0xa5, 0x26, 0xa4, 0xbe, 0x22, 0xad, 0x88, 0xca, 0x92, 0xb4, 0x14, 0x9b, 0x54, 0x34, 0x4d, 0x71, 0x2e, 0xa7, 0x7e, 0xdf, 0xeb, 0xee, 0xcb, 0x79, 0xe7, 0xae, 0x7d, 0xcf, 0xf8, 0xee, 0xf3, 0x8a, 0x0b, 0x3e, 0xf7, 0x39, 0x6b, 0xaf, 0xf5, 0xad, 0xb5, 0xd7, 0x3b, 0xc3, 0x3e, 0x7b, 0x5f, 0x9b, 0x34, 0x69, 0x64, 0x39, 0x7c, 0xf8, 0x70, 0x4b, 0xd0, 0x15, 0xb4, 0x6e, 0xe4, 0x54, 0x8a, 0xe1, 0x6b, 0x8a, 0x47, 0x0d, 0x78, 0xa0, 0x81, 0x43, 0x3f, 0x04, 0x5c, 0x0a, 0x4e, 0x05, 0xa7, 0x15, 0x5a, 0x1d, 0xb7, 0x07, 0x2e, 0x8f, 0x1d, 0x1c, 0x6f, 0x28, 0x60, 0x7d, 0xa1, 0xfd, 0x91, 0xf6, 0xa3, 0x9a, 0x9a, 0x9a, 0x6d, 0xb4, 0xd5, 0x2d, 0x14, 0xe2, 0x24, 0x70, 0x1b, 0x78, 0x17, 0xec, 0x00, 0x59, 0x64, 0x3f, 0xce, 0x8b, 0xc0, 0x78, 0xd0, 0xb5, 0xea, 0x2a, 0x43, 0xd2, 0xc3, 0xc0, 0x7c, 0xf0, 0x0f, 0x68, 0x08, 0x39, 0x04, 0xe9, 0x57, 0xe0, 0x5e, 0xd0, 0xe2, 0x88, 0x2e, 0x10, 0x09, 0x0e, 0x00, 0x4b, 0x41, 0x25, 0x65, 0x2d, 0xc1, 0x46, 0x03, 0x77, 0xfb, 0x1d, 0x19, 0x35, 0x22, 0xa1, 0xee, 0x40, 0xb7, 0x48, 0x63, 0x8a, 0xae, 0x9c, 0x41, 0x79, 0x55, 0x24, 0x75, 0x85, 0x49, 0x42, 0x97, 0xee, 0x74, 0x30, 0x01, 0xc4, 0xbd, 0x8c, 0x0f, 0x61, 0xbb, 0x06, 0x6c, 0x04, 0x1b, 0x02, 0xed, 0x5f, 0x1c, 0x9f, 0x0c, 0xf4, 0xe0, 0x15, 0x4e, 0x29, 0xb4, 0x67, 0xd2, 0x9e, 0x00, 0xe2, 0xca, 0x87, 0x18, 0xde, 0xc3, 0x43, 0x59, 0xfc, 0x95, 0x15, 0x0a, 0xd2, 0x11, 0xd4, 0x82, 0x38, 0xb2, 0x1b, 0xa3, 0xb9, 0xe0, 0x2e, 0xd0, 0x21, 0x49, 0xa6, 0xd8, 0xb7, 0x02, 0xc3, 0xc1, 0xeb, 0x60, 0x33, 0x88, 0x23, 0x7f, 0x62, 0xd4, 0x2f, 0x49, 0x9c, 0xcc, 0xb6, 0x04, 0xec, 0x0d, 0xd6, 0x81, 0x28, 0xf9, 0x18, 0x03, 0x3d, 0x74, 0x93, 0xfc, 0xa5, 0xbd, 0xf9, 0xc1, 0xd3, 0x14, 0x5c, 0x09, 0x5e, 0x01, 0x7a, 0x1b, 0x95, 0x93, 0x3d, 0x74, 0x8e, 0xf6, 0x92, 0xe5, 0xd9, 0x41, 0x20, 0x0d, 0x72, 0x67, 0xb9, 0x6c, 0xe8, 0xfb, 0x1a, 0x5c, 0x93, 0x67, 0xdc, 0x30, 0x17, 0xfc, 0x67, 0x83, 0x39, 0x20, 0x4a, 0xa6, 0x61, 0x90, 0xfa, 0x11, 0x11, 0x8e, 0x5b, 0x72, 0x0e, 0xf9, 0xfd, 0xe0, 0x60, 0x99, 0x2c, 0xd6, 0xd0, 0x57, 0xd1, 0x37, 0x01, 0xf1, 0x2e, 0x01, 0x51, 0x6f, 0xbc, 0xd9, 0xd8, 0x34, 0x2f, 0x19, 0x50, 0x56, 0x05, 0xa4, 0x23, 0x81, 0xe6, 0x07, 0x3e, 0x99, 0x45, 0xc7, 0xf1, 0x59, 0xe3, 0xa4, 0xf5, 0x27, 0xf6, 0x44, 0x50, 0xee, 0x0f, 0x36, 0x23, 0x2d, 0xb7, 0xe9, 0x47, 0xb0, 0x0b, 0xc1, 0x2e, 0x60, 0x89, 0x0a, 0x35, 0xc9, 0x74, 0xac, 0xb0, 0x92, 0x3c, 0x6e, 0x00, 0xdb, 0xad, 0x24, 0x0b, 0xba, 0x31, 0xb9, 0xa4, 0x04, 0x59, 0x27, 0xf0, 0x9b, 0x27, 0x90, 0x9e, 0x2d, 0xc3, 0x73, 0x09, 0x94, 0x13, 0x09, 0xf9, 0xf4, 0x04, 0x3f, 0x7b, 0xf2, 0xfd, 0x17, 0xfd, 0xd5, 0x99, 0x42, 0x41, 0xd0, 0x02, 0x7c, 0xee, 0x09, 0xa0, 0x6f, 0x99, 0x3e, 0x99, 0x02, 0x34, 0x90, 0x33, 0x79, 0xb5, 0x05, 0xdf, 0x79, 0xf2, 0xde, 0x82, 0x5e, 0x73, 0x9f, 0x74, 0x82, 0xf3, 0x0c, 0x0f, 0xb1, 0xee, 0xdd, 0x9b, 0xd3, 0xb1, 0x56, 0xc6, 0x8b, 0xfc, 0xba, 0x81, 0x4d, 0xc0, 0x92, 0x1f, 0x50, 0x26, 0x7f, 0xf0, 0xe2, 0xd4, 0x0b, 0x1c, 0xb0, 0x18, 0xd1, 0x3d, 0x5c, 0x99, 0xa1, 0x65, 0x8b, 0x42, 0x9e, 0x9a, 0xd3, 0xf8, 0x3e, 0x4a, 0xc7, 0x27, 0x66, 0x87, 0xec, 0x03, 0x60, 0xc9, 0x5b, 0x89, 0xc9, 0xca, 0x38, 0x10, 0xa0, 0x39, 0xb8, 0x13, 0x2c, 0x04, 0x53, 0x40, 0xa7, 0x32, 0xe6, 0x89, 0xbb, 0xe0, 0xbb, 0x1b, 0x58, 0xa2, 0xd9, 0xf1, 0x89, 0xb1, 0x09, 0x31, 0xbe, 0xca, 0x62, 0x41, 0xb7, 0x0a, 0xe4, 0xfa, 0xda, 0x85, 0x4f, 0xeb, 0x2d, 0x41, 0x79, 0x33, 0x76, 0xa2, 0x31, 0x0d, 0x21, 0x7f, 0x2d, 0x18, 0x20, 0x70, 0xfc, 0x78, 0x2c, 0x0a, 0x1c, 0x6a, 0xc0, 0x97, 0x01, 0xc7, 0xe0, 0xe1, 0xc8, 0x58, 0x24, 0x09, 0x8c, 0x20, 0xff, 0x28, 0x18, 0x80, 0x63, 0x4d, 0xcf, 0xb5, 0x4a, 0x97, 0x9b, 0xc0, 0xd7, 0x05, 0x58, 0x53, 0x0a, 0x7d, 0x93, 0xe9, 0xe3, 0xb3, 0xbc, 0x60, 0x74, 0x2b, 0xb0, 0x64, 0x59, 0x79, 0xcf, 0x74, 0xbd, 0x04, 0x5a, 0x66, 0x04, 0x3b, 0x29, 0x1d, 0x9b, 0xdf, 0x8b, 0x18, 0x9a, 0xee, 0x5b, 0xf2, 0xaa, 0xdf, 0xab, 0xd0, 0x83, 0xd7, 0x17, 0x96, 0x27, 0xba, 0xab, 0x22, 0x9d, 0x53, 0x18, 0xc0, 0x5b, 0xa9, 0xa2, 0x9c, 0x48, 0x2c, 0xeb, 0x2b, 0x5b, 0x1f, 0x96, 0x6d, 0xc3, 0xa9, 0x37, 0x75, 0x0a, 0x3a, 0xbb, 0x70, 0xac, 0x85, 0xe5, 0xb0, 0x68, 0xd1, 0xb8, 0x36, 0xac, 0xac, 0xa6, 0x73, 0xf2, 0xdf, 0x49, 0xbe, 0xd3, 0x8c, 0x9c, 0xf5, 0x6a, 0x2e, 0x99, 0x5e, 0x14, 0x8b, 0x42, 0xa7, 0x66, 0xa7, 0xd6, 0x17, 0xe5, 0xf3, 0x06, 0x59, 0x35, 0xaa, 0x74, 0xab, 0xec, 0x36, 0x12, 0x1f, 0x11, 0xd6, 0x05, 0x8b, 0x52, 0xd2, 0x89, 0xb1, 0xb6, 0x1c, 0x96, 0x84, 0x9d, 0xaa, 0xf1, 0x9c, 0xab, 0x65, 0x1f, 0x79, 0x2f, 0x34, 0x72, 0xbf, 0x9e, 0xbb, 0xa4, 0xde, 0x9a, 0x4f, 0x5d, 0x51, 0x50, 0xea, 0xe1, 0x36, 0xd8, 0x70, 0x98, 0x0f, 0xd9, 0x7e, 0x43, 0x5f, 0xad, 0xaa, 0xf7, 0x8d, 0xc4, 0x5b, 0xa1, 0xbb, 0x2e, 0xa8, 0x77, 0x57, 0xca, 0x8d, 0x28, 0xad, 0x75, 0x56, 0x8b, 0x24, 0xe8, 0x5f, 0x6d, 0xc7, 0x5a, 0xc3, 0x3d, 0x68, 0x24, 0x5d, 0xef, 0x2e, 0x71, 0x45, 0xb1, 0xde, 0x2e, 0x07, 0x70, 0x9e, 0x6f, 0x10, 0x54, 0xad, 0x8a, 0xab, 0xfe, 0x6f, 0x92, 0xff, 0xcc, 0x18, 0x40, 0xbd, 0xf1, 0xbb, 0xa2, 0x68, 0x1b, 0x33, 0x2c, 0x2b, 0x20, 0xd9, 0x1e, 0x56, 0x1e, 0x05, 0xe7, 0xb5, 0xc6, 0x18, 0xea, 0x8d, 0xbf, 0x5c, 0x51, 0xb4, 0x05, 0x71, 0x34, 0x8a, 0xf6, 0xa8, 0xc3, 0xa2, 0x4d, 0x7e, 0xed, 0x69, 0xd7, 0x89, 0x2b, 0x8a, 0x35, 0xdd, 0x3d, 0x5a, 0x8b, 0xe2, 0x1b, 0x57, 0xb1, 0x06, 0xda, 0x36, 0x68, 0x46, 0x79, 0xac, 0xaf, 0xd3, 0xca, 0x6f, 0x28, 0x15, 0xfe, 0x52, 0x0d, 0xdc, 0x44, 0x17, 0x85, 0x04, 0x3a, 0x03, 0x15, 0x26, 0x2c, 0x3e, 0xe7, 0xb0, 0x5d, 0xb5, 0x9d, 0x5b, 0xb7, 0x8f, 0xc6, 0x50, 0x7c, 0xae, 0xe8, 0xf6, 0xd1, 0x16, 0xa5, 0x25, 0x9b, 0x2c, 0xe5, 0x51, 0xa0, 0xd3, 0x16, 0xad, 0xb6, 0x6f, 0xc3, 0xa2, 0xcf, 0x9c, 0x3a, 0x51, 0x51, 0x76, 0x15, 0x8e, 0xc3, 0x4d, 0xea, 0x5f, 0x16, 0x71, 0x4b, 0x9e, 0x0c, 0x6e, 0x07, 0xbd, 0xc3, 0xa4, 0x59, 0xce, 0xe1, 0xd3, 0xa2, 0xd4, 0xcd, 0xa0, 0x7b, 0x06, 0x1e, 0xcd, 0x5e, 0xdd, 0xb3, 0x34, 0x48, 0x53, 0xac, 0x83, 0x3a, 0x7d, 0x97, 0x93, 0xef, 0x0a, 0x0a, 0x12, 0x95, 0x1c, 0x93, 0xf0, 0x65, 0x28, 0x75, 0xeb, 0x69, 0x95, 0x4e, 0x0b, 0xc8, 0x0f, 0x94, 0x18, 0xa5, 0x50, 0xc0, 0xa3, 0x8f, 0xd5, 0x5f, 0xc1, 0x3c, 0xf0, 0x0b, 0xe7, 0x93, 0x69, 0xd3, 0x88, 0x6f, 0x5c, 0xc5, 0xc7, 0x45, 0x53, 0xe6, 0x22, 0xaa, 0x90, 0xbe, 0x22, 0xc3, 0x52, 0x7c, 0x1a, 0x87, 0x3b, 0x22, 0xce, 0xc7, 0xd1, 0xaf, 0xa9, 0xb3, 0x44, 0x45, 0x7f, 0x96, 0x01, 0x4c, 0xac, 0x3b, 0x2b, 0xfd, 0xc7, 0xba, 0x8c, 0x4b, 0x74, 0xf8, 0xf7, 0xc7, 0x75, 0x01, 0x70, 0xf7, 0xbd, 0x3e, 0x5c, 0xb5, 0x63, 0xd9, 0xa2, 0x94, 0x32, 0x52, 0x13, 0x5d, 0x94, 0x02, 0x45, 0xb1, 0x4a, 0x01, 0x4a, 0x9f, 0x73, 0xc0, 0xc4, 0x3c, 0xdc, 0x6a, 0x68, 0x9f, 0xf1, 0x14, 0x66, 0xaf, 0x61, 0x5b, 0x4f, 0x87, 0x5f, 0x3f, 0x6c, 0x3e, 0x01, 0xfa, 0xa9, 0x46, 0x50, 0xf4, 0x87, 0xdc, 0x1f, 0x54, 0xc4, 0x3c, 0xf6, 0x8d, 0xab, 0x78, 0xc7, 0xb8, 0x7b, 0xab, 0xa8, 0x08, 0x10, 0xfb, 0x9c, 0x03, 0x26, 0xe6, 0xe1, 0xcb, 0x68, 0xf7, 0x18, 0x3d, 0x56, 0x61, 0xc2, 0x76, 0xfb, 0xb9, 0x72, 0x8b, 0xdf, 0x26, 0x14, 0xa4, 0x0f, 0x3c, 0x2a, 0x48, 0x1b, 0x83, 0xef, 0x39, 0x6c, 0x0f, 0x1b, 0xfa, 0x28, 0x95, 0x35, 0x2e, 0xf1, 0xd4, 0x9f, 0x82, 0x10, 0x7c, 0x26, 0x08, 0xcb, 0x46, 0x14, 0xd6, 0xfa, 0x4a, 0x54, 0xd0, 0x26, 0xf8, 0x0d, 0x06, 0x5a, 0x03, 0xb5, 0x64, 0xac, 0x23, 0xa0, 0xf3, 0x9d, 0x90, 0x41, 0xf1, 0xb3, 0x02, 0x7d, 0x0f, 0xa0, 0xcd, 0x2b, 0x4b, 0x9e, 0x74, 0x1c, 0x49, 0x5b, 0xc8, 0x5e, 0x32, 0x08, 0xf5, 0x46, 0xaa, 0x2f, 0x18, 0x4d, 0x36, 0x0c, 0xa5, 0xd2, 0xbd, 0x9c, 0x4a, 0xf0, 0xbd, 0x06, 0x58, 0x85, 0xd1, 0x12, 0xe0, 0xb5, 0x22, 0xa5, 0x7d, 0x03, 0x04, 0xa5, 0xee, 0x36, 0x46, 0xd1, 0x06, 0x68, 0xf7, 0xc0, 0x92, 0xe9, 0xa9, 0x12, 0x2a, 0x38, 0x41, 0xf8, 0xbb, 0x41, 0x5a, 0xfa, 0x91, 0x88, 0xd1, 0x05, 0x86, 0xa1, 0x54, 0x8f, 0x65, 0x4c, 0xc0, 0x57, 0x98, 0x6d, 0x70, 0xeb, 0x37, 0x26, 0x2f, 0x2a, 0x48, 0x40, 0xd6, 0x72, 0xac, 0xd7, 0xee, 0xa2, 0x80, 0x2e, 0x78, 0xf8, 0x44, 0xc6, 0x7c, 0xfa, 0x06, 0xc9, 0x02, 0xc7, 0x0f, 0x9a, 0xbc, 0x18, 0x28, 0xa1, 0xb0, 0xac, 0x30, 0x8d, 0x13, 0x28, 0x21, 0xbc, 0x1a, 0x58, 0x57, 0xcc, 0x6a, 0xf4, 0xaf, 0x86, 0x02, 0x6a, 0x4b, 0x33, 0x5c, 0x28, 0x67, 0x92, 0xa9, 0x20, 0x4a, 0x19, 0xa2, 0xa9, 0x8e, 0x2c, 0xd4, 0xf6, 0x30, 0x87, 0x84, 0x91, 0x1e, 0x84, 0x96, 0x9c, 0x61, 0x3a, 0x24, 0x50, 0x42, 0x3a, 0x08, 0x58, 0x7b, 0x2f, 0x07, 0x42, 0x01, 0xf5, 0xcb, 0x00, 0x4b, 0xe2, 0x6d, 0x5c, 0x45, 0xe4, 0x04, 0xf1, 0xb7, 0x06, 0xf9, 0x4a, 0xaf, 0x1b, 0xc6, 0x57, 0x1a, 0x0e, 0x52, 0x4d, 0xf2, 0x3a, 0x25, 0xe8, 0x80, 0x67, 0x10, 0xb0, 0x0a, 0x83, 0xba, 0xac, 0x64, 0xba, 0x85, 0x5d, 0x8a, 0x44, 0xd0, 0xcf, 0x34, 0x2c, 0xf1, 0x17, 0x1c, 0x6b, 0x7d, 0x31, 0x5b, 0x3b, 0xf5, 0x5b, 0xd1, 0x5b, 0xaf, 0x44, 0x17, 0x2f, 0x76, 0x0b, 0x8f, 0xb6, 0x64, 0x93, 0x14, 0x26, 0x97, 0x82, 0x28, 0x41, 0xe2, 0xce, 0x01, 0x96, 0x5c, 0x5c, 0x76, 0x00, 0x78, 0xbc, 0x60, 0x79, 0xa1, 0x7b, 0xaa, 0xac, 0x63, 0x82, 0x4e, 0xb8, 0xe2, 0x16, 0x66, 0x5a, 0x02, 0xda, 0xb2, 0xa6, 0xc4, 0xd4, 0x6f, 0xe3, 0x2c, 0x59, 0x5d, 0xd6, 0x51, 0x9d, 0x78, 0x69, 0xdf, 0x55, 0xbf, 0x50, 0x0a, 0x8b, 0xf6, 0x78, 0xdd, 0x14, 0x3b, 0x92, 0x27, 0xca, 0x00, 0xae, 0x81, 0xc0, 0x8a, 0xe3, 0xe2, 0x3e, 0x1a, 0xc5, 0x91, 0xa4, 0x1f, 0xd2, 0x25, 0x8e, 0x38, 0xd4, 0x8e, 0x8a, 0xc5, 0x83, 0xd3, 0x23, 0x21, 0x47, 0x77, 0x1a, 0xbd, 0xef, 0x1a, 0x2b, 0xc2, 0xff, 0x46, 0x90, 0xfa, 0x0a, 0x33, 0x35, 0x01, 0x4d, 0xa4, 0x29, 0x71, 0x6e, 0x72, 0x03, 0x08, 0xb5, 0xcb, 0x23, 0x9d, 0x9d, 0x01, 0x8e, 0xad, 0x81, 0x66, 0xb3, 0x61, 0x39, 0x88, 0x62, 0xa8, 0xb3, 0xcb, 0xa3, 0x85, 0xef, 0x0a, 0xe0, 0x9e, 0x63, 0xe2, 0x7f, 0x28, 0x0f, 0x5e, 0xc7, 0x01, 0x5f, 0x07, 0xb0, 0x0e, 0x58, 0x32, 0xd0, 0xd9, 0xc5, 0x6a, 0x61, 0x18, 0x63, 0xb1, 0xa0, 0xd3, 0xa4, 0xeb, 0x9c, 0x58, 0x24, 0x31, 0x8d, 0xe0, 0x53, 0xe2, 0xa3, 0xc0, 0xb9, 0x31, 0x5d, 0x62, 0x99, 0xc1, 0x77, 0x1c, 0x58, 0x0a, 0x2c, 0x49, 0xbe, 0x9f, 0x05, 0x8b, 0x66, 0x95, 0x3f, 0x59, 0x6c, 0xe8, 0x34, 0xe9, 0xca, 0xe5, 0x6d, 0x14, 0x6b, 0x74, 0x29, 0x8d, 0xc8, 0xf1, 0x15, 0x4f, 0xfe, 0x9a, 0x1b, 0xf5, 0x4c, 0x45, 0x8b, 0xe3, 0x00, 0xb0, 0xd7, 0x43, 0xbc, 0x00, 0x7d, 0xb3, 0x54, 0xc4, 0x15, 0x70, 0x22, 0xb7, 0x09, 0x9e, 0xbc, 0xa5, 0xce, 0x76, 0x8b, 0x42, 0xa0, 0x25, 0x45, 0x9f, 0xbc, 0x47, 0x87, 0x96, 0xf6, 0x8e, 0x28, 0x21, 0xa7, 0xb1, 0x20, 0x3c, 0x53, 0x76, 0x63, 0x98, 0x99, 0x4b, 0xb2, 0xb0, 0x4d, 0x77, 0x8c, 0x46, 0xfb, 0x0d, 0xba, 0xae, 0xb9, 0x04, 0xca, 0x48, 0x42, 0x1e, 0xba, 0xe5, 0xad, 0x65, 0x01, 0x97, 0xf6, 0x72, 0x0e, 0x5a, 0x66, 0x0c, 0xf3, 0xbf, 0x3b, 0x44, 0xfa, 0x1d, 0xdc, 0x5c, 0xc7, 0x6c, 0xb4, 0x7a, 0x53, 0x69, 0xfd, 0xb4, 0xd1, 0x84, 0xf8, 0xed, 0xc1, 0x62, 0xe0, 0x93, 0x3f, 0xe8, 0x28, 0xae, 0xd6, 0xe7, 0x92, 0x28, 0x84, 0x7a, 0x4d, 0x7f, 0xef, 0x8b, 0x88, 0x7e, 0x1f, 0xd0, 0x9a, 0xe9, 0x71, 0xb9, 0x04, 0x4c, 0x40, 0x42, 0xcc, 0x21, 0x60, 0x2d, 0xf0, 0x89, 0xbe, 0xd0, 0xfb, 0x26, 0xa0, 0x8c, 0x6f, 0x0a, 0x71, 0x3b, 0xf0, 0xa9, 0x2f, 0x72, 0x41, 0xbf, 0x86, 0x76, 0x34, 0x48, 0xb5, 0x62, 0x17, 0x3f, 0x9b, 0xba, 0xd9, 0x77, 0x1f, 0xe2, 0xe8, 0x81, 0x5f, 0x4e, 0xd6, 0xd3, 0x99, 0x7a, 0xa1, 0x2c, 0x56, 0x3e, 0x04, 0x88, 0xba, 0x6f, 0x5d, 0x82, 0xfa, 0x8f, 0x50, 0x83, 0x63, 0x91, 0x26, 0x34, 0x82, 0xf7, 0x74, 0x30, 0x0b, 0x1c, 0x02, 0xe5, 0x44, 0x39, 0xa4, 0xdd, 0x91, 0x48, 0x98, 0x15, 0xe6, 0x04, 0x1b, 0x07, 0xb4, 0xac, 0x18, 0x25, 0xab, 0x30, 0x78, 0x1a, 0x5c, 0x0e, 0xdc, 0x22, 0x79, 0xe2, 0x80, 0xf8, 0x9e, 0x05, 0x26, 0x02, 0x4d, 0xc6, 0x7c, 0x6f, 0x16, 0xba, 0x8a, 0xf2, 0x36, 0x47, 0xa9, 0xdf, 0x8c, 0xa9, 0x2f, 0x73, 0x82, 0xea, 0x4a, 0x98, 0x0d, 0xda, 0xc5, 0x1c, 0xe5, 0x66, 0xec, 0xe6, 0x81, 0x65, 0x40, 0x6b, 0xb1, 0x1b, 0x0b, 0xed, 0x16, 0x56, 0xe5, 0xeb, 0xf6, 0x7a, 0xe0, 0x74, 0xff, 0xe3, 0x54, 0x2b, 0xee, 0xfa, 0x2b, 0x9f, 0x07, 0x86, 0x81, 0x5e, 0x20, 0x8e, 0x68, 0x55, 0x7e, 0x0a, 0x7c, 0x99, 0x57, 0xe9, 0xe2, 0x04, 0x33, 0x6d, 0x18, 0x44, 0x47, 0xa0, 0xe5, 0x06, 0xdf, 0x8a, 0x19, 0x5d, 0x91, 0xa2, 0x2b, 0x6e, 0x03, 0xb0, 0x96, 0x2c, 0x23, 0x9d, 0x03, 0x06, 0x8b, 0x38, 0xbe, 0xc8, 0x4c, 0xb4, 0x31, 0x94, 0x24, 0xa3, 0xcb, 0x5b, 0x93, 0xb9, 0xc6, 0x10, 0xbd, 0x15, 0x87, 0x36, 0xc6, 0xb8, 0x63, 0xc5, 0x24, 0x39, 0x2d, 0xe6, 0xd4, 0x56, 0xa8, 0x32, 0xda, 0xaa, 0xb8, 0x03, 0xa4, 0x7e, 0x56, 0xf9, 0x06, 0x95, 0xfa, 0x99, 0xe2, 0x23, 0x94, 0x9e, 0x44, 0xcf, 0xa7, 0xb9, 0x05, 0x8c, 0x00, 0x79, 0xce, 0x11, 0xd6, 0xc3, 0x37, 0xb7, 0x80, 0xa5, 0x3c, 0x3b, 0xd2, 0x6c, 0x9b, 0xe2, 0xde, 0xc8, 0x42, 0x81, 0xba, 0x81, 0xfb, 0xc0, 0x62, 0xe0, 0xfb, 0xc0, 0xa4, 0xcb, 0x14, 0xad, 0xb1, 0xac, 0x04, 0xfa, 0xd4, 0xe8, 0x0f, 0x1a, 0xe4, 0x8f, 0x18, 0x2e, 0x51, 0x45, 0x82, 0x04, 0x83, 0x32, 0xb0, 0xf6, 0x9c, 0xeb, 0xcd, 0xe2, 0xa0, 0x65, 0xce, 0xce, 0x60, 0x3b, 0xd0, 0x5b, 0xc9, 0x41, 0x57, 0xc5, 0x26, 0xae, 0x86, 0xe2, 0xde, 0x32, 0xe7, 0xc7, 0xe4, 0x58, 0x05, 0x8e, 0xa0, 0x0a, 0xfc, 0x07, 0x9a, 0x90, 0xb7, 0xcb, 0x6c, 0x98, 0xf8, 0x98, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; #pragma mark - Content Type Icons static const u_int8_t FLEXJSONIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x03, 0xa8, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x30, 0x39, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x32, 0x3a, 0x32, 0x33, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x03, 0x64, 0xa2, 0xe8, 0x00, 0x00, 0x06, 0x37, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x59, 0x7b, 0x48, 0x57, 0x57, 0x1c, 0x3f, 0xc7, 0x47, 0x3e, 0xf2, 0x31, 0xc9, 0x50, 0xdb, 0x56, 0x13, 0x73, 0x59, 0x3e, 0x57, 0x46, 0x66, 0xe2, 0x28, 0x1b, 0x51, 0x6a, 0x2a, 0xb1, 0xcd, 0x6a, 0x43, 0xaa, 0x8d, 0x06, 0xe5, 0xa0, 0xac, 0x86, 0x8c, 0xcd, 0xbf, 0x4c, 0xb3, 0x65, 0x81, 0xb4, 0xd2, 0xa6, 0x11, 0xe8, 0x60, 0xb4, 0x2d, 0x96, 0x2e, 0xb6, 0x08, 0x35, 0x62, 0x15, 0x3e, 0x96, 0xda, 0x16, 0xe1, 0x7c, 0xe2, 0x62, 0x51, 0x12, 0xe5, 0x2f, 0xcd, 0xf7, 0xdd, 0xe7, 0xfe, 0xce, 0xe5, 0x78, 0xfb, 0xa9, 0xf7, 0xfe, 0x7e, 0xbf, 0x7b, 0xf6, 0x08, 0x7e, 0x87, 0xb8, 0x7c, 0xcf, 0xf7, 0x7c, 0xce, 0xf7, 0x7d, 0xbe, 0xe7, 0xf4, 0x93, 0x4a, 0x92, 0x44, 0x5e, 0xe4, 0xe1, 0xf4, 0x22, 0x1b, 0x2f, 0xdb, 0xee, 0x70, 0xe0, 0xbf, 0xce, 0xa0, 0x23, 0x03, 0x8e, 0x0c, 0x18, 0x8c, 0x80, 0xa3, 0x84, 0x0c, 0x06, 0xd0, 0xf0, 0x76, 0x17, 0xc3, 0x12, 0x14, 0x01, 0xb5, 0xbf, 0x93, 0xeb, 0x7f, 0x48, 0xa3, 0xe3, 0xf2, 0x34, 0x29, 0x9c, 0xbe, 0xb9, 0xd4, 0x52, 0xf0, 0xe7, 0xdf, 0x2a, 0x37, 0x66, 0xd0, 0x4b, 0xf4, 0x9d, 0x38, 0x32, 0xcf, 0xcb, 0x12, 0x60, 0xdf, 0x5c, 0x8c, 0x03, 0x39, 0x55, 0x52, 0x45, 0xfd, 0xd4, 0x8d, 0xee, 0xe5, 0x3e, 0x83, 0x03, 0x25, 0x3f, 0x73, 0x80, 0xf4, 0xc5, 0x8f, 0xf4, 0xa7, 0x4f, 0x68, 0x68, 0xa0, 0x7d, 0x36, 0x3f, 0xb7, 0x4b, 0xc0, 0x19, 0x68, 0xe8, 0x24, 0x6a, 0xeb, 0x9f, 0x13, 0x3f, 0xcb, 0xe4, 0xd1, 0x53, 0x29, 0xf7, 0x1b, 0xee, 0xcf, 0x2c, 0x20, 0xeb, 0xd8, 0x02, 0x32, 0x00, 0x07, 0xf8, 0x28, 0xc8, 0x74, 0x7a, 0x2b, 0x82, 0xcc, 0xf3, 0xe6, 0x8c, 0x29, 0xe2, 0xd7, 0xc3, 0x4e, 0x4f, 0x86, 0xc8, 0xc7, 0xe7, 0xa4, 0xdb, 0x7d, 0xb2, 0xe9, 0x0d, 0x1d, 0x53, 0x4b, 0x46, 0x28, 0x01, 0x0e, 0x98, 0x9e, 0x29, 0xb1, 0xf4, 0xf5, 0xa4, 0x1f, 0x25, 0xcd, 0x6a, 0x4c, 0xf0, 0x7c, 0x79, 0x69, 0x6b, 0x3c, 0xbd, 0x6d, 0x8e, 0xbd, 0x69, 0x18, 0xbb, 0xe8, 0xac, 0x68, 0xab, 0x17, 0x04, 0x94, 0x10, 0xd7, 0xe5, 0x31, 0x87, 0x93, 0xb3, 0x12, 0xee, 0xae, 0xb3, 0x2e, 0xd9, 0xb7, 0x20, 0xd2, 0x01, 0xfb, 0x2c, 0x30, 0xb8, 0xcb, 0xe1, 0x00, 0x21, 0x8f, 0x06, 0x95, 0x20, 0xba, 0x3a, 0xeb, 0x47, 0x53, 0x8d, 0x79, 0x3c, 0xa4, 0x8f, 0xd7, 0x45, 0x18, 0xcd, 0xc0, 0xfd, 0x27, 0xa4, 0xe6, 0x96, 0xa2, 0x65, 0x49, 0x90, 0xae, 0x3a, 0xb2, 0x64, 0xc1, 0x14, 0xa6, 0xac, 0x76, 0x8a, 0xb6, 0x9b, 0x32, 0xe4, 0x40, 0xf1, 0x25, 0xb2, 0xea, 0x33, 0xe9, 0xfe, 0x63, 0xb9, 0x0b, 0x79, 0xcc, 0xa1, 0x39, 0xc9, 0xfa, 0x5d, 0x25, 0x36, 0x98, 0x6c, 0x8c, 0x56, 0x60, 0x05, 0x3f, 0x4c, 0xae, 0xcb, 0x97, 0x7a, 0xfb, 0xed, 0x36, 0x5e, 0xde, 0x68, 0xc8, 0x81, 0xe6, 0x6e, 0x69, 0xc0, 0xdc, 0x43, 0xfd, 0xbd, 0x69, 0xd9, 0x07, 0x34, 0x6e, 0xb1, 0xbe, 0x29, 0x94, 0x92, 0xb2, 0x0f, 0xe9, 0xa6, 0x18, 0xc5, 0x87, 0x5b, 0x3d, 0xd2, 0xa3, 0xa7, 0xfa, 0xbb, 0x34, 0x10, 0x86, 0x1c, 0x48, 0x8f, 0xa5, 0xe1, 0xaf, 0xc8, 0xa6, 0xf4, 0x9b, 0xa4, 0xf7, 0xbf, 0x9c, 0xfc, 0xae, 0x41, 0x43, 0x91, 0xb2, 0x34, 0x3c, 0x46, 0x36, 0x16, 0x4a, 0x97, 0x5a, 0xe4, 0xa4, 0xb9, 0xb9, 0xd2, 0xf7, 0x12, 0xe8, 0x02, 0x3f, 0xfd, 0x5d, 0x1a, 0x08, 0x43, 0x0e, 0xbc, 0xbd, 0x8a, 0xd4, 0x7e, 0x4a, 0x23, 0x5f, 0x55, 0xc2, 0x79, 0xa6, 0x4e, 0xb9, 0xd1, 0x34, 0xf4, 0x5d, 0x6f, 0x27, 0xbf, 0xfd, 0xa9, 0xc0, 0x4a, 0x77, 0xd1, 0x92, 0x2c, 0x1a, 0xe0, 0xab, 0x01, 0xd7, 0x5f, 0x32, 0xe4, 0x00, 0xc4, 0xcf, 0x71, 0x21, 0x49, 0x11, 0x8a, 0x1a, 0x6b, 0xaa, 0x59, 0x8d, 0xd9, 0x14, 0xa3, 0x6f, 0x9f, 0x2e, 0xc2, 0xa8, 0x03, 0xb2, 0x0f, 0x56, 0x74, 0xcf, 0x19, 0xed, 0x50, 0xb7, 0xd4, 0x19, 0x01, 0xd6, 0x30, 0x05, 0x38, 0x60, 0x8d, 0x9a, 0x7f, 0x0e, 0xe3, 0x70, 0x40, 0x15, 0x5b, 0xd3, 0x33, 0x32, 0xa9, 0x77, 0x8c, 0x85, 0xdc, 0xbe, 0x2a, 0x9d, 0x44, 0xc0, 0x73, 0x7a, 0xbe, 0x0f, 0xba, 0x90, 0x6c, 0xf8, 0xe0, 0x88, 0x94, 0x7d, 0x8e, 0xac, 0x5b, 0x86, 0xde, 0x4a, 0xc2, 0x54, 0x37, 0x2e, 0xd3, 0xf7, 0x7d, 0x23, 0x19, 0x78, 0x46, 0xca, 0x6a, 0x15, 0x17, 0xcd, 0xbb, 0xd4, 0x96, 0xd8, 0x49, 0x0b, 0x70, 0x20, 0x71, 0x29, 0x71, 0x71, 0xa6, 0xe3, 0x13, 0xb2, 0x65, 0x5f, 0xff, 0x22, 0xe1, 0x5f, 0x6e, 0x9a, 0xd3, 0x74, 0x07, 0x76, 0x95, 0x4d, 0xaa, 0x6d, 0x4c, 0x0a, 0x57, 0xcf, 0xec, 0xa7, 0x05, 0x9c, 0x81, 0xd7, 0x03, 0x49, 0xd1, 0x56, 0x8a, 0x5b, 0xc9, 0x7a, 0x2b, 0xde, 0x78, 0x8d, 0x1e, 0x7e, 0xd7, 0x06, 0xbc, 0x86, 0x64, 0x2a, 0xea, 0xe7, 0xf5, 0xbf, 0x1e, 0x93, 0xe6, 0x6e, 0xc2, 0x7e, 0x95, 0x58, 0xf6, 0xf2, 0x0c, 0x25, 0x74, 0xa1, 0x49, 0x31, 0x03, 0x37, 0xd7, 0xea, 0xc5, 0x04, 0x6f, 0x0a, 0x21, 0x43, 0x98, 0x03, 0x42, 0xac, 0xb1, 0x43, 0x88, 0x80, 0x12, 0xb2, 0x43, 0xab, 0xc0, 0x2d, 0x0e, 0x07, 0x04, 0x06, 0xd3, 0x2e, 0x51, 0x8e, 0x0c, 0xd8, 0x15, 0x36, 0x81, 0x9b, 0x6c, 0xcb, 0xc0, 0x57, 0xe6, 0x21, 0x50, 0xbd, 0x71, 0x51, 0x36, 0xb4, 0xd1, 0x91, 0x91, 0x11, 0x4f, 0x4f, 0x4f, 0x67, 0x67, 0xe7, 0xa1, 0xa1, 0x21, 0x17, 0x17, 0x01, 0x57, 0xb8, 0x71, 0xeb, 0x21, 0xc1, 0x36, 0x3b, 0x7c, 0x7d, 0x7d, 0x3d, 0x3c, 0x3c, 0xfe, 0x3f, 0xd6, 0xc3, 0x01, 0x1b, 0x32, 0x00, 0xf4, 0xe4, 0xa4, 0xfc, 0x9e, 0x71, 0x72, 0xd2, 0x2f, 0x3c, 0x20, 0xad, 0x81, 0x41, 0x9a, 0xc1, 0xa1, 0x6f, 0xca, 0xb5, 0x6b, 0xd7, 0xd6, 0xae, 0x5d, 0xbb, 0xca, 0x3c, 0x56, 0x9b, 0xc7, 0xb1, 0x63, 0xc7, 0x2c, 0xb4, 0x56, 0x57, 0x57, 0x67, 0x65, 0x65, 0x05, 0x04, 0x04, 0xa0, 0xc6, 0x56, 0xae, 0x5c, 0x19, 0x15, 0x15, 0xb5, 0x79, 0xf3, 0x66, 0x35, 0xe6, 0xde, 0xbd, 0x7b, 0x3b, 0x76, 0xec, 0x08, 0x0b, 0x0b, 0xf3, 0xf2, 0xf2, 0x8a, 0x89, 0x89, 0x39, 0x74, 0xe8, 0xd0, 0xe0, 0xa0, 0xf2, 0x7b, 0xd8, 0xd9, 0xb3, 0x67, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xea, 0xeb, 0xeb, 0x33, 0x33, 0x33, 0xfd, 0xfc, 0xfc, 0x16, 0x2e, 0x5c, 0x58, 0x5a, 0x5a, 0xaa, 0xde, 0xae, 0x45, 0xe3, 0x2d, 0xa4, 0x3d, 0xf2, 0xf3, 0xf3, 0x2d, 0xf6, 0x6f, 0xdb, 0xb6, 0x4d, 0xbd, 0xe5, 0xca, 0x95, 0x2b, 0x0c, 0xb0, 0x61, 0xc3, 0x86, 0xf5, 0xeb, 0xd7, 0x53, 0xf3, 0x2b, 0x07, 0xb6, 0x72, 0x4c, 0x73, 0x73, 0xb3, 0x8f, 0x8f, 0x8f, 0x85, 0x90, 0xe0, 0xe0, 0xe0, 0xfe, 0xfe, 0x7e, 0x60, 0x52, 0x52, 0x52, 0xd8, 0x12, 0x4c, 0xe7, 0x18, 0x08, 0xb9, 0x73, 0xe7, 0x0e, 0x97, 0xa0, 0x41, 0x10, 0x8d, 0x35, 0xb6, 0x84, 0x23, 0x7b, 0xf9, 0xf2, 0xe5, 0x0b, 0xe6, 0xb1, 0x65, 0xcb, 0x16, 0xe8, 0xb0, 0x70, 0xe0, 0xe0, 0xc1, 0x83, 0x60, 0x86, 0x87, 0x87, 0x33, 0x7c, 0x77, 0x77, 0xf7, 0xce, 0x9d, 0x3b, 0x91, 0x10, 0x36, 0x1d, 0x1b, 0x1b, 0x8b, 0x88, 0x90, 0xff, 0xdb, 0x1f, 0x14, 0x14, 0x74, 0xf2, 0xe4, 0xc9, 0x8e, 0x8e, 0x8e, 0xbc, 0xbc, 0x3c, 0x1c, 0x24, 0x2e, 0xe7, 0xc1, 0x83, 0x07, 0xc8, 0x2b, 0xa6, 0xee, 0xee, 0xee, 0xe5, 0xe5, 0xe5, 0xc8, 0x83, 0xbf, 0xbf, 0x3f, 0xa6, 0x47, 0x8f, 0x1e, 0x65, 0x12, 0xb4, 0xbf, 0xfa, 0x0e, 0xa8, 0xf7, 0xe7, 0xe6, 0xe6, 0x72, 0xc5, 0x9c, 0x7f, 0xfe, 0xfc, 0x79, 0x30, 0x31, 0x50, 0x39, 0xd9, 0xd9, 0xd9, 0x15, 0x15, 0x15, 0x5d, 0x5d, 0x5d, 0x7c, 0xb5, 0xad, 0xad, 0x8d, 0xad, 0x16, 0x17, 0x17, 0x73, 0xe6, 0xf6, 0xed, 0xdb, 0xc1, 0x84, 0xc5, 0xe3, 0xe3, 0xe3, 0x60, 0x66, 0x64, 0x64, 0x60, 0x9a, 0x9c, 0x9c, 0xcc, 0x00, 0xa9, 0xa9, 0xa9, 0x98, 0xee, 0xdb, 0xb7, 0x8f, 0xe3, 0x35, 0x08, 0xfd, 0x33, 0xc0, 0xd4, 0x6b, 0x7c, 0xd3, 0xd3, 0xd3, 0x13, 0x12, 0x12, 0x00, 0x80, 0xad, 0x25, 0x25, 0x25, 0x08, 0x7f, 0x48, 0x48, 0xc8, 0x9e, 0x3d, 0x7b, 0xd8, 0x96, 0xbb, 0x77, 0xef, 0x32, 0x22, 0x2d, 0x2d, 0x8d, 0x0b, 0x61, 0xf4, 0xf0, 0xf0, 0x70, 0x4f, 0x4f, 0x0f, 0x67, 0xae, 0x58, 0xb1, 0x82, 0xd1, 0x8b, 0x16, 0x2d, 0x02, 0x81, 0x55, 0xbe, 0xa4, 0x41, 0x08, 0x70, 0x00, 0x5d, 0x15, 0x35, 0xd6, 0xda, 0xda, 0x8a, 0xa3, 0x99, 0x98, 0x98, 0xe8, 0xea, 0xea, 0x8a, 0x80, 0x9d, 0x3a, 0x75, 0x0a, 0xb5, 0x04, 0xc5, 0xa8, 0x1c, 0xa6, 0xbe, 0xb1, 0xb1, 0x91, 0xdb, 0xc1, 0x68, 0x14, 0x7a, 0x60, 0xe0, 0xd4, 0x1f, 0xfa, 0x78, 0xd7, 0x62, 0xa7, 0x88, 0x83, 0xb5, 0x09, 0x01, 0x0e, 0xa0, 0x36, 0xd0, 0x37, 0x70, 0x54, 0x8e, 0x1c, 0x39, 0x72, 0xf5, 0xea, 0xd5, 0xce, 0xce, 0x4e, 0xd4, 0x06, 0x7c, 0xa8, 0xab, 0xab, 0x83, 0x6e, 0xd4, 0x95, 0xb7, 0xb7, 0xfc, 0x37, 0x33, 0x24, 0xa7, 0xaf, 0xaf, 0x0f, 0x44, 0x4b, 0x4b, 0x4b, 0x55, 0x55, 0x15, 0x08, 0xb4, 0xa3, 0xb9, 0x73, 0xe7, 0x82, 0x00, 0x18, 0x5f, 0xd6, 0xa3, 0x39, 0xc1, 0x98, 0x98, 0xea, 0x0c, 0xe0, 0xac, 0x1f, 0x33, 0x9e, 0x81, 0xbd, 0x7b, 0xf7, 0x42, 0x87, 0x9b, 0x9b, 0x1b, 0xaa, 0xb6, 0xb2, 0xb2, 0xf2, 0xc0, 0x81, 0x03, 0x98, 0x22, 0x9c, 0xa8, 0x28, 0x26, 0xf9, 0xf4, 0xe9, 0xd3, 0xcc, 0x08, 0xe4, 0x2a, 0x3a, 0x3a, 0x9a, 0xd1, 0xb8, 0xd1, 0x6f, 0xdc, 0xb8, 0x01, 0xc0, 0xfe, 0xfd, 0xfb, 0x39, 0x07, 0x87, 0xb8, 0xa8, 0xa8, 0x88, 0xa5, 0x02, 0x79, 0x28, 0x2c, 0x2c, 0xd4, 0xb5, 0xcd, 0xb6, 0x9b, 0x78, 0xc6, 0xba, 0x44, 0x8b, 0x84, 0x32, 0x18, 0x77, 0xfc, 0xf8, 0x71, 0x66, 0x0a, 0xda, 0x48, 0x41, 0x41, 0x41, 0x64, 0x64, 0x24, 0x9b, 0xee, 0xde, 0xbd, 0x1b, 0x18, 0x38, 0xdf, 0xdb, 0xdb, 0x8b, 0x4a, 0x03, 0x13, 0xe5, 0x7e, 0xe2, 0xc4, 0x09, 0xf4, 0x7e, 0xd0, 0xf0, 0x04, 0x63, 0x62, 0x62, 0x02, 0xb5, 0x07, 0x39, 0xb0, 0x1e, 0xa2, 0x46, 0x47, 0x47, 0xf1, 0xb5, 0xaa, 0x96, 0x74, 0x5d, 0x54, 0x03, 0x58, 0xbb, 0xc8, 0xc9, 0xc9, 0x51, 0x33, 0x4d, 0x26, 0x13, 0x9a, 0x23, 0xfa, 0x49, 0x7b, 0x7b, 0xfb, 0xcd, 0x9b, 0x37, 0xf1, 0xc5, 0xab, 0x49, 0x0d, 0xe0, 0xf4, 0xc3, 0x87, 0x0f, 0x91, 0x96, 0x81, 0x81, 0x01, 0xce, 0x31, 0x4e, 0xe8, 0x67, 0xe0, 0xe2, 0xc5, 0x8b, 0x4d, 0x4d, 0x4d, 0xb8, 0x65, 0x1b, 0x1a, 0x1a, 0x6a, 0x6a, 0x6a, 0x10, 0xb3, 0x35, 0x6b, 0xd6, 0xb0, 0xd0, 0xb2, 0x2f, 0x2e, 0x57, 0x0c, 0xd0, 0xa1, 0xa1, 0xa1, 0x6a, 0xfe, 0x74, 0x1a, 0x99, 0xc1, 0x98, 0xce, 0x37, 0xc4, 0xd1, 0x8d, 0x81, 0xc5, 0xa3, 0x80, 0xdf, 0x50, 0xba, 0x1b, 0xff, 0x1d, 0x80, 0xfe, 0x63, 0x0e, 0x37, 0x25, 0xfa, 0x09, 0x1a, 0x36, 0xae, 0xfa, 0xd8, 0xd8, 0xd8, 0xe5, 0xcb, 0x97, 0x1b, 0x0a, 0x98, 0xe8, 0xcd, 0xfa, 0x0e, 0x88, 0xd6, 0x28, 0x58, 0x9e, 0x80, 0x7b, 0x40, 0xb0, 0x45, 0x36, 0x8a, 0x73, 0x38, 0x60, 0x63, 0xc0, 0x84, 0xc3, 0x1d, 0x19, 0x10, 0x1e, 0x52, 0x1b, 0x05, 0x3a, 0x32, 0x60, 0x63, 0xc0, 0x84, 0xc3, 0x5f, 0xf8, 0x0c, 0xfc, 0x0d, 0x80, 0x98, 0xbd, 0xed, 0xf7, 0x50, 0xda, 0x08, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXJSONIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0d, 0x5c, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x09, 0x8c, 0x15, 0x49, 0xf5, 0x71, 0x0c, 0x37, 0x3b, 0x80, 0x9c, 0x4b, 0x80, 0xe1, 0x0a, 0xe0, 0x22, 0x20, 0xb7, 0x24, 0x72, 0x0c, 0x12, 0xc3, 0x21, 0x4e, 0xb2, 0x8b, 0x60, 0x88, 0x44, 0x19, 0x96, 0x51, 0x20, 0x30, 0x84, 0x05, 0x51, 0x43, 0x42, 0x96, 0x71, 0x81, 0x44, 0x10, 0xd8, 0x48, 0xb2, 0x86, 0x63, 0xb3, 0xdc, 0xe7, 0x42, 0x60, 0xc1, 0x15, 0x44, 0x09, 0x67, 0x04, 0x16, 0x11, 0x59, 0x40, 0x58, 0x1c, 0x21, 0x1c, 0x0e, 0xa7, 0xb3, 0xc8, 0xb5, 0x40, 0xfb, 0x5e, 0x35, 0x55, 0x5d, 0xdd, 0x5d, 0xfd, 0xbb, 0x7b, 0xaa, 0xff, 0xff, 0x0d, 0xf3, 0x2b, 0xf9, 0xbf, 0x5f, 0xbd, 0xbb, 0x5e, 0x75, 0x57, 0x5f, 0xf5, 0xaa, 0x2b, 0x18, 0x58, 0x20, 0x44, 0xa9, 0x18, 0x82, 0x97, 0xb1, 0x2a, 0x05, 0xea, 0xbc, 0x65, 0xaa, 0xe1, 0x5b, 0x9b, 0x52, 0x72, 0xc9, 0xab, 0x5c, 0xbd, 0xed, 0xa6, 0x80, 0x13, 0x95, 0xfd, 0xa6, 0x1d, 0xe3, 0xac, 0xbb, 0x04, 0x64, 0x76, 0x95, 0x05, 0x65, 0x1b, 0x8a, 0xd6, 0x99, 0x5e, 0xaf, 0xf8, 0xd4, 0xe6, 0x3d, 0xab, 0x54, 0x20, 0x8d, 0x6e, 0xb4, 0x37, 0x46, 0x69, 0xc1, 0x9b, 0x1d, 0xc0, 0x26, 0x50, 0xa1, 0x42, 0x05, 0xd3, 0x2c, 0x6e, 0x6b, 0xd5, 0xaa, 0xa5, 0x94, 0xab, 0xcc, 0xb1, 0x8f, 0x1f, 0x3f, 0xe6, 0x20, 0x05, 0x42, 0xc0, 0x4e, 0x20, 0x74, 0x1b, 0x42, 0x0b, 0xd8, 0xda, 0xe0, 0x34, 0xaf, 0xaa, 0xbb, 0x04, 0xc6, 0xfc, 0xc6, 0x62, 0xfb, 0xcf, 0x3d, 0x0b, 0x16, 0x90, 0xdc, 0xb3, 0x04, 0xcb, 0xbb, 0x82, 0x0c, 0x73, 0x3e, 0x97, 0x85, 0x82, 0x21, 0x42, 0x17, 0x34, 0xac, 0x63, 0xc1, 0x1c, 0x72, 0x09, 0x7c, 0xb0, 0x8b, 0x93, 0x00, 0x4a, 0x14, 0x2e, 0xb9, 0xa2, 0xf4, 0xe4, 0x29, 0x40, 0x15, 0xd1, 0x3b, 0x96, 0x30, 0x87, 0x5c, 0x02, 0x9c, 0xe0, 0xb5, 0x75, 0xb9, 0xe4, 0xc5, 0xc8, 0xf1, 0x2e, 0x81, 0x96, 0x2d, 0x5b, 0xc2, 0x90, 0x21, 0x66, 0xcb, 0x69, 0xdf, 0xe2, 0x3f, 0x2e, 0x20, 0x0e, 0xa0, 0x71, 0xe3, 0xc6, 0xf1, 0xc8, 0x25, 0xdc, 0x86, 0x6e, 0x43, 0x68, 0x01, 0xe1, 0x5a, 0x40, 0xc0, 0xd5, 0xe6, 0x80, 0x72, 0x81, 0xd9, 0x02, 0x19, 0x50, 0x8d, 0x7f, 0xd3, 0x97, 0x03, 0xa8, 0xf0, 0x4e, 0xcb, 0x81, 0x0c, 0x38, 0x85, 0xa8, 0x3e, 0xff, 0x27, 0x2a, 0xac, 0x1b, 0x57, 0x66, 0x03, 0x37, 0x14, 0xbb, 0xbd, 0x5b, 0xbd, 0x63, 0x0c, 0x53, 0x31, 0x70, 0xdc, 0xb8, 0xc5, 0x1c, 0x32, 0xb7, 0x9d, 0x7e, 0x66, 0xaf, 0x7b, 0xd5, 0xd2, 0xb7, 0x17, 0xd5, 0xac, 0x59, 0xd3, 0xcb, 0xa9, 0x50, 0x78, 0x65, 0x1f, 0x0c, 0x1b, 0x36, 0x0c, 0x1e, 0x3c, 0x78, 0x20, 0x14, 0xf5, 0xee, 0xdd, 0x1b, 0x8e, 0x1f, 0x3f, 0x0e, 0x27, 0x4e, 0x9c, 0x80, 0x7b, 0xf7, 0xee, 0xb1, 0xc3, 0x75, 0xec, 0xd8, 0xb1, 0x50, 0xbd, 0x7a, 0x75, 0xb8, 0x7f, 0xff, 0x3e, 0xab, 0x17, 0x14, 0x14, 0x40, 0xdb, 0xb6, 0x6d, 0x85, 0x8c, 0x00, 0xbc, 0x8e, 0x5b, 0x64, 0x10, 0x24, 0x82, 0xe9, 0xb7, 0x76, 0xed, 0x5a, 0x86, 0xe3, 0xf5, 0xdd, 0xbb, 0x77, 0x1b, 0x1b, 0x37, 0x6e, 0x64, 0xb4, 0xbc, 0xbc, 0x3c, 0xb6, 0x15, 0x42, 0x2f, 0x80, 0xf4, 0xf5, 0x01, 0x7a, 0x19, 0x49, 0x79, 0xf9, 0x5b, 0xa0, 0xdc, 0x8b, 0x22, 0x89, 0xcd, 0x0b, 0x25, 0xf1, 0x30, 0x40, 0xa3, 0x66, 0xbd, 0x1f, 0x58, 0xed, 0xa2, 0x7a, 0x90, 0x91, 0x94, 0x24, 0x7c, 0x5b, 0xd0, 0xf0, 0x87, 0xa6, 0xe2, 0x3b, 0x1b, 0x2d, 0x03, 0xf7, 0x36, 0x5b, 0xb0, 0x1f, 0xe4, 0x6b, 0xe0, 0xc9, 0x57, 0x7e, 0x2a, 0x12, 0xd3, 0x7d, 0x0d, 0x90, 0x78, 0x8d, 0xaa, 0x6a, 0x25, 0x7b, 0x4f, 0xaa, 0xf1, 0x36, 0xac, 0xf3, 0xd0, 0x96, 0xeb, 0x7b, 0x3e, 0xb3, 0x5f, 0x5c, 0xc9, 0x34, 0xba, 0xa4, 0x56, 0x5d, 0x6c, 0xc9, 0x3c, 0x04, 0x5b, 0x03, 0x8e, 0x93, 0xf2, 0xa2, 0x4e, 0x4a, 0x54, 0x8a, 0x08, 0x57, 0xfa, 0xc0, 0x43, 0x48, 0x42, 0x07, 0x0a, 0x91, 0xad, 0xc9, 0x52, 0xa5, 0x76, 0x75, 0xa9, 0xe2, 0x01, 0xfa, 0x1a, 0xf8, 0x7a, 0x73, 0x53, 0xf2, 0xbd, 0x0d, 0x96, 0x06, 0x7e, 0x23, 0x61, 0x61, 0xbc, 0x21, 0x5f, 0x03, 0x87, 0x17, 0x02, 0xfc, 0x62, 0xa4, 0x5b, 0xc1, 0xda, 0x9f, 0xbb, 0x71, 0x2a, 0xcc, 0x2b, 0x3a, 0xd8, 0x6d, 0xde, 0x1c, 0xe2, 0x50, 0x55, 0xc5, 0x45, 0xc2, 0xa5, 0x3e, 0x44, 0x74, 0x32, 0xa7, 0x93, 0xfe, 0xbc, 0x79, 0xf3, 0x98, 0x1f, 0xf2, 0xfd, 0x1e, 0x21, 0xa8, 0xbe, 0x74, 0xe9, 0x52, 0xb6, 0xa5, 0x7a, 0xbd, 0x7a, 0xf5, 0xd8, 0x7d, 0x20, 0xe7, 0x23, 0x9c, 0xad, 0x48, 0xc7, 0x84, 0x0d, 0x9c, 0x3b, 0x77, 0x2e, 0xab, 0x5f, 0xbf, 0x7e, 0x9d, 0x9d, 0xcc, 0x51, 0x88, 0xd5, 0xf9, 0x76, 0xfe, 0xfc, 0xf9, 0xc6, 0x99, 0x33, 0x67, 0x8c, 0x3e, 0x7d, 0xfa, 0xd8, 0xf0, 0x36, 0x25, 0x58, 0xf1, 0x3c, 0x92, 0xb9, 0x01, 0x52, 0xc8, 0x7f, 0x24, 0x8c, 0x97, 0x26, 0xb6, 0x3a, 0xa7, 0xd1, 0x76, 0xf8, 0xf0, 0xe1, 0x4e, 0xfd, 0x6e, 0x03, 0x3d, 0x7a, 0xf4, 0x30, 0xaa, 0x56, 0xad, 0x6a, 0x3c, 0x7a, 0xf4, 0xc8, 0xc5, 0x5c, 0x16, 0x44, 0xea, 0x3b, 0x19, 0x9b, 0x1a, 0x69, 0xf1, 0x1d, 0x2a, 0x74, 0xad, 0x25, 0x3d, 0x44, 0xba, 0x0e, 0xfa, 0xc9, 0x27, 0x3d, 0x42, 0x7e, 0x0e, 0xe8, 0xd2, 0x33, 0x0d, 0xd0, 0x8d, 0xa0, 0xae, 0x7c, 0x82, 0xe7, 0x36, 0xc1, 0x55, 0xcf, 0xc1, 0x27, 0x89, 0x0b, 0xb6, 0x58, 0xfc, 0x15, 0xb1, 0x5f, 0xe5, 0xcb, 0x4c, 0x4e, 0x71, 0x5e, 0xcb, 0x86, 0xb9, 0xfc, 0xe4, 0x3a, 0x9c, 0xdb, 0x48, 0x0e, 0x62, 0xd9, 0x31, 0x3f, 0xa7, 0xae, 0xde, 0x06, 0x78, 0xa3, 0xc0, 0x74, 0xc3, 0x8f, 0xd7, 0xe9, 0xac, 0xaa, 0x9e, 0xf2, 0x63, 0xe0, 0xc5, 0xb3, 0x56, 0x95, 0x2f, 0x65, 0xc2, 0x69, 0x35, 0xa0, 0xb8, 0x04, 0xa0, 0xd9, 0x8f, 0x2c, 0xbb, 0x1f, 0xcf, 0xb2, 0x60, 0x2f, 0xe8, 0xf5, 0x7a, 0x74, 0xc5, 0x60, 0x52, 0xa9, 0xe7, 0xa6, 0xfe, 0xde, 0x8b, 0x33, 0x18, 0x5e, 0xab, 0x01, 0x39, 0x0d, 0x01, 0x36, 0xfc, 0xd2, 0x32, 0xf4, 0xfe, 0x0e, 0x0b, 0x4e, 0x04, 0xf1, 0x07, 0xd7, 0x5f, 0x7b, 0x0d, 0xe0, 0xb7, 0xe3, 0x13, 0x71, 0xfa, 0xd3, 0xb4, 0x1a, 0x40, 0xea, 0xfb, 0x74, 0xb0, 0x8c, 0xec, 0xfb, 0x9b, 0x05, 0x7b, 0x41, 0xd7, 0xee, 0x58, 0x94, 0x2f, 0x56, 0x58, 0x70, 0x59, 0x21, 0xed, 0x06, 0x84, 0x35, 0xfc, 0xbf, 0x47, 0x61, 0x25, 0x12, 0xf3, 0x47, 0x32, 0x0a, 0xdd, 0xb8, 0x0b, 0xd0, 0xfe, 0x6d, 0xcb, 0x50, 0x90, 0x61, 0x94, 0x1e, 0xe9, 0xff, 0x73, 0x99, 0x25, 0x53, 0x56, 0x28, 0x92, 0x06, 0x94, 0xd5, 0x78, 0x14, 0x72, 0x29, 0xdf, 0x85, 0xa2, 0x70, 0x5a, 0xd6, 0x51, 0xbe, 0x1a, 0x90, 0x9f, 0x9f, 0x2f, 0xee, 0xf6, 0xe4, 0x28, 0xa4, 0x15, 0x0e, 0x72, 0x9f, 0x85, 0x0e, 0x8a, 0x1b, 0x3d, 0x82, 0x17, 0x2f, 0x5e, 0x6c, 0x13, 0x5b, 0xb0, 0x60, 0x81, 0x8d, 0x4e, 0x3c, 0xa7, 0x4e, 0x9d, 0x12, 0x3c, 0x25, 0x25, 0x25, 0x46, 0xc5, 0x8a, 0x15, 0x05, 0xcf, 0xa0, 0x41, 0x83, 0x04, 0xed, 0xf4, 0xe9, 0xd3, 0x02, 0x9f, 0x9b, 0x9b, 0x2b, 0x60, 0x7c, 0x56, 0x2e, 0x78, 0x12, 0x01, 0x9e, 0x77, 0xad, 0x5e, 0x42, 0xe4, 0x1c, 0xbf, 0xa3, 0xe5, 0x3c, 0xbc, 0x81, 0x8d, 0x1b, 0x37, 0x36, 0xa6, 0x4e, 0x9d, 0x6a, 0x5c, 0xbd, 0x7a, 0x95, 0x93, 0x8c, 0x6e, 0xdd, 0xba, 0x31, 0xa7, 0x9e, 0x3d, 0x7b, 0x26, 0x70, 0xa3, 0x46, 0x8d, 0x62, 0x38, 0x7c, 0x2d, 0xcb, 0x70, 0x74, 0x6b, 0x4d, 0x3a, 0x78, 0xe1, 0xfa, 0x78, 0x3d, 0xd1, 0x36, 0x92, 0xab, 0x51, 0x34, 0xc8, 0xde, 0x2e, 0xd0, 0xdb, 0x03, 0x2a, 0xfc, 0x21, 0x02, 0x1a, 0x86, 0xa1, 0x43, 0x87, 0xb2, 0xb7, 0x0c, 0xb4, 0xfb, 0xad, 0x5c, 0xb9, 0x92, 0xd1, 0xd7, 0xaf, 0x5f, 0xcf, 0xb6, 0x55, 0xaa, 0x54, 0x61, 0x5b, 0xad, 0xbf, 0x44, 0xad, 0x73, 0xd2, 0x28, 0x8a, 0x68, 0xcc, 0x58, 0xb5, 0x6a, 0x95, 0x8d, 0x44, 0x6f, 0x25, 0xe8, 0x31, 0xc0, 0xae, 0x5d, 0xbb, 0x8c, 0xfd, 0xfb, 0xf7, 0xdb, 0x68, 0x72, 0xe5, 0xc0, 0x81, 0x03, 0xc6, 0x95, 0x2b, 0x57, 0x64, 0x94, 0x36, 0xec, 0x7b, 0x1e, 0x78, 0xf8, 0xf0, 0x21, 0xd4, 0xa8, 0x51, 0x83, 0x05, 0xa9, 0x72, 0xe5, 0xca, 0x70, 0xf2, 0xe4, 0x49, 0xe8, 0xd8, 0xb1, 0xa3, 0x56, 0xd0, 0xa2, 0x14, 0xf6, 0x6d, 0x40, 0x94, 0xc6, 0x92, 0xa1, 0xab, 0x7c, 0x9d, 0x07, 0x92, 0x11, 0x41, 0x5d, 0x9d, 0x99, 0x5d, 0x48, 0x37, 0x82, 0xba, 0xf2, 0x99, 0x63, 0x40, 0x37, 0x82, 0xba, 0xf2, 0x99, 0x1e, 0xd0, 0x8d, 0xa0, 0xae, 0x7c, 0xa6, 0x07, 0x74, 0x23, 0xa8, 0x2b, 0x1f, 0xc9, 0xd5, 0xa8, 0xf3, 0xa6, 0x5e, 0xf5, 0xc8, 0x50, 0x7e, 0xfc, 0x98, 0xf7, 0x2d, 0x80, 0x0f, 0xa7, 0xe9, 0xba, 0x6e, 0xca, 0x47, 0xb2, 0x0b, 0xc9, 0x4f, 0x24, 0x82, 0xb8, 0xb5, 0xed, 0x08, 0x80, 0xfc, 0x76, 0x38, 0x88, 0x8c, 0x17, 0x4f, 0x24, 0x0d, 0xe0, 0xca, 0x7f, 0x3a, 0x14, 0x40, 0x15, 0x7d, 0xa2, 0x13, 0x5e, 0xa6, 0x2d, 0xd9, 0xce, 0xa5, 0xf4, 0xb6, 0xda, 0x0d, 0x78, 0xfe, 0xdc, 0x72, 0xa0, 0x30, 0xcf, 0x82, 0xfd, 0xa0, 0x47, 0x4f, 0xfc, 0x38, 0x82, 0xd1, 0xb5, 0x1b, 0x20, 0x9b, 0xe1, 0x0f, 0x6d, 0x65, 0x5c, 0xb2, 0xe1, 0x48, 0x1b, 0x90, 0x6c, 0x67, 0x55, 0xfa, 0xb5, 0x1b, 0xb0, 0x47, 0x9a, 0x1a, 0xd3, 0x30, 0x5b, 0x65, 0xc2, 0x8e, 0xab, 0xee, 0x31, 0xc5, 0xc6, 0xce, 0x15, 0xbc, 0xa6, 0xd5, 0x00, 0x7a, 0xb6, 0x3f, 0x72, 0xae, 0x69, 0x8c, 0x9e, 0x87, 0x06, 0xd9, 0x85, 0x56, 0xbd, 0x63, 0x39, 0xd7, 0x7c, 0x0c, 0xc0, 0xa5, 0x1b, 0x56, 0xbd, 0x2c, 0x90, 0x56, 0x03, 0x56, 0xfe, 0xd1, 0x34, 0xd9, 0xa8, 0x2e, 0xc0, 0xe7, 0x1f, 0x04, 0x33, 0xff, 0x9d, 0x6f, 0x02, 0x8c, 0x1f, 0x6c, 0xf2, 0x96, 0xe2, 0x34, 0xc9, 0x73, 0x57, 0x82, 0xc9, 0x79, 0x71, 0x69, 0xdf, 0xd0, 0xb4, 0xc2, 0x19, 0xb7, 0x77, 0xbe, 0x34, 0xd5, 0xcb, 0xc3, 0xa4, 0x97, 0xc1, 0xbc, 0x77, 0x01, 0xfe, 0xf2, 0x77, 0x93, 0xfa, 0x87, 0x22, 0x80, 0xde, 0xed, 0xbd, 0x38, 0x83, 0xe1, 0xb5, 0x7a, 0x80, 0x4c, 0x7c, 0xf6, 0xbe, 0x65, 0x48, 0x39, 0xcf, 0xde, 0x22, 0x33, 0x88, 0x3b, 0x4f, 0x15, 0x5d, 0xe7, 0x49, 0x87, 0x76, 0x03, 0x5e, 0x33, 0x9f, 0xb8, 0x90, 0xae, 0xb4, 0x14, 0xed, 0x06, 0xa4, 0xc5, 0x6b, 0xc9, 0x68, 0xa4, 0x0d, 0x88, 0xfa, 0xf5, 0x91, 0xe4, 0xa7, 0x27, 0xa8, 0xdd, 0x00, 0x1a, 0x3e, 0x79, 0xe9, 0x55, 0x08, 0xe0, 0x35, 0x6d, 0x8f, 0xf0, 0xf9, 0x8b, 0x38, 0x27, 0xc0, 0x37, 0x72, 0x2c, 0x58, 0x07, 0xd2, 0x1e, 0x85, 0xc8, 0x78, 0x9b, 0x7c, 0x80, 0x5b, 0xff, 0xb5, 0xdc, 0x50, 0x8d, 0x46, 0xf2, 0xe5, 0x34, 0x71, 0xd2, 0xfb, 0x31, 0x55, 0xea, 0x8b, 0xa5, 0x25, 0x18, 0x14, 0xc9, 0xfd, 0xc0, 0x45, 0xcc, 0x8e, 0xf8, 0xe4, 0x18, 0xc0, 0xc9, 0x8b, 0xde, 0x46, 0xdf, 0x79, 0xd3, 0xa4, 0x55, 0xc9, 0xc2, 0x97, 0xdb, 0x78, 0xd1, 0x97, 0x15, 0x89, 0x65, 0x3c, 0x79, 0xd2, 0xe3, 0x61, 0x6f, 0xb3, 0xf1, 0xa7, 0x48, 0x7b, 0x70, 0xfc, 0x9d, 0x55, 0x79, 0x98, 0x69, 0x80, 0x2a, 0x2a, 0xa9, 0xc4, 0x95, 0xaf, 0x1e, 0xc8, 0xce, 0xce, 0x86, 0x2d, 0x5b, 0xa4, 0xa9, 0x59, 0xa9, 0x0c, 0xb5, 0x87, 0xad, 0x50, 0x3d, 0x50, 0x5a, 0x5a, 0x0a, 0x87, 0x0f, 0x1f, 0xf6, 0x50, 0x95, 0x1e, 0x74, 0xa8, 0xd1, 0x38, 0x8e, 0x23, 0xae, 0x6f, 0x0f, 0x4c, 0x99, 0x32, 0x85, 0xbd, 0x36, 0xa5, 0x57, 0xa7, 0xfc, 0xe7, 0x8c, 0x35, 0xcd, 0xd1, 0xe6, 0x34, 0x7a, 0x11, 0xc8, 0x5f, 0xb3, 0x72, 0xbe, 0xf1, 0xe3, 0xc7, 0x0b, 0x3a, 0xd1, 0xce, 0x9e, 0x3d, 0xcb, 0x49, 0x40, 0xd9, 0x65, 0x84, 0x3b, 0x78, 0xf0, 0xa0, 0xe0, 0xc1, 0xf7, 0xc8, 0x82, 0xee, 0x0b, 0xf8, 0xbd, 0xe7, 0x3c, 0x74, 0xe8, 0x90, 0x81, 0xef, 0x7a, 0xd9, 0xaf, 0x4e, 0x9d, 0x3a, 0xb6, 0x17, 0xd2, 0x24, 0x3b, 0x70, 0xe0, 0x40, 0x86, 0xe3, 0x2f, 0xb2, 0xfb, 0xf6, 0xed, 0x6b, 0xe3, 0x69, 0xd1, 0xa2, 0x05, 0xab, 0x0f, 0x1e, 0x3c, 0xd8, 0x58, 0xbe, 0x7c, 0xb9, 0x78, 0x63, 0xcf, 0xdf, 0xe4, 0xcf, 0x98, 0x31, 0x83, 0xd1, 0xd1, 0x51, 0x63, 0xd2, 0xa4, 0x49, 0x46, 0xb5, 0x6a, 0xd5, 0x6c, 0xf2, 0x7e, 0xfe, 0x59, 0xaf, 0xc7, 0xfd, 0x38, 0x91, 0xde, 0xb3, 0x67, 0x4f, 0x97, 0x72, 0x9e, 0xad, 0x46, 0x0e, 0xd0, 0x14, 0x02, 0x7c, 0x89, 0x6d, 0xd3, 0x44, 0xf8, 0x76, 0xed, 0xda, 0xb9, 0x70, 0x59, 0x59, 0x59, 0x02, 0x47, 0x3c, 0xfd, 0xfa, 0xf5, 0x63, 0x75, 0x3e, 0xf5, 0x40, 0x7e, 0xdb, 0x2f, 0x18, 0x15, 0x80, 0x76, 0x03, 0x48, 0xe7, 0xc4, 0x89, 0x13, 0x45, 0x14, 0xc9, 0x19, 0xfa, 0x51, 0xe1, 0x2f, 0xc6, 0x47, 0x8c, 0x18, 0xc1, 0xea, 0xfc, 0x4f, 0xe6, 0x21, 0x1c, 0xd5, 0x67, 0xcf, 0x9e, 0xcd, 0xc8, 0xf4, 0x22, 0x9c, 0xea, 0x97, 0x2e, 0x5d, 0xe2, 0xec, 0x09, 0xb7, 0xbe, 0xc7, 0x00, 0x2a, 0x4b, 0x58, 0x68, 0x2a, 0x01, 0x46, 0x8d, 0x3c, 0x66, 0xbf, 0xe9, 0xd3, 0xa7, 0x33, 0x7e, 0x4c, 0x3d, 0x00, 0x9c, 0xe0, 0xc1, 0xe0, 0x4d, 0x9b, 0x36, 0xc1, 0x93, 0x27, 0xe6, 0xa3, 0xb8, 0x85, 0x0b, 0x31, 0xb3, 0x05, 0x0b, 0xcf, 0x4b, 0x66, 0x15, 0x9d, 0xbf, 0x84, 0xcd, 0x73, 0x10, 0xbb, 0x77, 0xef, 0x2e, 0xa2, 0xcb, 0x49, 0xad, 0x5a, 0xb5, 0x62, 0x38, 0xf4, 0xc1, 0xc0, 0x37, 0xfa, 0x02, 0xe6, 0xf4, 0x73, 0xe7, 0xce, 0x09, 0x1c, 0xf1, 0xd0, 0x0f, 0x0f, 0x7a, 0x4e, 0xb6, 0xd1, 0xee, 0xde, 0xbd, 0x6b, 0xab, 0x0b, 0xa6, 0x04, 0x80, 0xd9, 0xd7, 0x09, 0x18, 0x64, 0x12, 0x9e, 0xc8, 0x98, 0x01, 0x19, 0x47, 0x5d, 0x4e, 0xb3, 0x4e, 0x30, 0x6d, 0x94, 0x65, 0x71, 0x7a, 0x75, 0x3d, 0x4d, 0xb9, 0xd9, 0xbb, 0x77, 0xaf, 0xf1, 0xfc, 0xf9, 0x73, 0x59, 0x5c, 0x1b, 0xf6, 0xbd, 0x9c, 0x1e, 0x39, 0x72, 0x24, 0xd0, 0x6a, 0x05, 0x34, 0xd4, 0x6d, 0xdb, 0xb6, 0x0d, 0xda, 0xb4, 0x69, 0x03, 0x17, 0x2e, 0x5c, 0xd0, 0xe9, 0xf4, 0x68, 0x65, 0xfd, 0x42, 0x40, 0x23, 0x0b, 0xef, 0x7a, 0xdc, 0x6f, 0xfd, 0xd8, 0x53, 0x4e, 0xf7, 0xed, 0x81, 0x68, 0xc3, 0x15, 0xbd, 0x36, 0xed, 0x51, 0x28, 0x7a, 0x97, 0xc2, 0x69, 0xcc, 0x34, 0x20, 0x5c, 0xbc, 0xa2, 0xe7, 0xce, 0xf4, 0x40, 0xf4, 0x31, 0x0d, 0xa7, 0xf1, 0xa5, 0x1f, 0x85, 0xc2, 0x35, 0x37, 0x7e, 0xdc, 0x2f, 0xfd, 0x21, 0x10, 0xbf, 0x90, 0x86, 0xf3, 0x28, 0xd3, 0x01, 0xe1, 0xe2, 0x15, 0x39, 0x77, 0xa6, 0x03, 0x22, 0x0f, 0x69, 0x38, 0x85, 0x99, 0x0e, 0x08, 0x17, 0xaf, 0xc8, 0xb9, 0x33, 0x1d, 0x10, 0x79, 0x48, 0xc3, 0x29, 0xcc, 0x74, 0x40, 0xb8, 0x78, 0x45, 0xce, 0x1d, 0xea, 0xb1, 0x74, 0xe4, 0xd6, 0x3d, 0x14, 0x52, 0xb6, 0xf4, 0x99, 0x7f, 0x03, 0x7c, 0xf5, 0xcc, 0x62, 0xa0, 0xd4, 0xdd, 0x21, 0x3d, 0xac, 0x7a, 0x22, 0x88, 0x5e, 0x95, 0xca, 0x25, 0xab, 0x12, 0x40, 0xc7, 0x1c, 0x00, 0xca, 0x01, 0x8e, 0x5b, 0x89, 0xcd, 0x7d, 0xc0, 0x43, 0x7c, 0xe2, 0x98, 0x3b, 0x13, 0xe0, 0xec, 0x65, 0x75, 0x88, 0xbc, 0x12, 0x4b, 0x55, 0xdc, 0xce, 0x97, 0xe9, 0x32, 0x0f, 0x75, 0xc4, 0xde, 0xf7, 0x00, 0xaa, 0x45, 0x90, 0x38, 0x25, 0xeb, 0x2d, 0x2b, 0x1c, 0x9b, 0x23, 0xe0, 0x7b, 0xb3, 0xd5, 0xc1, 0x6f, 0xf3, 0x3a, 0x40, 0x4e, 0x23, 0x9c, 0x0e, 0xf4, 0x22, 0x7b, 0x3d, 0x48, 0x43, 0x69, 0x32, 0x18, 0x95, 0x7f, 0xe1, 0x2c, 0xb6, 0x2f, 0xae, 0x9b, 0x30, 0xff, 0xff, 0x47, 0x31, 0xc0, 0xf7, 0xdf, 0x05, 0xf8, 0xb4, 0x88, 0x63, 0xd2, 0xbb, 0x8d, 0xc5, 0x11, 0x40, 0x53, 0x66, 0xe5, 0x55, 0xd4, 0x78, 0x48, 0x68, 0x65, 0x29, 0xbe, 0x7c, 0x15, 0xc7, 0x85, 0xdd, 0x9e, 0x2e, 0x06, 0xf8, 0xb6, 0x34, 0xbd, 0x90, 0xcb, 0xab, 0xa6, 0xa1, 0x70, 0x5a, 0x2a, 0xb7, 0xb1, 0x3e, 0x09, 0xd3, 0x52, 0x07, 0xba, 0x25, 0x0a, 0x1d, 0xba, 0x3e, 0x24, 0x92, 0x8f, 0x75, 0x07, 0x24, 0x72, 0xfc, 0x55, 0xa1, 0xc5, 0xa2, 0x03, 0x4a, 0x1f, 0xaa, 0xc3, 0x19, 0x62, 0xd8, 0x57, 0x2b, 0x40, 0xac, 0x97, 0x8e, 0x2f, 0x3d, 0x6c, 0x7a, 0x2a, 0x4a, 0x12, 0x21, 0xed, 0x1d, 0x70, 0x13, 0xa7, 0x07, 0xd2, 0xd5, 0x8f, 0xb3, 0xf4, 0x6c, 0x17, 0xcd, 0x34, 0xc0, 0x26, 0x78, 0xe9, 0xd9, 0x4d, 0xb1, 0x10, 0x6d, 0xff, 0x19, 0x00, 0xb7, 0x4b, 0x9d, 0x56, 0x53, 0x5f, 0x4f, 0xdb, 0x49, 0x98, 0xd6, 0xe0, 0x7c, 0xeb, 0xd7, 0xee, 0x06, 0xbf, 0xd1, 0x02, 0xe0, 0x57, 0x38, 0xb9, 0x23, 0xe8, 0x35, 0xbf, 0x5b, 0x83, 0x1a, 0xb3, 0xf3, 0xaf, 0x98, 0x69, 0x84, 0xb9, 0xfb, 0x9f, 0x5f, 0x76, 0xd3, 0x69, 0xb9, 0xc8, 0xa8, 0xed, 0xb9, 0xad, 0xa8, 0x31, 0x69, 0x3b, 0x02, 0xea, 0xe3, 0x09, 0xb6, 0x43, 0x73, 0xb7, 0x53, 0x74, 0x03, 0x36, 0xe1, 0x77, 0xfa, 0x89, 0x10, 0xb2, 0xe6, 0x8b, 0xd7, 0x00, 0x26, 0xa1, 0x4e, 0x55, 0xf0, 0xc9, 0x87, 0x28, 0x26, 0xdc, 0xca, 0xf6, 0xc2, 0xc0, 0x69, 0x3b, 0x02, 0x64, 0x27, 0xb7, 0xe1, 0xa4, 0xb5, 0x1f, 0x9b, 0x53, 0x3e, 0x04, 0xba, 0x16, 0xae, 0x48, 0x71, 0xe5, 0xa3, 0x60, 0x59, 0x2c, 0x42, 0x48, 0x01, 0x3c, 0xc3, 0x4b, 0x5c, 0x5a, 0x35, 0xeb, 0x81, 0xf5, 0x25, 0x04, 0xc6, 0xf5, 0x11, 0x4e, 0x41, 0x19, 0xde, 0x4b, 0x21, 0x90, 0x62, 0x54, 0xda, 0x8e, 0x00, 0xb9, 0x9d, 0xc3, 0x7b, 0xcb, 0x35, 0x13, 0xbe, 0x8f, 0x27, 0xc9, 0x12, 0x69, 0xfa, 0xb8, 0x9b, 0x23, 0x18, 0x86, 0x72, 0x48, 0x9c, 0xc1, 0x27, 0xc9, 0x38, 0x04, 0x9f, 0xfc, 0x88, 0x45, 0x07, 0x90, 0x23, 0xe5, 0xb5, 0x64, 0x3a, 0x20, 0xcd, 0x3d, 0x1f, 0x8b, 0x0e, 0xc0, 0x79, 0x5f, 0x38, 0x17, 0xd0, 0x1d, 0x89, 0xad, 0x87, 0xdc, 0xb8, 0xb0, 0x98, 0x2d, 0x07, 0xdd, 0x12, 0x95, 0xf0, 0xe9, 0x68, 0x5c, 0x4a, 0x2c, 0x4e, 0xc2, 0x14, 0x8c, 0x42, 0xcc, 0xba, 0xfc, 0x70, 0x8f, 0x77, 0x58, 0xa2, 0x7a, 0x1a, 0x4a, 0x16, 0xf2, 0xbf, 0x8b, 0x2b, 0xa1, 0xbe, 0xed, 0x6d, 0x2b, 0x95, 0x14, 0xc5, 0x7e, 0x97, 0x4a, 0xf3, 0x96, 0xad, 0x45, 0x05, 0x00, 0x7f, 0x9a, 0x8b, 0xb9, 0x5b, 0xf8, 0xf4, 0x33, 0x59, 0xa5, 0x6d, 0x53, 0x80, 0x3f, 0xcf, 0x8f, 0x4f, 0xf0, 0xa9, 0x9d, 0xb1, 0x39, 0x02, 0x92, 0x15, 0xf4, 0xb8, 0xeb, 0x8d, 0xcd, 0x11, 0x10, 0xf7, 0x40, 0x25, 0xcb, 0xbf, 0x4c, 0x07, 0x24, 0x2b, 0xb2, 0x01, 0xf5, 0x66, 0x3a, 0x20, 0x60, 0xa0, 0x92, 0xc5, 0x96, 0xe9, 0x80, 0x64, 0x45, 0x36, 0xa0, 0xde, 0xa4, 0x76, 0x00, 0x25, 0x77, 0xe0, 0x4a, 0x9d, 0x01, 0x5d, 0x29, 0x9f, 0x6c, 0x49, 0xeb, 0x00, 0x4a, 0x30, 0xc4, 0xbc, 0x3b, 0xf6, 0x95, 0xb8, 0x69, 0xd3, 0xa6, 0x95, 0xcf, 0xe8, 0x06, 0x68, 0x75, 0xd2, 0x3a, 0x80, 0x32, 0xe0, 0x72, 0x72, 0x72, 0x00, 0x13, 0xc9, 0x60, 0xc0, 0x80, 0x01, 0x01, 0x5c, 0x29, 0x9f, 0x2c, 0x91, 0xdd, 0x07, 0x50, 0x6a, 0xe1, 0xcd, 0x9b, 0x37, 0x95, 0x51, 0xac, 0x84, 0xf7, 0xfe, 0x98, 0xf0, 0x09, 0x94, 0x8a, 0x9b, 0xa8, 0xd0, 0xf7, 0x14, 0x71, 0x51, 0x61, 0xc0, 0xe4, 0x39, 0xf6, 0xad, 0x45, 0x4a, 0xe3, 0x25, 0x39, 0xfc, 0xfe, 0x10, 0x6c, 0xdd, 0xba, 0x15, 0x30, 0x93, 0x0f, 0x30, 0xa3, 0x95, 0xe5, 0xac, 0x79, 0xe9, 0x21, 0x1e, 0xcc, 0x0e, 0x84, 0xf3, 0xe7, 0xcf, 0xb3, 0xe1, 0x8f, 0xe4, 0xdb, 0xb7, 0x6f, 0x0f, 0x4d, 0x9a, 0x34, 0xf1, 0x12, 0x61, 0x79, 0x70, 0x94, 0xfb, 0x86, 0x49, 0x78, 0xec, 0x88, 0xa5, 0x6f, 0x39, 0x12, 0x8c, 0x5f, 0x63, 0x82, 0xa3, 0x47, 0x8f, 0x32, 0xb8, 0x4b, 0x97, 0x2e, 0xd0, 0xab, 0x57, 0x12, 0x9e, 0x5f, 0x47, 0x91, 0x98, 0xc6, 0x53, 0x84, 0xb1, 0x85, 0x22, 0x27, 0xce, 0x09, 0x3b, 0xd7, 0xd2, 0x96, 0xed, 0xd2, 0x2a, 0xcf, 0x32, 0x7f, 0xdd, 0xba, 0x75, 0x8d, 0xfe, 0xfd, 0xfb, 0x1b, 0x1d, 0x3a, 0x74, 0x30, 0x30, 0x41, 0xd0, 0x46, 0xa3, 0x6c, 0x45, 0x67, 0xc1, 0x2c, 0x78, 0x83, 0xa7, 0x32, 0xcb, 0x7a, 0x9c, 0x30, 0x65, 0xf8, 0x2e, 0x5b, 0xb6, 0xcc, 0x26, 0x4e, 0xd9, 0xc1, 0x4e, 0xbe, 0x46, 0x8d, 0x1a, 0xb9, 0x70, 0x9c, 0x07, 0x73, 0x55, 0x6d, 0xf2, 0xba, 0x95, 0x50, 0xa9, 0x9e, 0x61, 0x8d, 0xf1, 0xf4, 0x68, 0x72, 0x3e, 0x51, 0x07, 0xc8, 0xf9, 0xae, 0x04, 0x53, 0xca, 0xb3, 0x5c, 0x28, 0x3d, 0x74, 0xc3, 0x86, 0x0d, 0xc6, 0xa2, 0x45, 0x8b, 0x5c, 0xa9, 0xa2, 0x93, 0x27, 0x4f, 0xb6, 0x05, 0xab, 0xa8, 0xa8, 0x48, 0x16, 0x65, 0x30, 0x2d, 0xd7, 0x2d, 0x77, 0x24, 0xff, 0xc6, 0x98, 0xcc, 0xc8, 0x17, 0x47, 0x27, 0x5f, 0x69, 0x15, 0xf8, 0xcb, 0x97, 0x2f, 0x0b, 0xf2, 0x8a, 0x15, 0x2b, 0x84, 0x0d, 0x3c, 0x9a, 0x8d, 0xa7, 0x4f, 0x9f, 0x0a, 0x9a, 0x2e, 0x10, 0x8b, 0x0e, 0xc0, 0xe1, 0xc2, 0xe0, 0x79, 0xbc, 0x7c, 0x4f, 0x73, 0x6e, 0xa9, 0xe1, 0xa3, 0x47, 0x8f, 0x36, 0x6e, 0xdd, 0xba, 0x25, 0xda, 0x8c, 0x19, 0xd4, 0x06, 0x2e, 0xce, 0x2e, 0x82, 0x33, 0x66, 0xcc, 0x18, 0x41, 0x73, 0x02, 0x4b, 0x96, 0x2c, 0x11, 0x7c, 0xa4, 0x9b, 0x72, 0x83, 0xe5, 0x22, 0x77, 0xc0, 0xac, 0x59, 0xb3, 0x64, 0x12, 0xeb, 0x0c, 0xd9, 0x1f, 0xaf, 0x7c, 0x62, 0x9b, 0x50, 0xc0, 0x4a, 0xd2, 0x4e, 0xc2, 0xe8, 0x70, 0xe0, 0x52, 0xbb, 0x76, 0x6d, 0x58, 0xbd, 0x7a, 0x35, 0x1b, 0x6b, 0xd1, 0x6f, 0xb8, 0x76, 0xed, 0x1a, 0xe0, 0x50, 0x03, 0x73, 0xe6, 0xcc, 0x81, 0xdc, 0xdc, 0x5c, 0xa6, 0x07, 0x73, 0xef, 0x61, 0xcd, 0x9a, 0x35, 0xd0, 0xa0, 0x41, 0x03, 0xf6, 0xed, 0x5d, 0x42, 0xe2, 0x90, 0x02, 0x98, 0x41, 0x2e, 0xec, 0x90, 0x8c, 0xfc, 0xfd, 0x5e, 0x41, 0x40, 0x60, 0xfb, 0xf6, 0xed, 0xa2, 0x4a, 0xeb, 0xc4, 0x77, 0xee, 0xdc, 0x59, 0xd4, 0x9d, 0x00, 0x9d, 0xb3, 0xe4, 0x42, 0x79, 0xd2, 0x49, 0x2b, 0x01, 0x3b, 0xaa, 0x4c, 0x6c, 0x41, 0x87, 0xa0, 0xd6, 0xad, 0x5b, 0x8b, 0xbd, 0x93, 0x60, 0x5c, 0x1f, 0xc0, 0xc0, 0x13, 0x3a, 0xb3, 0x59, 0x5c, 0x5c, 0x2c, 0x96, 0xa5, 0xc0, 0x20, 0x18, 0x78, 0x32, 0xb5, 0xf9, 0x42, 0x5f, 0x04, 0xc0, 0x60, 0x0a, 0x79, 0xe2, 0xc1, 0x93, 0xae, 0x51, 0x58, 0x58, 0x68, 0xcc, 0x9c, 0x39, 0x93, 0x7d, 0xd2, 0x90, 0x70, 0xfc, 0x47, 0xa9, 0xfa, 0xf2, 0xf0, 0x42, 0x76, 0x28, 0x9f, 0x9b, 0xce, 0x0f, 0x9c, 0x87, 0xce, 0x27, 0xb4, 0x7c, 0x05, 0x95, 0x9d, 0x3b, 0x77, 0xb2, 0x73, 0x11, 0xa7, 0xd1, 0x96, 0x56, 0x90, 0xc0, 0x0e, 0xb5, 0xf9, 0x51, 0xd6, 0x4a, 0x52, 0x87, 0x20, 0xf9, 0xf3, 0x8b, 0xf8, 0x91, 0x49, 0x4f, 0x1f, 0xe9, 0x84, 0x4b, 0x0d, 0xc3, 0xbd, 0xdb, 0x36, 0xa4, 0xc8, 0x8d, 0xee, 0xd4, 0xa9, 0x13, 0xeb, 0x18, 0x2f, 0x25, 0xb4, 0xe4, 0x00, 0xd9, 0xa8, 0x5f, 0xbf, 0xbe, 0x08, 0x24, 0x97, 0xc7, 0x85, 0x4d, 0x8c, 0x09, 0x13, 0x26, 0x18, 0xb7, 0x6f, 0xe3, 0x27, 0x63, 0x1d, 0x05, 0x8f, 0x36, 0xa3, 0x6b, 0xd7, 0xae, 0xb6, 0x73, 0x04, 0xad, 0x1b, 0x42, 0x1f, 0xd0, 0xa0, 0xb2, 0x6e, 0xdd, 0x3a, 0xa3, 0x69, 0xd3, 0xa6, 0x36, 0x9d, 0xb4, 0x13, 0x38, 0x3f, 0x0f, 0xe1, 0x50, 0x1b, 0xb8, 0x9a, 0xb4, 0x0e, 0xd8, 0xb1, 0x63, 0x87, 0xcd, 0xe9, 0x63, 0xc7, 0x8e, 0x05, 0x76, 0xaa, 0x3c, 0x31, 0x26, 0xbe, 0x30, 0xc7, 0x5d, 0xc8, 0xaf, 0x1c, 0x39, 0x72, 0x04, 0xf0, 0x04, 0xe6, 0xc9, 0x46, 0xcb, 0x8e, 0xec, 0xdb, 0xb7, 0xcf, 0x36, 0x56, 0x7b, 0x32, 0x97, 0x43, 0x42, 0x64, 0x37, 0x62, 0xb8, 0xa2, 0x08, 0xbb, 0x81, 0xa2, 0x8f, 0xa1, 0xe0, 0x21, 0x0b, 0xcd, 0x9a, 0x35, 0xf3, 0xbd, 0xf1, 0x2a, 0x87, 0xf1, 0x76, 0x35, 0x39, 0xb2, 0x0e, 0x70, 0x69, 0xce, 0x20, 0x02, 0x45, 0x20, 0x16, 0x97, 0xa1, 0x81, 0x3c, 0x7d, 0x45, 0x99, 0x32, 0x1d, 0x90, 0xe6, 0x8e, 0xcd, 0x74, 0x40, 0xa6, 0x03, 0xd2, 0x1c, 0x81, 0x34, 0x9b, 0xcf, 0x1c, 0x01, 0x69, 0xee, 0x80, 0xff, 0x03, 0x75, 0x4b, 0xcb, 0x8b, 0xae, 0x16, 0xbd, 0x22, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXTextPlainIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x03, 0xa8, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x30, 0x39, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x32, 0x3a, 0x34, 0x33, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x04, 0xa5, 0xd6, 0xff, 0x00, 0x00, 0x05, 0x7b, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x58, 0x5b, 0x48, 0x23, 0x57, 0x18, 0xce, 0x24, 0x31, 0xf1, 0x52, 0xad, 0x31, 0x78, 0xdf, 0xd6, 0x2a, 0xb6, 0x52, 0xad, 0x28, 0x8a, 0xe8, 0x82, 0x62, 0xe9, 0x45, 0xa5, 0x45, 0xf0, 0x82, 0x58, 0x41, 0xb4, 0xed, 0x83, 0x0f, 0xe2, 0x83, 0x6f, 0x8a, 0x20, 0xb8, 0x3e, 0x28, 0x05, 0x45, 0x7d, 0x12, 0x84, 0x82, 0x8a, 0x88, 0x22, 0x0a, 0xbe, 0x54, 0xb4, 0xc2, 0x3e, 0xe9, 0x82, 0x37, 0x50, 0x1a, 0x45, 0x2a, 0x5e, 0x8a, 0xdd, 0x20, 0x58, 0x35, 0xb1, 0x8a, 0x49, 0x4c, 0xd2, 0x2f, 0x7b, 0xe2, 0x38, 0x3b, 0xc6, 0x89, 0x33, 0x39, 0xac, 0x2b, 0x9d, 0xf3, 0x30, 0xfc, 0xe7, 0xfc, 0xdf, 0x7f, 0xff, 0xcf, 0x3f, 0x93, 0x30, 0x4e, 0xa7, 0x53, 0xf1, 0x94, 0x97, 0xf2, 0x29, 0x3b, 0xef, 0xf2, 0x5d, 0x0e, 0xe0, 0xb1, 0x2b, 0x28, 0x57, 0x40, 0xae, 0x80, 0x8f, 0x19, 0x90, 0x5b, 0xc8, 0xc7, 0x04, 0xfa, 0x2c, 0x2e, 0x57, 0xc0, 0xe7, 0x14, 0xfa, 0xa8, 0x40, 0xae, 0x80, 0x8f, 0x09, 0xf4, 0x59, 0x5c, 0xed, 0xb3, 0x06, 0x97, 0x02, 0xa7, 0x43, 0xf1, 0x7a, 0xc3, 0x61, 0x32, 0x3a, 0x9d, 0x0e, 0x2f, 0x9f, 0x86, 0x8c, 0x92, 0xf9, 0x30, 0x86, 0x89, 0x49, 0x55, 0x32, 0x94, 0x6a, 0x4f, 0x27, 0x80, 0xfd, 0x57, 0xf6, 0xdf, 0x5e, 0x58, 0x1e, 0x9e, 0x8b, 0xef, 0x5e, 0x68, 0xe3, 0x9f, 0xab, 0x1e, 0x8e, 0x17, 0x40, 0xd2, 0xc9, 0x83, 0xf9, 0xc8, 0x4b, 0xe2, 0x79, 0x1e, 0x98, 0x8d, 0xe2, 0xf0, 0x3c, 0x71, 0xee, 0x96, 0x4e, 0x05, 0x6c, 0x57, 0x6e, 0x9d, 0xfe, 0x21, 0x4c, 0xa0, 0x8e, 0xc1, 0xc6, 0xf2, 0xaf, 0xf3, 0xe2, 0x1f, 0xb7, 0x97, 0x41, 0x7a, 0xa5, 0xf6, 0x03, 0x17, 0xe0, 0xf2, 0xd4, 0x79, 0x65, 0x76, 0x1d, 0x5e, 0x8b, 0xa8, 0x96, 0x4b, 0x50, 0x60, 0xd1, 0x09, 0xc0, 0x6e, 0x71, 0xfb, 0xfa, 0xc5, 0xf7, 0xea, 0xec, 0x9f, 0xfc, 0x60, 0x6f, 0x73, 0xc6, 0xfe, 0xb2, 0xc7, 0xed, 0x66, 0xf6, 0x8f, 0x7e, 0x9f, 0x17, 0xba, 0x1a, 0xe6, 0xd5, 0xaf, 0xb6, 0xb5, 0x71, 0x1b, 0x88, 0xeb, 0x1b, 0xbc, 0x80, 0x67, 0x0f, 0x64, 0xd1, 0x69, 0xa1, 0x6b, 0xab, 0xdb, 0x9c, 0x4a, 0xe3, 0x4a, 0xff, 0x7d, 0x4b, 0xe5, 0x0a, 0xcd, 0xb5, 0xec, 0x37, 0x78, 0xb2, 0xf5, 0xe5, 0x49, 0x29, 0x80, 0x9b, 0x96, 0x60, 0x5d, 0xf4, 0xe8, 0x93, 0x5a, 0xeb, 0x0e, 0x8f, 0x0d, 0xd8, 0x23, 0x4c, 0xd4, 0x21, 0x9d, 0x00, 0x74, 0x1f, 0xb9, 0x3d, 0x0b, 0x89, 0x12, 0xaa, 0x80, 0x3e, 0xc1, 0x6d, 0x2e, 0xf4, 0x99, 0x10, 0x4c, 0x54, 0x00, 0x74, 0xee, 0x40, 0xd2, 0x37, 0x6a, 0xcb, 0x85, 0x42, 0xa9, 0x52, 0x7c, 0x22, 0x38, 0x1c, 0x9f, 0xa5, 0x29, 0x9f, 0xff, 0xac, 0xb9, 0xb6, 0x3a, 0x93, 0xbe, 0xa6, 0x63, 0x17, 0xa1, 0xd2, 0x51, 0xa4, 0x0d, 0x56, 0x64, 0x55, 0x7b, 0x57, 0xa5, 0xd2, 0x28, 0x32, 0x7e, 0xf0, 0x0e, 0x13, 0x55, 0x01, 0x3a, 0x2d, 0x24, 0xca, 0x24, 0x5d, 0xb0, 0x1c, 0x00, 0xdd, 0x7c, 0x8a, 0xd7, 0xf6, 0xe4, 0x2b, 0x40, 0xf9, 0x4a, 0x59, 0xce, 0x15, 0x27, 0x7f, 0x39, 0x90, 0x47, 0xd3, 0x6b, 0xd7, 0x93, 0x2c, 0xd3, 0xdf, 0x0e, 0xa3, 0x81, 0xc1, 0xe0, 0xd4, 0x7d, 0xac, 0xc4, 0x75, 0xa7, 0xbb, 0x18, 0x8a, 0xff, 0x8d, 0xfe, 0xf9, 0xd2, 0xfe, 0xfb, 0x2f, 0x56, 0x81, 0x2f, 0x6a, 0x7c, 0x4b, 0x7f, 0xdb, 0xac, 0xf9, 0xf4, 0x4b, 0x3a, 0xdf, 0xa1, 0x24, 0x11, 0x34, 0x5b, 0xc8, 0x68, 0x70, 0x08, 0x78, 0x0f, 0x7b, 0xe0, 0x1a, 0xff, 0xb8, 0xad, 0x0c, 0x95, 0x52, 0xd0, 0x0c, 0xe0, 0xb3, 0xaf, 0x54, 0xfa, 0x78, 0x21, 0x85, 0xe0, 0x02, 0x43, 0xc5, 0x6f, 0x56, 0x09, 0xcd, 0x16, 0x62, 0x95, 0xbe, 0x4b, 0x42, 0x28, 0x61, 0xef, 0xd2, 0x0f, 0xc9, 0xb6, 0xe4, 0x00, 0x24, 0xa7, 0x8e, 0x92, 0xa0, 0x5c, 0x01, 0x4a, 0x89, 0x94, 0xac, 0xe6, 0xff, 0x5d, 0x81, 0xeb, 0x37, 0xcb, 0xe1, 0x90, 0xfe, 0x6e, 0x5a, 0x5d, 0x5d, 0x6d, 0x6f, 0x6f, 0x3f, 0x3a, 0x3a, 0x92, 0x5c, 0x01, 0x05, 0x3e, 0x25, 0x24, 0xac, 0x9d, 0x9d, 0x9d, 0xb0, 0xb0, 0x30, 0x62, 0x35, 0x34, 0x34, 0x54, 0x82, 0x06, 0x22, 0x52, 0x55, 0x55, 0x05, 0x25, 0xc3, 0xc3, 0xc3, 0x92, 0x35, 0x48, 0x6c, 0xa1, 0xe0, 0xe0, 0xe0, 0xa4, 0xa4, 0xa4, 0x98, 0x98, 0x18, 0x98, 0xbf, 0xb8, 0xb8, 0x90, 0x9c, 0x3f, 0xe8, 0x51, 0xa9, 0x54, 0x91, 0x91, 0x91, 0x92, 0x35, 0x48, 0xac, 0x00, 0x49, 0xd8, 0xca, 0xca, 0x0a, 0x0c, 0xfb, 0xf9, 0xf9, 0x09, 0xe7, 0x0f, 0x3d, 0x26, 0x00, 0xb0, 0x5a, 0xad, 0x02, 0x5c, 0xaf, 0x2c, 0x0f, 0x15, 0x68, 0x6d, 0x6d, 0xcd, 0xce, 0xce, 0x2e, 0x28, 0x28, 0x58, 0x5a, 0x5a, 0xca, 0xc9, 0xc9, 0x09, 0x0c, 0x0c, 0x4c, 0x4d, 0x4d, 0xed, 0xea, 0xea, 0x12, 0xd5, 0xeb, 0x5b, 0x5b, 0x5b, 0xb5, 0xb5, 0xb5, 0x59, 0x59, 0x59, 0xc8, 0x31, 0x34, 0xa4, 0xa7, 0xa7, 0x4f, 0x4f, 0x4f, 0x73, 0xd3, 0x3c, 0x37, 0x37, 0x97, 0x9f, 0x9f, 0x0f, 0x43, 0xb9, 0xb9, 0xb9, 0xbd, 0xbd, 0xbd, 0x5c, 0xd6, 0xe9, 0xe9, 0x69, 0x49, 0x49, 0x09, 0x58, 0xf5, 0xf5, 0xf5, 0x93, 0x93, 0x93, 0xb0, 0x1e, 0x14, 0x14, 0x54, 0x54, 0x54, 0x64, 0x34, 0x1a, 0xb9, 0x30, 0x37, 0x7d, 0x37, 0xc4, 0xf8, 0xf8, 0x78, 0xc2, 0x83, 0x18, 0x57, 0xa0, 0xba, 0xba, 0x9a, 0x07, 0x16, 0xa8, 0x00, 0x22, 0x87, 0x2c, 0xc3, 0x30, 0x88, 0x21, 0x22, 0x22, 0x02, 0xb4, 0x56, 0xab, 0xdd, 0xdf, 0xdf, 0x67, 0x35, 0x34, 0x37, 0x37, 0xb3, 0xca, 0x11, 0x2a, 0x7b, 0x0e, 0x82, 0xa8, 0x05, 0x17, 0xc1, 0xab, 0xd5, 0xb7, 0xbf, 0x58, 0x2a, 0x2b, 0x2b, 0xb9, 0x30, 0x42, 0x7b, 0x68, 0xa1, 0xdd, 0xdd, 0x5d, 0xd2, 0x94, 0x3a, 0x9d, 0x6e, 0x60, 0x60, 0x60, 0x63, 0x63, 0xa3, 0xb4, 0xb4, 0x94, 0x18, 0x9b, 0x9f, 0x9f, 0xe7, 0xaa, 0x10, 0x08, 0x60, 0x6a, 0x6a, 0xaa, 0xbb, 0xbb, 0xfb, 0xf0, 0xf0, 0x10, 0x78, 0x8b, 0xc5, 0x42, 0x62, 0xe0, 0x5e, 0xd6, 0xf3, 0xf3, 0xf3, 0x99, 0x99, 0x19, 0xa2, 0x99, 0x17, 0x00, 0x44, 0x46, 0x46, 0x46, 0x88, 0xc5, 0xc2, 0xc2, 0x42, 0x58, 0xa9, 0xab, 0xab, 0xc3, 0x16, 0x09, 0xe5, 0x5a, 0x27, 0xb4, 0x87, 0x00, 0xc0, 0x48, 0x48, 0x48, 0x80, 0x00, 0x2a, 0x48, 0x40, 0xc7, 0xc7, 0xc7, 0xb8, 0x6a, 0x38, 0x69, 0x6a, 0x6a, 0xe2, 0xaa, 0x10, 0x08, 0x00, 0x30, 0x44, 0xdb, 0xd1, 0xd1, 0xd1, 0xd8, 0xd8, 0xd8, 0xd6, 0xd6, 0x96, 0x96, 0x96, 0x06, 0xf1, 0xce, 0xce, 0x4e, 0xae, 0x38, 0x68, 0x28, 0xc4, 0xf9, 0xdd, 0x00, 0xd6, 0xd7, 0xd7, 0x71, 0x8e, 0xb5, 0xbc, 0xbc, 0x0c, 0x18, 0xa6, 0x2d, 0xd9, 0x9a, 0x4c, 0x26, 0x9e, 0x86, 0xdb, 0x02, 0x11, 0x04, 0xf7, 0x89, 0x1e, 0x25, 0x5b, 0xbd, 0x5e, 0x9f, 0x92, 0x92, 0x82, 0x52, 0x20, 0xa3, 0x5c, 0xc0, 0x7d, 0xb4, 0xcd, 0x66, 0x2b, 0x2e, 0x2e, 0x9e, 0x9d, 0x9d, 0x05, 0x40, 0xa3, 0xd1, 0xa0, 0x91, 0x50, 0x04, 0xd0, 0x76, 0xbb, 0xfd, 0x3e, 0x91, 0xfb, 0xce, 0x33, 0x33, 0x33, 0xc1, 0x8a, 0x8b, 0x8b, 0x23, 0x80, 0xab, 0xab, 0xab, 0x90, 0x90, 0x10, 0x2e, 0xd8, 0xc3, 0x25, 0x66, 0xd9, 0x0b, 0x0b, 0x0b, 0x84, 0xc6, 0xad, 0xda, 0xdc, 0xdc, 0x04, 0xcd, 0x5e, 0x0f, 0x16, 0xe3, 0x91, 0x40, 0x03, 0x10, 0xef, 0x27, 0x26, 0x26, 0x90, 0xb3, 0xcb, 0xcb, 0xcb, 0xb2, 0xb2, 0x32, 0x8f, 0x48, 0xaf, 0x87, 0x08, 0x1e, 0x18, 0xf2, 0xf4, 0x08, 0x16, 0x0a, 0x60, 0x74, 0x74, 0x74, 0x6c, 0x6c, 0x6c, 0x6f, 0x6f, 0xaf, 0xa1, 0xa1, 0x01, 0xef, 0x5c, 0xc8, 0xa3, 0x23, 0x59, 0x2d, 0x38, 0x61, 0x33, 0xca, 0xa5, 0x01, 0xc0, 0x08, 0xc2, 0x33, 0x3c, 0x3c, 0x1c, 0x2d, 0xee, 0xef, 0xef, 0x8f, 0x41, 0x79, 0x72, 0x72, 0x82, 0x13, 0x54, 0x9f, 0x15, 0x07, 0x0d, 0x29, 0x32, 0xd9, 0xb8, 0x34, 0x01, 0xb0, 0x48, 0x42, 0xb0, 0x03, 0x90, 0x3d, 0x67, 0xf5, 0x08, 0xdd, 0x81, 0x5b, 0xd0, 0x1b, 0xaa, 0xa6, 0xa6, 0x06, 0xf2, 0x58, 0x66, 0xb3, 0x39, 0x36, 0x36, 0x96, 0xc7, 0xc5, 0x16, 0x93, 0x97, 0x00, 0x06, 0x07, 0x07, 0x09, 0x37, 0x23, 0x23, 0xa3, 0xa2, 0xa2, 0x82, 0x05, 0xe3, 0x8d, 0x31, 0x34, 0x34, 0x04, 0x0c, 0x2a, 0x13, 0x10, 0x10, 0xc0, 0xd3, 0x80, 0x66, 0xeb, 0xef, 0xef, 0x07, 0xf7, 0xe0, 0xe0, 0x00, 0xf3, 0x83, 0x70, 0x13, 0x13, 0x13, 0xcf, 0xce, 0xce, 0xa2, 0xa3, 0xa3, 0xc9, 0x16, 0xd3, 0x05, 0xd6, 0x89, 0x15, 0xf2, 0x14, 0xaa, 0x40, 0x4b, 0x4b, 0x4b, 0x5e, 0x5e, 0x5e, 0x54, 0x54, 0x14, 0x66, 0x62, 0x4f, 0x4f, 0x0f, 0xeb, 0x16, 0x74, 0x91, 0x3b, 0xcd, 0xf3, 0x80, 0x2d, 0x34, 0x06, 0x2e, 0x8a, 0x86, 0xc9, 0xb3, 0xb6, 0xb6, 0x06, 0x5f, 0xd1, 0xc1, 0x7d, 0x7d, 0x7d, 0xe8, 0x5d, 0x48, 0x91, 0x4a, 0x42, 0x50, 0xa9, 0xe4, 0x9b, 0x66, 0xc5, 0xc1, 0x45, 0x30, 0x44, 0x39, 0x86, 0x2f, 0xd2, 0xcf, 0xdd, 0xc2, 0xef, 0xb7, 0xec, 0x72, 0xa3, 0x61, 0x69, 0x32, 0x85, 0x16, 0x17, 0x17, 0xd9, 0x13, 0x09, 0x04, 0x1a, 0xcc, 0x60, 0x30, 0xdc, 0x9d, 0x1b, 0x12, 0x54, 0x09, 0x88, 0xf0, 0xa7, 0x10, 0xec, 0xe1, 0xe5, 0x87, 0x27, 0xa2, 0x1c, 0x1f, 0x1f, 0xdf, 0xde, 0xde, 0x2e, 0x2f, 0x2f, 0xc7, 0x0b, 0xe5, 0xad, 0xa0, 0x1f, 0xb6, 0x41, 0x8e, 0x93, 0x93, 0x93, 0x1f, 0x86, 0xf5, 0x01, 0xc5, 0x0b, 0x0e, 0x6f, 0x2e, 0x9e, 0x32, 0x74, 0x0e, 0x0f, 0xf3, 0x5e, 0x6d, 0xf9, 0x15, 0xc0, 0x47, 0x08, 0xbe, 0x2e, 0xf1, 0x9a, 0x24, 0x61, 0xa0, 0x71, 0x31, 0xd1, 0x79, 0x21, 0xbd, 0x57, 0x5b, 0xf9, 0x7f, 0xa1, 0xc7, 0x2e, 0x07, 0x7f, 0x96, 0x3d, 0xb6, 0x3f, 0xa2, 0xed, 0xcb, 0x01, 0x88, 0x4e, 0x19, 0x65, 0x01, 0xb9, 0x02, 0x94, 0x13, 0x2a, 0x5a, 0x9d, 0x5c, 0x01, 0xd1, 0x29, 0xa3, 0x2c, 0xf0, 0x1f, 0x15, 0xdc, 0xd7, 0x70, 0xbb, 0x15, 0xe8, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXTextPlainIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0e, 0xc4, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x09, 0x90, 0x54, 0xc5, 0xf5, 0xcd, 0xcc, 0x9e, 0xdc, 0x57, 0x40, 0x2e, 0x5d, 0xee, 0xfb, 0x08, 0x2b, 0x22, 0x87, 0x58, 0x12, 0xce, 0x70, 0xc7, 0x14, 0x10, 0x81, 0x18, 0x52, 0x21, 0x72, 0x05, 0x39, 0xb6, 0x8c, 0x86, 0xaa, 0x68, 0x82, 0x40, 0x15, 0x1a, 0x20, 0x80, 0x28, 0x90, 0x18, 0x24, 0xa6, 0x8a, 0x32, 0x51, 0x63, 0x89, 0x21, 0x01, 0x53, 0x50, 0x04, 0x10, 0xb1, 0x90, 0x02, 0x42, 0x20, 0xc5, 0xa9, 0x72, 0x83, 0xe1, 0x5a, 0x98, 0xdd, 0xd9, 0x99, 0xfd, 0x79, 0xaf, 0x67, 0xbb, 0xa7, 0xff, 0x39, 0xff, 0xff, 0x9d, 0xf9, 0xb3, 0x8b, 0xbf, 0xab, 0x66, 0xba, 0xfb, 0xf5, 0xbb, 0xfa, 0xbd, 0xee, 0xfe, 0xdd, 0xff, 0x77, 0xff, 0x1f, 0x50, 0x30, 0x80, 0x83, 0x10, 0x74, 0x80, 0xcb, 0x50, 0x53, 0x4b, 0xf0, 0xc2, 0xd0, 0x7b, 0xb0, 0x76, 0x76, 0x89, 0x4a, 0x89, 0xa4, 0x12, 0xe6, 0xbc, 0x96, 0xa7, 0x22, 0x00, 0xaa, 0xb4, 0x1c, 0x9e, 0x1f, 0x72, 0x57, 0xa1, 0x1f, 0x85, 0x33, 0x87, 0x63, 0x22, 0xcd, 0x71, 0x02, 0x94, 0x50, 0xb3, 0xb0, 0xce, 0x25, 0x55, 0x49, 0x4b, 0xae, 0x23, 0x08, 0x04, 0x02, 0x5a, 0x1c, 0x55, 0x5e, 0x45, 0xf0, 0xe5, 0x97, 0x5f, 0x8a, 0xc2, 0x8d, 0x1b, 0x37, 0xb2, 0x74, 0x61, 0x61, 0xa1, 0x80, 0xb1, 0x04, 0xaf, 0xcc, 0xec, 0xd9, 0xb3, 0x79, 0xd2, 0x32, 0x76, 0x5c, 0x69, 0xc7, 0x04, 0xaa, 0x3a, 0xa8, 0x95, 0x35, 0xce, 0x39, 0x26, 0xc8, 0x92, 0xf9, 0x50, 0xdb, 0xe1, 0xa1, 0x5d, 0x61, 0x08, 0x6e, 0x5d, 0x53, 0x60, 0xd2, 0x0b, 0xb9, 0xd0, 0xb4, 0x8d, 0x64, 0x6a, 0xad, 0x49, 0x78, 0xb3, 0x20, 0xf8, 0xe5, 0xb3, 0xe5, 0xda, 0x62, 0x45, 0xa5, 0xd2, 0x1b, 0xf3, 0x4a, 0x99, 0x80, 0x97, 0xc6, 0x85, 0x59, 0xbc, 0xea, 0xa7, 0x61, 0x90, 0xa5, 0x12, 0xb0, 0x0a, 0x5a, 0x89, 0xd5, 0x81, 0x1a, 0xdc, 0xd2, 0xa5, 0x4b, 0x99, 0xde, 0x4c, 0x4f, 0xcc, 0xcb, 0x8d, 0xb0, 0x7f, 0xff, 0xfe, 0x22, 0xcf, 0xea, 0x40, 0x85, 0x68, 0x0e, 0x41, 0x60, 0x95, 0x60, 0x12, 0xec, 0x22, 0x33, 0xe9, 0x64, 0x68, 0x2b, 0x8e, 0x95, 0x2d, 0x53, 0xf9, 0xad, 0xb2, 0xcc, 0x8c, 0xe8, 0x55, 0x4d, 0x4f, 0x8b, 0xa0, 0x6d, 0x14, 0x54, 0xbe, 0x6c, 0x7b, 0x0d, 0x86, 0x46, 0x65, 0xf3, 0x36, 0xe4, 0x43, 0x93, 0x02, 0xa9, 0x59, 0x6a, 0x19, 0x60, 0xde, 0xb2, 0x06, 0xfd, 0xc7, 0x67, 0x0b, 0x86, 0xc4, 0x78, 0xc4, 0x4f, 0x72, 0x18, 0x8b, 0xf7, 0x56, 0x46, 0x58, 0xfc, 0xe1, 0xba, 0x08, 0xfc, 0xf1, 0xa5, 0x08, 0x14, 0xdf, 0x30, 0xe0, 0xcc, 0x41, 0xe4, 0x83, 0x64, 0x41, 0xee, 0x1f, 0x1c, 0x97, 0x60, 0x5b, 0x96, 0x95, 0xb2, 0xec, 0xe6, 0x5f, 0xc6, 0x63, 0x5e, 0x26, 0xc7, 0x8e, 0xbb, 0x02, 0x57, 0xcc, 0x6e, 0x6c, 0x69, 0x22, 0xbb, 0x4c, 0xac, 0xf0, 0x92, 0x0a, 0xd8, 0xb9, 0x73, 0xa7, 0x15, 0x3d, 0x0c, 0x1b, 0x36, 0xcc, 0xb2, 0x5c, 0x75, 0xa9, 0x42, 0x4c, 0xe5, 0x99, 0x67, 0x9e, 0xa1, 0x7e, 0x21, 0xcc, 0x28, 0xa7, 0x6b, 0xd6, 0xac, 0xa9, 0x94, 0x96, 0x96, 0xaa, 0xca, 0xeb, 0xd4, 0xa9, 0x23, 0x70, 0xeb, 0xd7, 0xaf, 0xaf, 0x10, 0x8e, 0x4c, 0x93, 0xe0, 0x84, 0x68, 0x54, 0xc0, 0x7f, 0x44, 0xc5, 0xd3, 0x9c, 0x40, 0xce, 0xdf, 0xb8, 0x71, 0xc3, 0xb2, 0x7c, 0xcc, 0x98, 0x31, 0x4c, 0x70, 0xf5, 0x77, 0x72, 0xf5, 0xaf, 0x41, 0xd2, 0x66, 0x8a, 0x8e, 0xad, 0x54, 0xa8, 0xfe, 0x02, 0x4c, 0x87, 0x6b, 0xa3, 0xa1, 0x9a, 0x6c, 0x25, 0x0f, 0xd7, 0x3c, 0x6d, 0x65, 0x43, 0x4b, 0x13, 0xc9, 0x0c, 0xe4, 0x34, 0x9f, 0xea, 0x52, 0xfc, 0xfa, 0xb3, 0xf1, 0xc9, 0x85, 0x99, 0x10, 0x53, 0x01, 0x7d, 0x46, 0x65, 0xeb, 0x68, 0xba, 0x0c, 0x88, 0x57, 0xb8, 0xee, 0xb7, 0xe2, 0x64, 0x14, 0x37, 0x68, 0x6a, 0x7d, 0xc1, 0x51, 0x0d, 0x15, 0xac, 0x6f, 0x6b, 0xfe, 0x68, 0xdc, 0x37, 0xbb, 0x1e, 0x68, 0x50, 0x0d, 0xb3, 0xa6, 0x35, 0xd0, 0xa9, 0x6f, 0x02, 0x28, 0x8f, 0x81, 0xa5, 0x99, 0x2c, 0x7b, 0xb2, 0xd6, 0xd1, 0xb2, 0x1f, 0xe4, 0xb2, 0x99, 0xbf, 0xcd, 0x83, 0x07, 0x3b, 0x19, 0xeb, 0x6a, 0x29, 0xc0, 0x44, 0x69, 0x47, 0x60, 0x63, 0xb1, 0x8e, 0x58, 0x58, 0x23, 0xdf, 0xe7, 0x02, 0xe8, 0x7a, 0x7c, 0xe5, 0xca, 0x15, 0x4b, 0x1b, 0x74, 0xeb, 0xd6, 0xcd, 0xb2, 0x3c, 0x69, 0x3f, 0x18, 0x3e, 0x7c, 0xb8, 0x61, 0xfb, 0xb6, 0x0b, 0x14, 0x3e, 0x58, 0xb7, 0x6e, 0x1d, 0x9b, 0x73, 0x2f, 0x5e, 0xbc, 0x58, 0xcc, 0xbd, 0xb5, 0xaa, 0xcd, 0x9b, 0x37, 0x0f, 0x96, 0x2f, 0x5f, 0x0e, 0x7b, 0xf6, 0xec, 0x61, 0x45, 0x53, 0xa7, 0x4e, 0x15, 0xb8, 0xc5, 0xc5, 0xc5, 0x2c, 0x8d, 0x93, 0x06, 0x68, 0xd7, 0xae, 0x5d, 0x82, 0x54, 0xd6, 0xe4, 0xb1, 0xc7, 0x1e, 0x63, 0x59, 0x2c, 0x15, 0x60, 0x5e, 0x83, 0xd3, 0xa7, 0x4f, 0x2b, 0x8d, 0x1b, 0x37, 0x56, 0x56, 0xaf, 0x5e, 0xad, 0xe0, 0xe2, 0x40, 0x94, 0xcb, 0xb8, 0x3c, 0xcd, 0x63, 0x42, 0x4a, 0x70, 0xc2, 0x4c, 0xed, 0xda, 0xb5, 0xd9, 0x4c, 0xe1, 0xc8, 0x91, 0x23, 0x8c, 0x01, 0x21, 0xf2, 0xdf, 0xb5, 0x6b, 0xb8, 0x08, 0x93, 0xf2, 0x8c, 0x58, 0xca, 0xbf, 0xf3, 0xce, 0x3b, 0xac, 0x7c, 0xdc, 0xb8, 0x71, 0x2c, 0x66, 0x0c, 0xf0, 0x4f, 0x0c, 0xd7, 0x33, 0x67, 0xce, 0x84, 0x26, 0x4d, 0x9a, 0xc0, 0xed, 0xdb, 0xb7, 0x91, 0x4f, 0x3c, 0x60, 0x39, 0x4f, 0xb2, 0xd8, 0x69, 0x9e, 0x88, 0xfc, 0x9e, 0xac, 0x32, 0xa1, 0x51, 0x26, 0xed, 0x26, 0x32, 0x12, 0x9a, 0x4a, 0x98, 0xe8, 0x67, 0xa9, 0x64, 0xea, 0x25, 0x2f, 0xbf, 0x02, 0x5e, 0x5a, 0xdb, 0x48, 0x96, 0x18, 0x87, 0x8c, 0x0a, 0xcd, 0x60, 0xf2, 0x6c, 0xc2, 0x0c, 0x87, 0xc3, 0xe5, 0x59, 0x08, 0xc1, 0x38, 0xed, 0x8b, 0xef, 0xd7, 0x80, 0xbc, 0x9a, 0x1c, 0xcb, 0x7d, 0x5c, 0xa9, 0x26, 0x34, 0x7f, 0x63, 0x3e, 0xd0, 0x4f, 0x0e, 0x46, 0x30, 0xb9, 0x9c, 0xa7, 0xe9, 0xee, 0x5b, 0x2a, 0x82, 0x2b, 0x0f, 0x70, 0xc1, 0x8d, 0x1f, 0xd2, 0x4f, 0x79, 0x8d, 0x60, 0xa7, 0x3e, 0x2f, 0x87, 0xdf, 0x3f, 0xaf, 0xbe, 0xed, 0x4c, 0x37, 0xf7, 0xe4, 0x90, 0x57, 0x33, 0x00, 0x2f, 0xbe, 0xaf, 0x36, 0x86, 0x5c, 0x6e, 0x96, 0x4e, 0xc9, 0x30, 0xca, 0x9b, 0x05, 0x09, 0xd1, 0x36, 0x19, 0xad, 0x60, 0x8e, 0x6b, 0xe7, 0x26, 0x93, 0x96, 0xd6, 0x28, 0x5f, 0xa9, 0x26, 0x64, 0xc4, 0xd0, 0x2e, 0xac, 0xf4, 0x5e, 0x6a, 0x9a, 0x90, 0x6b, 0x0f, 0xfc, 0xf9, 0x95, 0x08, 0x7c, 0xbe, 0x23, 0x6a, 0xaa, 0xef, 0x03, 0xad, 0x82, 0xf0, 0xec, 0x7a, 0xcd, 0xc3, 0x07, 0xc4, 0xe6, 0x1e, 0x30, 0x23, 0x4c, 0xe6, 0x41, 0x2d, 0x9d, 0xeb, 0x0a, 0x68, 0x19, 0x39, 0xcd, 0xdf, 0xbd, 0x05, 0xf0, 0xdf, 0x03, 0x31, 0x68, 0xd8, 0x2c, 0x00, 0x2d, 0x71, 0x4d, 0x14, 0x74, 0xd9, 0x16, 0x32, 0x56, 0x01, 0xa7, 0x15, 0x36, 0xc3, 0x77, 0x59, 0x6f, 0x33, 0x76, 0xde, 0xc3, 0xfd, 0x0a, 0x78, 0x6f, 0x73, 0xb5, 0x44, 0xd7, 0x1e, 0x98, 0x3f, 0x7f, 0x3e, 0x5b, 0xe5, 0xd1, 0x03, 0x99, 0x11, 0x23, 0x46, 0xa8, 0xb9, 0x3a, 0xc8, 0x11, 0xbd, 0xfc, 0xc4, 0xc7, 0x01, 0x69, 0x1c, 0x95, 0x2f, 0xce, 0xe4, 0xb8, 0x45, 0x8b, 0x16, 0x6c, 0xe1, 0xb6, 0x63, 0xc7, 0x0e, 0x16, 0x23, 0x26, 0x5b, 0x10, 0x96, 0x97, 0xeb, 0x9f, 0xa5, 0x51, 0x19, 0x5f, 0x79, 0xca, 0x3c, 0x66, 0xcc, 0x98, 0x21, 0x68, 0x09, 0xa7, 0x7b, 0xf7, 0xee, 0x72, 0x31, 0x4b, 0x13, 0x9c, 0xff, 0xe4, 0xc2, 0xa3, 0x47, 0x8f, 0x0a, 0xf8, 0xa0, 0x41, 0x83, 0x44, 0xfa, 0xde, 0xbd, 0x7b, 0x32, 0x5a, 0x9c, 0x87, 0x0e, 0x52, 0x01, 0x20, 0xc6, 0x6d, 0xdb, 0xb6, 0x15, 0xc5, 0xf4, 0x08, 0x95, 0x60, 0xb4, 0x7a, 0x95, 0x03, 0xc1, 0x8c, 0x2a, 0x40, 0x37, 0xe5, 0xe7, 0xcc, 0x99, 0xa3, 0xd0, 0x52, 0x7b, 0xfa, 0xf4, 0xe9, 0x8c, 0x76, 0xd9, 0xb2, 0x65, 0x32, 0x29, 0x4b, 0xf7, 0xea, 0xd5, 0x8b, 0x95, 0x69, 0x0b, 0xfa, 0xf5, 0xeb, 0xa7, 0x82, 0x1b, 0x55, 0x94, 0x68, 0x54, 0xcb, 0x6e, 0x99, 0x09, 0x27, 0x38, 0x77, 0xee, 0x9c, 0x42, 0x35, 0xe7, 0x79, 0x19, 0x87, 0x31, 0x30, 0xa8, 0x00, 0x29, 0x4e, 0xf8, 0x67, 0xce, 0x9c, 0x61, 0xe8, 0xfc, 0xc6, 0xbf, 0x7c, 0x3f, 0x80, 0xf3, 0x49, 0x6b, 0x05, 0x70, 0xa9, 0xcf, 0xe5, 0xb8, 0x8a, 0xb7, 0x6f, 0xdf, 0xae, 0x5c, 0xbd, 0x7a, 0xd5, 0x15, 0xad, 0x5d, 0x22, 0xc3, 0x0b, 0x99, 0xb6, 0x53, 0x21, 0x33, 0x34, 0x68, 0xd5, 0x0c, 0x86, 0xd3, 0xe9, 0xaa, 0xac, 0xb0, 0xd6, 0x8c, 0xae, 0x87, 0x51, 0x2d, 0xa3, 0x4c, 0xe5, 0xab, 0x7d, 0x05, 0x0c, 0xfb, 0x40, 0xa6, 0xac, 0xe9, 0x46, 0x6e, 0xb5, 0xf7, 0x80, 0x5f, 0x01, 0x37, 0x6e, 0x4f, 0x25, 0x8d, 0xef, 0x81, 0x54, 0x5a, 0xd3, 0x0d, 0x2f, 0xdf, 0x03, 0x6e, 0xac, 0x96, 0x4a, 0x1a, 0xcf, 0x3d, 0x40, 0x77, 0x23, 0x52, 0x19, 0x0c, 0xe7, 0x42, 0x56, 0x02, 0xce, 0xfd, 0xbb, 0x1c, 0xd6, 0x2f, 0x50, 0xdf, 0x26, 0x34, 0xc3, 0x1f, 0x33, 0x3b, 0x07, 0xfa, 0x8e, 0x4d, 0x88, 0xd8, 0x58, 0x54, 0x0a, 0x67, 0x8e, 0xc4, 0xd8, 0x0a, 0x6c, 0xe9, 0x3f, 0x9c, 0xdf, 0x46, 0x34, 0x92, 0xe3, 0xd8, 0x03, 0x27, 0x3e, 0xc5, 0x27, 0xe8, 0x36, 0xc3, 0x7f, 0xf6, 0xa9, 0x71, 0x6f, 0x56, 0xdc, 0xd0, 0x4d, 0xe5, 0x64, 0x31, 0x61, 0x1e, 0x9b, 0x4a, 0xdd, 0xbb, 0x1d, 0x9f, 0x5a, 0x77, 0x7c, 0x24, 0x04, 0x23, 0xa6, 0xe7, 0x40, 0xa4, 0x44, 0x81, 0xd7, 0x7e, 0x16, 0xf7, 0xc8, 0x84, 0x9f, 0xe7, 0x42, 0xf3, 0xb6, 0x41, 0xf8, 0xec, 0xef, 0x51, 0xd8, 0xf3, 0x6e, 0x19, 0x84, 0x8b, 0xd3, 0x3f, 0x0d, 0x77, 0x5e, 0x81, 0x3b, 0xf1, 0x9a, 0xd6, 0x6b, 0x1c, 0x04, 0xba, 0x13, 0x1d, 0x09, 0x27, 0xee, 0x50, 0x37, 0x78, 0x20, 0xc0, 0x60, 0x8d, 0x5a, 0xc4, 0x61, 0xe1, 0xe2, 0x38, 0xee, 0x67, 0xdb, 0x62, 0xf0, 0xe9, 0x47, 0x65, 0x70, 0xfb, 0x7a, 0xa2, 0x42, 0x7c, 0xe7, 0x08, 0x61, 0x34, 0x6c, 0x16, 0x84, 0x1f, 0x2c, 0x8a, 0x6f, 0x00, 0x8c, 0x53, 0xd8, 0xff, 0x77, 0x5c, 0x81, 0x58, 0x24, 0xae, 0x44, 0xae, 0xc5, 0xc3, 0x89, 0x5a, 0x75, 0xe3, 0x15, 0x88, 0xc6, 0x37, 0x23, 0xc2, 0x7b, 0x2b, 0xf5, 0x5b, 0x5b, 0x2e, 0x9c, 0x2c, 0x17, 0x5a, 0x52, 0xda, 0xb3, 0x0a, 0x3c, 0xb9, 0x30, 0x17, 0x36, 0x14, 0x95, 0xc0, 0x90, 0xa7, 0xf5, 0xdb, 0x66, 0xb8, 0x46, 0x5d, 0x06, 0x84, 0x80, 0x6e, 0xee, 0x7e, 0xbf, 0x28, 0x6e, 0xd5, 0x1f, 0x2f, 0xcb, 0x83, 0xfd, 0x1f, 0x46, 0xe1, 0xd4, 0xe7, 0x31, 0xd6, 0xe4, 0x08, 0xaf, 0x73, 0xbf, 0x84, 0xed, 0x3a, 0xf4, 0x76, 0xdc, 0x15, 0xb9, 0xa8, 0xca, 0x3f, 0x89, 0x8e, 0xe0, 0x73, 0x8a, 0x17, 0xc7, 0xc6, 0xf7, 0x3e, 0xcf, 0x58, 0x99, 0x07, 0x0f, 0x75, 0x31, 0x57, 0xe6, 0x95, 0xa7, 0x4b, 0xe0, 0x7f, 0x97, 0xe2, 0x96, 0x77, 0x7a, 0x17, 0x5a, 0x68, 0xac, 0x49, 0x98, 0x4b, 0xd3, 0x20, 0x56, 0xd5, 0xac, 0xa7, 0x15, 0xc0, 0x9b, 0x70, 0x29, 0x0f, 0x9e, 0x56, 0x20, 0xaf, 0x96, 0xb1, 0xfe, 0x74, 0xbd, 0x78, 0x69, 0x6c, 0x18, 0x2e, 0x9e, 0x4a, 0x8c, 0x52, 0xc6, 0x98, 0x7a, 0xa8, 0xeb, 0x25, 0xa5, 0x9b, 0x27, 0x2d, 0x7b, 0xdf, 0x8f, 0xc2, 0xd6, 0xd7, 0x2b, 0x86, 0x26, 0xbd, 0x2e, 0xd0, 0xb4, 0x75, 0x10, 0xe6, 0xbe, 0xa1, 0x7f, 0xaa, 0x63, 0x80, 0x2a, 0x40, 0x69, 0xf3, 0x40, 0xb4, 0x4c, 0xc8, 0x10, 0x89, 0xfe, 0xe3, 0xb3, 0xa0, 0x7e, 0x13, 0x63, 0x91, 0x4d, 0x1e, 0x72, 0xae, 0x3c, 0x31, 0x76, 0xed, 0x01, 0xa1, 0x95, 0xcb, 0xc4, 0x17, 0xc7, 0xca, 0x71, 0x97, 0xba, 0x82, 0xa7, 0x34, 0x6c, 0xec, 0x50, 0xb4, 0x90, 0x91, 0xb1, 0x0a, 0x58, 0xe8, 0xe4, 0xa8, 0xc8, 0xd8, 0x9f, 0x8e, 0x58, 0x64, 0x16, 0xd9, 0xaf, 0x40, 0x66, 0xed, 0x9f, 0xe4, 0xc8, 0x48, 0xa6, 0x95, 0xb3, 0x23, 0xdf, 0x6f, 0x42, 0x76, 0xac, 0x94, 0x4e, 0x9c, 0x6a, 0xef, 0x81, 0xc4, 0xa4, 0xdc, 0xa1, 0x99, 0xf0, 0xd9, 0x19, 0x9c, 0x3d, 0x7b, 0x96, 0x51, 0x3d, 0xf1, 0xc4, 0x13, 0x0e, 0xa9, 0x13, 0xe8, 0xf4, 0x34, 0x28, 0x1a, 0x8d, 0x42, 0x28, 0x14, 0x4a, 0x00, 0x1d, 0xa4, 0x5c, 0x5f, 0xc8, 0xe4, 0xc7, 0x50, 0x6e, 0x17, 0xe9, 0xf8, 0xf0, 0x0f, 0x1a, 0x34, 0x68, 0x00, 0x17, 0x2e, 0x5c, 0x80, 0x66, 0xcd, 0x9a, 0x39, 0x50, 0x3b, 0x81, 0xea, 0xba, 0x09, 0x91, 0xd2, 0x0b, 0x16, 0x2c, 0x48, 0x70, 0x72, 0x91, 0xc2, 0x33, 0x50, 0xf4, 0x94, 0xd4, 0xb5, 0xf2, 0x24, 0x52, 0x57, 0x01, 0x72, 0x27, 0x59, 0x97, 0x7e, 0x43, 0x87, 0x0e, 0x15, 0x69, 0xda, 0xbf, 0x6d, 0x37, 0xdc, 0xbd, 0x7b, 0x17, 0xb2, 0xb3, 0xb3, 0x05, 0x2d, 0xf1, 0x3a, 0x74, 0xe8, 0x90, 0x8a, 0xfc, 0xa9, 0xa7, 0x9e, 0x12, 0xe5, 0xeb, 0xd7, 0xaf, 0x57, 0x95, 0xe1, 0xc1, 0x2e, 0x56, 0x46, 0xfb, 0xc0, 0xb9, 0x2e, 0x93, 0x26, 0x4d, 0x52, 0xe1, 0x88, 0x0c, 0x5a, 0x40, 0x17, 0xe8, 0x01, 0x37, 0x22, 0x28, 0xc1, 0x60, 0x50, 0xd9, 0xbc, 0x79, 0xb3, 0x82, 0x6e, 0x66, 0xf9, 0xb7, 0xde, 0x7a, 0x4b, 0x85, 0x8b, 0x1e, 0x60, 0x70, 0x15, 0x10, 0x33, 0x1f, 0x7f, 0xfc, 0xb1, 0xd2, 0xb2, 0x65, 0x4b, 0x16, 0xe3, 0xc6, 0x73, 0x86, 0x43, 0xfc, 0xe4, 0x40, 0x8f, 0x60, 0x47, 0x8e, 0x1c, 0xc9, 0xca, 0x56, 0xae, 0x5c, 0x29, 0x17, 0x29, 0xcf, 0x3d, 0xf7, 0x9c, 0xa0, 0xa1, 0x67, 0xce, 0x79, 0x79, 0x79, 0x86, 0x72, 0x88, 0x48, 0xcd, 0xb5, 0x82, 0xcd, 0xc2, 0x85, 0x0b, 0x75, 0x04, 0xa4, 0x80, 0x56, 0x09, 0xb3, 0x0a, 0x10, 0x9b, 0xb7, 0xdf, 0x7e, 0x5b, 0xa1, 0xbd, 0xe6, 0xf4, 0x14, 0xbf, 0xa0, 0xa0, 0x40, 0x47, 0x5b, 0x21, 0x8a, 0xc1, 0xb5, 0x15, 0xa0, 0x32, 0x92, 0xf5, 0xf8, 0xe3, 0x8f, 0x33, 0x34, 0xbe, 0xf5, 0x00, 0xfb, 0x0a, 0xcb, 0xcb, 0x7f, 0xba, 0x26, 0x84, 0x84, 0x95, 0x0e, 0xf5, 0xea, 0xd5, 0x83, 0x29, 0x53, 0xa6, 0x40, 0xa3, 0x46, 0x8d, 0x00, 0xf7, 0x4c, 0xb0, 0x66, 0xe0, 0x86, 0x29, 0x1f, 0xdd, 0x88, 0x1f, 0x05, 0xdc, 0xbe, 0xa0, 0x63, 0x63, 0x59, 0x81, 0x47, 0x1f, 0x7d, 0x94, 0x11, 0x90, 0x12, 0x14, 0x66, 0xcd, 0x9a, 0xc5, 0xe2, 0x64, 0x7f, 0xb7, 0x6e, 0xdd, 0xc2, 0x2d, 0x64, 0x41, 0xa0, 0x33, 0xec, 0x63, 0xc7, 0x8e, 0x85, 0xba, 0x75, 0xeb, 0x26, 0x23, 0x71, 0x5f, 0x2e, 0xbb, 0x83, 0xa7, 0x79, 0x13, 0x42, 0xae, 0xcc, 0x95, 0x14, 0xe3, 0x71, 0x10, 0x5e, 0xac, 0xa0, 0x42, 0x02, 0x2e, 0xe3, 0xe0, 0x01, 0x7c, 0x86, 0xd3, 0xb4, 0x69, 0x53, 0x55, 0x39, 0x6f, 0xc3, 0x84, 0x4b, 0x61, 0xdf, 0xbe, 0x7d, 0xaa, 0x72, 0xce, 0xa3, 0x4d, 0x9b, 0x36, 0xac, 0x9c, 0xe7, 0x29, 0xe6, 0xfb, 0x2c, 0x38, 0x8c, 0x21, 0x48, 0x7f, 0x96, 0x17, 0x32, 0xc4, 0x43, 0x3a, 0x7d, 0xb8, 0x79, 0xf3, 0xa6, 0x1e, 0x28, 0x41, 0x2e, 0x5e, 0xbc, 0xc8, 0x2e, 0x72, 0xe7, 0xcf, 0x9f, 0x07, 0xdc, 0xad, 0x22, 0x95, 0xc4, 0x93, 0x7d, 0xfb, 0xf6, 0x65, 0xc3, 0xa7, 0xae, 0xa0, 0x02, 0xa0, 0x95, 0xab, 0xcd, 0xab, 0xe8, 0xa4, 0xca, 0xb0, 0x24, 0x1e, 0xce, 0x51, 0x3a, 0x76, 0xec, 0xc8, 0x2c, 0x84, 0x43, 0x97, 0xb2, 0x77, 0xef, 0x5e, 0x2d, 0x4a, 0x95, 0xca, 0xeb, 0x46, 0xa1, 0x21, 0x43, 0x86, 0xa8, 0xdc, 0x3b, 0x7e, 0xfc, 0xf8, 0x2a, 0xa5, 0xb0, 0x56, 0x19, 0xd7, 0x53, 0x09, 0x95, 0x1b, 0x33, 0x98, 0xb1, 0x1c, 0x85, 0x32, 0xa8, 0x97, 0x6d, 0xd1, 0x7e, 0x05, 0x6c, 0x9b, 0x2a, 0x4d, 0x88, 0xbe, 0x07, 0xd2, 0x64, 0x58, 0xdb, 0x6c, 0xab, 0xfd, 0x28, 0x64, 0xbb, 0xa6, 0x55, 0x14, 0xb1, 0xda, 0x77, 0x81, 0x2a, 0x6a, 0x57, 0xdb, 0x6a, 0xf9, 0x0e, 0xb0, 0x6d, 0xaa, 0xf4, 0x20, 0xfa, 0x0e, 0x48, 0x8f, 0x5d, 0x6d, 0x73, 0xf5, 0x1d, 0x60, 0xdb, 0x54, 0xe9, 0x41, 0xf4, 0x1d, 0x90, 0x1e, 0xbb, 0xda, 0xe6, 0xea, 0x3b, 0xc0, 0xb6, 0xa9, 0xd2, 0x83, 0xe8, 0x3b, 0x20, 0x3d, 0x76, 0xb5, 0xcd, 0xd5, 0xf2, 0x86, 0x96, 0x6d, 0x2e, 0x36, 0x10, 0x2f, 0x9d, 0x56, 0xe0, 0xfa, 0x85, 0x72, 0x30, 0xda, 0x3b, 0x61, 0x83, 0x5c, 0x85, 0x12, 0xc4, 0x87, 0x38, 0x75, 0x1b, 0x05, 0xa0, 0xa0, 0xab, 0x75, 0xfb, 0x39, 0x79, 0xb0, 0x1c, 0xfe, 0xb6, 0x21, 0x02, 0xf8, 0x0e, 0x49, 0xa8, 0x5d, 0x3f, 0x00, 0x7d, 0x46, 0x67, 0xc3, 0x77, 0xa6, 0x78, 0x56, 0x65, 0x95, 0xce, 0x66, 0x19, 0x4f, 0x16, 0x62, 0x7b, 0xde, 0x8d, 0xc2, 0x47, 0xeb, 0xcd, 0x77, 0xd7, 0x98, 0x29, 0x97, 0x0c, 0xde, 0x73, 0x50, 0x16, 0x4c, 0x7c, 0xde, 0x7c, 0x93, 0xe3, 0x6f, 0xa6, 0x95, 0x30, 0xa7, 0xcb, 0x7c, 0xe6, 0xff, 0x2e, 0x1f, 0x1a, 0x3f, 0x98, 0x86, 0x0d, 0x57, 0xb2, 0x10, 0x07, 0x69, 0xeb, 0x26, 0xe4, 0x80, 0x91, 0x15, 0xea, 0x8d, 0x2b, 0xc6, 0xb7, 0x86, 0xad, 0x68, 0xec, 0x94, 0x25, 0xe3, 0x5b, 0x9e, 0xd8, 0xcf, 0x29, 0xd8, 0x29, 0x06, 0x30, 0x51, 0x98, 0x81, 0x84, 0x27, 0xfd, 0x91, 0xef, 0x51, 0xe6, 0xf5, 0x7b, 0x78, 0x58, 0x16, 0x3c, 0xb9, 0x30, 0xd1, 0x72, 0xe5, 0xfd, 0x9a, 0x1c, 0x87, 0x62, 0xed, 0xde, 0xcd, 0x7d, 0x7f, 0x8d, 0x02, 0xbd, 0xc7, 0x93, 0x87, 0xf0, 0x9d, 0xf4, 0x38, 0x96, 0xf3, 0xf7, 0x22, 0xf6, 0xc4, 0x01, 0x5a, 0x43, 0xd1, 0x9b, 0x3b, 0xdc, 0x84, 0xbc, 0x1a, 0x6a, 0x3a, 0x79, 0x43, 0x79, 0x18, 0xf7, 0x71, 0x2f, 0x99, 0x10, 0x86, 0x58, 0xcc, 0xda, 0x29, 0xda, 0xb7, 0x88, 0x68, 0xf5, 0xe8, 0xd0, 0x3b, 0x04, 0x3f, 0x5a, 0x92, 0xab, 0x05, 0xa7, 0x2d, 0xef, 0xc9, 0x10, 0xd4, 0xe6, 0xdb, 0xea, 0xad, 0x0f, 0xad, 0x7b, 0xba, 0x13, 0xdb, 0xac, 0x9d, 0x9a, 0xae, 0x75, 0xf7, 0x04, 0xdf, 0x2c, 0xec, 0x50, 0xa1, 0x44, 0xa7, 0x72, 0x6d, 0x30, 0xb7, 0x8d, 0xc3, 0xad, 0x40, 0x4f, 0x2e, 0xc2, 0xc9, 0x94, 0xb3, 0x3b, 0x04, 0x25, 0xe3, 0xa3, 0x2d, 0x97, 0xb7, 0x97, 0xf3, 0xb2, 0x54, 0xbd, 0x86, 0x86, 0xf3, 0xab, 0x6c, 0xac, 0x6e, 0x52, 0x95, 0xe5, 0xe6, 0xd3, 0x3b, 0xb6, 0x80, 0xef, 0x00, 0xc7, 0x26, 0x4b, 0x2d, 0xc1, 0x7d, 0xed, 0x80, 0x6c, 0x83, 0x6b, 0xc2, 0xf5, 0xf3, 0xc9, 0xe7, 0xa1, 0x26, 0x1b, 0x2a, 0x52, 0x6b, 0xf9, 0x0a, 0x6e, 0x9e, 0xcc, 0x82, 0x64, 0xcd, 0x69, 0x25, 0x4c, 0x1b, 0xed, 0xe9, 0x00, 0x63, 0x19, 0x7b, 0x81, 0x38, 0x40, 0xcc, 0xe4, 0xbd, 0x56, 0x9f, 0x6e, 0x8d, 0xc2, 0xf1, 0xfd, 0xf1, 0x99, 0x0f, 0x6d, 0x61, 0x6c, 0xd1, 0x21, 0x08, 0xad, 0x7b, 0x84, 0x20, 0x37, 0xfe, 0xfa, 0x7a, 0x99, 0xad, 0x61, 0xba, 0x51, 0xcb, 0x20, 0x5c, 0xf9, 0x42, 0x6d, 0xf0, 0x3f, 0x2d, 0x8e, 0xc0, 0xc3, 0xc3, 0x43, 0xd0, 0xa9, 0x6f, 0x08, 0xdf, 0x6a, 0x17, 0x80, 0x1b, 0x97, 0x15, 0x38, 0x7b, 0x34, 0x06, 0xc7, 0xf6, 0xc6, 0x40, 0x3b, 0x5b, 0xeb, 0xd4, 0x37, 0x0b, 0x7e, 0xf8, 0x2b, 0x03, 0x2f, 0x1a, 0x4a, 0x73, 0x07, 0xf4, 0xfc, 0x22, 0xfc, 0xeb, 0xef, 0x85, 0x2b, 0x7d, 0x1e, 0xb1, 0x68, 0x53, 0x3e, 0x7b, 0x11, 0x57, 0xb2, 0x2a, 0x97, 0xe1, 0x6e, 0xae, 0xe5, 0x53, 0xc3, 0x50, 0x7c, 0xd3, 0x7a, 0x6a, 0x6a, 0xc4, 0x27, 0x18, 0x0c, 0xc0, 0xa4, 0x5f, 0xe4, 0x40, 0xb7, 0x81, 0x89, 0x99, 0x96, 0x11, 0x5e, 0x65, 0x61, 0x9e, 0x3b, 0x80, 0xde, 0xf0, 0xf7, 0xaf, 0xbf, 0x94, 0xc1, 0xd9, 0x23, 0xd8, 0x03, 0x2a, 0x8e, 0x30, 0xda, 0xa9, 0x44, 0x30, 0x14, 0x80, 0x16, 0xed, 0x83, 0x50, 0x38, 0x2c, 0x04, 0x8f, 0x7c, 0xd7, 0x59, 0xc7, 0x8d, 0xe0, 0xb9, 0xd4, 0xed, 0x7f, 0x28, 0x83, 0x43, 0xff, 0x8c, 0x82, 0x76, 0x51, 0xc8, 0x65, 0xd7, 0xac, 0x13, 0x80, 0x8e, 0xd8, 0x2b, 0xba, 0xf6, 0x0f, 0x41, 0x7b, 0x5c, 0x0b, 0xd0, 0xfd, 0x26, 0x2f, 0x82, 0xe7, 0x0e, 0xf0, 0xa2, 0x52, 0xd5, 0x49, 0xc6, 0x7d, 0x7d, 0x11, 0xae, 0x0e, 0x8e, 0xf0, 0x1d, 0x90, 0x61, 0x2f, 0xf9, 0x0e, 0xf0, 0x1d, 0x90, 0x61, 0x0b, 0x64, 0x58, 0xbc, 0xdf, 0x03, 0x7c, 0x07, 0x64, 0xd8, 0x02, 0x19, 0x16, 0xef, 0xf7, 0x00, 0xdf, 0x01, 0x19, 0xb6, 0x00, 0x8a, 0x4f, 0x76, 0x6a, 0x26, 0x9d, 0x1a, 0x7a, 0xde, 0x03, 0xd6, 0xae, 0x5d, 0x2b, 0x0e, 0x10, 0xf2, 0x83, 0x84, 0x14, 0xd3, 0x81, 0x2c, 0xaf, 0xc3, 0x9d, 0x3b, 0x77, 0x98, 0x2e, 0x74, 0x6a, 0x93, 0x74, 0xa0, 0xe3, 0x49, 0x5e, 0x07, 0x67, 0x6b, 0xfa, 0x14, 0x68, 0x37, 0x7a, 0xf4, 0x68, 0xa0, 0xf3, 0x57, 0xf8, 0xf1, 0x1d, 0x38, 0x7c, 0xf8, 0x30, 0x9c, 0x3c, 0x79, 0x92, 0x71, 0x8d, 0x44, 0x12, 0xcf, 0x7a, 0x53, 0x20, 0xc6, 0x16, 0x0b, 0xfc, 0x76, 0x0f, 0xe0, 0x79, 0x48, 0xd8, 0xbd, 0x7b, 0x37, 0xf4, 0xee, 0xdd, 0x9b, 0x7d, 0x6a, 0xc7, 0x16, 0x61, 0x0a, 0x91, 0x6c, 0xdf, 0x8a, 0xc0, 0x77, 0x4a, 0x33, 0x63, 0xd1, 0x79, 0x45, 0x3c, 0x79, 0x07, 0xed, 0xdb, 0xb7, 0x07, 0x82, 0x1d, 0x3b, 0x76, 0x0c, 0x0e, 0x1c, 0x38, 0x00, 0x64, 0xc0, 0xae, 0x5d, 0xbb, 0x42, 0x8f, 0x1e, 0x3d, 0x00, 0xbf, 0x8b, 0x68, 0x4b, 0x45, 0x3c, 0x11, 0x08, 0x2b, 0x56, 0xac, 0x60, 0xb8, 0x78, 0x28, 0x14, 0xb6, 0x6d, 0xdb, 0x66, 0x8b, 0x8e, 0x3e, 0xbd, 0x84, 0xe7, 0xd7, 0x00, 0xdf, 0x9a, 0xcc, 0x86, 0x0f, 0x3a, 0x22, 0xdc, 0xbc, 0x79, 0x73, 0xc0, 0x93, 0x7e, 0x30, 0x70, 0xe0, 0x40, 0x5b, 0x67, 0x33, 0xbf, 0xfa, 0xea, 0x2b, 0xa0, 0x03, 0xd7, 0x3c, 0x64, 0x65, 0x65, 0x41, 0x87, 0x0e, 0x1d, 0x2c, 0x4f, 0x8e, 0x53, 0xdd, 0xa9, 0xc1, 0x50, 0xbd, 0xf3, 0xf3, 0xf3, 0xd9, 0x67, 0x9f, 0xb8, 0x0d, 0xf6, 0xef, 0xdf, 0xcf, 0xe0, 0x3d, 0x7b, 0xf6, 0x84, 0x3e, 0x7d, 0xfa, 0x70, 0xb6, 0xc9, 0x63, 0xed, 0xb9, 0x31, 0xa3, 0xfc, 0xa5, 0x4b, 0x97, 0x14, 0xec, 0xa2, 0x74, 0x4b, 0x51, 0xfc, 0xe8, 0xe3, 0x93, 0x72, 0x5e, 0x9b, 0x9e, 0x30, 0x61, 0x82, 0x11, 0x2b, 0x15, 0x8c, 0x9f, 0xd6, 0x25, 0x5a, 0xa3, 0x77, 0x6b, 0xab, 0x90, 0x31, 0xb3, 0x6a, 0xd5, 0x2a, 0x9d, 0x4c, 0xfa, 0x02, 0x15, 0xff, 0xf0, 0x25, 0xd7, 0xa1, 0x4b, 0x97, 0x2e, 0x0a, 0x36, 0x08, 0x2d, 0xb9, 0xc8, 0x77, 0xee, 0xdc, 0x59, 0xc7, 0x87, 0x68, 0x8d, 0x4e, 0xfd, 0x72, 0xa2, 0x2d, 0x5b, 0xb6, 0xe8, 0x68, 0xf0, 0xe3, 0x54, 0x3a, 0x18, 0xd7, 0x01, 0x1b, 0x16, 0x27, 0xb5, 0x8c, 0x75, 0x07, 0x0d, 0xad, 0xb0, 0xb5, 0x67, 0x58, 0xdf, 0x7c, 0xf3, 0x4d, 0x15, 0xfa, 0xa9, 0x53, 0xa7, 0x94, 0x1a, 0x35, 0x6a, 0x08, 0xa5, 0xe8, 0xcc, 0xb6, 0x55, 0x70, 0xea, 0x00, 0xe2, 0x15, 0x0e, 0x87, 0x15, 0x6c, 0x75, 0x3a, 0xb6, 0x64, 0x70, 0x3c, 0x55, 0x2c, 0x64, 0x6f, 0xda, 0xb4, 0x49, 0x87, 0x63, 0x04, 0xe0, 0xef, 0x15, 0x4f, 0xe6, 0x00, 0x4e, 0xcb, 0x5f, 0x98, 0x4e, 0xf8, 0x74, 0x9e, 0x9d, 0x9f, 0xcf, 0xa5, 0x72, 0xb2, 0x07, 0x77, 0x00, 0xbe, 0x83, 0x41, 0xc1, 0x9e, 0xc9, 0xc9, 0x4c, 0x63, 0xd7, 0x17, 0x61, 0xba, 0x68, 0x4e, 0x9b, 0x36, 0x0d, 0xe5, 0x25, 0x02, 0x0d, 0x01, 0x7c, 0x48, 0x21, 0x28, 0x75, 0x73, 0x1a, 0x2a, 0x52, 0x15, 0x76, 0xed, 0xda, 0x05, 0xad, 0x5a, 0xb5, 0x62, 0x27, 0xa9, 0xe5, 0x0b, 0x38, 0xa5, 0x73, 0x72, 0x72, 0x54, 0xb3, 0x19, 0x2f, 0x2e, 0xa8, 0x8b, 0x16, 0x2d, 0x02, 0x6c, 0x64, 0xa2, 0x7a, 0x83, 0x07, 0x0f, 0x16, 0xe9, 0x58, 0x2c, 0x06, 0xf2, 0xd7, 0xcb, 0x45, 0x81, 0x26, 0xe1, 0xda, 0x01, 0xf8, 0xc6, 0x01, 0x9d, 0x00, 0x74, 0x33, 0x3b, 0x62, 0xce, 0x65, 0xd0, 0xb5, 0x80, 0x3e, 0x6f, 0x9d, 0x8a, 0x80, 0x5f, 0xca, 0x03, 0x3a, 0x3f, 0x7f, 0xf9, 0xf2, 0x65, 0xc6, 0xee, 0xf8, 0xf1, 0xe3, 0xec, 0xc0, 0x34, 0xc9, 0xe4, 0x3f, 0xba, 0x8e, 0x78, 0x19, 0xb4, 0x6f, 0x1a, 0xa1, 0x86, 0xe0, 0x38, 0x98, 0xf6, 0x0d, 0x83, 0x02, 0xed, 0x10, 0x84, 0xc2, 0x58, 0x97, 0xa3, 0x8f, 0x41, 0xe0, 0x3b, 0x2e, 0x44, 0xf7, 0x23, 0x38, 0x5e, 0x14, 0x15, 0x3c, 0xf7, 0xaf, 0xe2, 0xb2, 0x64, 0xc9, 0x12, 0xa5, 0x53, 0xa7, 0x4e, 0x0a, 0xe1, 0xd3, 0xaf, 0x56, 0xad, 0x5a, 0x82, 0x86, 0x8e, 0xd4, 0x73, 0x38, 0x1d, 0x9b, 0xa7, 0xef, 0x29, 0xc8, 0x61, 0xcd, 0x9a, 0x35, 0x02, 0x97, 0xf8, 0x17, 0x16, 0x16, 0x2a, 0xaf, 0xbe, 0xfa, 0xaa, 0x82, 0xef, 0xcb, 0x50, 0xe6, 0xce, 0x9d, 0xab, 0x1a, 0x7e, 0xa8, 0xbc, 0x61, 0xc3, 0x86, 0x4a, 0x51, 0x51, 0x91, 0xcc, 0x82, 0x7d, 0x44, 0x99, 0xae, 0x4d, 0xd8, 0x8b, 0x84, 0x2c, 0x59, 0x6f, 0x1a, 0xc2, 0xb8, 0x0e, 0xf4, 0x31, 0x0a, 0x7c, 0xcf, 0x87, 0xa0, 0xa7, 0x6f, 0x42, 0xd0, 0x39, 0x70, 0x2d, 0x3e, 0xbd, 0xd2, 0x82, 0xc2, 0xd6, 0xad, 0x5b, 0x59, 0xdd, 0xb8, 0x4d, 0x28, 0xc6, 0x8b, 0xba, 0xf2, 0xc1, 0x07, 0x1f, 0x08, 0x1e, 0x46, 0x09, 0xd7, 0xd3, 0x50, 0xfa, 0x42, 0xe2, 0xc4, 0x89, 0x13, 0x81, 0x5e, 0x46, 0x72, 0xe2, 0xc4, 0x09, 0x36, 0x33, 0x42, 0xa5, 0x01, 0xc7, 0x48, 0x40, 0xa5, 0x20, 0x37, 0x57, 0xbf, 0xbb, 0xec, 0xeb, 0xaf, 0xbf, 0x66, 0x53, 0x50, 0x9a, 0x7f, 0x6b, 0x43, 0x49, 0x49, 0x09, 0x2b, 0x23, 0x38, 0x0d, 0x27, 0x78, 0xe1, 0x57, 0xa1, 0x10, 0xcf, 0x01, 0x03, 0x06, 0xb0, 0x97, 0x3e, 0x7c, 0xf2, 0xc9, 0x27, 0x70, 0xf0, 0xe0, 0x41, 0xf6, 0x23, 0xa4, 0x82, 0x82, 0x02, 0x78, 0xf9, 0xe5, 0x97, 0x61, 0xf2, 0xe4, 0xc9, 0x30, 0x6a, 0xd4, 0x28, 0xc0, 0xf7, 0x21, 0x00, 0x7e, 0x8f, 0x82, 0x0d, 0x81, 0x32, 0x13, 0x9a, 0xb1, 0xd0, 0xd0, 0x44, 0x43, 0x23, 0xcd, 0x9c, 0xb4, 0x81, 0x16, 0x64, 0x7c, 0x51, 0x46, 0x2f, 0x4e, 0x21, 0x7d, 0x79, 0x28, 0x2b, 0x2b, 0x63, 0x79, 0x99, 0x8e, 0x74, 0xbe, 0x7e, 0xfd, 0x3a, 0x43, 0xa1, 0x3a, 0xc9, 0x5f, 0x8b, 0x24, 0x20, 0xe5, 0xb5, 0x30, 0xce, 0x4f, 0xc4, 0x46, 0x5e, 0x31, 0x83, 0xc9, 0x3d, 0xa0, 0xb2, 0xdf, 0xad, 0x30, 0x93, 0xf1, 0x4d, 0x83, 0xdb, 0x5a, 0x07, 0xd0, 0x67, 0x4e, 0x71, 0x86, 0x23, 0x9c, 0xa6, 0x4d, 0x50, 0xcb, 0xa7, 0x45, 0x95, 0x1f, 0x9c, 0x5b, 0xc0, 0x96, 0x03, 0xf8, 0x62, 0xc3, 0xa8, 0x3b, 0xd1, 0x32, 0x1e, 0xc7, 0x75, 0x5b, 0x8b, 0x1f, 0xe7, 0xea, 0xdd, 0xff, 0x14, 0xb6, 0x1c, 0x70, 0xff, 0x9b, 0x21, 0x73, 0x35, 0x74, 0x3d, 0x0d, 0xcd, 0x9c, 0xca, 0xf7, 0x97, 0x64, 0xdf, 0x01, 0x19, 0xf6, 0xa7, 0xef, 0x00, 0xdf, 0x01, 0x19, 0xb6, 0x40, 0x86, 0xc5, 0xfb, 0x3d, 0x20, 0xc3, 0x0e, 0xf8, 0x3f, 0xb8, 0x3e, 0x1b, 0x7a, 0x52, 0x1b, 0x0a, 0x83, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHTMLIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x03, 0xa8, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x30, 0x39, 0x54, 0x32, 0x33, 0x3a, 0x30, 0x32, 0x3a, 0x39, 0x35, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x3c, 0x50, 0x22, 0x3f, 0x00, 0x00, 0x05, 0xe7, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x59, 0x6b, 0x4c, 0x5b, 0x55, 0x1c, 0xa7, 0xa5, 0xe5, 0xe5, 0x78, 0xb5, 0x94, 0xf2, 0x4a, 0x00, 0x91, 0x47, 0xd8, 0x32, 0x51, 0x24, 0x7c, 0x70, 0x20, 0xf2, 0x34, 0x31, 0x13, 0x1f, 0x0c, 0x30, 0x2c, 0xbc, 0x24, 0xca, 0xc6, 0x07, 0x1e, 0x09, 0x1f, 0xf8, 0x60, 0x42, 0x4c, 0xd0, 0x84, 0x98, 0x98, 0xf1, 0xc5, 0x69, 0x54, 0x60, 0x3a, 0x82, 0x3a, 0x5d, 0x98, 0x24, 0xdb, 0x88, 0x01, 0x24, 0x3a, 0x32, 0xca, 0x63, 0x4c, 0x26, 0x8f, 0x05, 0x48, 0x86, 0x06, 0x28, 0x14, 0x4a, 0x5b, 0x1e, 0x85, 0xb6, 0xf8, 0x83, 0xa3, 0xd7, 0x6b, 0xcb, 0x6e, 0x6f, 0x7b, 0xe9, 0x08, 0xc9, 0x3d, 0x69, 0x2e, 0xff, 0xf3, 0x3b, 0xff, 0xf7, 0xff, 0x7f, 0xce, 0x69, 0x2f, 0x82, 0xdd, 0xdd, 0x5d, 0xa7, 0xe3, 0x3c, 0x84, 0xc7, 0xd9, 0xf9, 0x3d, 0xdf, 0xf9, 0x00, 0x8e, 0xba, 0x82, 0x7c, 0x05, 0xf8, 0x0a, 0x70, 0xcc, 0x00, 0xdf, 0x42, 0x1c, 0x13, 0xc8, 0x59, 0x9c, 0xaf, 0x00, 0xe7, 0x14, 0x72, 0x54, 0xc0, 0x57, 0x80, 0x63, 0x02, 0x39, 0x8b, 0xf3, 0x15, 0x38, 0x28, 0x85, 0xab, 0x86, 0x15, 0x4b, 0x78, 0xdd, 0xa4, 0x33, 0x39, 0x99, 0x2c, 0x71, 0x8e, 0xc8, 0x21, 0x57, 0xe0, 0xc1, 0xc6, 0x68, 0xed, 0xec, 0xc5, 0x94, 0xfb, 0xcf, 0x5b, 0xba, 0x35, 0xa0, 0xbd, 0x93, 0x3c, 0xfa, 0xec, 0x67, 0x0b, 0x97, 0x34, 0x46, 0xb5, 0xe5, 0xaa, 0xdd, 0x88, 0xe0, 0x50, 0x7e, 0x0f, 0x20, 0xb5, 0xb7, 0x57, 0x7f, 0x6a, 0x5e, 0xb8, 0xac, 0xd0, 0xf6, 0xc3, 0x15, 0x57, 0xa1, 0xdb, 0xc4, 0x0b, 0x0b, 0x66, 0x3e, 0xf5, 0xac, 0x75, 0x95, 0x4e, 0xe6, 0x02, 0x74, 0x77, 0xf6, 0x78, 0x53, 0x9a, 0x57, 0x1c, 0x50, 0xfe, 0x8c, 0x5b, 0xb4, 0x19, 0x8f, 0x1d, 0x53, 0x91, 0x1d, 0x32, 0x74, 0x91, 0x35, 0xa3, 0xba, 0x7d, 0xa9, 0xf5, 0xeb, 0xc5, 0x2f, 0xfe, 0xd2, 0xcf, 0xd1, 0x71, 0x06, 0x7a, 0xd3, 0xb8, 0x71, 0x55, 0xd9, 0x8c, 0xcf, 0x19, 0xef, 0x94, 0x12, 0xf9, 0x85, 0x54, 0x9f, 0x2c, 0x06, 0x66, 0xab, 0x4b, 0xf6, 0x07, 0xf0, 0x70, 0x73, 0xa2, 0x65, 0xf1, 0xf2, 0x75, 0xd5, 0x77, 0x70, 0x88, 0x6e, 0xc6, 0xdf, 0x25, 0xe0, 0x9c, 0x5f, 0x01, 0x1d, 0x21, 0x74, 0x8c, 0xfb, 0xc9, 0x37, 0xfc, 0xf2, 0x6e, 0xae, 0xdc, 0xd8, 0x32, 0x6d, 0x12, 0xe4, 0xd7, 0xb5, 0x5e, 0x7c, 0xc2, 0xdc, 0x9e, 0x2e, 0x94, 0xbf, 0x7b, 0x4e, 0x56, 0x70, 0x42, 0xe8, 0x69, 0x29, 0x65, 0x15, 0xb1, 0xb9, 0x85, 0x76, 0x9d, 0x76, 0x7b, 0xd4, 0x5d, 0xcd, 0x8b, 0x9f, 0xc2, 0x36, 0x5d, 0xbb, 0x58, 0x28, 0x4e, 0xf5, 0x79, 0x25, 0xd7, 0xef, 0x7c, 0x8a, 0x4f, 0x86, 0xf0, 0xf1, 0xbf, 0x93, 0xb4, 0xc6, 0xb5, 0x0e, 0xd5, 0xb5, 0xf6, 0xa5, 0x2b, 0x0f, 0xd6, 0x47, 0xe9, 0xe2, 0x27, 0x9c, 0x3d, 0x11, 0x43, 0x91, 0xfc, 0xbd, 0x50, 0xd7, 0x70, 0x3a, 0x6e, 0x95, 0xb6, 0x39, 0x80, 0xe2, 0xa9, 0x9c, 0x5f, 0xd4, 0x3f, 0xd3, 0xf5, 0x46, 0x7b, 0xc4, 0xe6, 0xca, 0xce, 0xbf, 0x2e, 0xcd, 0x93, 0x88, 0xa4, 0x74, 0x9c, 0x99, 0xfe, 0x63, 0xe3, 0x3e, 0xc2, 0x40, 0x30, 0x1a, 0xc3, 0x7f, 0x7b, 0x5a, 0x20, 0x10, 0x7c, 0x14, 0x76, 0x29, 0x4f, 0x56, 0xc8, 0x2c, 0x4b, 0x5f, 0xb5, 0xf9, 0x14, 0x5a, 0x35, 0xa8, 0x28, 0xf9, 0x2c, 0xc9, 0xd9, 0x8e, 0x93, 0x3d, 0xb7, 0x4e, 0xdd, 0x29, 0x95, 0x5f, 0xb4, 0xc9, 0x7b, 0x68, 0x88, 0xf5, 0x38, 0xfd, 0x41, 0xe8, 0xc7, 0x03, 0xcf, 0x4d, 0x7c, 0x12, 0xf1, 0x79, 0xb8, 0x5b, 0x04, 0xd1, 0x89, 0x13, 0x45, 0x65, 0x58, 0xa6, 0xf4, 0xb3, 0x21, 0xec, 0xdf, 0x03, 0xd0, 0xde, 0xad, 0xbe, 0x85, 0x6e, 0x79, 0xdb, 0xbf, 0x38, 0xc9, 0xeb, 0x65, 0x36, 0xc6, 0xcc, 0x78, 0xf4, 0xbb, 0x5b, 0x9d, 0xaa, 0xeb, 0xdf, 0x2e, 0x5d, 0x99, 0xdd, 0x9a, 0x36, 0x5b, 0x62, 0x3f, 0xb5, 0x39, 0x80, 0x7c, 0x59, 0x91, 0x6a, 0x67, 0x99, 0x9c, 0x39, 0x3b, 0xa6, 0x9d, 0x9b, 0x2b, 0x1d, 0xf8, 0x84, 0xb8, 0x86, 0xe6, 0xcb, 0x0a, 0xd1, 0xc4, 0xfe, 0xe2, 0x00, 0x36, 0xb6, 0xff, 0xed, 0x9f, 0xef, 0x35, 0x86, 0x35, 0x3a, 0x7f, 0xa2, 0xd7, 0x8b, 0x67, 0x6c, 0xcc, 0x85, 0xcd, 0x7b, 0x00, 0xf6, 0xcc, 0x4e, 0x7d, 0xca, 0x03, 0x91, 0x40, 0x84, 0x33, 0x31, 0xdf, 0xbf, 0xe8, 0x25, 0xef, 0xf4, 0x03, 0xf7, 0xb1, 0xce, 0xa4, 0xbd, 0xa1, 0xfa, 0xa1, 0x5d, 0xd9, 0xf2, 0xfb, 0xfa, 0x3d, 0x4a, 0x0a, 0x84, 0x8b, 0xd0, 0xe5, 0x35, 0x69, 0x4e, 0x89, 0xbc, 0x1c, 0x7d, 0x45, 0xc7, 0xd9, 0xd0, 0xf6, 0x04, 0x40, 0xe9, 0x1d, 0xdb, 0xb8, 0x87, 0xcb, 0xab, 0x73, 0xe5, 0xc7, 0x6d, 0xd3, 0x36, 0x05, 0x82, 0x08, 0x75, 0x0b, 0xef, 0x3d, 0x3d, 0x42, 0x47, 0x40, 0x8f, 0xe8, 0x14, 0x05, 0x93, 0xd9, 0x66, 0x67, 0xae, 0x4c, 0xec, 0x5f, 0x20, 0x7f, 0xa7, 0xc0, 0xbf, 0xd4, 0x4f, 0x24, 0x33, 0xe3, 0x67, 0x39, 0xe5, 0x14, 0x00, 0xb1, 0xb1, 0xbc, 0xa3, 0xbc, 0xaa, 0xfc, 0xea, 0x1b, 0xe5, 0x97, 0xcb, 0x3b, 0x4b, 0x04, 0x61, 0xbe, 0x89, 0x09, 0xcf, 0xa9, 0xa7, 0xe2, 0x4a, 0x02, 0xca, 0xcf, 0x4a, 0xde, 0x12, 0x0b, 0xc4, 0x2c, 0x7d, 0x3d, 0x98, 0x0d, 0x1b, 0xff, 0x50, 0x86, 0xde, 0xa4, 0xbf, 0xb6, 0xd4, 0xf6, 0xea, 0x58, 0x72, 0xd8, 0x5d, 0xef, 0x68, 0x85, 0xdc, 0x52, 0x67, 0xb7, 0xfa, 0x36, 0x96, 0x22, 0x06, 0x24, 0x17, 0x1e, 0x16, 0xe2, 0x7b, 0x91, 0x25, 0x83, 0x7d, 0x88, 0x93, 0x7d, 0x62, 0x0c, 0x52, 0x77, 0x35, 0xbf, 0x55, 0x4e, 0x97, 0x59, 0x32, 0x00, 0xff, 0xf0, 0xd1, 0xfb, 0x7f, 0xea, 0x1f, 0x59, 0x2e, 0x71, 0x41, 0x0e, 0xa1, 0x85, 0x0e, 0xae, 0xec, 0x93, 0x42, 0x6d, 0xbe, 0xc8, 0x9e, 0x94, 0x63, 0x6c, 0xed, 0xf0, 0x01, 0xb0, 0xcd, 0x94, 0xa3, 0xf8, 0xf8, 0x0a, 0x38, 0x2a, 0xb3, 0x6c, 0xf5, 0xf2, 0x15, 0x60, 0x9b, 0x29, 0x47, 0xf1, 0x1d, 0xfb, 0x0a, 0xb0, 0xfa, 0x3a, 0x8d, 0x9b, 0xd2, 0x68, 0x34, 0x0a, 0xf7, 0x87, 0xa3, 0x32, 0xb9, 0xaf, 0xd7, 0x60, 0x30, 0xe0, 0xaf, 0x4d, 0x86, 0xac, 0x57, 0x60, 0x7c, 0x7c, 0x5c, 0x2a, 0x95, 0x8a, 0xc5, 0x62, 0x99, 0x8c, 0xe9, 0x0b, 0x23, 0x6c, 0xf7, 0xf4, 0xf4, 0xcc, 0xcf, 0xcf, 0xdb, 0x17, 0xe1, 0xf4, 0xf4, 0x34, 0xb1, 0x02, 0x43, 0x20, 0xd8, 0x2b, 0xb1, 0x1e, 0x80, 0x97, 0x97, 0x57, 0x48, 0x48, 0x08, 0x34, 0x6a, 0x34, 0x1a, 0x06, 0xbd, 0x5d, 0x5d, 0x5d, 0xa9, 0xa9, 0xa9, 0x65, 0x65, 0x65, 0x0c, 0x3c, 0x0c, 0x4b, 0x9e, 0x9e, 0x9e, 0xd1, 0xd1, 0xd1, 0x41, 0x41, 0x41, 0xe0, 0x59, 0x5f, 0x5f, 0x67, 0xe0, 0x34, 0x5f, 0x62, 0xf3, 0x45, 0x6a, 0x68, 0x68, 0x08, 0x62, 0x22, 0x91, 0x88, 0x81, 0xb9, 0xa5, 0xa5, 0x05, 0x3c, 0x09, 0x09, 0x09, 0x0c, 0x3c, 0x56, 0x97, 0x06, 0x07, 0x07, 0xa1, 0x04, 0x45, 0xb0, 0xca, 0x49, 0x31, 0x58, 0xaf, 0x00, 0x34, 0x52, 0xa3, 0xad, 0xad, 0x2d, 0x32, 0x32, 0x12, 0xd9, 0xca, 0xce, 0xce, 0x56, 0x2a, 0x95, 0x14, 0x8e, 0x36, 0x9b, 0x98, 0x98, 0xc0, 0x54, 0xab, 0xd5, 0xf6, 0xee, 0x8f, 0x81, 0x81, 0x01, 0xd2, 0xd0, 0xd8, 0x3c, 0x39, 0x39, 0x39, 0x89, 0x89, 0x89, 0x8d, 0x8d, 0x8d, 0xe9, 0xe9, 0xe9, 0x90, 0xad, 0xac, 0xac, 0xac, 0xad, 0xad, 0x95, 0x48, 0x24, 0x59, 0x59, 0x59, 0xfd, 0xfd, 0x7b, 0x6f, 0xf2, 0x38, 0x0d, 0x2a, 0x14, 0x06, 0x82, 0x54, 0x00, 0x66, 0x9c, 0x9d, 0x9d, 0x29, 0x63, 0x75, 0x75, 0x75, 0x44, 0x64, 0x78, 0x78, 0x98, 0x02, 0xe9, 0x44, 0x43, 0x43, 0x03, 0x18, 0x96, 0x97, 0x99, 0xde, 0x32, 0x44, 0x45, 0x45, 0xd1, 0xed, 0x3a, 0xbc, 0x02, 0x19, 0x19, 0x19, 0xc8, 0x74, 0x4d, 0x4d, 0x0d, 0x1c, 0xed, 0xec, 0xec, 0x24, 0xee, 0x62, 0x87, 0x24, 0x25, 0x25, 0xc9, 0xe5, 0x72, 0x4c, 0x5d, 0x5d, 0x5d, 0x51, 0x22, 0x8c, 0xb8, 0xb8, 0x38, 0xb4, 0x13, 0x10, 0xec, 0xc8, 0xf6, 0xf6, 0x76, 0x10, 0x38, 0x5b, 0xc6, 0xc6, 0xc6, 0xd2, 0xd2, 0xd2, 0x40, 0x27, 0x27, 0x27, 0x4f, 0x4d, 0x4d, 0x01, 0xc1, 0x53, 0xaf, 0xd7, 0x03, 0xb1, 0x7f, 0xd0, 0x13, 0xf0, 0x38, 0x9a, 0xaa, 0x00, 0x32, 0x04, 0x1e, 0x85, 0x42, 0x41, 0xec, 0xad, 0xae, 0xae, 0x52, 0x22, 0x0c, 0x7b, 0x60, 0x64, 0x64, 0xef, 0xf7, 0x71, 0x4c, 0x4c, 0x0c, 0x98, 0xab, 0xab, 0xab, 0x41, 0xd7, 0xd7, 0xd7, 0x83, 0x0e, 0x0c, 0x0c, 0x04, 0xbd, 0xb0, 0xb0, 0x40, 0x29, 0x71, 0x6c, 0x05, 0xb0, 0x89, 0xe3, 0xe3, 0xe3, 0x61, 0x92, 0x1c, 0x4a, 0x20, 0xb6, 0xb7, 0xff, 0xf7, 0x5b, 0x1e, 0x08, 0xc3, 0xf0, 0xf0, 0xf0, 0xc0, 0x2a, 0xde, 0xbd, 0xe1, 0x49, 0xa7, 0x4d, 0x26, 0x4e, 0xff, 0x34, 0xb0, 0x6d, 0x13, 0x13, 0xff, 0x88, 0x13, 0x66, 0xbe, 0xa2, 0x1f, 0x80, 0xe8, 0x74, 0x3a, 0x33, 0xdc, 0xd1, 0x53, 0x56, 0x01, 0xa0, 0xc4, 0x74, 0x3f, 0xa8, 0x29, 0x45, 0x60, 0xd5, 0xdb, 0xdb, 0x1b, 0x4f, 0xf4, 0xf4, 0xec, 0xec, 0x2c, 0xda, 0xba, 0xbb, 0xbb, 0xbb, 0xa2, 0xa2, 0xa2, 0xb5, 0xb5, 0x15, 0x20, 0x61, 0xa3, 0x33, 0xd3, 0x69, 0x4a, 0x33, 0x4e, 0x2d, 0x1c, 0x59, 0x64, 0x4a, 0xa7, 0x29, 0x86, 0x83, 0x09, 0xe8, 0x62, 0x1e, 0x33, 0x33, 0x33, 0xbe, 0xbe, 0xbe, 0x44, 0x38, 0x36, 0x36, 0x56, 0xad, 0x56, 0x93, 0xde, 0x05, 0x02, 0x02, 0xb7, 0x1b, 0x11, 0x9f, 0x9c, 0x9c, 0x24, 0x3c, 0x28, 0x85, 0xbb, 0xbb, 0x3b, 0xa1, 0x33, 0x33, 0x33, 0x37, 0x37, 0x37, 0x03, 0x02, 0xfe, 0x79, 0x5d, 0x57, 0x55, 0x55, 0x45, 0x0e, 0x00, 0xec, 0xf5, 0xbe, 0xbe, 0x3e, 0x72, 0x6d, 0x61, 0x6f, 0x40, 0x49, 0x70, 0x70, 0x30, 0x11, 0xa1, 0x3f, 0x21, 0xce, 0xec, 0x1b, 0x56, 0xad, 0x57, 0x00, 0x3d, 0x8a, 0xee, 0x27, 0x7a, 0xc9, 0x5d, 0x86, 0x8b, 0x86, 0x4c, 0xc9, 0x8d, 0x43, 0x68, 0x1c, 0x88, 0x4d, 0x4d, 0x4d, 0x38, 0xe6, 0xc1, 0x8f, 0x0a, 0xc0, 0x2d, 0xdc, 0xca, 0xcd, 0xcd, 0xcd, 0xb0, 0x41, 0xc4, 0xd1, 0x78, 0x88, 0x0d, 0xf7, 0x3a, 0xf8, 0x81, 0x80, 0x0d, 0xcc, 0xa0, 0xc9, 0xd1, 0x4c, 0x3f, 0xa0, 0x89, 0x42, 0x3c, 0x0f, 0xec, 0x55, 0x6a, 0x95, 0x10, 0x87, 0xfc, 0x56, 0x02, 0xee, 0xce, 0xcd, 0xcd, 0xa1, 0x62, 0xc4, 0x39, 0x33, 0x63, 0x8e, 0x98, 0x1e, 0x72, 0x00, 0x8e, 0x70, 0x91, 0x59, 0xa7, 0xf5, 0x16, 0x62, 0x96, 0x3f, 0xf2, 0x55, 0x3e, 0x80, 0xa3, 0x2e, 0x01, 0x5f, 0x01, 0xbe, 0x02, 0x1c, 0x33, 0xc0, 0xb7, 0x10, 0xc7, 0x04, 0x72, 0x16, 0xe7, 0x2b, 0xc0, 0x39, 0x85, 0x1c, 0x15, 0x1c, 0xfb, 0x0a, 0xfc, 0x0d, 0x0a, 0x08, 0x48, 0x44, 0xec, 0xf6, 0xcb, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHTMLIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0f, 0x8d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x09, 0x54, 0x14, 0x47, 0xf6, 0x73, 0x05, 0x10, 0xd4, 0x78, 0x24, 0x28, 0x8a, 0x46, 0xc1, 0xdd, 0x28, 0xde, 0x06, 0x41, 0x8d, 0x26, 0xae, 0xc7, 0xaa, 0xf1, 0x40, 0xa3, 0x59, 0x45, 0x4d, 0x5c, 0x13, 0x37, 0xe6, 0x78, 0x66, 0xf1, 0x4e, 0x36, 0x51, 0xdc, 0xd5, 0x98, 0xb7, 0x3e, 0xcf, 0x68, 0xa2, 0x79, 0x26, 0xcf, 0xac, 0x67, 0x36, 0xc7, 0x4b, 0xc4, 0xe8, 0x86, 0x55, 0x14, 0x41, 0x41, 0xf1, 0x8a, 0x1b, 0x09, 0x04, 0x0f, 0x44, 0x14, 0x41, 0x10, 0x05, 0xe5, 0x86, 0xde, 0xfa, 0x85, 0x5d, 0x74, 0xcf, 0x74, 0xcf, 0x74, 0x4f, 0x77, 0xcf, 0x60, 0xd2, 0xf5, 0x5e, 0x4f, 0xff, 0xfa, 0xff, 0xd7, 0x3f, 0xea, 0x57, 0x55, 0xd7, 0x74, 0x77, 0x55, 0xbb, 0x71, 0x24, 0x81, 0x8a, 0xe4, 0xae, 0x82, 0x97, 0xb2, 0x6a, 0x2f, 0xf0, 0x87, 0xa3, 0x61, 0x54, 0x52, 0x69, 0xcd, 0x7d, 0x7a, 0xe6, 0xf3, 0xcc, 0x12, 0xf4, 0x81, 0x4f, 0x83, 0x13, 0x9e, 0xe2, 0x41, 0x4e, 0x0e, 0x06, 0xc6, 0xf1, 0x00, 0xb8, 0x51, 0x96, 0x63, 0x89, 0x12, 0xe5, 0xdd, 0x30, 0xc7, 0xd4, 0x29, 0x00, 0xb4, 0x3b, 0x2d, 0xa5, 0xc4, 0xcd, 0xcd, 0x8d, 0xa1, 0x6d, 0x6a, 0x10, 0x32, 0xf2, 0x25, 0x8c, 0xf7, 0xc1, 0x78, 0x0d, 0x36, 0x9d, 0xe6, 0x1d, 0x15, 0x9e, 0x45, 0x05, 0x2e, 0xdd, 0xcf, 0x84, 0xe2, 0xea, 0xbb, 0x94, 0x3e, 0x35, 0x35, 0x12, 0x46, 0x24, 0x0d, 0x10, 0xf2, 0xd6, 0xc1, 0x7c, 0xdc, 0x47, 0x1f, 0x7b, 0x96, 0x07, 0x45, 0xe7, 0x6f, 0xae, 0x7f, 0x21, 0xca, 0x5b, 0xb5, 0x25, 0xa4, 0xf2, 0x0d, 0x6f, 0x62, 0xca, 0x08, 0x11, 0x33, 0x66, 0x9c, 0x58, 0x4b, 0x7c, 0x33, 0x98, 0x3b, 0x77, 0xae, 0xb5, 0xa3, 0x02, 0x8c, 0xa8, 0x96, 0xf8, 0x42, 0x48, 0x1f, 0x3d, 0x7a, 0x34, 0xec, 0xd9, 0xb3, 0x47, 0xc0, 0x5a, 0x07, 0x1a, 0xef, 0x83, 0x6a, 0x0d, 0x56, 0x36, 0xda, 0x41, 0x88, 0x7c, 0xb6, 0xc3, 0xeb, 0x10, 0xd9, 0x21, 0x05, 0x56, 0x43, 0x9b, 0x2d, 0xd5, 0x56, 0x4d, 0xeb, 0x01, 0x02, 0x5b, 0xdf, 0xcb, 0xa7, 0x26, 0x5b, 0x91, 0xf9, 0x56, 0x29, 0x24, 0x20, 0x6e, 0xf3, 0xe5, 0xf5, 0x42, 0x14, 0x83, 0x25, 0x9b, 0x36, 0x4f, 0xc5, 0x82, 0xe3, 0x93, 0x87, 0xf1, 0x59, 0xd6, 0xe4, 0x19, 0x82, 0x00, 0xc8, 0x13, 0x93, 0xb6, 0x48, 0x88, 0x12, 0xc1, 0x36, 0x15, 0x08, 0x39, 0x5f, 0x4c, 0x7d, 0x9e, 0xbb, 0x53, 0x59, 0x24, 0x44, 0x29, 0x82, 0x5d, 0xd3, 0x8a, 0x0e, 0x1f, 0x3e, 0x6c, 0x2b, 0x6c, 0x92, 0x34, 0xd9, 0x32, 0x52, 0x7e, 0x12, 0x09, 0xdc, 0x96, 0x2d, 0x5b, 0xf0, 0x8a, 0x44, 0xc9, 0x78, 0xe6, 0x8f, 0x73, 0xe7, 0xce, 0x31, 0xb8, 0x5b, 0xb7, 0x6e, 0xac, 0x38, 0xcf, 0xcb, 0x10, 0x0f, 0x00, 0xc9, 0x18, 0x20, 0xf3, 0xfd, 0xfb, 0xf7, 0x99, 0x82, 0xf2, 0xf2, 0x72, 0x26, 0x74, 0xda, 0xb4, 0x69, 0x0c, 0xde, 0xbd, 0x7b, 0x37, 0xe7, 0xe9, 0xe9, 0xc9, 0xf2, 0x52, 0x4a, 0x5c, 0x13, 0x03, 0x62, 0x89, 0x6e, 0xe9, 0xe1, 0xf7, 0xc0, 0xa1, 0xb1, 0x48, 0x4d, 0xfd, 0xfd, 0x06, 0x15, 0x5c, 0x28, 0x3e, 0x0f, 0xef, 0xa7, 0xbf, 0xa7, 0xbc, 0x96, 0x2c, 0x7b, 0x9e, 0x30, 0x8f, 0x23, 0x25, 0x19, 0xfb, 0x85, 0x28, 0x36, 0xa2, 0x56, 0xd6, 0x56, 0x50, 0x38, 0xf5, 0x76, 0x8a, 0x88, 0x6e, 0x99, 0x91, 0xec, 0xc9, 0x5f, 0xe6, 0xec, 0x62, 0x82, 0x2c, 0x0b, 0x0c, 0x3d, 0x1a, 0x2e, 0x42, 0xcd, 0x3b, 0xff, 0xba, 0x2c, 0x2f, 0x32, 0x4a, 0x2a, 0x40, 0xcb, 0xb7, 0x5e, 0xd9, 0x24, 0x12, 0x84, 0x19, 0xc4, 0x4b, 0x25, 0xc4, 0x0f, 0x4b, 0x8c, 0x90, 0x22, 0x49, 0x2b, 0x40, 0xce, 0x0f, 0x2f, 0xae, 0xb2, 0x12, 0x68, 0xa9, 0x60, 0xfa, 0xc9, 0x09, 0x56, 0x3c, 0x96, 0x5a, 0x24, 0x3d, 0x10, 0x32, 0x0d, 0x4f, 0xec, 0x47, 0xb3, 0x52, 0x16, 0xa6, 0x97, 0x5c, 0x10, 0xb2, 0x4a, 0xc2, 0x76, 0x15, 0xf0, 0xa5, 0x2c, 0xad, 0xe7, 0xf1, 0xf6, 0xce, 0xe6, 0x58, 0x64, 0xb7, 0xc3, 0x59, 0x8d, 0x45, 0x85, 0x85, 0x85, 0x50, 0x56, 0x56, 0x66, 0xb7, 0xa0, 0x90, 0x01, 0xaf, 0xc7, 0x79, 0x79, 0x79, 0x42, 0x54, 0x3d, 0x2c, 0x15, 0xa4, 0x95, 0x2b, 0x57, 0x8a, 0xd0, 0x84, 0x5b, 0x94, 0x97, 0xca, 0x8c, 0x18, 0x61, 0xfd, 0xf7, 0x00, 0xf9, 0x24, 0x4b, 0xa2, 0xc0, 0x16, 0x2d, 0x5a, 0x50, 0x39, 0x08, 0xf3, 0x07, 0x2d, 0x40, 0xf2, 0x1f, 0x7c, 0xf0, 0x01, 0x17, 0x12, 0x12, 0x42, 0xf1, 0x94, 0x89, 0xfc, 0xa8, 0x52, 0x80, 0x1e, 0x5c, 0xbc, 0x78, 0x91, 0xbb, 0x7c, 0xf9, 0x32, 0x53, 0xc2, 0x0b, 0xe2, 0xbd, 0x39, 0x7b, 0xf6, 0xac, 0x63, 0x0a, 0x78, 0x6b, 0xf9, 0x33, 0x0a, 0xe6, 0xe1, 0x51, 0xa3, 0x46, 0x51, 0xf8, 0xf6, 0xed, 0xdb, 0x0c, 0x27, 0xa4, 0xf3, 0xca, 0x79, 0x63, 0xf0, 0x6c, 0xf6, 0x03, 0x52, 0x2b, 0xb6, 0x93, 0x55, 0x3f, 0xb0, 0xcd, 0xae, 0x9e, 0x6a, 0x78, 0x0c, 0xd4, 0x9b, 0xa4, 0xae, 0x84, 0xe1, 0x35, 0xa4, 0xce, 0x1c, 0xf5, 0xdc, 0xa6, 0x03, 0xea, 0xeb, 0x4c, 0xdf, 0x12, 0x4e, 0x8b, 0xc0, 0xcd, 0xf2, 0x1b, 0xfa, 0x5a, 0xfe, 0x40, 0x9a, 0x66, 0x07, 0x96, 0xa7, 0xbf, 0x0b, 0xf6, 0x6e, 0x5b, 0x90, 0xe9, 0x14, 0x44, 0x9d, 0x1c, 0x27, 0x72, 0x00, 0xcb, 0x44, 0x9f, 0x9f, 0x2d, 0xc2, 0x39, 0x92, 0x71, 0x68, 0x14, 0x9a, 0x72, 0x72, 0x0c, 0xe4, 0x95, 0xdf, 0xa4, 0xfa, 0xde, 0x0a, 0x59, 0x08, 0xe3, 0x02, 0x27, 0xc9, 0xea, 0x46, 0xc3, 0xb1, 0xf6, 0xbf, 0x1f, 0x90, 0x00, 0xbe, 0x1e, 0x8d, 0x44, 0x7c, 0x49, 0x85, 0x47, 0x60, 0xc9, 0x85, 0x05, 0x14, 0xe7, 0xef, 0xd9, 0x18, 0xf6, 0xf6, 0x8f, 0x17, 0xd1, 0x95, 0x64, 0x14, 0x3b, 0x30, 0x34, 0x31, 0x1c, 0x6a, 0xb9, 0x5a, 0x2a, 0xf3, 0xab, 0x88, 0xff, 0x40, 0xf3, 0x47, 0x5a, 0xd8, 0x95, 0xff, 0x8f, 0xf4, 0xbf, 0xc1, 0xe1, 0xfc, 0x38, 0xf8, 0xb0, 0xe7, 0xa7, 0x10, 0xda, 0xa4, 0xbb, 0x4d, 0xfe, 0xb2, 0x9a, 0x52, 0x18, 0x7b, 0x7c, 0x08, 0xd4, 0x70, 0xd5, 0x94, 0x2f, 0x7e, 0x50, 0xaa, 0x4d, 0x7e, 0x46, 0x14, 0x5e, 0x98, 0xed, 0xc1, 0x69, 0xc5, 0xff, 0xa3, 0xd3, 0x74, 0x9c, 0x00, 0xe3, 0x71, 0xf9, 0xfe, 0x45, 0xd9, 0x22, 0x78, 0xaf, 0x15, 0x79, 0x76, 0x5f, 0xfb, 0x5c, 0x96, 0x07, 0x09, 0xb7, 0x2a, 0xf2, 0x45, 0x32, 0x93, 0x0a, 0x8e, 0xd8, 0xe4, 0xb7, 0x24, 0x2a, 0x8e, 0x00, 0xf3, 0x58, 0x00, 0x4c, 0x26, 0x4d, 0x29, 0xff, 0x41, 0x53, 0x5a, 0xd7, 0xe3, 0x13, 0xe8, 0xde, 0xb4, 0x17, 0xa5, 0xfe, 0x72, 0x2f, 0x1d, 0x66, 0x9f, 0x99, 0x0e, 0xfd, 0x5b, 0x0c, 0x82, 0xe5, 0xa1, 0xab, 0x05, 0x25, 0xea, 0xc0, 0xc2, 0xca, 0x02, 0x98, 0x94, 0x32, 0x92, 0x66, 0xfc, 0x3d, 0xfd, 0xe1, 0xdb, 0xfe, 0x07, 0xc1, 0x1d, 0x3c, 0xac, 0xf8, 0x94, 0x20, 0x34, 0x39, 0x20, 0xa5, 0xa0, 0xa2, 0xb6, 0x02, 0x46, 0x26, 0x3d, 0x4d, 0x9a, 0x58, 0x4b, 0xf8, 0x2a, 0xe2, 0x80, 0x14, 0x8b, 0xae, 0x38, 0xdd, 0x1d, 0x40, 0xeb, 0x12, 0x0a, 0x0e, 0xc1, 0x33, 0x2d, 0x87, 0xe8, 0x6a, 0xa8, 0x9c, 0x30, 0x43, 0x1c, 0x90, 0x53, 0x66, 0x04, 0x5e, 0xf3, 0x75, 0xc0, 0x08, 0xa3, 0xd4, 0xc8, 0xfc, 0x6d, 0x38, 0x80, 0x4f, 0x67, 0xf0, 0x20, 0xff, 0xf6, 0x64, 0x2b, 0x27, 0x21, 0x21, 0x01, 0xbc, 0xbc, 0xbc, 0x64, 0xe9, 0xf6, 0x08, 0xd1, 0xd1, 0xd1, 0x54, 0x07, 0xea, 0x19, 0x39, 0xb2, 0x6e, 0x84, 0xb2, 0x57, 0x06, 0xe9, 0x8a, 0x22, 0x40, 0xc6, 0x5e, 0x2a, 0xab, 0xa8, 0xa8, 0x88, 0x29, 0x99, 0x39, 0x73, 0x26, 0x93, 0x7f, 0xf0, 0xe0, 0x41, 0x58, 0xba, 0x74, 0x29, 0x54, 0x57, 0x57, 0xc3, 0xda, 0xb5, 0x6b, 0xe9, 0x91, 0x98, 0x98, 0x48, 0xe9, 0xe8, 0x34, 0x1a, 0x35, 0x7f, 0xfe, 0x7c, 0x7a, 0x6e, 0xd2, 0xa4, 0x09, 0x90, 0xbf, 0xc4, 0x14, 0x1e, 0x3f, 0x7e, 0x3c, 0x93, 0x81, 0xe5, 0x78, 0x3d, 0x0c, 0xa9, 0x04, 0xb0, 0xbc, 0x30, 0xc8, 0xe5, 0x89, 0x2c, 0x6e, 0xf0, 0xe0, 0xc1, 0x94, 0x7c, 0xe6, 0xcc, 0x19, 0xfa, 0xaf, 0xf5, 0xca, 0x95, 0x2b, 0x8c, 0x9d, 0xd4, 0x20, 0xc5, 0x31, 0x84, 0x00, 0xc0, 0xb2, 0x43, 0x87, 0x0e, 0xa5, 0x18, 0x84, 0x53, 0x52, 0x52, 0x38, 0xbc, 0x8f, 0x8e, 0xb0, 0x65, 0x42, 0x9c, 0xdc, 0x7f, 0x78, 0x4b, 0x5e, 0xcc, 0x2b, 0x8a, 0x00, 0x11, 0x4a, 0xd3, 0xf0, 0xe1, 0xc3, 0xe9, 0x19, 0x6b, 0x11, 0x13, 0x29, 0x4f, 0xcf, 0xf8, 0xd3, 0xb8, 0x71, 0x63, 0x0a, 0x5f, 0xbd, 0x7a, 0x95, 0x9e, 0x57, 0xad, 0x5a, 0x05, 0xc2, 0x28, 0x79, 0x7b, 0x7b, 0x53, 0x3c, 0xfe, 0x08, 0x61, 0x86, 0x74, 0x10, 0xd0, 0x7d, 0x18, 0xcd, 0xcc, 0xcc, 0xa4, 0xa6, 0x74, 0xea, 0xd4, 0xc9, 0x41, 0x93, 0xd4, 0x15, 0xd3, 0xdd, 0x01, 0x75, 0xea, 0xb5, 0x73, 0xab, 0x6a, 0x42, 0xda, 0xd5, 0xe9, 0x2f, 0xc1, 0x74, 0x40, 0xff, 0x3a, 0x55, 0x27, 0xd1, 0xec, 0x03, 0xea, 0xea, 0x4b, 0x7f, 0x6e, 0xb3, 0x0f, 0xe8, 0x5f, 0xa7, 0xea, 0x24, 0x9a, 0x11, 0x50, 0x57, 0x5f, 0xfa, 0x73, 0x9b, 0x11, 0xd0, 0xbf, 0x4e, 0xd5, 0x49, 0x34, 0x23, 0xa0, 0xa4, 0xbe, 0x32, 0xef, 0x65, 0x28, 0x61, 0x73, 0x88, 0xc7, 0x29, 0x57, 0x62, 0xfe, 0xe6, 0xaf, 0xe2, 0xdb, 0x85, 0x2a, 0x5c, 0xd1, 0xd4, 0x84, 0x8a, 0xaa, 0x0a, 0xe9, 0x9d, 0xe9, 0x4f, 0xb3, 0x3e, 0x96, 0x55, 0x99, 0x57, 0x91, 0x4b, 0x69, 0xdd, 0x9a, 0xf6, 0x64, 0x3c, 0x87, 0x6e, 0xfd, 0x40, 0xcb, 0x65, 0x97, 0x65, 0x31, 0x9c, 0xa3, 0x80, 0xa7, 0xa3, 0x05, 0xdf, 0x3c, 0x37, 0x13, 0xc8, 0xbd, 0x52, 0x5a, 0xfc, 0x4e, 0xd5, 0x6d, 0x59, 0x31, 0xaf, 0x9c, 0x8e, 0xa2, 0xb4, 0xb5, 0x3d, 0xb6, 0x30, 0x9e, 0x6b, 0xa5, 0x75, 0xff, 0xda, 0x66, 0xa4, 0x4e, 0x82, 0x36, 0xbe, 0x41, 0xb0, 0x3d, 0xec, 0x1b, 0x46, 0x53, 0x0b, 0xa8, 0x6e, 0x42, 0xc7, 0x6f, 0x1f, 0x85, 0x77, 0x7f, 0x9a, 0x47, 0xf5, 0xe0, 0x2d, 0xf1, 0xef, 0xfa, 0x1f, 0x02, 0x72, 0xcf, 0x42, 0x52, 0x6f, 0x2d, 0xd4, 0x02, 0x79, 0x0b, 0x05, 0xfc, 0xc8, 0xfd, 0xcf, 0xd8, 0xfe, 0x87, 0xad, 0x78, 0x84, 0xb7, 0xe9, 0xa3, 0x3b, 0xbd, 0x0d, 0x63, 0x5a, 0x4f, 0xb0, 0xe2, 0xb1, 0x87, 0x50, 0xec, 0x00, 0x1a, 0x33, 0xe6, 0xd8, 0x60, 0xc0, 0xdb, 0xe0, 0x98, 0x36, 0xf4, 0xdc, 0x0a, 0x5d, 0x9b, 0xf4, 0xb0, 0x29, 0x7f, 0xce, 0x8f, 0xaf, 0xc0, 0x4f, 0x77, 0x7f, 0x84, 0xdd, 0xe1, 0x7b, 0x21, 0xc0, 0xbb, 0xb5, 0x24, 0xef, 0xb5, 0xb2, 0xab, 0xf0, 0x52, 0xea, 0x44, 0x4a, 0xf3, 0x70, 0xf3, 0x84, 0xd8, 0x01, 0x87, 0xc1, 0xc7, 0xdd, 0x47, 0x92, 0x57, 0x0a, 0xa9, 0xa8, 0x0f, 0x7c, 0x46, 0xda, 0x38, 0xd6, 0x24, 0x1a, 0x3f, 0xe4, 0xf1, 0x3f, 0x02, 0x76, 0x46, 0x7b, 0xc6, 0xa3, 0x32, 0x34, 0x1e, 0x93, 0x9c, 0xf1, 0x48, 0x0b, 0xf2, 0x6d, 0x4f, 0xe5, 0xbd, 0xd0, 0x76, 0x1a, 0x7d, 0x36, 0x30, 0x2a, 0x69, 0x20, 0xc4, 0xfc, 0xbc, 0x18, 0x49, 0x8a, 0x92, 0x22, 0x07, 0x1e, 0xf3, 0x0e, 0x60, 0xc2, 0x12, 0x0b, 0x0e, 0x43, 0x79, 0x6d, 0x39, 0xcb, 0xcb, 0x01, 0x9f, 0x5c, 0xf9, 0x90, 0x92, 0xde, 0xed, 0xbc, 0x42, 0x8e, 0x85, 0xe1, 0x31, 0xba, 0xfb, 0x6f, 0x7e, 0xc7, 0xf2, 0x81, 0x3e, 0x6d, 0x18, 0x6c, 0x0f, 0x50, 0xdc, 0x84, 0x50, 0xd0, 0x6b, 0x67, 0x5f, 0x82, 0x8c, 0x92, 0x34, 0x2a, 0xb3, 0xc7, 0xa3, 0x7d, 0x60, 0x6d, 0xf7, 0xcd, 0xb2, 0xf2, 0x95, 0x0e, 0x9d, 0xf8, 0x8c, 0x2d, 0x3e, 0xff, 0x07, 0x2a, 0xa7, 0xb5, 0x6f, 0x1b, 0xd8, 0x19, 0xf6, 0xad, 0xac, 0x4c, 0x29, 0x82, 0xa2, 0x08, 0xf0, 0x05, 0x3f, 0xee, 0xf5, 0x39, 0x6d, 0xa3, 0x1e, 0x6e, 0x1e, 0xf0, 0xe3, 0x9d, 0xd3, 0x74, 0x28, 0x3c, 0x70, 0x73, 0x2f, 0x4f, 0x66, 0xe7, 0x63, 0x85, 0x09, 0x14, 0x8e, 0x6c, 0xf3, 0x02, 0xc3, 0x59, 0x02, 0x27, 0x8a, 0x8e, 0xd3, 0xf2, 0xbc, 0xf1, 0x7b, 0xc2, 0xf7, 0xa9, 0x36, 0x1e, 0x65, 0xaa, 0x8a, 0x80, 0xd0, 0x88, 0x23, 0x05, 0x07, 0xe1, 0xef, 0x69, 0x6f, 0x53, 0x14, 0xde, 0x3a, 0x3c, 0x34, 0xf0, 0x24, 0x23, 0xdb, 0xab, 0xfd, 0x11, 0xe4, 0x01, 0x08, 0x79, 0xf9, 0x8e, 0xf2, 0xbf, 0x19, 0x32, 0x0f, 0x26, 0x04, 0x4e, 0x66, 0x65, 0x55, 0x03, 0x52, 0xb7, 0xeb, 0xd4, 0xe0, 0xc8, 0x90, 0xca, 0x65, 0x97, 0x66, 0xb1, 0x22, 0xfc, 0x33, 0xaf, 0x59, 0xa7, 0xa7, 0x32, 0x9c, 0x25, 0x70, 0xaf, 0xfa, 0x1e, 0xf7, 0xfa, 0xd9, 0x97, 0x2c, 0xd1, 0x0e, 0xe5, 0x1d, 0x8e, 0x80, 0x5c, 0x4d, 0x45, 0x26, 0x0f, 0x85, 0xe2, 0xaa, 0xbb, 0x10, 0x37, 0x30, 0x19, 0x3c, 0xc9, 0xb0, 0x68, 0x74, 0x52, 0xd5, 0x07, 0x94, 0x18, 0x83, 0xc6, 0x3f, 0xe2, 0xee, 0xed, 0x14, 0xe3, 0xd1, 0x1e, 0xdd, 0x23, 0x40, 0xde, 0x33, 0x83, 0xeb, 0x65, 0xd7, 0xa0, 0xad, 0x6f, 0x3b, 0x25, 0xfe, 0x6a, 0xe6, 0xd1, 0xdd, 0x01, 0xcd, 0x16, 0xa9, 0x14, 0xa0, 0x7b, 0x13, 0x52, 0xa9, 0x5f, 0x33, 0xbb, 0xe9, 0x80, 0xe6, 0x2a, 0xd4, 0x28, 0xc0, 0x8c, 0x80, 0xc6, 0x0a, 0xd4, 0x5c, 0x5c, 0xd1, 0x95, 0xe6, 0xc8, 0x91, 0x23, 0xe0, 0xe1, 0xe1, 0x01, 0x03, 0x07, 0x0e, 0xd4, 0xac, 0x50, 0x4e, 0x40, 0x56, 0x56, 0x16, 0x90, 0x87, 0x86, 0x94, 0x4c, 0x1e, 0x26, 0xca, 0xb1, 0x59, 0xe3, 0xed, 0x5d, 0xbf, 0x77, 0xec, 0xd8, 0x41, 0x9f, 0x26, 0x92, 0x92, 0x36, 0x59, 0x7d, 0x7c, 0x7c, 0xb8, 0xed, 0xdb, 0xb7, 0xdb, 0xe4, 0xb1, 0x45, 0x44, 0xf9, 0xfc, 0x61, 0x8b, 0xcf, 0x92, 0x66, 0xdb, 0xaa, 0x07, 0xdc, 0x7d, 0xfa, 0xf4, 0xa1, 0xc2, 0x2d, 0x0b, 0x0b, 0xf3, 0xa8, 0x3c, 0x32, 0x32, 0x52, 0x88, 0x52, 0x0d, 0x93, 0xb5, 0x91, 0x76, 0xf5, 0x58, 0x0a, 0x55, 0xd5, 0x89, 0x07, 0x0c, 0x18, 0xc0, 0x1e, 0x74, 0x57, 0x56, 0x56, 0x12, 0x9b, 0x01, 0x6a, 0x6a, 0x6a, 0x60, 0xdd, 0xba, 0x75, 0x14, 0x26, 0xaf, 0x5c, 0xd3, 0x87, 0xdc, 0x1b, 0x36, 0x6c, 0xa0, 0x79, 0xfc, 0xc1, 0x99, 0x2a, 0x1e, 0x2d, 0x5b, 0xb6, 0xa4, 0xe7, 0xe5, 0xcb, 0x97, 0x33, 0x1c, 0x63, 0xd2, 0x02, 0x58, 0x7a, 0x24, 0x95, 0xe7, 0x23, 0x10, 0x1c, 0x1c, 0xcc, 0x91, 0x27, 0xf2, 0xb4, 0x96, 0x82, 0x82, 0x82, 0x28, 0x6b, 0x5c, 0x5c, 0x1c, 0x0b, 0x3d, 0xb1, 0x83, 0xc1, 0xa7, 0x4e, 0x9d, 0xa2, 0xf4, 0x21, 0x43, 0x86, 0x50, 0xdc, 0xec, 0xd9, 0xb3, 0x19, 0x6d, 0xdb, 0xb6, 0x6d, 0x14, 0x8e, 0x89, 0x89, 0x11, 0xa9, 0x73, 0x24, 0x02, 0x0e, 0x35, 0x21, 0xde, 0x50, 0xa1, 0x76, 0xc4, 0x49, 0x35, 0xa1, 0xfd, 0xfb, 0xf7, 0x53, 0x63, 0x91, 0x37, 0x34, 0x34, 0x94, 0xc1, 0xc8, 0x1f, 0x15, 0x15, 0x25, 0x14, 0xc1, 0x39, 0xe2, 0x80, 0xaa, 0x26, 0x44, 0x94, 0x36, 0xb8, 0xa4, 0xab, 0x03, 0x87, 0x0e, 0x1d, 0xa2, 0x0e, 0x66, 0x64, 0x64, 0x40, 0xdf, 0xbe, 0x7d, 0x9d, 0xe3, 0xac, 0x28, 0x86, 0x12, 0x19, 0x1c, 0x1a, 0x89, 0x25, 0xf4, 0x70, 0x77, 0x77, 0xe7, 0xc6, 0x8d, 0x1b, 0xc7, 0xf2, 0x63, 0xc7, 0x8e, 0x65, 0x25, 0xda, 0xb5, 0x6b, 0xc7, 0xf0, 0x3c, 0x7f, 0x6d, 0x6d, 0xad, 0x08, 0xc7, 0x37, 0x21, 0xbe, 0xa9, 0x20, 0x5f, 0x55, 0x55, 0x15, 0xd7, 0xb4, 0x69, 0x53, 0x11, 0x1f, 0x5f, 0x3e, 0x3b, 0x3b, 0x9b, 0xc9, 0x97, 0x03, 0x74, 0x9d, 0x4e, 0xe3, 0x88, 0x44, 0xde, 0x44, 0x81, 0x5e, 0xbd, 0x7a, 0x41, 0xa3, 0x46, 0xe2, 0x97, 0x5c, 0x89, 0x51, 0x86, 0x24, 0x5d, 0x1d, 0x30, 0xc4, 0x42, 0x3b, 0x42, 0x75, 0xed, 0x03, 0x76, 0x74, 0x19, 0x42, 0x36, 0x1d, 0x30, 0xa4, 0x5a, 0x55, 0x08, 0x35, 0x23, 0xa0, 0xa2, 0xb2, 0x0c, 0x61, 0x35, 0x23, 0x60, 0x48, 0xb5, 0xaa, 0x10, 0xfa, 0xd0, 0x5f, 0x07, 0x54, 0xf8, 0xda, 0x20, 0x59, 0x1f, 0xfa, 0x2e, 0xd0, 0x20, 0x6b, 0x55, 0x85, 0x51, 0x66, 0x00, 0x54, 0x54, 0x96, 0x11, 0xac, 0x66, 0x00, 0x8c, 0xa8, 0x55, 0x15, 0x32, 0xcd, 0x00, 0xa8, 0xa8, 0x2c, 0x23, 0x58, 0xcd, 0x00, 0x18, 0x51, 0xab, 0x2a, 0x64, 0x9a, 0x01, 0x50, 0x51, 0x59, 0x46, 0xb0, 0x9a, 0x01, 0x30, 0xa2, 0x56, 0x55, 0xc8, 0x34, 0x03, 0xa0, 0xa2, 0xb2, 0x8c, 0x60, 0x55, 0xf4, 0x68, 0xcc, 0x08, 0xc5, 0x46, 0xc8, 0x7c, 0xe3, 0xdc, 0x0c, 0xf8, 0xa5, 0x24, 0x1d, 0x9e, 0x6e, 0xf9, 0x2c, 0xfc, 0xa9, 0xed, 0x74, 0x78, 0xb2, 0x71, 0xa8, 0x11, 0x6a, 0x74, 0x95, 0xd9, 0x20, 0x7a, 0xc0, 0xa9, 0xa2, 0x13, 0xb0, 0xf8, 0xa7, 0xb7, 0xa0, 0x8a, 0xab, 0x7b, 0x58, 0xe5, 0x88, 0x87, 0xcb, 0xc8, 0xeb, 0x81, 0x3f, 0x17, 0x5f, 0x20, 0xaf, 0x0b, 0xd6, 0x40, 0xc2, 0x2d, 0xf2, 0x02, 0x28, 0x79, 0x98, 0x25, 0x95, 0xde, 0xcf, 0x58, 0x02, 0x3f, 0xe4, 0xed, 0x93, 0x22, 0xb9, 0x04, 0xe7, 0x92, 0x5b, 0x11, 0xf8, 0x1e, 0x4e, 0x6c, 0xee, 0x37, 0xb0, 0xf5, 0xca, 0x46, 0x20, 0xaf, 0x6c, 0x31, 0xc7, 0x3d, 0xdd, 0x3d, 0xe1, 0x93, 0xde, 0x3b, 0xe0, 0x89, 0x46, 0xc1, 0x0c, 0xa7, 0x04, 0xf8, 0xf8, 0xf2, 0x5a, 0x20, 0xfb, 0xbe, 0x31, 0xd6, 0x65, 0xa1, 0xff, 0x84, 0x81, 0x2d, 0xc4, 0x0f, 0x98, 0x4b, 0xaa, 0x8b, 0x61, 0xc6, 0xa9, 0x49, 0x50, 0x54, 0x59, 0xff, 0x6a, 0xb1, 0x97, 0xbb, 0x17, 0x4c, 0x0e, 0x7a, 0x11, 0xa6, 0xb5, 0x9b, 0x09, 0x5e, 0x6e, 0x8f, 0xb0, 0xf2, 0xce, 0x04, 0x9c, 0x16, 0x00, 0x7c, 0xbf, 0x74, 0x5b, 0xd6, 0x16, 0xf8, 0xfa, 0xfa, 0x6e, 0xda, 0x4a, 0x85, 0x4e, 0xfe, 0xae, 0xf1, 0x93, 0xf0, 0xd7, 0x90, 0xc5, 0x0e, 0x0d, 0x19, 0x5f, 0x11, 0x79, 0x1f, 0x5d, 0x5a, 0xc3, 0xc4, 0xbd, 0x11, 0x3c, 0x17, 0x9e, 0x6f, 0x33, 0x85, 0xe5, 0x2d, 0x81, 0x1b, 0xe5, 0xd7, 0x81, 0x6c, 0x6f, 0x07, 0x27, 0x6e, 0x1f, 0xb3, 0x24, 0xd1, 0x17, 0x79, 0x5f, 0x27, 0xe5, 0x9b, 0x79, 0x35, 0xb7, 0xa2, 0x19, 0x85, 0x30, 0x34, 0x00, 0x05, 0x95, 0xf9, 0xb0, 0x81, 0x38, 0x4b, 0x36, 0xe8, 0x10, 0xd9, 0x8f, 0xc3, 0xc3, 0xf0, 0x80, 0xe7, 0xe0, 0xd5, 0x0e, 0x73, 0xe0, 0x51, 0xaf, 0x66, 0x22, 0x9a, 0x9a, 0x4c, 0x62, 0x41, 0x3c, 0x2c, 0x4d, 0x5b, 0xc4, 0x8a, 0x4c, 0x6a, 0x1b, 0x05, 0xaf, 0x75, 0x8c, 0x66, 0x79, 0x7b, 0x00, 0x6e, 0x7b, 0xb1, 0x3d, 0x7b, 0x2b, 0xfc, 0x3b, 0x67, 0x07, 0x54, 0xd7, 0xd6, 0xed, 0x82, 0xc2, 0x97, 0xe9, 0xd2, 0xa4, 0x1b, 0xe0, 0x3b, 0xe8, 0xc1, 0x7e, 0xc6, 0xae, 0x24, 0xd6, 0x3d, 0x00, 0x19, 0xf7, 0xd2, 0x60, 0xcd, 0x2f, 0x2b, 0x21, 0x93, 0x6c, 0x48, 0x22, 0x4c, 0x8d, 0x3c, 0xfd, 0x60, 0x46, 0xfb, 0x57, 0x61, 0x02, 0x79, 0x7f, 0xd7, 0xd1, 0x8d, 0x47, 0x84, 0xf2, 0xd2, 0xc9, 0x4b, 0xcf, 0xe4, 0x6d, 0x53, 0x86, 0x1a, 0xf4, 0xd8, 0x10, 0x88, 0xe9, 0x2c, 0xbf, 0x4d, 0x01, 0x63, 0xb4, 0x01, 0x1c, 0xb9, 0xf5, 0x5f, 0xf8, 0x88, 0x0c, 0x67, 0x05, 0x15, 0xb7, 0x44, 0x5c, 0x01, 0x3e, 0xad, 0x60, 0x0e, 0xd9, 0xb7, 0xa7, 0x5f, 0x73, 0xfd, 0xdf, 0xcf, 0xd1, 0x35, 0x00, 0x7f, 0x39, 0x33, 0x0d, 0x2e, 0x5a, 0xac, 0xf0, 0x79, 0xc2, 0xaf, 0x23, 0xdd, 0x11, 0x26, 0xd0, 0xa7, 0xad, 0xc8, 0x29, 0x2d, 0x99, 0x5b, 0x15, 0x79, 0x64, 0x87, 0xa5, 0x48, 0xb6, 0x77, 0x4f, 0xe7, 0x26, 0xa1, 0xb0, 0xa9, 0xe7, 0x36, 0x2d, 0x22, 0x45, 0x65, 0xef, 0x56, 0xdd, 0x81, 0xf7, 0x33, 0xde, 0x03, 0xb2, 0x09, 0xa9, 0x08, 0x8f, 0xd7, 0xa8, 0xb8, 0xa7, 0x93, 0x45, 0x38, 0xad, 0x19, 0x5d, 0x03, 0x80, 0xc6, 0x14, 0x91, 0xf5, 0x33, 0xb8, 0x56, 0x20, 0x2e, 0xef, 0x7b, 0x7c, 0x71, 0x44, 0x64, 0x1f, 0xae, 0x3b, 0x78, 0xad, 0xe3, 0x5b, 0xf0, 0xec, 0x63, 0xc3, 0x44, 0x78, 0x35, 0x99, 0xf2, 0xda, 0x32, 0x78, 0x21, 0xe5, 0x39, 0x72, 0xf1, 0x2e, 0xa1, 0xc5, 0x1e, 0x27, 0xad, 0x73, 0x57, 0xdf, 0x6f, 0x35, 0xf7, 0xaa, 0xf3, 0x77, 0xcf, 0xd0, 0xe1, 0x92, 0xec, 0x91, 0x64, 0x65, 0x4e, 0x58, 0xf3, 0x08, 0x78, 0x33, 0x78, 0x3e, 0x5d, 0x9c, 0x61, 0x45, 0xd4, 0x88, 0xd0, 0x3d, 0x00, 0x42, 0x7b, 0x70, 0xb6, 0x83, 0xcb, 0x09, 0xb6, 0x66, 0x6d, 0x02, 0xb2, 0xa7, 0xb7, 0x90, 0x04, 0xb8, 0xe4, 0x60, 0x5c, 0xe0, 0x44, 0xf8, 0xf3, 0x13, 0xb3, 0xc1, 0xcf, 0xc3, 0x5f, 0x44, 0x93, 0xcb, 0xa0, 0xbc, 0x17, 0x53, 0x27, 0x90, 0x37, 0x99, 0x73, 0x28, 0x8b, 0xaf, 0x87, 0x2f, 0x7c, 0x11, 0xfe, 0x3d, 0xe0, 0xda, 0x23, 0xb5, 0xa9, 0x9a, 0xec, 0x7c, 0xf5, 0x65, 0xce, 0x4e, 0xd8, 0x91, 0xfd, 0x29, 0x59, 0x49, 0x53, 0x26, 0x2a, 0xee, 0xed, 0xe1, 0x03, 0x51, 0x41, 0x33, 0xc8, 0x0c, 0x69, 0xba, 0xe1, 0xb3, 0x23, 0x43, 0x03, 0x20, 0xf2, 0x8a, 0x64, 0xb2, 0x4a, 0x2f, 0xd1, 0x56, 0x76, 0x8e, 0xac, 0xf5, 0xb0, 0x4c, 0xf8, 0xa7, 0x69, 0x4e, 0xc8, 0x02, 0x9b, 0x33, 0xa1, 0xe8, 0xf3, 0xaf, 0x92, 0x75, 0x22, 0x67, 0x68, 0x51, 0xbc, 0x90, 0xff, 0x2b, 0xec, 0x6b, 0x68, 0xe3, 0x13, 0x64, 0x29, 0x4a, 0x36, 0x8f, 0x33, 0xa0, 0x4d, 0x97, 0x56, 0x43, 0x72, 0x61, 0xdd, 0x2e, 0x2f, 0x42, 0xc6, 0x0e, 0x7e, 0xc1, 0xb4, 0x95, 0xf7, 0x7a, 0xf4, 0x29, 0x21, 0xda, 0x70, 0xd8, 0xa9, 0x01, 0x10, 0x7a, 0x83, 0x33, 0x90, 0x9d, 0xd9, 0x9f, 0xd1, 0x19, 0x48, 0x65, 0x6d, 0xdd, 0x1f, 0x30, 0xdc, 0x51, 0x0c, 0x77, 0x16, 0x93, 0x4a, 0xf8, 0x07, 0xea, 0x60, 0xde, 0x01, 0x46, 0x5a, 0xd3, 0x63, 0x33, 0xf4, 0x6c, 0xda, 0x87, 0xe5, 0x95, 0x00, 0x6b, 0x2f, 0xae, 0x84, 0xd8, 0x1b, 0x75, 0x4b, 0x04, 0x31, 0x80, 0x23, 0x03, 0xc6, 0xc2, 0x2b, 0x1d, 0xde, 0xd0, 0x34, 0x13, 0x53, 0xa2, 0xd7, 0x16, 0x8f, 0xcb, 0x02, 0x60, 0x69, 0x54, 0x2e, 0x69, 0x9d, 0xad, 0x65, 0xd6, 0x8a, 0xe1, 0x10, 0xb6, 0x2b, 0x7b, 0x1b, 0x2b, 0xb2, 0xf8, 0xf7, 0x31, 0x74, 0x1a, 0xcb, 0x10, 0x2a, 0x00, 0x5c, 0x88, 0x6a, 0x6b, 0x11, 0x9e, 0x0a, 0x51, 0xba, 0xb0, 0x36, 0x98, 0x00, 0xc8, 0x79, 0xb3, 0x37, 0xf7, 0x6b, 0x58, 0x97, 0x59, 0x3f, 0xbd, 0xc4, 0x6b, 0xc6, 0xf4, 0x76, 0x2f, 0xcb, 0xb1, 0x3f, 0x74, 0xf8, 0x06, 0x71, 0x2f, 0x48, 0xae, 0xd6, 0x70, 0x35, 0x25, 0x5e, 0xac, 0xf9, 0xa5, 0x43, 0xcf, 0xb5, 0x8e, 0xfc, 0x55, 0x55, 0x3e, 0xfa, 0xdd, 0xe0, 0x7b, 0x80, 0x5c, 0x70, 0x7e, 0x2d, 0xf8, 0x06, 0xdd, 0x03, 0x7e, 0x2d, 0x95, 0x6c, 0xcb, 0x0f, 0x33, 0x00, 0xb6, 0x6a, 0xc7, 0x09, 0x34, 0x33, 0x00, 0x4e, 0xa8, 0x64, 0x5b, 0x2a, 0xcc, 0x00, 0xd8, 0xaa, 0x1d, 0x27, 0xd0, 0xcc, 0x00, 0x38, 0xa1, 0x92, 0x6d, 0xa9, 0x30, 0x03, 0x60, 0xab, 0x76, 0x9c, 0x40, 0xd3, 0x25, 0x00, 0xc9, 0xc9, 0xc9, 0x40, 0xd6, 0x30, 0xb1, 0x45, 0x84, 0xf8, 0x37, 0x7f, 0xe3, 0xc6, 0x8d, 0x0e, 0x9b, 0x5f, 0x5a, 0x5a, 0x0a, 0x77, 0xee, 0xdc, 0x71, 0xb8, 0xbc, 0x96, 0x82, 0x68, 0x37, 0xda, 0x6f, 0x79, 0x90, 0xf5, 0x59, 0x5a, 0xc4, 0xca, 0x96, 0xd5, 0x25, 0x00, 0xdd, 0xbb, 0x77, 0x87, 0x25, 0x4b, 0x96, 0x40, 0xb3, 0x66, 0xf5, 0x4f, 0xb7, 0xee, 0xdd, 0xab, 0x7f, 0xd6, 0x2b, 0xab, 0x5d, 0x82, 0xf0, 0xce, 0x3b, 0xef, 0x80, 0x9f, 0x9f, 0x1f, 0x95, 0x45, 0xd6, 0x22, 0x4a, 0x70, 0x18, 0x8b, 0x1a, 0x33, 0x66, 0x0c, 0x2c, 0x5a, 0xb4, 0x08, 0x26, 0x4e, 0x9c, 0x08, 0xc2, 0x7d, 0x75, 0xf9, 0xd5, 0xad, 0x7a, 0x6b, 0xd7, 0xe5, 0xb5, 0x14, 0xac, 0x30, 0xb2, 0xf2, 0x14, 0xf6, 0xed, 0xdb, 0x07, 0xa7, 0x4f, 0xd7, 0xdf, 0xe9, 0x44, 0xa3, 0xc9, 0xaa, 0x55, 0x8a, 0xf3, 0xf7, 0xf7, 0x87, 0xde, 0xbd, 0x7b, 0x43, 0x8f, 0x1e, 0xb6, 0x77, 0x7d, 0x22, 0x9f, 0x43, 0x64, 0x3e, 0x2a, 0xfd, 0xb0, 0xd4, 0xf5, 0xeb, 0xd7, 0x01, 0x3f, 0x44, 0x85, 0x8b, 0xeb, 0xc8, 0x67, 0x9a, 0xe8, 0xc7, 0xa5, 0x70, 0xaf, 0xeb, 0x92, 0x92, 0x12, 0xc0, 0x9d, 0x98, 0x3b, 0x76, 0xec, 0x08, 0xf9, 0xf9, 0xf9, 0x10, 0x1f, 0x1f, 0x0f, 0xb9, 0xb9, 0xb9, 0x40, 0x56, 0x0c, 0xc2, 0xb0, 0x61, 0xc3, 0x24, 0xdf, 0x9c, 0x68, 0xdf, 0xbe, 0x3d, 0xdb, 0x00, 0x7c, 0xde, 0xbc, 0x79, 0xb0, 0x66, 0x4d, 0xfd, 0xf3, 0x66, 0x66, 0x98, 0x9e, 0x80, 0xdc, 0x12, 0x44, 0x47, 0xf0, 0xfc, 0xb2, 0x62, 0x62, 0x1f, 0x47, 0x96, 0xd8, 0x4b, 0x2e, 0x7f, 0x44, 0x1a, 0xd9, 0x72, 0x5a, 0x24, 0xfe, 0xc0, 0x81, 0x03, 0xb2, 0xbc, 0xc8, 0x6f, 0x79, 0x9c, 0x38, 0x71, 0x82, 0x95, 0x9f, 0x3a, 0x75, 0xaa, 0x15, 0xdd, 0x92, 0x5f, 0x2e, 0x4f, 0xb6, 0x02, 0x60, 0x72, 0xa4, 0x00, 0xe1, 0x92, 0x4e, 0x35, 0x7b, 0x75, 0x4b, 0xc9, 0x92, 0xc3, 0xe9, 0x32, 0x04, 0x11, 0x07, 0xad, 0x12, 0x51, 0x08, 0xd7, 0xae, 0x5d, 0xa3, 0x4f, 0xc5, 0x10, 0x26, 0x6b, 0x5e, 0x19, 0xcf, 0xc2, 0x85, 0x0b, 0x45, 0x9f, 0x4d, 0xc3, 0x56, 0x8a, 0xad, 0x13, 0x77, 0x57, 0x1f, 0x34, 0x68, 0x10, 0xe3, 0xeb, 0xda, 0xb5, 0x2b, 0x6d, 0x81, 0xd8, 0x0a, 0xf1, 0x58, 0xbf, 0x7e, 0x3d, 0x24, 0x25, 0x25, 0x89, 0x16, 0xf2, 0x92, 0x2d, 0x09, 0x80, 0x2c, 0xa9, 0x66, 0x65, 0x70, 0xd7, 0x76, 0xd4, 0x87, 0x07, 0xc2, 0x7c, 0xda, 0xbc, 0x79, 0x33, 0xc5, 0xad, 0x5e, 0xbd, 0x9a, 0x47, 0x49, 0x7e, 0x80, 0x98, 0x11, 0x9d, 0x04, 0xe8, 0x32, 0x04, 0x49, 0xd9, 0xba, 0x6c, 0xd9, 0x32, 0x68, 0xdb, 0xb6, 0xfe, 0x39, 0x70, 0xbf, 0x7e, 0xfd, 0x18, 0x1b, 0x56, 0xce, 0xcd, 0x9b, 0x37, 0xa1, 0x43, 0x87, 0x0e, 0x14, 0x87, 0x17, 0x70, 0xdc, 0x28, 0x02, 0x0f, 0xb2, 0x40, 0x18, 0x8e, 0x1e, 0x3d, 0x4a, 0xf1, 0x38, 0x9c, 0xe0, 0x9e, 0xfd, 0x6a, 0x52, 0x44, 0x44, 0x04, 0x63, 0x6f, 0xd5, 0xaa, 0x15, 0x1d, 0x9a, 0x10, 0x11, 0x1e, 0x1e, 0x4e, 0xf1, 0x81, 0x81, 0x81, 0x8c, 0x5e, 0x5c, 0x5c, 0xcc, 0x60, 0x57, 0x01, 0x86, 0xf5, 0x00, 0xf2, 0xf9, 0x5a, 0x87, 0x7c, 0xc2, 0xdd, 0x41, 0xf8, 0xe4, 0xaa, 0x99, 0x10, 0xaf, 0xdf, 0x29, 0x67, 0xd2, 0x1a, 0x35, 0x27, 0xd2, 0x9a, 0x39, 0x32, 0x8c, 0x70, 0xe4, 0xeb, 0x14, 0x6c, 0x3c, 0xc6, 0x6f, 0x1a, 0x2e, 0x58, 0xb0, 0x80, 0xca, 0x8e, 0x8d, 0x8d, 0xe5, 0xba, 0x74, 0xe9, 0xc2, 0x68, 0xc4, 0x31, 0xba, 0xfb, 0x02, 0xb9, 0x68, 0x5b, 0xe9, 0xde, 0xb9, 0x73, 0xa7, 0x88, 0x0f, 0x97, 0xe8, 0x87, 0x85, 0x85, 0x71, 0x01, 0x01, 0x01, 0x22, 0x3c, 0xca, 0x48, 0x4b, 0x4b, 0xe3, 0xf0, 0xfa, 0x41, 0x2e, 0xb2, 0x8c, 0xe6, 0xeb, 0xeb, 0xcb, 0x91, 0x4f, 0x1a, 0x50, 0xb9, 0xfc, 0xf2, 0x7c, 0xe4, 0x45, 0xfd, 0xc7, 0x8f, 0x1f, 0x67, 0xdf, 0x64, 0x40, 0x1c, 0x99, 0x3c, 0xd0, 0x5d, 0x1e, 0x78, 0x23, 0x56, 0xac, 0x58, 0xc1, 0x75, 0xee, 0xdc, 0x99, 0x23, 0x3d, 0x97, 0x1e, 0x64, 0xe2, 0xc0, 0xe4, 0xe2, 0xae, 0x2a, 0x3c, 0x1e, 0xb7, 0xce, 0x98, 0x35, 0x6b, 0x16, 0x5f, 0x4c, 0xd3, 0x59, 0x97, 0x1e, 0x80, 0x33, 0x17, 0xfc, 0xac, 0x25, 0x7e, 0x5d, 0x83, 0x4f, 0x38, 0x97, 0xe7, 0x3f, 0x75, 0x89, 0x5d, 0x1d, 0xbf, 0xcc, 0x21, 0x4c, 0xe4, 0xb3, 0x8c, 0x56, 0x38, 0xa4, 0xe3, 0xd4, 0x13, 0x67, 0x2c, 0x53, 0xa6, 0x4c, 0xa1, 0x9f, 0x1b, 0x21, 0x7b, 0x1e, 0x40, 0x6a, 0x6a, 0x2a, 0x95, 0x85, 0x9f, 0x1f, 0xc1, 0xeb, 0xc5, 0xae, 0x5d, 0xbb, 0xe8, 0x78, 0x4e, 0x2a, 0x0b, 0x50, 0xb6, 0x70, 0x28, 0xc1, 0x99, 0x17, 0xaf, 0x17, 0x67, 0x34, 0x7c, 0x42, 0xfd, 0x38, 0x2b, 0x22, 0x81, 0x64, 0x9f, 0x31, 0xa9, 0xa8, 0xa8, 0xa0, 0x43, 0x21, 0xcf, 0x83, 0x33, 0xa9, 0x9c, 0x9c, 0x1c, 0x76, 0x08, 0xa7, 0xd2, 0xe8, 0x23, 0x4f, 0xc3, 0x6b, 0x1b, 0xce, 0xa6, 0xf4, 0x48, 0xe6, 0xf3, 0x00, 0x3d, 0x6a, 0x51, 0x83, 0x0c, 0x5d, 0x7a, 0x80, 0x06, 0xfd, 0xbf, 0xf9, 0xa2, 0x66, 0x00, 0x5c, 0xdc, 0x04, 0xcc, 0x00, 0x98, 0x01, 0x70, 0x71, 0x0d, 0xb8, 0x58, 0xbd, 0xd9, 0x03, 0xcc, 0x00, 0xb8, 0xb8, 0x06, 0x5c, 0xac, 0xde, 0xec, 0x01, 0x66, 0x00, 0x5c, 0x5c, 0x03, 0x2e, 0x56, 0xff, 0x7f, 0x0f, 0x62, 0xce, 0xd5, 0xad, 0xd5, 0x92, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXAudioIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x32, 0x3a, 0x32, 0x39, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0xa6, 0xa8, 0x92, 0xdf, 0x00, 0x00, 0x09, 0x77, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x99, 0x57, 0x4c, 0x94, 0x4b, 0x14, 0xc7, 0xd9, 0xa2, 0xd2, 0x44, 0x40, 0x2c, 0x14, 0x51, 0xc0, 0xa8, 0xd8, 0x30, 0x8a, 0x77, 0xc5, 0xcb, 0xb5, 0xbc, 0x20, 0xf6, 0x68, 0xa2, 0x89, 0x3d, 0x24, 0xbe, 0x58, 0x5e, 0x7c, 0xd0, 0x18, 0xbd, 0x46, 0xe3, 0x8b, 0x4f, 0x1a, 0x8d, 0x31, 0xea, 0x83, 0x35, 0x6a, 0x2c, 0xb9, 0x24, 0x46, 0x0d, 0x8a, 0x41, 0x54, 0x04, 0x0d, 0x08, 0x42, 0x58, 0x69, 0xa2, 0xec, 0x52, 0xa4, 0x08, 0xb2, 0x74, 0x41, 0x81, 0xfb, 0x93, 0xb9, 0x99, 0x7c, 0x77, 0x29, 0xba, 0x1f, 0x4d, 0x93, 0xfd, 0xb2, 0xd9, 0x9c, 0x99, 0x39, 0x33, 0xdf, 0xf9, 0x9f, 0x36, 0x73, 0xe6, 0xd3, 0xb4, 0xb7, 0xb7, 0x3b, 0xfc, 0xce, 0x8f, 0xf6, 0x77, 0x16, 0xfe, 0xbb, 0xec, 0x76, 0x00, 0x83, 0x6d, 0x41, 0xbb, 0x05, 0x6c, 0xb4, 0x40, 0x65, 0x65, 0x65, 0x7e, 0x7e, 0xfe, 0xb7, 0x6f, 0xdf, 0x6c, 0x9c, 0xd7, 0x2d, 0xbb, 0xa6, 0x6f, 0xb3, 0x50, 0x71, 0x71, 0x71, 0xb5, 0xc5, 0xa2, 0xd7, 0xe9, 0xbc, 0xc7, 0x7a, 0xbb, 0x7b, 0xb8, 0x2b, 0x5f, 0xcb, 0x8b, 0x34, 0x1a, 0x4d, 0x5d, 0x5d, 0xdd, 0xf3, 0xe7, 0xcf, 0x1b, 0x1b, 0x1b, 0x97, 0x2e, 0x5d, 0xea, 0xea, 0xea, 0xaa, 0x64, 0x50, 0x47, 0xf7, 0x25, 0x80, 0x9c, 0x9c, 0x9c, 0x4c, 0xa3, 0xd1, 0xa1, 0x5d, 0xd3, 0xdc, 0xd2, 0xac, 0xd7, 0xeb, 0x02, 0x03, 0x02, 0xa6, 0x4f, 0x9f, 0xee, 0xec, 0xec, 0x2c, 0x24, 0x43, 0xf4, 0xf2, 0xf2, 0x72, 0x3f, 0x3f, 0x3f, 0xad, 0x56, 0xfb, 0xf0, 0xe1, 0xc3, 0xcf, 0x9f, 0x3f, 0xaf, 0x5f, 0xbf, 0x5e, 0x8e, 0xaa, 0x93, 0x9e, 0x59, 0xba, 0x23, 0x47, 0x8e, 0xa8, 0x9e, 0xac, 0x9c, 0xd8, 0xd0, 0xd0, 0x90, 0x9a, 0x9a, 0x86, 0x9a, 0x1f, 0x3f, 0x8e, 0x8d, 0x7f, 0x12, 0x57, 0x55, 0x59, 0xd9, 0xd4, 0xd8, 0x44, 0xa7, 0x97, 0xd7, 0x28, 0x27, 0x27, 0x47, 0x38, 0xf5, 0x7a, 0x3d, 0x42, 0xbf, 0x79, 0xf3, 0xc6, 0xdb, 0xdb, 0x7b, 0xda, 0xb4, 0x69, 0xa0, 0xcd, 0xcd, 0xcd, 0x9d, 0x31, 0x63, 0x86, 0x72, 0x11, 0x15, 0x74, 0x9f, 0x01, 0x30, 0x9b, 0xcd, 0x38, 0x46, 0x7c, 0x7c, 0x7c, 0x74, 0xf4, 0x3f, 0xc8, 0xf1, 0xb9, 0xba, 0xda, 0x64, 0x36, 0xe1, 0xeb, 0xad, 0xad, 0xad, 0x63, 0xc6, 0x8c, 0x71, 0x72, 0x72, 0x42, 0xfd, 0x82, 0x78, 0xf5, 0xea, 0xd5, 0xf8, 0xf1, 0xe3, 0x27, 0x4c, 0x98, 0xf0, 0xe2, 0xc5, 0x8b, 0xa1, 0x43, 0x87, 0xfa, 0xf8, 0xf8, 0xa8, 0x90, 0x5b, 0x4e, 0xe9, 0x33, 0x00, 0x65, 0x65, 0x65, 0xb5, 0xb5, 0x75, 0x0f, 0x1e, 0xdc, 0xd7, 0x68, 0x1c, 0xc6, 0x8e, 0x1d, 0xeb, 0xe2, 0xe2, 0x82, 0xca, 0x09, 0x09, 0x8b, 0xa5, 0x1a, 0x0c, 0xe3, 0xc6, 0x8d, 0x1b, 0x36, 0x6c, 0x98, 0xd1, 0x68, 0x0c, 0x08, 0x08, 0xc0, 0x85, 0xc0, 0x30, 0x67, 0xce, 0x1c, 0x7a, 0x9e, 0x3d, 0x7b, 0x36, 0x75, 0xea, 0x54, 0x47, 0xc7, 0xef, 0x26, 0x52, 0xf7, 0xf4, 0x59, 0x1a, 0x45, 0x2c, 0x44, 0x47, 0x14, 0x6f, 0x6f, 0x1f, 0x4f, 0x4f, 0x4f, 0x0f, 0x0f, 0x8f, 0x11, 0x23, 0x46, 0xb8, 0xbb, 0xbb, 0x17, 0x14, 0x14, 0x20, 0x6e, 0x42, 0x42, 0x02, 0xee, 0x0e, 0x0c, 0x5c, 0x28, 0x38, 0x38, 0x18, 0x6c, 0xa9, 0xa9, 0xa9, 0xb3, 0x66, 0xcd, 0xa2, 0x93, 0x51, 0x75, 0xa2, 0x8b, 0x59, 0x7d, 0x03, 0xa0, 0xa9, 0xa9, 0x29, 0x3b, 0x3b, 0x1b, 0xe9, 0x3d, 0x3c, 0xdc, 0xbd, 0xbc, 0xbc, 0x46, 0x8e, 0x1c, 0x29, 0x31, 0xd0, 0x24, 0x6f, 0x26, 0x25, 0x25, 0xbe, 0x7e, 0xfd, 0x1a, 0x6f, 0x01, 0xd5, 0xbb, 0x77, 0xef, 0x16, 0x2e, 0x5c, 0x98, 0x96, 0x96, 0x56, 0x5f, 0x5f, 0x3f, 0x7f, 0xfe, 0xfc, 0xcc, 0xcc, 0x4c, 0xe2, 0x5b, 0x35, 0x86, 0xbe, 0x01, 0x40, 0x76, 0x7f, 0xff, 0xfe, 0xbd, 0x4e, 0xa7, 0x77, 0x77, 0xf7, 0x70, 0x73, 0x73, 0x43, 0xf1, 0xc2, 0x02, 0xd0, 0xc3, 0x87, 0x0f, 0x87, 0xce, 0xcb, 0x7b, 0xf7, 0xf2, 0xe5, 0x4b, 0xc2, 0x60, 0xf2, 0xe4, 0xc9, 0x26, 0x93, 0x89, 0x90, 0x20, 0x0c, 0x92, 0x92, 0x92, 0xa6, 0x4c, 0x99, 0x82, 0x11, 0x80, 0x34, 0xc8, 0x00, 0x4a, 0x4a, 0x3e, 0xf2, 0x23, 0x64, 0x51, 0x3c, 0xd9, 0x1d, 0xb9, 0x79, 0x80, 0x21, 0x09, 0x9d, 0x4e, 0x47, 0xda, 0x49, 0x4c, 0x4c, 0x1c, 0x32, 0x64, 0x08, 0x59, 0x08, 0xad, 0x13, 0x03, 0x44, 0xc8, 0xd7, 0xaf, 0x5f, 0xf1, 0xa8, 0xbc, 0xbc, 0x3c, 0xe2, 0x44, 0x1d, 0x86, 0x3e, 0xb0, 0x00, 0xef, 0x36, 0x99, 0x0a, 0xb0, 0x40, 0x5d, 0x5d, 0x2d, 0x79, 0x86, 0x26, 0x18, 0x04, 0x0c, 0x30, 0xe0, 0x33, 0x18, 0x61, 0xd4, 0xa8, 0x51, 0x44, 0x79, 0x46, 0x46, 0x06, 0xa2, 0xa3, 0xf5, 0xaa, 0xaa, 0x2a, 0x8c, 0x80, 0x77, 0x7d, 0xf8, 0xf0, 0x81, 0x20, 0xa6, 0x59, 0x5b, 0x5b, 0x3b, 0x38, 0x00, 0x2c, 0x16, 0x4b, 0x4c, 0xcc, 0xc3, 0x94, 0x94, 0x94, 0xaa, 0xaa, 0x4a, 0x93, 0xc9, 0x4c, 0x98, 0xea, 0xf5, 0x43, 0xd8, 0x71, 0x9b, 0x9b, 0x9b, 0x09, 0x6b, 0x81, 0x44, 0xfc, 0xe3, 0x48, 0xa4, 0x5a, 0xbc, 0x85, 0x7e, 0x68, 0xbc, 0x2e, 0x28, 0x28, 0xa8, 0xb0, 0xb0, 0x10, 0x84, 0xc4, 0x34, 0xf0, 0x06, 0x01, 0x00, 0x81, 0x6b, 0x36, 0x97, 0x14, 0x14, 0x98, 0xd8, 0xb0, 0xf0, 0x9f, 0x8c, 0x8c, 0x74, 0x12, 0x28, 0xe2, 0xd2, 0x24, 0xcd, 0xe3, 0xdc, 0x6d, 0x6d, 0x6d, 0x68, 0x9a, 0x07, 0x1a, 0x77, 0xa2, 0x1f, 0xa8, 0x9f, 0x3e, 0x7d, 0x22, 0x9a, 0x2b, 0x2a, 0x2a, 0x60, 0x26, 0x7c, 0xd9, 0xfb, 0xb0, 0x5b, 0x69, 0x69, 0xe9, 0x40, 0x03, 0xa8, 0xa9, 0xa9, 0x49, 0x4f, 0x37, 0x56, 0x56, 0x56, 0x39, 0x38, 0x68, 0x5a, 0x5b, 0xbf, 0x7b, 0xff, 0xdb, 0xb7, 0x6f, 0x11, 0xc8, 0xc7, 0xc7, 0x1b, 0x47, 0x9f, 0x39, 0x73, 0x66, 0x64, 0x64, 0x24, 0xbb, 0x01, 0x62, 0xb1, 0x5b, 0x91, 0xf2, 0xc9, 0x51, 0x10, 0xd5, 0xd5, 0xd5, 0x64, 0x52, 0xd2, 0x14, 0xbb, 0x1e, 0xa3, 0x3c, 0x98, 0x8b, 0xa8, 0x50, 0x9d, 0x88, 0xd4, 0xc7, 0x00, 0xdb, 0x56, 0x43, 0x43, 0x3d, 0xa2, 0x18, 0x0c, 0x86, 0xc6, 0xc6, 0x26, 0xd2, 0x0b, 0x61, 0x90, 0x9c, 0x9c, 0x82, 0x63, 0xb4, 0xb4, 0xb4, 0x20, 0x37, 0x7e, 0x42, 0xaa, 0xc1, 0x97, 0x90, 0x1b, 0x48, 0xf8, 0x09, 0xc6, 0xe1, 0x34, 0x61, 0x36, 0x17, 0x62, 0x2e, 0x3c, 0x07, 0x5f, 0x42, 0x74, 0x2c, 0xc0, 0x22, 0x8c, 0xaa, 0xb3, 0x80, 0xca, 0x69, 0xbc, 0xcc, 0xd5, 0xd5, 0x05, 0xd3, 0x07, 0x05, 0x05, 0xea, 0x74, 0xd0, 0xae, 0x44, 0x2a, 0xe1, 0x88, 0x7c, 0x48, 0x8c, 0xe8, 0x29, 0x29, 0xaf, 0xbf, 0x7c, 0xf9, 0xc2, 0xfe, 0x80, 0xac, 0xe8, 0x1e, 0x35, 0xf3, 0x20, 0x25, 0xcd, 0xb2, 0xf2, 0x52, 0xa0, 0x8a, 0x70, 0x1f, 0x3d, 0x7a, 0x34, 0xd8, 0xf0, 0x31, 0xa6, 0x0c, 0x34, 0x00, 0x5e, 0x89, 0xe8, 0x1a, 0x4d, 0x9b, 0xb3, 0xb3, 0xcb, 0x82, 0x05, 0x0b, 0xee, 0xdd, 0xbb, 0xb7, 0x6b, 0xd7, 0xae, 0xe6, 0xe6, 0x96, 0xec, 0xec, 0x9c, 0xe0, 0xe0, 0x29, 0xf1, 0xf1, 0x4f, 0xb0, 0x8f, 0x88, 0x0d, 0x24, 0x43, 0xcd, 0x3c, 0x10, 0x88, 0x5b, 0x5e, 0x56, 0x86, 0xfb, 0xe1, 0x63, 0x44, 0x08, 0x61, 0x00, 0x2a, 0x10, 0x62, 0x2b, 0x75, 0x00, 0xd4, 0xbb, 0x10, 0xef, 0x43, 0xf1, 0x45, 0x45, 0x85, 0xc8, 0xf5, 0xe7, 0x9f, 0xe1, 0xf8, 0x74, 0x71, 0xf1, 0x47, 0xb2, 0x26, 0x82, 0x92, 0x16, 0x03, 0x02, 0x82, 0xb2, 0xb2, 0xb2, 0xf1, 0x6c, 0xfc, 0x07, 0xad, 0x23, 0x6b, 0x07, 0x84, 0x76, 0xdc, 0x06, 0x7f, 0x03, 0x1a, 0x04, 0xa2, 0x33, 0xca, 0x3a, 0xc4, 0x37, 0x48, 0x06, 0x01, 0xc0, 0xa4, 0x49, 0x93, 0xd2, 0xde, 0xa4, 0x7f, 0x2c, 0xa9, 0x68, 0x6b, 0xd3, 0x86, 0x86, 0xce, 0x4d, 0x4c, 0x4c, 0x40, 0xe7, 0x1d, 0x19, 0xe6, 0x93, 0x9f, 0x9f, 0x2f, 0x25, 0x01, 0xc9, 0x91, 0x2d, 0x8c, 0xdd, 0x4a, 0x60, 0x60, 0x8b, 0x00, 0x09, 0x7e, 0x65, 0xa9, 0xb1, 0x60, 0x04, 0x29, 0x31, 0x66, 0x81, 0x4d, 0x36, 0x6d, 0x22, 0x7a, 0x65, 0x01, 0xd4, 0xe6, 0xe4, 0xe8, 0x94, 0x90, 0xf0, 0x9c, 0xfd, 0x28, 0x34, 0xd4, 0x80, 0x7c, 0x89, 0x89, 0x2f, 0xc8, 0x8f, 0x15, 0x15, 0xe5, 0x9c, 0xe1, 0xfe, 0x30, 0x18, 0xd8, 0x9e, 0xc0, 0x40, 0x30, 0xf0, 0x10, 0x1b, 0x04, 0x37, 0x0f, 0x04, 0xa7, 0x20, 0xbc, 0xcb, 0x26, 0x41, 0xbb, 0x63, 0xee, 0x15, 0x00, 0x16, 0x0d, 0x0b, 0x33, 0x64, 0x65, 0x19, 0x39, 0x48, 0xa0, 0xd7, 0xad, 0x5b, 0xa3, 0x38, 0x2f, 0xc4, 0xc6, 0x3e, 0x4a, 0x4e, 0x4e, 0xbe, 0x7f, 0xff, 0x7e, 0x6d, 0x4d, 0x4d, 0x44, 0xc4, 0x12, 0xdc, 0x89, 0x1c, 0x8f, 0xc4, 0xf8, 0x18, 0x0f, 0x6c, 0x18, 0x41, 0xab, 0xd5, 0xf1, 0xdf, 0x9d, 0x4c, 0x36, 0xf5, 0xf7, 0x0a, 0x00, 0x6e, 0x4d, 0x1e, 0x5c, 0xbe, 0x2c, 0x32, 0x37, 0x2f, 0x27, 0x24, 0x64, 0xc6, 0xaa, 0x55, 0xcb, 0x0f, 0x1e, 0xfc, 0xdb, 0xd1, 0xd1, 0xc9, 0xd7, 0xd7, 0xd7, 0xc7, 0xc7, 0x97, 0xda, 0x17, 0x7d, 0x87, 0x87, 0xff, 0x85, 0xfa, 0x39, 0xc0, 0x61, 0x17, 0x72, 0x14, 0xff, 0x6c, 0x6a, 0xe4, 0x4d, 0xdc, 0xc6, 0x26, 0x41, 0xbb, 0x63, 0x56, 0x5f, 0xd0, 0x20, 0x3d, 0x5a, 0x44, 0x44, 0x7f, 0x7f, 0x7f, 0x9d, 0x4e, 0x63, 0x34, 0x66, 0xb2, 0x5f, 0x79, 0x7a, 0x8e, 0x1c, 0xee, 0xe6, 0xa6, 0xd5, 0x68, 0xe7, 0xcd, 0x33, 0x40, 0x37, 0x35, 0x35, 0xe2, 0xdc, 0x41, 0x41, 0x13, 0x21, 0x38, 0xb1, 0x71, 0x1a, 0x25, 0x76, 0x57, 0xad, 0x5c, 0xe5, 0xef, 0x3f, 0x8e, 0x13, 0x91, 0x6a, 0xbf, 0x57, 0x82, 0x51, 0x59, 0xd4, 0x23, 0x3d, 0xab, 0xe0, 0xf4, 0x00, 0xc0, 0x2b, 0x68, 0xb2, 0x0d, 0xf3, 0x20, 0x13, 0xea, 0x9f, 0x38, 0x71, 0x22, 0x99, 0x91, 0x14, 0x44, 0x0c, 0xe0, 0x36, 0xf8, 0x4f, 0x6b, 0x6b, 0x3b, 0x2d, 0x0e, 0x11, 0x1c, 0xef, 0x28, 0x16, 0x28, 0x7a, 0xc2, 0xc2, 0xe6, 0x75, 0xf8, 0x12, 0x65, 0x90, 0x46, 0x29, 0x90, 0xad, 0x74, 0xaf, 0x00, 0x20, 0x01, 0xe9, 0x05, 0x00, 0x88, 0x08, 0x01, 0x18, 0x12, 0x0e, 0x18, 0xc0, 0x43, 0x7e, 0xe4, 0xf8, 0x80, 0x9f, 0xd0, 0x0f, 0x8c, 0xfc, 0xfc, 0xf7, 0xf5, 0x0d, 0xf5, 0xe4, 0x4d, 0xac, 0x86, 0x11, 0x42, 0x43, 0x43, 0x39, 0x9f, 0x02, 0x12, 0x7a, 0x70, 0x00, 0x08, 0x3d, 0x21, 0x28, 0x0f, 0x30, 0x04, 0x12, 0xfe, 0x69, 0x22, 0x90, 0xc8, 0xf1, 0x82, 0x87, 0x4e, 0x80, 0x61, 0x2b, 0x40, 0x82, 0x04, 0x0c, 0x88, 0x2e, 0x8e, 0x46, 0xbd, 0x97, 0x9e, 0x57, 0xa8, 0xb4, 0x80, 0xad, 0x86, 0xee, 0x3f, 0xfe, 0x5e, 0x65, 0xa1, 0xfe, 0x13, 0xeb, 0xe7, 0x57, 0xb6, 0x03, 0xf8, 0x79, 0x5d, 0xf5, 0x0f, 0xa7, 0xdd, 0x02, 0xfd, 0xa3, 0xd7, 0x9f, 0x5f, 0xd5, 0x6e, 0x81, 0x1f, 0xe9, 0x4a, 0x6c, 0x11, 0x6c, 0x67, 0x9d, 0x19, 0xb9, 0x5d, 0x3c, 0x7a, 0xf4, 0x28, 0xe7, 0x8b, 0xce, 0x43, 0x36, 0xf4, 0x74, 0xec, 0x45, 0xfd, 0xf5, 0x77, 0xe0, 0xc0, 0x01, 0x79, 0xe0, 0xd9, 0xb1, 0x63, 0x87, 0xd5, 0x6b, 0x36, 0x6c, 0xd8, 0x80, 0xa0, 0x57, 0xaf, 0x5e, 0xb5, 0xea, 0xb7, 0xa9, 0xd9, 0xbf, 0x2e, 0x44, 0xa1, 0x48, 0x8d, 0xcf, 0x85, 0x05, 0x82, 0xb2, 0x0d, 0x5b, 0xe9, 0x95, 0x32, 0x1a, 0x78, 0x14, 0xc7, 0x56, 0xfd, 0xb6, 0x35, 0x7b, 0x86, 0x2b, 0x4e, 0x07, 0x3d, 0xf3, 0xfc, 0x70, 0xf4, 0xc4, 0x89, 0x13, 0xc8, 0xb4, 0x79, 0xf3, 0xe6, 0xce, 0x9c, 0x9c, 0x32, 0x3a, 0x77, 0xda, 0xd4, 0xd3, 0x85, 0x05, 0xb8, 0xae, 0xda, 0xb6, 0x6d, 0xdb, 0xdc, 0xb9, 0x73, 0xd1, 0x10, 0x67, 0x77, 0x2e, 0xc1, 0xef, 0xde, 0xbd, 0x2b, 0xb5, 0x72, 0xec, 0xd8, 0xb1, 0x79, 0x1d, 0x0f, 0x55, 0x0b, 0x9d, 0xe7, 0xcf, 0x9f, 0x0f, 0xa3, 0xa8, 0x31, 0x18, 0xa8, 0x60, 0x24, 0x0f, 0xc4, 0xad, 0x5b, 0xb7, 0xb6, 0x6c, 0xd9, 0x12, 0x11, 0x11, 0xc1, 0x17, 0xa0, 0xce, 0xba, 0x8f, 0x8d, 0x8d, 0xe5, 0x82, 0x9a, 0x59, 0xe1, 0xe1, 0xe1, 0x27, 0x4f, 0x9e, 0x54, 0x4e, 0x14, 0x74, 0x49, 0x49, 0x49, 0x54, 0x54, 0x14, 0x47, 0x6e, 0xca, 0x6e, 0x04, 0xd8, 0xb7, 0x6f, 0x5f, 0xb7, 0x15, 0x5c, 0x67, 0xb8, 0x88, 0xc7, 0x2a, 0x9c, 0xc9, 0xc0, 0xc0, 0xb5, 0x07, 0x34, 0x67, 0x2f, 0x2a, 0x12, 0xc1, 0x29, 0x3f, 0x0a, 0xdd, 0xbe, 0x7d, 0x9b, 0x9e, 0x25, 0x4b, 0x96, 0x88, 0x57, 0x1e, 0x3f, 0x7e, 0x5c, 0x2e, 0xb5, 0x7d, 0xfb, 0x76, 0xd1, 0x29, 0xfe, 0xc5, 0x79, 0x53, 0x69, 0x81, 0xfd, 0xfb, 0xf7, 0x4b, 0x06, 0x94, 0x25, 0x27, 0x0a, 0x82, 0xe0, 0xa6, 0xcc, 0x97, 0x0c, 0x82, 0xe0, 0xcb, 0x08, 0xb7, 0x91, 0x56, 0x9c, 0x34, 0xff, 0xbb, 0xf0, 0x50, 0x0e, 0x44, 0x47, 0x47, 0x23, 0x0d, 0x57, 0xc7, 0x74, 0x52, 0xbf, 0x0a, 0x0c, 0x32, 0xd4, 0xb8, 0xcd, 0x0c, 0x0c, 0x0c, 0x64, 0x51, 0x01, 0x80, 0x1c, 0x32, 0x7b, 0xf6, 0x6c, 0x9a, 0x12, 0x80, 0x34, 0x17, 0xda, 0xbd, 0x78, 0xf1, 0xe2, 0xe2, 0xc5, 0x8b, 0x85, 0x04, 0x9b, 0x36, 0x6d, 0x92, 0x6f, 0xa1, 0x54, 0x88, 0x89, 0x89, 0x59, 0xb3, 0x66, 0x0d, 0x43, 0x56, 0x00, 0x38, 0x90, 0xf3, 0x69, 0x90, 0x7e, 0x6a, 0xbd, 0x33, 0x67, 0xce, 0xf0, 0x6d, 0xe1, 0xf0, 0xe1, 0xc3, 0x5c, 0x1c, 0xd1, 0xb3, 0x71, 0xe3, 0x46, 0xb9, 0x82, 0x24, 0xba, 0xb8, 0xd8, 0x62, 0xdd, 0xb8, 0xb8, 0x38, 0x24, 0xa6, 0xfc, 0xa3, 0xfe, 0x60, 0x21, 0x08, 0x6c, 0x2a, 0xe4, 0xe0, 0xfa, 0x96, 0x4e, 0x68, 0xa1, 0x57, 0xe0, 0x59, 0x45, 0xe1, 0xa5, 0x4b, 0x97, 0x18, 0xa5, 0x9f, 0xcf, 0x47, 0x1c, 0x98, 0xf1, 0x04, 0xee, 0x49, 0xb9, 0xd6, 0x15, 0xd3, 0xc5, 0x3f, 0x8e, 0xc1, 0xc5, 0xe3, 0xd3, 0xa7, 0x4f, 0x95, 0x9d, 0x82, 0xc6, 0x81, 0xf9, 0x12, 0x05, 0xbd, 0x77, 0xef, 0xde, 0x9d, 0x3b, 0x77, 0x42, 0xe0, 0x84, 0xc0, 0xb8, 0x7e, 0xfd, 0x3a, 0x9a, 0xe5, 0x58, 0x2e, 0xd3, 0x9a, 0xe0, 0xb7, 0x06, 0x80, 0x02, 0x56, 0xae, 0x5c, 0xf9, 0xe8, 0xd1, 0x23, 0x86, 0x29, 0x4a, 0x90, 0x12, 0x23, 0x40, 0x33, 0x53, 0x4c, 0xf8, 0xe1, 0x3f, 0x75, 0x3d, 0x3c, 0xcb, 0x96, 0x2d, 0x43, 0x7a, 0xc1, 0xbc, 0x62, 0xc5, 0x0a, 0x74, 0xf9, 0xc3, 0x89, 0x82, 0x41, 0x4c, 0x87, 0x5e, 0xbd, 0x7a, 0xb5, 0x9c, 0x02, 0x0d, 0x00, 0x51, 0x5b, 0x93, 0xd6, 0x64, 0x3f, 0x84, 0x75, 0x10, 0x5f, 0xbb, 0x76, 0x4d, 0x48, 0x7f, 0xe7, 0xce, 0x1d, 0xae, 0x6e, 0x28, 0x08, 0xd7, 0xae, 0x5d, 0xab, 0x9c, 0x00, 0x2d, 0x74, 0xc0, 0x75, 0x83, 0xe8, 0x27, 0x3c, 0x94, 0x0c, 0x68, 0x97, 0x26, 0xd7, 0x2a, 0xb2, 0x53, 0x49, 0xcb, 0xce, 0xee, 0x08, 0x0c, 0x2e, 0x86, 0xb8, 0xc7, 0x96, 0x3c, 0x82, 0x46, 0x9b, 0x9d, 0xef, 0xbf, 0xac, 0x01, 0x60, 0x41, 0xa6, 0x51, 0x34, 0xe1, 0x48, 0x94, 0x7c, 0xa4, 0x39, 0xae, 0x12, 0xe8, 0xc1, 0xe7, 0xe4, 0x72, 0x22, 0x2a, 0xf8, 0x48, 0x4a, 0x27, 0x6e, 0x20, 0xa6, 0x48, 0x06, 0x3e, 0xbd, 0xc0, 0xc9, 0x28, 0xdf, 0x63, 0x20, 0x78, 0x37, 0x0e, 0x29, 0xe7, 0x0a, 0x02, 0x66, 0xf6, 0x66, 0x72, 0x34, 0x4d, 0x25, 0x4d, 0x93, 0x2b, 0x47, 0xb2, 0x1f, 0xc4, 0xe9, 0xd3, 0xa7, 0x8b, 0x8a, 0x8a, 0x20, 0xd2, 0xd3, 0xd3, 0x51, 0x3f, 0x04, 0xe9, 0x88, 0xdb, 0x6c, 0x88, 0xff, 0x3d, 0xcc, 0x57, 0x3e, 0x97, 0x2f, 0x5f, 0x16, 0xc3, 0x84, 0xe6, 0xba, 0x75, 0xeb, 0xa8, 0xd0, 0x45, 0x93, 0xea, 0xf6, 0xca, 0x95, 0x2b, 0x82, 0x93, 0xfd, 0x5f, 0x74, 0xf2, 0xd9, 0x5d, 0x10, 0xfc, 0xe3, 0x6f, 0x37, 0x6f, 0xde, 0x84, 0x81, 0x28, 0x97, 0xaf, 0x21, 0x0f, 0x4a, 0x06, 0xf4, 0x77, 0xe8, 0xd0, 0x21, 0x18, 0xb0, 0xad, 0x08, 0x4a, 0x39, 0x24, 0xa6, 0x9f, 0x3d, 0x7b, 0x56, 0xac, 0x7f, 0xee, 0xdc, 0x39, 0x31, 0x44, 0xfd, 0x19, 0x12, 0x12, 0x22, 0x68, 0xcc, 0xce, 0x57, 0x36, 0xc1, 0xa0, 0xfc, 0xb7, 0xce, 0x42, 0x28, 0x66, 0xf7, 0xee, 0xdd, 0x42, 0xc7, 0xcc, 0xe4, 0x2b, 0xe2, 0xa9, 0x53, 0xa7, 0x48, 0x6a, 0x58, 0xe3, 0xc2, 0x85, 0x0b, 0x62, 0x26, 0xce, 0x43, 0x0a, 0x17, 0xeb, 0x2e, 0x5a, 0xb4, 0x68, 0xcf, 0x9e, 0x3d, 0xd0, 0xec, 0x18, 0xe8, 0x49, 0x30, 0xf0, 0x26, 0xbe, 0x7c, 0x09, 0x06, 0x5c, 0x82, 0x4c, 0x2f, 0xaa, 0x64, 0x09, 0x40, 0x22, 0x14, 0x3c, 0xfc, 0x93, 0xa9, 0x25, 0x00, 0x16, 0xb9, 0x71, 0xe3, 0x86, 0xf2, 0xba, 0x17, 0xab, 0xf2, 0xa1, 0x56, 0x29, 0xb7, 0xa4, 0xbb, 0xae, 0x89, 0x31, 0x2e, 0xc1, 0x84, 0x82, 0x3b, 0xe7, 0x63, 0xf9, 0x4a, 0x34, 0x0d, 0x2a, 0x09, 0x55, 0xf6, 0x4b, 0x82, 0x0b, 0x39, 0x76, 0x1f, 0x62, 0x4e, 0xe4, 0x2b, 0xd9, 0xff, 0xf3, 0x04, 0x89, 0x9f, 0x45, 0x48, 0x62, 0xc2, 0xa9, 0xba, 0x9c, 0xd8, 0x35, 0x80, 0x2e, 0x59, 0x7f, 0xcd, 0x4e, 0xeb, 0x20, 0xfe, 0x35, 0xa5, 0xec, 0x41, 0x2a, 0x3b, 0x80, 0x1e, 0x94, 0x33, 0x20, 0x43, 0x76, 0x0b, 0x0c, 0x88, 0x9a, 0x7b, 0x78, 0x89, 0xdd, 0x02, 0x3d, 0x28, 0x67, 0x40, 0x86, 0xec, 0x16, 0x18, 0x10, 0x35, 0xf7, 0xf0, 0x12, 0xbb, 0x05, 0x7a, 0x50, 0xce, 0x80, 0x0c, 0xfd, 0x0b, 0xbe, 0x35, 0x47, 0x3f, 0x08, 0xc9, 0x3b, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXAudioIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x1e, 0x78, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x5d, 0x09, 0x7c, 0x94, 0xd5, 0xb5, 0xff, 0x27, 0x93, 0x4c, 0x32, 0xd9, 0xc9, 0x46, 0xc8, 0x06, 0x04, 0x12, 0x08, 0x09, 0x24, 0x40, 0x58, 0x02, 0x28, 0x9b, 0x40, 0x15, 0xaa, 0xaf, 0x08, 0x42, 0x9f, 0xd6, 0xa5, 0x82, 0x88, 0x3f, 0xea, 0xd3, 0xb6, 0x56, 0x5b, 0x5f, 0x4b, 0x5d, 0xb0, 0x5a, 0xab, 0x3e, 0xad, 0xc2, 0xd3, 0xa7, 0xa8, 0x45, 0x7c, 0xe2, 0x82, 0xa2, 0x65, 0x91, 0x45, 0xd9, 0x21, 0x80, 0x81, 0x40, 0x08, 0xbb, 0x40, 0x12, 0x92, 0x90, 0x7d, 0x5f, 0x67, 0xf2, 0xbd, 0xff, 0xf9, 0xe0, 0x1b, 0x26, 0x99, 0x49, 0x26, 0xcb, 0x84, 0xa5, 0x2f, 0xf7, 0xf7, 0x9b, 0xf9, 0xee, 0x77, 0xef, 0xb9, 0xdb, 0x39, 0xf7, 0x9e, 0xbb, 0x9d, 0x73, 0x3e, 0x27, 0x85, 0x0e, 0xed, 0x70, 0xce, 0xad, 0xc1, 0x7e, 0x56, 0x59, 0x6f, 0x15, 0x6d, 0x33, 0x81, 0xab, 0xab, 0x0e, 0x5e, 0x5e, 0x5e, 0x48, 0xd6, 0x11, 0xbe, 0x59, 0x05, 0x6c, 0x26, 0xc8, 0x2d, 0xae, 0x82, 0xab, 0xab, 0x2b, 0xe6, 0x4d, 0x9f, 0x8e, 0x06, 0xa6, 0x59, 0x5d, 0x66, 0x34, 0x97, 0xe4, 0xd4, 0xbc, 0x0d, 0x46, 0xa3, 0x11, 0xbf, 0xfe, 0xed, 0xef, 0xd0, 0x6f, 0xc8, 0x18, 0xe4, 0x64, 0x9d, 0x06, 0x0b, 0xc3, 0x93, 0x4f, 0xfe, 0x16, 0xde, 0xae, 0x2e, 0x97, 0x12, 0x49, 0x02, 0x4b, 0xf7, 0xe9, 0x67, 0x9f, 0x29, 0x09, 0xc3, 0x47, 0x2a, 0xb7, 0x4d, 0x9f, 0xae, 0x2c, 0xff, 0xf4, 0x5b, 0x25, 0x36, 0x7e, 0x88, 0x65, 0xb4, 0xc2, 0x2a, 0x5a, 0xbb, 0xe4, 0xd1, 0xa3, 0x95, 0x99, 0x3f, 0xff, 0x85, 0x75, 0x04, 0x43, 0xac, 0xaa, 0x64, 0xae, 0x6c, 0x0b, 0x1e, 0x9b, 0x8d, 0x6e, 0x01, 0x56, 0x0d, 0x36, 0x27, 0x58, 0xb9, 0x72, 0xa5, 0x19, 0xee, 0x77, 0xbf, 0xfb, 0x9d, 0xd9, 0xef, 0xe4, 0xe4, 0x84, 0xc6, 0xc6, 0xc6, 0x2b, 0xef, 0x52, 0x51, 0xf3, 0x5b, 0x1b, 0x3c, 0xe6, 0x12, 0xda, 0x00, 0xab, 0x82, 0x38, 0xae, 0xd1, 0x2b, 0x72, 0xab, 0x6d, 0x16, 0x6a, 0x55, 0xa5, 0xf7, 0x56, 0x7e, 0x01, 0x5f, 0xbf, 0x1e, 0x38, 0xb6, 0xf4, 0x35, 0x35, 0x41, 0x63, 0xf3, 0x26, 0xda, 0xa2, 0x8e, 0xbb, 0xbb, 0xbb, 0x12, 0x17, 0x3f, 0x58, 0x79, 0xe5, 0x1f, 0x9f, 0x5b, 0x45, 0x5f, 0xee, 0x20, 0x57, 0x4a, 0x1f, 0x38, 0x28, 0x1e, 0x5f, 0xad, 0xf9, 0x0a, 0xe9, 0x67, 0x4b, 0x71, 0xb6, 0xb4, 0x0c, 0x59, 0x65, 0x95, 0x88, 0xf0, 0xf5, 0x32, 0x03, 0x58, 0x35, 0x7a, 0xce, 0x3d, 0x0f, 0xa0, 0x24, 0x2f, 0x1b, 0xd5, 0xd5, 0xd5, 0x48, 0x3f, 0x76, 0x14, 0xd1, 0x31, 0x71, 0xd8, 0x9f, 0xb2, 0xcb, 0x9c, 0xc0, 0xaa, 0x2f, 0x99, 0x4c, 0x26, 0xe5, 0x96, 0x5b, 0x6f, 0xb7, 0xaa, 0x8a, 0x16, 0x60, 0x55, 0xc2, 0x95, 0xac, 0x6c, 0xfb, 0xac, 0xb0, 0xa4, 0x81, 0x49, 0x97, 0xb0, 0xe5, 0xd4, 0x04, 0x12, 0xe9, 0xe6, 0xe6, 0x06, 0x79, 0x06, 0x05, 0x05, 0x21, 0x27, 0x27, 0xa7, 0x09, 0xec, 0x5d, 0x77, 0xdd, 0xa5, 0xc6, 0x49, 0xa0, 0xe3, 0xaa, 0xd4, 0xa4, 0x08, 0x8b, 0x97, 0x76, 0x97, 0x60, 0x91, 0xb6, 0x4d, 0xde, 0x16, 0x91, 0xd4, 0x3c, 0xf5, 0xfb, 0x9f, 0x6d, 0xc4, 0xdf, 0xff, 0x67, 0x25, 0x64, 0xec, 0x6c, 0x4e, 0x3f, 0xa5, 0x46, 0x37, 0xc8, 0x4b, 0xf3, 0xae, 0xd6, 0x3c, 0xa1, 0x46, 0xbf, 0xd6, 0x9e, 0x3b, 0xf6, 0xec, 0x53, 0xa6, 0xfd, 0xf4, 0x4e, 0xc5, 0xdb, 0xc7, 0x47, 0x09, 0x09, 0xef, 0xad, 0x3c, 0xfe, 0xbf, 0xff, 0x54, 0x16, 0xbf, 0xb6, 0xec, 0x52, 0x12, 0xf6, 0x0b, 0xa5, 0x91, 0x3d, 0xba, 0x05, 0xd7, 0x26, 0x14, 0x09, 0x2b, 0x4c, 0xd9, 0x77, 0x00, 0x67, 0x0b, 0x6a, 0x90, 0x90, 0x94, 0x80, 0xb4, 0xb4, 0x0c, 0x2c, 0x7d, 0x69, 0x31, 0x22, 0x07, 0xc4, 0x62, 0xea, 0xf4, 0xdb, 0x11, 0x37, 0x65, 0x32, 0x46, 0x79, 0x08, 0x17, 0xb6, 0x76, 0x76, 0x51, 0xf4, 0xb3, 0x59, 0x73, 0xf0, 0xe0, 0xc2, 0x47, 0xa5, 0x3f, 0x60, 0xcd, 0x47, 0xcb, 0xb0, 0x6c, 0xc9, 0xd3, 0x28, 0xcc, 0xb9, 0x80, 0xda, 0xaa, 0x72, 0xe4, 0x5f, 0xc8, 0xc2, 0xea, 0x0f, 0x97, 0xe3, 0xe3, 0xe7, 0x5e, 0xb0, 0xce, 0xf9, 0x72, 0x48, 0x9b, 0x5a, 0xf0, 0xcb, 0x79, 0xf3, 0xf0, 0xf9, 0xe7, 0x5f, 0xc0, 0xd8, 0x60, 0xc4, 0xc8, 0xa4, 0xe1, 0x44, 0xbb, 0x11, 0x6f, 0xbf, 0xf3, 0x2e, 0x76, 0x6c, 0xdf, 0x06, 0x45, 0xef, 0x03, 0x93, 0xb1, 0x1e, 0x0b, 0xe7, 0xdd, 0x67, 0xb3, 0x90, 0x36, 0x15, 0x60, 0x33, 0x65, 0x1b, 0x03, 0xed, 0xa2, 0xa8, 0x79, 0x3e, 0xdb, 0xb7, 0x6f, 0xc7, 0xfe, 0xfd, 0xfb, 0xcd, 0xc1, 0x06, 0x83, 0xc1, 0xec, 0xb7, 0xe9, 0xb1, 0x24, 0xfe, 0x94, 0x29, 0x53, 0x94, 0xb4, 0xb4, 0x34, 0x65, 0xf2, 0xe4, 0xc9, 0xca, 0xf9, 0xf3, 0xe7, 0xd5, 0x4e, 0xb8, 0x6e, 0xdd, 0x3a, 0x25, 0x30, 0x30, 0x50, 0x05, 0x63, 0x06, 0x8a, 0x87, 0x87, 0xc7, 0xa5, 0xce, 0xc9, 0x90, 0xa7, 0x9f, 0x7e, 0xda, 0xec, 0x17, 0x00, 0x89, 0x7f, 0xef, 0xbd, 0xf7, 0x9a, 0x86, 0xa9, 0x29, 0x2f, 0xff, 0x09, 0x80, 0xf6, 0xd3, 0x12, 0x74, 0xe4, 0xdd, 0xdf, 0xdf, 0xdf, 0x9c, 0xed, 0xf5, 0x47, 0x03, 0xb6, 0xa8, 0x5d, 0xee, 0xc6, 0x6f, 0x41, 0xbb, 0xbb, 0xa9, 0x30, 0xb7, 0x4f, 0xca, 0xeb, 0x50, 0x61, 0xba, 0xb2, 0x80, 0x68, 0x15, 0x67, 0x66, 0x72, 0xb7, 0xe2, 0xd9, 0xb4, 0xeb, 0x90, 0xb2, 0x78, 0xc9, 0xab, 0x4a, 0xfa, 0xc9, 0xb3, 0x64, 0x6c, 0x64, 0x6e, 0x97, 0xdd, 0x47, 0x67, 0xf3, 0x35, 0x6f, 0x8b, 0x4f, 0xab, 0x59, 0xb3, 0x39, 0xa4, 0xc9, 0x64, 0x54, 0x3e, 0xdb, 0xb0, 0x57, 0xf1, 0xf6, 0xf6, 0x56, 0x7a, 0xf4, 0xf0, 0x57, 0xe6, 0xbd, 0xf1, 0x9e, 0xb2, 0xed, 0x87, 0x23, 0xca, 0x9b, 0x27, 0x72, 0x54, 0xd0, 0x2d, 0x25, 0xb5, 0xcd, 0x93, 0x34, 0x79, 0xb7, 0x8b, 0x22, 0x67, 0x67, 0x1d, 0x42, 0x7b, 0xf6, 0x40, 0x43, 0x43, 0x03, 0x64, 0x99, 0x71, 0x96, 0x6b, 0xdd, 0xa7, 0x52, 0xd2, 0x70, 0x87, 0x97, 0x13, 0x3e, 0xce, 0xab, 0xc4, 0x24, 0x3f, 0x37, 0xec, 0xaa, 0x35, 0xb5, 0x88, 0x25, 0xbb, 0x05, 0x48, 0xca, 0x5e, 0x3d, 0x3c, 0xf1, 0x93, 0x3b, 0xe6, 0xa2, 0xae, 0xb6, 0x06, 0x91, 0x55, 0xc5, 0xb8, 0xcb, 0xcb, 0x80, 0x67, 0x8f, 0x9c, 0xc1, 0x64, 0x5d, 0x1d, 0xbe, 0xad, 0x34, 0x62, 0xac, 0xbb, 0x0e, 0x2d, 0x51, 0xa4, 0x4d, 0x05, 0xfc, 0x65, 0xc9, 0x33, 0x58, 0xf6, 0xf7, 0x97, 0xf1, 0xfc, 0x5f, 0xfe, 0x86, 0xc7, 0x7e, 0xf9, 0x00, 0x6a, 0xca, 0x4b, 0x11, 0x93, 0x93, 0x8d, 0xc7, 0xd6, 0x6c, 0x82, 0xdf, 0xc9, 0xa3, 0x6a, 0xed, 0x57, 0x56, 0xd9, 0x2e, 0xc2, 0x6e, 0x01, 0xfe, 0x81, 0x81, 0x88, 0x4d, 0x48, 0xc2, 0x6f, 0x1f, 0x7f, 0x1c, 0x33, 0x6e, 0x9f, 0x81, 0x83, 0x07, 0xd3, 0x98, 0xa1, 0x0b, 0xb2, 0xf3, 0xf2, 0xe0, 0x1e, 0xdc, 0x1b, 0x9f, 0xa7, 0x5e, 0x2a, 0xe0, 0x17, 0x9e, 0xb6, 0xb3, 0xb2, 0x3b, 0xd0, 0x66, 0xfd, 0xfb, 0xbd, 0xc8, 0xc9, 0xce, 0x42, 0x6c, 0x4c, 0x7f, 0xd4, 0x70, 0x7d, 0x35, 0xe3, 0xce, 0x39, 0xd8, 0xfd, 0xfd, 0x06, 0xae, 0xb3, 0xce, 0xc0, 0xcb, 0xc7, 0x07, 0x20, 0x8d, 0x3e, 0x59, 0xf1, 0x01, 0xbc, 0x3c, 0xdc, 0xd5, 0x96, 0x34, 0xff, 0xb3, 0x5d, 0xac, 0x05, 0x94, 0x8b, 0x8b, 0x1e, 0x3e, 0xbe, 0x7e, 0x38, 0x75, 0xea, 0x14, 0x8a, 0x4b, 0x8a, 0xb1, 0x72, 0xf9, 0x7f, 0x23, 0x2f, 0x27, 0x1f, 0x81, 0x3d, 0x7b, 0xe1, 0x70, 0xea, 0x01, 0x64, 0x9d, 0xff, 0x11, 0x9b, 0xb6, 0xa5, 0x58, 0xa4, 0x68, 0xea, 0xb5, 0x5a, 0x3e, 0x36, 0x8d, 0x06, 0xc6, 0x8f, 0x49, 0xc2, 0x8b, 0x2f, 0xbd, 0x8c, 0x0b, 0xd9, 0x99, 0xe8, 0x1f, 0x3d, 0x10, 0xf7, 0xdd, 0x33, 0x17, 0xf3, 0x1f, 0x5a, 0x80, 0x95, 0x1f, 0xad, 0xc0, 0x6d, 0x53, 0x26, 0x00, 0x7a, 0x5f, 0xdc, 0x3c, 0x32, 0xae, 0x79, 0xb2, 0x2b, 0xef, 0x4d, 0x3a, 0x6d, 0x0b, 0x2f, 0xf3, 0xe6, 0xcf, 0x53, 0x12, 0x93, 0x46, 0x2a, 0x3f, 0xbf, 0xf7, 0x41, 0x25, 0xfd, 0xe8, 0xd1, 0x16, 0xa0, 0x6c, 0x07, 0xdb, 0xa5, 0xc1, 0x95, 0xaa, 0x74, 0xcc, 0x67, 0x97, 0x06, 0x1d, 0xcb, 0xf6, 0x4a, 0xaa, 0x76, 0x17, 0xf0, 0xfd, 0xf7, 0xdf, 0x9b, 0x53, 0x73, 0x3a, 0x35, 0xfb, 0x5b, 0xf2, 0xb4, 0x1b, 0x45, 0x3d, 0x7b, 0xf6, 0xc4, 0xc5, 0x8b, 0x17, 0x5b, 0xca, 0xcf, 0x3a, 0x5c, 0x23, 0xcd, 0xb4, 0x69, 0xd3, 0xcc, 0x13, 0xbd, 0x84, 0xe9, 0x74, 0x3a, 0x35, 0x8a, 0x29, 0xcc, 0x4f, 0x59, 0x0c, 0x68, 0xef, 0x12, 0x98, 0x94, 0x94, 0xa4, 0xc6, 0xc9, 0x5f, 0xef, 0xde, 0xbd, 0x95, 0x5b, 0x6f, 0xbd, 0x55, 0x71, 0x76, 0x76, 0x56, 0x17, 0x0e, 0x5a, 0x84, 0x99, 0x9b, 0x4a, 0xc2, 0xc5, 0x8b, 0x17, 0x9b, 0x33, 0xf0, 0xf4, 0xf4, 0x54, 0x61, 0xb4, 0x0c, 0xb5, 0x27, 0x37, 0x05, 0x5a, 0x5a, 0xe5, 0xeb, 0xaf, 0xbf, 0x36, 0xfb, 0xb5, 0x78, 0x09, 0x68, 0xe2, 0xd7, 0x20, 0x24, 0x50, 0x7e, 0xdc, 0x59, 0x28, 0x8f, 0x3d, 0xf6, 0x98, 0x5a, 0x0b, 0x2d, 0xec, 0x4f, 0x7f, 0xfa, 0x93, 0x32, 0x62, 0xc4, 0x08, 0x35, 0xbe, 0x6f, 0xdf, 0xbe, 0x4a, 0x72, 0x72, 0xb2, 0x32, 0x75, 0xea, 0x54, 0xf5, 0x5d, 0x60, 0x56, 0xac, 0x58, 0xa1, 0x1c, 0x3f, 0x7e, 0xdc, 0xfc, 0xae, 0xe5, 0x29, 0xcf, 0x76, 0xd3, 0x80, 0x19, 0xb6, 0xcb, 0xb5, 0xbb, 0x17, 0xb5, 0x2b, 0x77, 0x02, 0xdf, 0xf8, 0x05, 0x74, 0x39, 0x0d, 0xda, 0x8b, 0xd2, 0xf6, 0xc2, 0x77, 0x39, 0x09, 0x2c, 0x2b, 0x24, 0xdd, 0xd8, 0xd1, 0xce, 0xe1, 0x0d, 0xa8, 0xad, 0xaf, 0x47, 0x5d, 0xbd, 0x1c, 0xb0, 0x5d, 0x71, 0x15, 0x46, 0x13, 0x7e, 0x2c, 0x29, 0x87, 0x76, 0x76, 0xb0, 0xb3, 0xb8, 0x1a, 0xdb, 0x78, 0x1c, 0x07, 0xc5, 0xf6, 0x32, 0xe1, 0x4a, 0x4a, 0xfb, 0x3e, 0x87, 0x35, 0xe0, 0xcb, 0x75, 0x9b, 0xf0, 0xe9, 0x86, 0x14, 0x04, 0x07, 0x06, 0xc0, 0xc7, 0xdb, 0x0b, 0x3c, 0xe3, 0x81, 0x1f, 0x0f, 0x86, 0x7e, 0x36, 0xfb, 0x6e, 0xac, 0x38, 0x78, 0x16, 0x55, 0x35, 0xf5, 0x78, 0x2f, 0xa7, 0x18, 0x07, 0xab, 0xeb, 0x31, 0xce, 0xdf, 0x03, 0xe3, 0xfd, 0x3d, 0x59, 0x3b, 0x27, 0xfc, 0x33, 0xaf, 0x14, 0xc6, 0xc6, 0x8e, 0xd3, 0xc6, 0x21, 0x0d, 0x20, 0x3f, 0x43, 0x75, 0x55, 0x0d, 0x7a, 0x06, 0x05, 0xc0, 0xc9, 0xd9, 0x45, 0x3d, 0x86, 0x71, 0x63, 0x03, 0x9c, 0x9c, 0x9d, 0xb1, 0xf5, 0xbb, 0x0d, 0xd8, 0xf1, 0xc1, 0x5b, 0x98, 0x36, 0x6e, 0x14, 0x52, 0x5e, 0x7d, 0x15, 0x21, 0xd5, 0x95, 0xe0, 0xd2, 0x19, 0x6f, 0x1c, 0xbf, 0xa0, 0x52, 0x60, 0x46, 0x88, 0x1f, 0x5c, 0x9c, 0x9d, 0xb0, 0xa3, 0x8a, 0x54, 0xb3, 0x38, 0xee, 0xb3, 0x8f, 0xfb, 0x4b, 0x10, 0x0e, 0x1b, 0xc4, 0xe5, 0x15, 0x95, 0x38, 0x71, 0x2e, 0x13, 0xc5, 0xe5, 0x26, 0xcc, 0x98, 0x30, 0x94, 0x14, 0x30, 0x40, 0xe7, 0xe2, 0x02, 0x77, 0x0f, 0x2f, 0x8c, 0xbd, 0x7b, 0x01, 0x6a, 0x4f, 0xa7, 0x43, 0xd7, 0x58, 0x8f, 0xc6, 0x39, 0x0f, 0x61, 0x8a, 0x5e, 0xc1, 0xed, 0x23, 0x86, 0xa0, 0x4f, 0x64, 0x18, 0xbe, 0x2d, 0xab, 0xc3, 0x4f, 0x7c, 0xdd, 0xd4, 0xda, 0xd4, 0x90, 0x12, 0x45, 0x8d, 0x4e, 0x08, 0xb7, 0xbb, 0x8e, 0xba, 0xd2, 0x3c, 0x87, 0x50, 0x40, 0xb2, 0x93, 0x6e, 0xd3, 0x58, 0x53, 0x89, 0xe1, 0x83, 0x22, 0x70, 0x2a, 0xa7, 0x1c, 0x3a, 0x57, 0x3d, 0x16, 0xfd, 0xc7, 0x13, 0x78, 0xe1, 0xad, 0x7f, 0x60, 0xe2, 0xb0, 0xe1, 0x98, 0xfd, 0xe0, 0x02, 0xfc, 0x78, 0xfa, 0x0c, 0xa6, 0xa1, 0x0e, 0xeb, 0x97, 0xbd, 0x8e, 0xf7, 0xcb, 0x6a, 0xb1, 0xe4, 0xf5, 0x77, 0x30, 0xb8, 0xae, 0x02, 0x67, 0x88, 0x7c, 0x19, 0x35, 0x06, 0x52, 0x22, 0xdc, 0xc9, 0x84, 0x73, 0xc6, 0xb6, 0x77, 0x29, 0x87, 0x34, 0xa0, 0xa6, 0xa6, 0x06, 0x3c, 0x44, 0xc2, 0xba, 0x4d, 0xdb, 0x71, 0xc7, 0x4f, 0x6f, 0x47, 0x61, 0xd6, 0x31, 0xa4, 0x1d, 0x49, 0x47, 0x44, 0x74, 0x3c, 0xfa, 0x47, 0x04, 0x62, 0x78, 0x6c, 0x0c, 0x9c, 0x6b, 0x1b, 0x30, 0xff, 0x0f, 0x7f, 0x41, 0x51, 0x51, 0x21, 0x4c, 0x9e, 0xbe, 0x28, 0x5b, 0xf5, 0x31, 0x6a, 0x43, 0x7a, 0xe1, 0xa1, 0xf9, 0x0b, 0x51, 0x7e, 0xec, 0x04, 0xb6, 0xf0, 0x74, 0x6b, 0xdb, 0x91, 0xe3, 0x38, 0x5a, 0x54, 0x8e, 0xbc, 0xf2, 0x9a, 0x2b, 0x28, 0xb6, 0xe3, 0xeb, 0x74, 0x17, 0xba, 0x73, 0xd6, 0x2c, 0x14, 0x95, 0x56, 0xc2, 0xd3, 0xcb, 0x1b, 0x91, 0x11, 0xa1, 0x48, 0x1e, 0x35, 0x0a, 0xeb, 0xd7, 0xad, 0x85, 0x9b, 0xbb, 0x1b, 0x86, 0x0d, 0x4d, 0x44, 0xd2, 0x98, 0x89, 0xd8, 0x9e, 0x72, 0x08, 0x45, 0xb9, 0x99, 0xf0, 0xf7, 0xf3, 0xc6, 0x85, 0xac, 0x4c, 0x9c, 0x3e, 0x77, 0x1e, 0x1e, 0x72, 0x5c, 0xc9, 0xca, 0x99, 0x74, 0x3a, 0x28, 0x75, 0x75, 0xb8, 0xff, 0xe1, 0x5f, 0xe1, 0x67, 0x53, 0xc6, 0xda, 0xa9, 0xae, 0x75, 0x74, 0xa7, 0x1b, 0xf0, 0xe4, 0xd3, 0x8b, 0x91, 0x9b, 0x77, 0x91, 0x95, 0x71, 0x42, 0x2d, 0x2b, 0x52, 0x54, 0x90, 0x83, 0xa9, 0xb7, 0x4c, 0x42, 0x4a, 0xca, 0x7e, 0xee, 0x15, 0x1b, 0xd5, 0x4a, 0xd6, 0xd5, 0xd4, 0x22, 0x3e, 0x71, 0x18, 0x7a, 0xf5, 0x4f, 0xc4, 0x2b, 0x8b, 0x1f, 0x87, 0x2b, 0x2b, 0xef, 0xd7, 0xc3, 0x1f, 0x2e, 0x6e, 0xee, 0x30, 0xd0, 0xaf, 0xe8, 0x5c, 0xe0, 0xc5, 0x06, 0x3f, 0xff, 0xc2, 0x5f, 0x31, 0x2c, 0x3e, 0xda, 0xba, 0x96, 0xad, 0x84, 0x74, 0xba, 0x01, 0x67, 0xce, 0x9c, 0xc1, 0xfe, 0x1f, 0x52, 0xf1, 0xf6, 0xbb, 0x1f, 0x70, 0x63, 0x55, 0x85, 0x82, 0x8b, 0x79, 0x5c, 0xb1, 0xe6, 0xa2, 0x57, 0x48, 0x08, 0x82, 0x03, 0x02, 0x10, 0x37, 0x38, 0x1e, 0xc5, 0x45, 0x45, 0xf8, 0x91, 0x58, 0xf7, 0xe4, 0xc0, 0xf6, 0xf3, 0xf5, 0xc6, 0xc8, 0x71, 0x93, 0x11, 0x18, 0x1e, 0x85, 0xf5, 0x6b, 0x3e, 0xc1, 0xc1, 0xfd, 0x29, 0x30, 0x99, 0x1a, 0x70, 0xc7, 0x5d, 0xf7, 0x61, 0xc6, 0xad, 0xd3, 0x30, 0x7d, 0xea, 0xf8, 0x56, 0xaa, 0x6b, 0x1d, 0xd5, 0xe9, 0x06, 0x48, 0x96, 0xbc, 0xa6, 0xc1, 0x5d, 0xb3, 0xe7, 0xe0, 0xcc, 0xe9, 0xd3, 0x38, 0x78, 0xe8, 0x20, 0xbb, 0x93, 0x17, 0x06, 0xc6, 0x0d, 0x46, 0x48, 0xaf, 0x30, 0x62, 0x3a, 0x00, 0xf9, 0xf9, 0x79, 0xf8, 0xed, 0x63, 0x8b, 0x48, 0x99, 0x5b, 0xcc, 0x35, 0x28, 0x2c, 0x2c, 0x44, 0x2e, 0x4f, 0xba, 0xeb, 0xea, 0x6a, 0x70, 0x31, 0x37, 0x17, 0x23, 0x46, 0x8d, 0x46, 0x70, 0xcf, 0x10, 0x73, 0x7c, 0x5b, 0x3d, 0x0e, 0x69, 0x80, 0x65, 0x61, 0xb9, 0xb9, 0x39, 0x38, 0x74, 0xf0, 0x20, 0x5c, 0xf5, 0xae, 0x70, 0x37, 0x78, 0x23, 0x31, 0x71, 0x08, 0xbc, 0x3c, 0x65, 0xd2, 0xea, 0x1a, 0xe7, 0xf0, 0x06, 0x74, 0x4d, 0x35, 0x5b, 0xce, 0xd5, 0x21, 0x6c, 0xb4, 0xe5, 0xec, 0xbb, 0x3e, 0xa6, 0x4b, 0x1b, 0xb0, 0x67, 0xcf, 0x1e, 0xf5, 0xf6, 0x46, 0x6e, 0x77, 0xe4, 0xd7, 0xdc, 0x71, 0x6f, 0x66, 0x33, 0xbc, 0x39, 0x5c, 0xab, 0xef, 0x96, 0x1b, 0x34, 0xcd, 0xbf, 0x79, 0xf3, 0x66, 0x99, 0x0a, 0xcd, 0x3f, 0xd9, 0x08, 0x6a, 0xee, 0xe3, 0x8f, 0x3f, 0x36, 0x87, 0x4b, 0xd8, 0xcc, 0x99, 0x33, 0xd5, 0xf7, 0x88, 0x88, 0x08, 0x0d, 0x44, 0x7d, 0x8e, 0x1c, 0x39, 0x52, 0x0d, 0x4f, 0x4c, 0x4c, 0x54, 0x78, 0x35, 0xa5, 0xfa, 0x2d, 0x01, 0xe4, 0x42, 0x4f, 0x2b, 0xc3, 0x32, 0x5c, 0xf3, 0xcf, 0x9f, 0x3f, 0xdf, 0x1c, 0x2f, 0x70, 0x19, 0x19, 0x19, 0x5a, 0x54, 0x93, 0xa7, 0x79, 0x5b, 0xdc, 0x24, 0x94, 0x2f, 0xb2, 0x53, 0x9d, 0x34, 0x69, 0x92, 0x32, 0x6b, 0xd6, 0x2c, 0x35, 0x23, 0xcb, 0x46, 0xc8, 0x96, 0x59, 0x32, 0xd5, 0x9c, 0xf8, 0xb5, 0x03, 0xf9, 0xaf, 0xbe, 0xfa, 0x4a, 0x8d, 0x5b, 0xbe, 0x7c, 0xb9, 0x16, 0x6d, 0xae, 0x88, 0x39, 0xe0, 0xb2, 0x47, 0xb6, 0xd5, 0x96, 0xf9, 0x68, 0xf1, 0x12, 0x36, 0x60, 0xc0, 0x00, 0xed, 0x55, 0x7d, 0x4a, 0x18, 0x6f, 0xc3, 0x9b, 0x84, 0xc9, 0xcb, 0x95, 0x5a, 0x5c, 0x8e, 0x22, 0x7b, 0x53, 0x33, 0xbd, 0xff, 0xfe, 0xfb, 0xcd, 0xc0, 0x92, 0x58, 0xaf, 0xd7, 0x9b, 0xdf, 0x87, 0x0f, 0x1f, 0xae, 0xc2, 0xf0, 0xb2, 0x58, 0xc9, 0xcd, 0xcd, 0x55, 0xfd, 0x5a, 0x03, 0xb4, 0xed, 0xf7, 0xc0, 0x81, 0x03, 0x55, 0xf8, 0x92, 0x92, 0x12, 0x35, 0xde, 0x56, 0x45, 0x5b, 0x6b, 0x80, 0xc0, 0xd7, 0xd5, 0xd5, 0xa9, 0x79, 0xbc, 0xf2, 0xca, 0x2b, 0x6a, 0x1e, 0xb7, 0xdd, 0x76, 0x9b, 0xb9, 0x0e, 0x9a, 0xc7, 0xaa, 0x01, 0x5a, 0xc4, 0x81, 0x03, 0x07, 0x5a, 0x24, 0x9b, 0xc0, 0x48, 0xe6, 0x3b, 0x77, 0xee, 0xd4, 0xc0, 0xad, 0x9e, 0x72, 0xc9, 0xba, 0x61, 0xc3, 0x06, 0xab, 0xf0, 0xf6, 0x04, 0xe4, 0xe7, 0xe7, 0x2b, 0xd2, 0x9d, 0x05, 0x51, 0x2d, 0xb9, 0x6e, 0x36, 0x4a, 0x52, 0x5f, 0x53, 0xd7, 0xa5, 0x6c, 0xf4, 0x6a, 0xb4, 0xec, 0x86, 0x6f, 0x40, 0xf7, 0x18, 0xb8, 0x1a, 0xdd, 0xa4, 0xb5, 0x32, 0x6e, 0xf8, 0x2e, 0x74, 0xd5, 0x1a, 0x20, 0x47, 0x58, 0x32, 0x1b, 0x39, 0xda, 0xb5, 0xe3, 0x00, 0xa3, 0xe3, 0x45, 0x9f, 0xab, 0x31, 0xe1, 0x50, 0xad, 0x11, 0x39, 0x44, 0xd7, 0x78, 0x83, 0x33, 0x06, 0x71, 0xaf, 0x60, 0xbd, 0xb4, 0xeb, 0x60, 0xfe, 0x2d, 0xcd, 0x70, 0x1d, 0x09, 0x37, 0x1a, 0x4d, 0x4a, 0x4d, 0x6d, 0x9d, 0x22, 0x4f, 0x4b, 0x97, 0x92, 0x5f, 0x66, 0x7e, 0x35, 0x72, 0x56, 0x5d, 0x7a, 0xb6, 0x40, 0x29, 0x69, 0x30, 0x9a, 0xc3, 0x3a, 0xe3, 0x71, 0x18, 0x17, 0x2a, 0x28, 0x2a, 0xc6, 0xe6, 0xbd, 0xc7, 0x91, 0x73, 0xee, 0x04, 0x6a, 0x79, 0xcc, 0x32, 0x68, 0x50, 0x2c, 0x26, 0xde, 0x34, 0x0a, 0x7e, 0xde, 0x1e, 0xc8, 0x2a, 0x2a, 0x45, 0x08, 0xa5, 0xc1, 0x5c, 0x79, 0xd0, 0xa5, 0xb9, 0x6f, 0x79, 0xa4, 0x18, 0xe5, 0xe9, 0x8e, 0x68, 0x6f, 0xdb, 0x97, 0x90, 0x1a, 0x9c, 0xdd, 0x67, 0x67, 0x5a, 0xaf, 0xa5, 0x2d, 0x2d, 0x2b, 0x53, 0x3e, 0x5c, 0xbd, 0x45, 0x79, 0xf0, 0xe1, 0x47, 0xd5, 0x45, 0x97, 0x9b, 0x9b, 0xbb, 0x62, 0x30, 0xb8, 0x2b, 0xfd, 0x62, 0x06, 0x2a, 0x2f, 0xbd, 0xb5, 0x5c, 0x59, 0x7d, 0xbe, 0x54, 0xf9, 0xdb, 0x96, 0x14, 0x65, 0x45, 0x61, 0x95, 0x62, 0x89, 0xf7, 0xfc, 0x7a, 0xa3, 0xb2, 0x31, 0xbf, 0x5c, 0xcb, 0xa6, 0x43, 0x4f, 0x87, 0x0c, 0xe2, 0xcc, 0xec, 0x1c, 0x78, 0x7a, 0x18, 0x70, 0x2c, 0x23, 0x1d, 0x1e, 0x14, 0x96, 0xd1, 0xbb, 0xe9, 0xf9, 0x33, 0xf0, 0x88, 0x25, 0x1f, 0xab, 0x56, 0xad, 0xc0, 0x1b, 0xbf, 0x59, 0x84, 0x50, 0x93, 0x82, 0xd9, 0x5e, 0x2e, 0x58, 0x9d, 0x59, 0x88, 0x2c, 0x1e, 0x72, 0x89, 0x0b, 0xa2, 0xd0, 0xe7, 0xa8, 0x00, 0x2f, 0xec, 0x28, 0xb3, 0x16, 0x49, 0xb5, 0x8b, 0xf9, 0xcb, 0x00, 0x0e, 0x69, 0x80, 0x54, 0x5e, 0x31, 0xd5, 0x21, 0x61, 0xc4, 0x18, 0x18, 0x4d, 0x94, 0x0f, 0x20, 0x1d, 0x64, 0x90, 0xca, 0xe1, 0x6e, 0x66, 0x5a, 0x2a, 0xc2, 0x06, 0x0d, 0xc1, 0x93, 0x73, 0x6f, 0xc3, 0xdb, 0x6b, 0xb7, 0x60, 0x76, 0x84, 0x3f, 0x6a, 0xeb, 0x8d, 0xf8, 0xa1, 0x84, 0xc7, 0xeb, 0x74, 0x3e, 0x3c, 0x4e, 0x1c, 0xea, 0xa3, 0x47, 0x6e, 0x5d, 0xcb, 0x72, 0x05, 0x2a, 0x60, 0x0b, 0x7f, 0x0e, 0x69, 0x40, 0x2f, 0xde, 0x5e, 0xba, 0xb1, 0x7b, 0x4f, 0xbf, 0xfd, 0xce, 0xcb, 0x02, 0xa5, 0x8a, 0x7a, 0xa8, 0x65, 0x6a, 0xa8, 0xc7, 0xc8, 0x3f, 0xbd, 0x0e, 0x94, 0x5c, 0x44, 0xc2, 0xca, 0xf5, 0xf8, 0x96, 0xd7, 0xa8, 0xef, 0x7c, 0xb6, 0x16, 0xfd, 0xd9, 0xef, 0xfb, 0x52, 0x6e, 0x21, 0xed, 0xb2, 0x30, 0x30, 0xe5, 0x23, 0x90, 0xcf, 0x0a, 0x56, 0x71, 0x7f, 0xd2, 0x5e, 0xe7, 0x90, 0x06, 0xb0, 0xbf, 0xa3, 0x57, 0xb0, 0x3f, 0x62, 0xa3, 0xfb, 0x62, 0xdc, 0xa4, 0x69, 0x10, 0x99, 0x38, 0x95, 0xe9, 0x73, 0x1f, 0xec, 0x55, 0x9c, 0x8b, 0xad, 0x9f, 0x7f, 0x82, 0xa4, 0x13, 0x07, 0x51, 0x5e, 0x57, 0x8b, 0xe3, 0xbe, 0xc1, 0x78, 0x63, 0xf9, 0x2a, 0xf8, 0xbb, 0x3a, 0x23, 0xd4, 0xe0, 0x82, 0xc2, 0x86, 0x4b, 0x97, 0x1c, 0x09, 0x6e, 0x3a, 0x6c, 0xa8, 0x60, 0x03, 0xda, 0xdb, 0x88, 0x0e, 0x8d, 0x1c, 0x1b, 0x89, 0x2a, 0xab, 0xaa, 0x94, 0xcc, 0x9c, 0x8b, 0x4a, 0x4a, 0x46, 0x26, 0xb7, 0x7e, 0x7a, 0x0e, 0x62, 0x83, 0x2a, 0xeb, 0xd2, 0xbb, 0x6f, 0x7f, 0x65, 0xd1, 0xb3, 0x2f, 0x2b, 0x43, 0x86, 0x26, 0x29, 0x7f, 0xfb, 0xef, 0xf7, 0x95, 0x09, 0xb3, 0xe7, 0x2a, 0xaf, 0xa4, 0x9d, 0x53, 0x5e, 0x7d, 0x67, 0xa5, 0x2a, 0x3d, 0xb8, 0xaf, 0xc6, 0x78, 0x45, 0x88, 0xd0, 0x68, 0x54, 0xd2, 0x6b, 0x2d, 0x87, 0xb9, 0x8d, 0x82, 0x9a, 0x05, 0x39, 0x8c, 0x8d, 0x72, 0x87, 0x86, 0xac, 0x0b, 0x39, 0x08, 0x08, 0xee, 0x89, 0x25, 0x2f, 0xbf, 0x89, 0xed, 0xeb, 0xbf, 0xc0, 0xbc, 0x5f, 0x51, 0x5e, 0xd9, 0x3b, 0x10, 0x3a, 0xf6, 0xaf, 0xed, 0x9f, 0x7f, 0x88, 0x06, 0x27, 0x17, 0xf4, 0x89, 0x8a, 0x46, 0x66, 0x55, 0x29, 0x7a, 0xc7, 0x26, 0x20, 0x86, 0x2c, 0xf6, 0xbe, 0x19, 0x53, 0xb0, 0x9a, 0x98, 0x9f, 0xe9, 0x77, 0x89, 0xc5, 0xae, 0xae, 0x68, 0xc4, 0xcf, 0xbc, 0x9d, 0xdb, 0x3c, 0xd1, 0x39, 0xa4, 0x0b, 0x49, 0xbf, 0x3d, 0x70, 0x60, 0x3f, 0xfe, 0xf8, 0x9f, 0x7f, 0x44, 0x03, 0x65, 0x73, 0x9e, 0xfc, 0xf5, 0xa3, 0x1c, 0xd0, 0x63, 0x31, 0x30, 0x76, 0x20, 0x6e, 0x4a, 0x1e, 0x8a, 0x08, 0x1f, 0x2f, 0x4c, 0xfd, 0xf7, 0xf9, 0x28, 0x2b, 0x29, 0xe2, 0x31, 0xe2, 0x05, 0x84, 0xea, 0x0d, 0xc8, 0x0f, 0xef, 0x8f, 0x3d, 0xeb, 0xbe, 0xc1, 0xca, 0x2d, 0x7b, 0x60, 0x28, 0xce, 0x37, 0x2f, 0x33, 0x6e, 0x31, 0x38, 0xa1, 0xba, 0x1d, 0x43, 0xc1, 0x21, 0x14, 0xf8, 0xcd, 0x6f, 0x7e, 0x83, 0xed, 0x7b, 0xf6, 0x63, 0xca, 0xa4, 0xf1, 0x28, 0x29, 0x2e, 0xc1, 0xa2, 0x5f, 0x3d, 0xc2, 0x53, 0x00, 0x7f, 0xec, 0xdc, 0xb5, 0x1d, 0xa3, 0xc6, 0x8e, 0x45, 0x5e, 0x56, 0x2e, 0x4e, 0x9d, 0xcd, 0x86, 0x8b, 0xb7, 0x01, 0xbb, 0xbf, 0xdb, 0x88, 0xb2, 0x82, 0x8b, 0x18, 0x3b, 0x75, 0x1a, 0x8e, 0x64, 0x9e, 0x47, 0x3f, 0xbd, 0x1e, 0x4f, 0xfc, 0xfa, 0x57, 0xd8, 0x96, 0x71, 0x12, 0x21, 0xc1, 0xc1, 0xc8, 0x68, 0x70, 0xc2, 0xdd, 0xfd, 0x7a, 0x42, 0x67, 0xe3, 0x1c, 0xc9, 0xd6, 0x00, 0xef, 0x34, 0x05, 0x5e, 0x78, 0xe1, 0x05, 0x7c, 0xb3, 0x6e, 0x03, 0xbc, 0x3d, 0x3d, 0x10, 0x33, 0x60, 0x20, 0x32, 0x8e, 0x1d, 0xc3, 0xfe, 0x7d, 0xfb, 0x50, 0x98, 0x7d, 0x9c, 0x42, 0x34, 0x83, 0xb0, 0x67, 0xd7, 0x2e, 0x64, 0xe6, 0xe6, 0xa1, 0x92, 0x72, 0xa7, 0x35, 0xc4, 0xb4, 0x0b, 0x15, 0x05, 0x62, 0xc7, 0x4f, 0x44, 0x3a, 0xef, 0x30, 0x02, 0xfc, 0x83, 0xd1, 0x18, 0x18, 0x84, 0xd7, 0xbf, 0xdb, 0x8b, 0x19, 0x49, 0x43, 0x30, 0x38, 0x3c, 0x18, 0x3f, 0x6f, 0x47, 0xe5, 0xa5, 0x41, 0x57, 0xe6, 0x76, 0x5b, 0xcd, 0xb3, 0x13, 0x26, 0x72, 0xe0, 0x6f, 0xbf, 0xfb, 0x3e, 0x06, 0x0f, 0x49, 0xe4, 0x49, 0x73, 0x36, 0xe2, 0xe2, 0xe3, 0x31, 0x28, 0x36, 0x16, 0x6b, 0xd6, 0x7c, 0x83, 0xe4, 0xe4, 0x51, 0x08, 0x0b, 0x0f, 0x47, 0x78, 0x78, 0x0c, 0xbe, 0xdf, 0xbc, 0x8e, 0x97, 0x1b, 0xfe, 0xa8, 0xe3, 0x4d, 0x65, 0x51, 0xe6, 0x19, 0xe4, 0x9e, 0x3f, 0x8b, 0x1e, 0x1e, 0x6e, 0x38, 0x47, 0xd6, 0x4f, 0xe6, 0x03, 0x83, 0x8f, 0x3f, 0xca, 0x27, 0x8e, 0x86, 0x0f, 0x59, 0x6b, 0x7b, 0x5d, 0xa7, 0x28, 0x20, 0x17, 0x1a, 0xd1, 0xc4, 0xba, 0x8f, 0xaf, 0x2f, 0x62, 0x62, 0xe3, 0xb0, 0xe6, 0xcb, 0xd5, 0x18, 0x3d, 0x6a, 0xa4, 0xaa, 0x8e, 0xb1, 0x6b, 0xd7, 0x1e, 0xec, 0xd9, 0xbd, 0x1b, 0xfb, 0xb6, 0x7e, 0x8d, 0xb1, 0xe3, 0xa7, 0xa0, 0xac, 0x28, 0x17, 0x19, 0x69, 0x3f, 0x20, 0x9b, 0xe2, 0x4d, 0x8d, 0x75, 0xd5, 0x28, 0xe4, 0xa5, 0xa0, 0x93, 0x91, 0x72, 0x84, 0x35, 0x75, 0x28, 0xce, 0xcb, 0xc2, 0xf6, 0xfd, 0x22, 0x11, 0xd6, 0x7e, 0xd7, 0xa9, 0x06, 0x1c, 0x4c, 0x3d, 0x08, 0x7f, 0xff, 0x00, 0x1e, 0xa5, 0xeb, 0xa1, 0xe3, 0xac, 0xbb, 0x6e, 0xc3, 0x46, 0x14, 0x14, 0xe6, 0xab, 0xcb, 0x0a, 0x57, 0xaa, 0x84, 0x64, 0x65, 0x65, 0xe3, 0x58, 0xfa, 0x51, 0xfc, 0xef, 0xdb, 0x7f, 0x43, 0xf4, 0xe0, 0x11, 0x9c, 0x07, 0x9c, 0x90, 0x9b, 0x99, 0x89, 0xdc, 0x0b, 0xd9, 0x28, 0xcc, 0xbf, 0xc8, 0x8b, 0x8f, 0x42, 0xd4, 0x56, 0x94, 0xa1, 0xa2, 0xb2, 0x12, 0x87, 0x0e, 0x1d, 0xb9, 0x34, 0x8b, 0xb7, 0xb3, 0x0d, 0x9d, 0x6a, 0x40, 0x5c, 0x5c, 0x2c, 0xa6, 0xf0, 0x3a, 0xa9, 0xbe, 0xae, 0x1e, 0x95, 0x95, 0x15, 0xf0, 0xf1, 0xf1, 0xc3, 0x6b, 0xaf, 0xbf, 0x09, 0xb9, 0xf4, 0xcb, 0xcb, 0xc9, 0x95, 0x43, 0x33, 0xde, 0xda, 0xd7, 0xa1, 0xb4, 0xb4, 0x04, 0x1f, 0xbd, 0xf9, 0x12, 0x12, 0x86, 0x24, 0xe0, 0xa9, 0x25, 0xff, 0x85, 0x72, 0x56, 0x3a, 0xfb, 0xec, 0x29, 0x5c, 0xcc, 0xcb, 0x45, 0x71, 0x61, 0x01, 0x07, 0x7e, 0x31, 0x2a, 0xca, 0xcb, 0x51, 0x41, 0xaa, 0xb4, 0xd7, 0x75, 0xaa, 0x01, 0x21, 0xbc, 0x65, 0xf4, 0x32, 0xe8, 0x29, 0x31, 0xd0, 0x80, 0xb2, 0xd2, 0x52, 0x14, 0xb1, 0x32, 0x0a, 0xef, 0x7a, 0xbf, 0x59, 0xbb, 0x96, 0x8d, 0xa8, 0x86, 0x9e, 0x03, 0x36, 0x84, 0x57, 0x4d, 0xf5, 0x94, 0xad, 0xac, 0xe5, 0x1a, 0x69, 0xe7, 0xa6, 0xb5, 0xc8, 0xd8, 0xb3, 0x19, 0x7f, 0x7c, 0xee, 0x55, 0xfc, 0x72, 0xe1, 0xaf, 0xc1, 0xe3, 0x4a, 0x14, 0x14, 0x70, 0x80, 0x53, 0x5a, 0x51, 0x14, 0x32, 0xb8, 0x2c, 0x6a, 0xb7, 0xeb, 0x34, 0x1b, 0xe5, 0xd9, 0x27, 0xfe, 0xf2, 0xe2, 0x4b, 0xd8, 0xb0, 0x71, 0x13, 0xf2, 0x79, 0x3f, 0x26, 0xd8, 0x34, 0xb2, 0xc2, 0xa1, 0xe1, 0x11, 0xf0, 0xe6, 0x55, 0xd3, 0xcd, 0xe3, 0x92, 0x11, 0x1c, 0x14, 0x88, 0xf4, 0x8c, 0xe3, 0x78, 0xf3, 0xcd, 0x37, 0xd9, 0x55, 0x0e, 0xf1, 0xbe, 0xf8, 0x34, 0x78, 0x40, 0x0c, 0x93, 0xb3, 0x1e, 0x8d, 0x3a, 0x37, 0x36, 0xd4, 0x0d, 0x5e, 0xae, 0x26, 0xcc, 0x9a, 0x35, 0xf3, 0xea, 0x37, 0x40, 0x4a, 0xdc, 0x45, 0x56, 0xf9, 0x87, 0xa7, 0xff, 0x13, 0x29, 0x7b, 0xf7, 0x10, 0xdb, 0xf5, 0x88, 0xec, 0x1d, 0x85, 0xa8, 0xfe, 0xd1, 0x08, 0x0a, 0x0e, 0xa1, 0xf0, 0xa4, 0x4e, 0xed, 0x26, 0xeb, 0xff, 0xf9, 0x25, 0x31, 0x7c, 0x89, 0xe0, 0xf5, 0x14, 0x08, 0xc9, 0xce, 0xca, 0x42, 0x71, 0x71, 0x11, 0x8c, 0xec, 0x62, 0x15, 0x15, 0xe5, 0xb8, 0x65, 0xda, 0x6d, 0x1d, 0xba, 0x2b, 0xe8, 0x34, 0x05, 0x34, 0x94, 0xa5, 0x1e, 0x4c, 0xc5, 0xbc, 0x07, 0xe7, 0x73, 0x1f, 0x20, 0xd7, 0xa6, 0x7a, 0x84, 0x86, 0x86, 0xa9, 0x77, 0xc7, 0xa2, 0xe7, 0xb5, 0xfc, 0x9d, 0x65, 0x2d, 0x56, 0x4e, 0xc6, 0x89, 0xad, 0xcb, 0x0f, 0x2d, 0x5f, 0x7b, 0x4f, 0x87, 0x35, 0x40, 0x0a, 0x32, 0x1a, 0x1b, 0x70, 0x80, 0x9a, 0x00, 0x85, 0x05, 0x85, 0x08, 0xe2, 0x8d, 0xa3, 0xa7, 0x97, 0x27, 0xe2, 0xe3, 0x5a, 0x91, 0xfc, 0xb4, 0x57, 0xbb, 0x36, 0xc4, 0x3b, 0xb4, 0x01, 0x6d, 0x28, 0xcf, 0xe1, 0x20, 0x9d, 0xe2, 0x42, 0x0e, 0xaf, 0x4d, 0x07, 0x32, 0xec, 0x6e, 0x40, 0x07, 0x90, 0xe6, 0xd0, 0x24, 0x5d, 0x4a, 0x81, 0x8a, 0x8a, 0x0a, 0x88, 0x38, 0xb1, 0xfc, 0x84, 0xdb, 0x34, 0x77, 0x54, 0x8c, 0xc1, 0x87, 0x1f, 0x7e, 0xd8, 0x3c, 0xb8, 0x7d, 0xef, 0xcd, 0x76, 0x68, 0x0e, 0x7d, 0x65, 0x4d, 0xa4, 0xd6, 0xea, 0x2f, 0x25, 0x25, 0xc5, 0x2a, 0x6f, 0x89, 0x93, 0xeb, 0xd4, 0xce, 0xb8, 0x4e, 0x2d, 0xa7, 0xed, 0xa1, 0x8a, 0x15, 0x53, 0x41, 0x5a, 0xba, 0xe8, 0xd6, 0xe2, 0xed, 0xe5, 0xd3, 0x5a, 0xbc, 0xcd, 0x2e, 0x14, 0x13, 0x13, 0xa3, 0x4e, 0x2e, 0x5a, 0xc1, 0x4f, 0x3c, 0xf1, 0x84, 0x39, 0x0f, 0x17, 0x1e, 0x0f, 0x4a, 0x38, 0x31, 0xda, 0x62, 0xe5, 0xd6, 0xaf, 0x5f, 0x0f, 0x2a, 0x6f, 0x81, 0xb2, 0xda, 0xdc, 0x1b, 0xac, 0x31, 0xa7, 0xd5, 0x3c, 0x87, 0x0f, 0x1f, 0x36, 0xe7, 0xdf, 0xab, 0x57, 0x2f, 0x2d, 0xd8, 0xfc, 0x2c, 0x28, 0x28, 0x00, 0xe5, 0xc3, 0xcd, 0x30, 0x94, 0xa4, 0x36, 0xc7, 0x59, 0x79, 0x6c, 0x91, 0x8f, 0x95, 0x54, 0x96, 0x2d, 0xbb, 0xa4, 0x7b, 0x18, 0x1f, 0x1f, 0xaf, 0x76, 0x01, 0x0d, 0xee, 0xe1, 0x87, 0x1f, 0x56, 0xdf, 0xb7, 0x6e, 0xdd, 0xaa, 0x06, 0x8d, 0x1b, 0x37, 0xae, 0x49, 0xfc, 0xd2, 0xa5, 0x4b, 0xd5, 0x77, 0xb9, 0x89, 0xb7, 0x14, 0xe1, 0xb6, 0xec, 0x42, 0x95, 0x95, 0x95, 0xca, 0x8c, 0x19, 0x33, 0x54, 0x38, 0x5f, 0x5f, 0x5f, 0x2d, 0x6b, 0xf5, 0x69, 0x79, 0xaf, 0xfc, 0xd6, 0x5b, 0x6f, 0x29, 0x0f, 0x3c, 0xf0, 0x80, 0x39, 0xbf, 0x26, 0x80, 0x97, 0x5f, 0x64, 0x70, 0x59, 0xb9, 0x13, 0x27, 0x4e, 0x28, 0xf7, 0xde, 0x7b, 0xaf, 0x32, 0x61, 0xc2, 0x04, 0x65, 0xce, 0x9c, 0x39, 0x4d, 0x2a, 0x28, 0xc0, 0xc4, 0x82, 0xa2, 0x35, 0xe0, 0x8b, 0x2f, 0xbe, 0x68, 0x12, 0x2f, 0x71, 0xf2, 0xd3, 0xdc, 0xea, 0xd5, 0xab, 0xd5, 0x77, 0xcb, 0x06, 0x68, 0x71, 0x02, 0xd7, 0xbc, 0x01, 0xda, 0x25, 0xba, 0xdc, 0x33, 0x6b, 0x6e, 0xee, 0xdc, 0xb9, 0x6a, 0x1e, 0xda, 0xc5, 0xb7, 0x16, 0x2e, 0x4f, 0xab, 0x2e, 0xf4, 0xe8, 0xa3, 0x8f, 0x82, 0xd7, 0xfc, 0xdc, 0x39, 0x65, 0x43, 0xfc, 0xd4, 0x63, 0x62, 0x39, 0xd6, 0x4e, 0x53, 0xe9, 0x96, 0x6d, 0x65, 0x6b, 0x8e, 0xaa, 0x03, 0xad, 0x45, 0x5b, 0xc5, 0xf1, 0xa6, 0x5f, 0x0d, 0x93, 0xee, 0xa7, 0xb9, 0xa1, 0x43, 0x87, 0xaa, 0xde, 0xd3, 0x5c, 0xc5, 0x5a, 0x39, 0xcb, 0xd6, 0x88, 0x9f, 0x00, 0x4d, 0x30, 0xf8, 0xc1, 0x07, 0x1f, 0x34, 0x79, 0xd7, 0x60, 0x84, 0xb4, 0xe2, 0x44, 0x51, 0x4b, 0xd2, 0x68, 0x8e, 0xe3, 0x43, 0x7d, 0x97, 0x5b, 0x76, 0x71, 0xc3, 0x86, 0x0d, 0x53, 0xdf, 0xdb, 0x4a, 0x01, 0x91, 0xd1, 0x90, 0xfc, 0x9a, 0x8b, 0x3a, 0x58, 0x96, 0xa1, 0x95, 0x25, 0xcf, 0x2b, 0x25, 0x5f, 0x0e, 0x5d, 0xb2, 0x64, 0x89, 0x9a, 0x81, 0xd6, 0x10, 0xcb, 0xa7, 0x96, 0xd0, 0x32, 0x8c, 0xd8, 0x31, 0xc3, 0x4b, 0xbc, 0xf4, 0x6f, 0xcb, 0x78, 0x19, 0x4f, 0xda, 0xbb, 0xad, 0xf4, 0x5a, 0x9c, 0x3c, 0x35, 0x17, 0x1d, 0x1d, 0x6d, 0x4e, 0xa3, 0xc5, 0x93, 0x31, 0x68, 0xd1, 0x4d, 0x9e, 0x57, 0x52, 0x59, 0x04, 0x97, 0x97, 0x97, 0x2b, 0x6b, 0xd7, 0xae, 0x35, 0x0b, 0x5b, 0x58, 0x44, 0x99, 0xbd, 0xe4, 0x24, 0x4a, 0x15, 0x8f, 0x13, 0x5b, 0x72, 0x32, 0x8e, 0x32, 0x33, 0x33, 0x5b, 0x8a, 0x6e, 0x53, 0xf8, 0x8e, 0x1d, 0x3b, 0x94, 0xac, 0xac, 0xac, 0x56, 0x61, 0xbb, 0x57, 0xa3, 0x24, 0xf1, 0x35, 0x75, 0x57, 0x86, 0xfa, 0x35, 0xad, 0x46, 0xc7, 0x0b, 0xef, 0x6e, 0x40, 0xc7, 0x71, 0xe7, 0x98, 0x94, 0xdd, 0x14, 0x70, 0x0c, 0x1e, 0x3b, 0x9e, 0xcb, 0x0d, 0x4f, 0x81, 0x1b, 0x7e, 0x1e, 0xe8, 0x38, 0xed, 0xae, 0x8f, 0x94, 0x37, 0x7c, 0x0f, 0xba, 0x3e, 0xd0, 0xd8, 0xf1, 0x5a, 0x74, 0x13, 0xa0, 0xe3, 0xb8, 0x73, 0x48, 0xca, 0x2e, 0x3d, 0x92, 0x70, 0x48, 0x0d, 0xdb, 0x91, 0x89, 0xec, 0xc5, 0x8a, 0x78, 0x6b, 0x98, 0x45, 0xb1, 0x18, 0x8a, 0x31, 0x20, 0x90, 0xc7, 0xe2, 0xbe, 0xbc, 0x31, 0x71, 0xe7, 0x09, 0x44, 0x07, 0x2e, 0x4e, 0xda, 0x51, 0x72, 0xc7, 0x41, 0xff, 0x65, 0x08, 0x50, 0xcd, 0x6b, 0xad, 0x3d, 0x45, 0x55, 0xf0, 0xa0, 0x9d, 0x8e, 0x64, 0xca, 0x1f, 0x89, 0xb0, 0x8f, 0xa8, 0x35, 0x1e, 0xa3, 0x5c, 0xd2, 0xfe, 0x8a, 0x1a, 0xf8, 0xb9, 0xe8, 0x30, 0x8c, 0xd2, 0x61, 0x91, 0x14, 0x2e, 0x71, 0x6b, 0xe3, 0x05, 0x7a, 0xc7, 0xd1, 0xda, 0xf6, 0x94, 0xd7, 0xed, 0x24, 0x5c, 0x5e, 0x59, 0x85, 0x73, 0xd9, 0xf9, 0xa8, 0xaa, 0xad, 0xe7, 0x9d, 0x61, 0x23, 0x95, 0x5d, 0x9d, 0x79, 0xa5, 0x46, 0xed, 0x5e, 0x8a, 0x5c, 0xf8, 0xf9, 0x78, 0x53, 0x28, 0x4c, 0xdf, 0xa4, 0x57, 0x8b, 0x72, 0xec, 0xa9, 0x82, 0x52, 0x54, 0xd4, 0xd6, 0x62, 0x44, 0x04, 0x35, 0xb9, 0x6c, 0x20, 0x39, 0xbf, 0xce, 0x88, 0x8d, 0x17, 0x4b, 0x79, 0x15, 0xe7, 0x82, 0x91, 0x7e, 0x1e, 0x08, 0x75, 0x77, 0xb5, 0x09, 0xd7, 0x76, 0xf4, 0x75, 0x1e, 0xf2, 0xba, 0x23, 0x40, 0x71, 0x49, 0x29, 0xf6, 0x1e, 0x3a, 0x8e, 0xa2, 0x2a, 0xde, 0x2e, 0x97, 0x17, 0x22, 0x65, 0xfb, 0x46, 0x1c, 0xd8, 0xbb, 0x13, 0x25, 0xa5, 0xc5, 0xbc, 0x4a, 0x77, 0x83, 0xaf, 0x6f, 0x0f, 0x84, 0x84, 0x47, 0x22, 0x36, 0x2e, 0x91, 0xbf, 0x41, 0x48, 0xa2, 0x86, 0x5a, 0x54, 0x64, 0x28, 0x85, 0x79, 0xf4, 0x38, 0x57, 0x51, 0x8b, 0x43, 0xa7, 0xcf, 0xd3, 0x36, 0x9f, 0x17, 0x12, 0xfb, 0x45, 0x22, 0xc8, 0x99, 0xac, 0xa8, 0x09, 0x99, 0x2e, 0x21, 0x4c, 0x84, 0xab, 0x0e, 0x97, 0xd5, 0xe0, 0x48, 0x51, 0x05, 0x12, 0xa9, 0x92, 0x3d, 0xc8, 0xcf, 0x13, 0xb6, 0x8d, 0x91, 0x75, 0x1e, 0xc1, 0xf6, 0x72, 0xb8, 0x8e, 0x08, 0x40, 0xeb, 0x42, 0xd4, 0xa4, 0xde, 0xb1, 0x2f, 0x0d, 0xae, 0x9e, 0xfe, 0xbc, 0x8b, 0xd5, 0x61, 0xd9, 0x2b, 0x7f, 0xc6, 0xce, 0x6d, 0x9b, 0x79, 0x13, 0xee, 0xa6, 0x8a, 0xb7, 0x88, 0xfd, 0x08, 0xb5, 0x63, 0xb3, 0xb7, 0xcb, 0x91, 0xa0, 0xa9, 0xd1, 0x04, 0x17, 0x06, 0x0c, 0x9d, 0x3c, 0x0d, 0xc3, 0xef, 0x7d, 0x04, 0x51, 0x3a, 0x27, 0xdc, 0x3e, 0x3c, 0x1e, 0x27, 0x79, 0xd3, 0xee, 0x13, 0xe0, 0x07, 0x5d, 0x40, 0x10, 0xf6, 0xe7, 0x97, 0xc1, 0x87, 0xec, 0x67, 0x7a, 0x58, 0x0f, 0xf8, 0xda, 0xb8, 0x42, 0x2f, 0xa3, 0xb1, 0xa7, 0xfd, 0x25, 0xd5, 0xaa, 0x5e, 0xff, 0x84, 0x00, 0x4f, 0x9a, 0xcd, 0xbc, 0xba, 0xb3, 0xc5, 0x75, 0x33, 0x07, 0x98, 0x88, 0x88, 0xca, 0xaa, 0x6a, 0x9a, 0x96, 0x6b, 0x80, 0x17, 0x91, 0xe0, 0xee, 0xe1, 0x09, 0x1f, 0x51, 0xd5, 0xe5, 0x31, 0xbe, 0x7a, 0xfe, 0x4a, 0xa4, 0x3b, 0x39, 0x89, 0x5a, 0x2f, 0x67, 0x57, 0xb2, 0x23, 0x39, 0x76, 0x77, 0x75, 0xd2, 0xab, 0x1d, 0x2c, 0xff, 0x0c, 0x2d, 0x07, 0xa5, 0xee, 0xc1, 0xf7, 0xfb, 0xf6, 0xe2, 0xaf, 0x19, 0x07, 0x31, 0xf1, 0xa7, 0x73, 0x30, 0xf2, 0x8e, 0x99, 0x98, 0x14, 0xef, 0x8c, 0x87, 0x23, 0x83, 0x50, 0xa9, 0x38, 0xe1, 0x68, 0x71, 0x05, 0xf6, 0xe4, 0x97, 0x63, 0x72, 0xb8, 0x3f, 0x06, 0x53, 0xe0, 0x4a, 0x63, 0x51, 0xbe, 0x3a, 0x67, 0xdc, 0x12, 0xe8, 0x45, 0x18, 0x20, 0xb5, 0xc6, 0x88, 0x60, 0x96, 0xdd, 0x57, 0x64, 0x7f, 0xae, 0x92, 0xd3, 0xfd, 0x99, 0xee, 0x2a, 0x95, 0xd5, 0x6a, 0x31, 0x72, 0x08, 0xed, 0xcc, 0x1e, 0x9a, 0x9d, 0x93, 0xa7, 0xca, 0x7a, 0x04, 0x51, 0x66, 0x50, 0x47, 0x91, 0xba, 0x8c, 0xc3, 0x94, 0xd2, 0x24, 0xfb, 0x11, 0x49, 0x05, 0xb9, 0x4f, 0x11, 0xc4, 0x5d, 0x7a, 0x5c, 0x5e, 0xd9, 0x34, 0x1a, 0x61, 0x88, 0x1a, 0x84, 0xf0, 0x3b, 0x7f, 0x81, 0x24, 0x0a, 0x80, 0x21, 0x66, 0x10, 0x4e, 0x52, 0x0c, 0xe9, 0xc8, 0xca, 0xf7, 0x70, 0x31, 0xbf, 0x18, 0xe7, 0x4b, 0xab, 0x11, 0x15, 0x12, 0x84, 0x81, 0x81, 0x7e, 0x18, 0x15, 0xe4, 0x0b, 0x9a, 0x1a, 0xc0, 0xea, 0x9c, 0x52, 0x12, 0xd5, 0x09, 0xbd, 0x68, 0x5a, 0x4b, 0x23, 0x84, 0x9e, 0xe1, 0xe1, 0x5c, 0x3a, 0x55, 0x33, 0xf3, 0xb4, 0x6a, 0x23, 0x7c, 0x48, 0x18, 0x4a, 0xf1, 0x9a, 0xe3, 0x5b, 0xad, 0x7c, 0x27, 0x22, 0xaf, 0x23, 0x16, 0x24, 0xc6, 0x24, 0x1a, 0x49, 0x80, 0x5c, 0x9c, 0x39, 0x9f, 0x0d, 0x37, 0x1a, 0x71, 0xf0, 0xa2, 0x68, 0xec, 0xe7, 0x9f, 0x7e, 0x82, 0xa5, 0xaf, 0x2e, 0x41, 0x45, 0x59, 0xb1, 0xca, 0x8a, 0x04, 0x61, 0x42, 0x2c, 0x27, 0x8a, 0x8b, 0x28, 0x14, 0xdc, 0xf1, 0x0b, 0x09, 0xc3, 0xf8, 0xc7, 0x17, 0xe3, 0x14, 0x8d, 0x72, 0x95, 0x1d, 0xda, 0x85, 0x5b, 0xef, 0x5b, 0x80, 0x12, 0x4e, 0xe0, 0x67, 0x7c, 0x29, 0x7e, 0x15, 0x19, 0x85, 0x88, 0xca, 0x52, 0xc4, 0x19, 0x28, 0x03, 0xc3, 0x34, 0x53, 0xc7, 0x26, 0xa1, 0x6f, 0xef, 0x30, 0xa6, 0x75, 0x42, 0x09, 0x57, 0x48, 0x3b, 0x2b, 0xea, 0x10, 0x4f, 0x41, 0xd0, 0xbe, 0xee, 0xd6, 0x8c, 0x60, 0x73, 0xb5, 0x09, 0x3d, 0x49, 0x84, 0x41, 0xa4, 0x4c, 0x57, 0x72, 0xa5, 0xeb, 0x66, 0x04, 0x48, 0x27, 0x12, 0xc4, 0x78, 0x51, 0xe4, 0x51, 0x4f, 0x43, 0x14, 0x32, 0x77, 0xfa, 0x78, 0xb9, 0x63, 0xe8, 0xb0, 0x91, 0xf0, 0xf6, 0x0f, 0xc1, 0xc1, 0x03, 0x29, 0xa8, 0xa4, 0x78, 0x97, 0x7a, 0xe3, 0x47, 0x58, 0x1e, 0xb4, 0xab, 0x30, 0x75, 0x95, 0x65, 0x70, 0xa1, 0x89, 0x90, 0xa1, 0x13, 0xa7, 0xa1, 0x98, 0x82, 0xa8, 0x07, 0x36, 0xaf, 0xa7, 0xd0, 0x5e, 0x3c, 0x46, 0x87, 0xf6, 0x44, 0xd6, 0xc7, 0xef, 0x22, 0x2f, 0x37, 0x1b, 0xe5, 0x09, 0xa3, 0xe0, 0x16, 0x15, 0x83, 0x73, 0x19, 0xc7, 0xb0, 0x7b, 0xe7, 0x7e, 0xf4, 0x09, 0xeb, 0x85, 0x60, 0x1f, 0x4f, 0x0c, 0x20, 0xe2, 0x45, 0xc4, 0x79, 0x07, 0x09, 0x11, 0x4e, 0x42, 0xb8, 0x5a, 0xf4, 0xf8, 0x28, 0x8e, 0x86, 0x12, 0xee, 0x27, 0x52, 0x6b, 0x15, 0xf4, 0x72, 0x75, 0x22, 0xbb, 0x53, 0x8b, 0xeb, 0x44, 0x5f, 0xb7, 0x9d, 0xf4, 0xba, 0x1a, 0x01, 0x5a, 0x15, 0x6b, 0xb9, 0x94, 0xcc, 0xa5, 0xfd, 0x2e, 0x0f, 0x8e, 0x02, 0x1d, 0x97, 0x8c, 0x4e, 0xae, 0x5e, 0x34, 0xb4, 0xb1, 0x0d, 0xcf, 0xfd, 0xe1, 0x31, 0x9c, 0x39, 0x91, 0xce, 0xbb, 0x3a, 0x1f, 0x0a, 0x52, 0xf5, 0x41, 0xdf, 0x7e, 0xd1, 0x70, 0xf7, 0xf2, 0x43, 0x35, 0x4d, 0x3d, 0x04, 0x45, 0xf6, 0x46, 0xc2, 0x98, 0x71, 0xc8, 0xd8, 0xf5, 0x1d, 0xb6, 0xfe, 0xf3, 0x6b, 0x0c, 0x18, 0x3a, 0x02, 0x3f, 0x99, 0x79, 0x17, 0x0a, 0x29, 0x54, 0xb8, 0xfb, 0x9b, 0xd5, 0x70, 0x89, 0xea, 0x83, 0xa4, 0x85, 0x8f, 0x21, 0xa0, 0xde, 0x84, 0x8a, 0xc3, 0xa9, 0xd4, 0x26, 0xf1, 0xc2, 0xdc, 0xe9, 0x53, 0x28, 0x02, 0xea, 0xab, 0x22, 0x7a, 0x57, 0xa5, 0x09, 0x7d, 0xdc, 0x9c, 0x39, 0x22, 0x04, 0xd3, 0xfc, 0x5d, 0x76, 0xb9, 0x84, 0xdf, 0x5e, 0xa3, 0x60, 0x86, 0x8f, 0x0e, 0x9e, 0x16, 0xe1, 0x5a, 0x7c, 0x67, 0x9f, 0xd7, 0x25, 0x01, 0xa4, 0x51, 0xab, 0x57, 0x7f, 0x81, 0xe2, 0xd2, 0x72, 0xfc, 0x94, 0xd6, 0x4d, 0x9c, 0xc9, 0x0a, 0xf4, 0xee, 0x9e, 0x48, 0x3f, 0x99, 0x8d, 0x97, 0x5f, 0x7c, 0x16, 0xa6, 0x9a, 0x52, 0x4c, 0xbd, 0xf5, 0x0e, 0xf4, 0x8e, 0x1e, 0x00, 0xf7, 0x1e, 0x41, 0x5c, 0x21, 0xe9, 0x61, 0xa4, 0x98, 0x76, 0x11, 0xcd, 0x16, 0xe9, 0xdc, 0xf5, 0xa8, 0xa2, 0x8a, 0xc2, 0x57, 0xff, 0x78, 0x9f, 0x36, 0x8d, 0xb3, 0x10, 0x3f, 0x6c, 0x14, 0x12, 0x46, 0x8f, 0x43, 0xee, 0xb9, 0xd3, 0x38, 0xc6, 0x49, 0x3a, 0xf0, 0xdf, 0x66, 0xc1, 0x8d, 0x56, 0x52, 0x9c, 0x36, 0xae, 0x41, 0x04, 0xcd, 0xfc, 0xf5, 0xec, 0xd7, 0x17, 0x63, 0x63, 0xfb, 0xd1, 0x6a, 0x44, 0x28, 0x8e, 0x42, 0x8f, 0x30, 0x72, 0xa3, 0xde, 0xcd, 0x38, 0xd2, 0x89, 0xda, 0x46, 0x64, 0x92, 0x65, 0x4d, 0xa4, 0xd2, 0x95, 0xac, 0xba, 0x1c, 0xe9, 0xae, 0x2b, 0x02, 0xf0, 0x3a, 0x11, 0xcf, 0x3d, 0xf7, 0x2c, 0x56, 0x7f, 0xb9, 0x06, 0x49, 0xa3, 0xc6, 0xc2, 0xa0, 0x97, 0x49, 0x12, 0x34, 0x19, 0x33, 0x0f, 0xa1, 0x61, 0x61, 0xaa, 0x81, 0x1f, 0xc5, 0xc5, 0x93, 0xa2, 0xe5, 0x3f, 0xe0, 0xfc, 0xe9, 0x63, 0x18, 0x92, 0x30, 0x18, 0xfd, 0x29, 0xdc, 0x2c, 0x04, 0x68, 0xa0, 0x45, 0xa3, 0xc2, 0x82, 0x22, 0x5c, 0xc8, 0xcb, 0x47, 0x69, 0x75, 0x1d, 0x74, 0x54, 0xc3, 0xb8, 0x70, 0xf6, 0x24, 0xd2, 0x0e, 0xec, 0x43, 0x31, 0xc5, 0x6a, 0x03, 0x69, 0x64, 0x3a, 0x6e, 0x78, 0x12, 0xf4, 0x34, 0x7c, 0x52, 0xe0, 0xee, 0x81, 0xaa, 0xf8, 0xa1, 0x28, 0x5e, 0xb5, 0x1c, 0x11, 0x14, 0x4b, 0x9c, 0x36, 0xe7, 0x1e, 0x04, 0xd2, 0x94, 0x53, 0x3e, 0x45, 0x6e, 0x8b, 0x88, 0xdd, 0x71, 0x7d, 0xc2, 0xd0, 0x97, 0xd6, 0x91, 0x74, 0x24, 0xbc, 0xb8, 0x6a, 0xb2, 0xa2, 0xcf, 0xcb, 0x1b, 0x30, 0xd3, 0xc7, 0x55, 0x5d, 0xa1, 0xa9, 0x81, 0x0e, 0xfa, 0xbb, 0x2e, 0x08, 0x20, 0x2c, 0xe7, 0x99, 0x67, 0x9e, 0xc1, 0xd2, 0xa5, 0xcb, 0xd8, 0xd3, 0x0d, 0x18, 0x91, 0x3c, 0x16, 0xe3, 0xb9, 0xa2, 0x71, 0xa6, 0x05, 0x25, 0xb1, 0xe5, 0x70, 0x9c, 0x22, 0xf0, 0x0b, 0x1e, 0x9a, 0x87, 0x31, 0x63, 0xc7, 0xa8, 0xd6, 0x48, 0x83, 0x7a, 0x86, 0x21, 0xaf, 0xa4, 0x16, 0xa9, 0x87, 0xd2, 0x68, 0x16, 0x87, 0xba, 0x2b, 0x14, 0x4a, 0x95, 0x55, 0x4d, 0x29, 0x4d, 0x96, 0x97, 0x72, 0xd4, 0x54, 0x91, 0x25, 0x99, 0x48, 0x38, 0x03, 0x77, 0xcb, 0x26, 0x53, 0x3d, 0xad, 0x34, 0x95, 0x73, 0x45, 0x44, 0xd9, 0x66, 0x4e, 0xf0, 0x75, 0x95, 0xe5, 0x34, 0x3d, 0x15, 0x8c, 0xe8, 0x9f, 0xdc, 0x4e, 0x19, 0xcf, 0x5a, 0x64, 0x1d, 0xd8, 0x03, 0xa3, 0xb3, 0x2b, 0x86, 0x0c, 0x4e, 0x40, 0x78, 0x5c, 0x3c, 0xfa, 0xf4, 0x0a, 0x20, 0x2b, 0xa2, 0xd2, 0x02, 0x05, 0xc1, 0x73, 0x6b, 0x28, 0xc1, 0xcb, 0x2d, 0x9a, 0x27, 0x47, 0x55, 0xa2, 0x3f, 0x77, 0xdf, 0x97, 0x09, 0xe2, 0x20, 0xdc, 0xab, 0xd9, 0x5c, 0x53, 0x02, 0xc8, 0x44, 0xba, 0x7e, 0xc3, 0x06, 0xdc, 0x77, 0xdf, 0xfd, 0xaa, 0xaa, 0x8a, 0x88, 0x2e, 0x8b, 0x9c, 0xb5, 0x89, 0x4a, 0x43, 0x77, 0xcd, 0x99, 0x4b, 0xd9, 0xea, 0xc1, 0xd8, 0x47, 0xb6, 0x71, 0xf6, 0xc7, 0x1f, 0x51, 0xc5, 0xde, 0xe9, 0xc9, 0x09, 0x7a, 0xc4, 0x88, 0x24, 0xf4, 0xf0, 0xf3, 0xa3, 0x01, 0x26, 0x6f, 0x44, 0x44, 0x0d, 0x44, 0x76, 0x7e, 0x25, 0x32, 0x8e, 0x1f, 0x43, 0x8d, 0x4c, 0xc6, 0x1c, 0x09, 0x62, 0xd3, 0x4b, 0x31, 0x19, 0x39, 0x70, 0xb8, 0x49, 0xe3, 0x4e, 0x58, 0x34, 0x44, 0x8e, 0x1d, 0x3f, 0xc9, 0x79, 0xc4, 0x8d, 0x3b, 0xe8, 0x50, 0xf8, 0x86, 0x85, 0xa3, 0x9c, 0xd2, 0xc1, 0x45, 0xd9, 0x59, 0x30, 0x66, 0xfe, 0x08, 0x4f, 0x37, 0x57, 0xd5, 0x2c, 0x90, 0x89, 0xab, 0xaa, 0xd8, 0xf8, 0x44, 0xdc, 0x33, 0x73, 0x06, 0x27, 0xe9, 0x60, 0x22, 0xc7, 0xb1, 0xac, 0xa6, 0x25, 0xa2, 0x5d, 0x53, 0x02, 0x94, 0x95, 0x95, 0xe1, 0xe7, 0xf7, 0xfc, 0x02, 0x85, 0x54, 0xad, 0x0c, 0xa6, 0xea, 0x99, 0x58, 0x72, 0xd3, 0xeb, 0xdd, 0x51, 0x40, 0xeb, 0x3d, 0xa2, 0x72, 0xf6, 0xfb, 0x3f, 0x3c, 0xc5, 0x5e, 0x6c, 0xc0, 0x3a, 0xca, 0x8e, 0x8b, 0xa5, 0x1f, 0x6a, 0x39, 0x41, 0xa4, 0x94, 0x45, 0x60, 0x86, 0x86, 0x43, 0xd4, 0x7d, 0x43, 0x30, 0xb5, 0x54, 0x06, 0x0c, 0x1e, 0x8e, 0x1a, 0x93, 0x1b, 0x8e, 0x67, 0x1c, 0xc5, 0xd9, 0x13, 0x69, 0xe4, 0xd3, 0x5c, 0x9e, 0x92, 0x48, 0x22, 0x77, 0x7b, 0xee, 0xec, 0x79, 0x5c, 0xa4, 0xe5, 0x1f, 0x83, 0xc1, 0x83, 0x02, 0xc4, 0xae, 0xdc, 0x07, 0x38, 0xc1, 0xe0, 0xe2, 0xcc, 0x1d, 0xaf, 0x8e, 0xc6, 0xd5, 0x68, 0xca, 0x8b, 0xe7, 0x12, 0xa2, 0x29, 0xe0, 0x4c, 0x5b, 0xe3, 0x82, 0xf4, 0xe4, 0x49, 0xb7, 0x61, 0xc6, 0x2d, 0x37, 0x23, 0xb6, 0x5f, 0xb8, 0xba, 0x2a, 0x6b, 0x09, 0x71, 0x8e, 0x0a, 0xbf, 0xa6, 0x04, 0x38, 0x42, 0x55, 0x88, 0x67, 0x97, 0xbc, 0x88, 0x3a, 0xca, 0xb6, 0xcb, 0xca, 0x46, 0x0c, 0x1f, 0xcb, 0x1c, 0xa7, 0xe3, 0xb3, 0x50, 0xb4, 0x08, 0x0a, 0x2f, 0xaa, 0xf2, 0xeb, 0x34, 0x2e, 0x8a, 0xd4, 0x1f, 0x7e, 0x80, 0xd8, 0x8e, 0x12, 0x95, 0x3a, 0x13, 0xed, 0x04, 0xd2, 0x0a, 0xb3, 0xaa, 0x66, 0x21, 0x44, 0x33, 0x92, 0x28, 0xa2, 0x33, 0x32, 0x70, 0xf0, 0x30, 0x04, 0x47, 0x0e, 0x40, 0x35, 0x59, 0xda, 0xae, 0x8d, 0x5f, 0x61, 0xeb, 0xe6, 0x0d, 0x90, 0x79, 0x25, 0x98, 0x66, 0x99, 0xbc, 0x3c, 0xbd, 0x55, 0xe1, 0x7e, 0x67, 0x2a, 0x00, 0x34, 0x32, 0x7f, 0x77, 0x1e, 0x4f, 0xe8, 0x49, 0x04, 0x85, 0x9b, 0x3f, 0xd5, 0x6c, 0x96, 0x88, 0xa1, 0xd3, 0xae, 0x54, 0x20, 0xc5, 0xd3, 0xc7, 0x4f, 0x99, 0x81, 0xe9, 0x93, 0xc7, 0x22, 0x22, 0x24, 0xd0, 0x51, 0x78, 0x6e, 0x31, 0x9f, 0x6b, 0x4a, 0x80, 0x1f, 0x88, 0xd4, 0x83, 0x69, 0x47, 0x68, 0xbf, 0x2d, 0x03, 0xa2, 0x7c, 0xec, 0x4e, 0x31, 0xf7, 0x06, 0xf6, 0xda, 0x06, 0x22, 0x54, 0x7a, 0x7a, 0x45, 0x79, 0x19, 0x32, 0xcf, 0x9f, 0x43, 0x9f, 0x3e, 0x11, 0x48, 0x1c, 0x3c, 0x98, 0xb2, 0xf9, 0xc5, 0xb4, 0x3e, 0x96, 0xc7, 0xc9, 0xd1, 0x85, 0xc8, 0x74, 0x55, 0x59, 0x95, 0xb0, 0x31, 0x27, 0x22, 0x51, 0xac, 0xe6, 0xd7, 0x54, 0xd7, 0xa8, 0x87, 0x6a, 0xa1, 0x5c, 0x92, 0x0e, 0xbf, 0x69, 0x1a, 0xfc, 0x02, 0x43, 0x70, 0x24, 0x75, 0x2f, 0x36, 0xaf, 0xfd, 0x02, 0x99, 0x67, 0xcf, 0x70, 0x49, 0xeb, 0x0a, 0x0f, 0x4f, 0x2a, 0x31, 0x53, 0xad, 0xd6, 0x8d, 0x9a, 0x16, 0xa2, 0x13, 0x23, 0x79, 0xc9, 0xde, 0x42, 0x21, 0x51, 0x24, 0x2f, 0x03, 0xf3, 0x1d, 0x3b, 0x69, 0x3a, 0x6e, 0x99, 0x70, 0x33, 0x46, 0x0f, 0x1f, 0xc4, 0x53, 0xd7, 0x4e, 0x2a, 0x37, 0xb7, 0x88, 0xfa, 0x4b, 0x11, 0xd7, 0x94, 0x00, 0x62, 0xe2, 0x7b, 0xd3, 0xa6, 0xcd, 0x70, 0x21, 0x32, 0xb6, 0x6e, 0xdf, 0x8d, 0xb4, 0xc3, 0x47, 0x54, 0xa4, 0x88, 0x55, 0x6e, 0x99, 0x0b, 0xaa, 0xab, 0xaa, 0x54, 0x0b, 0xdd, 0x82, 0xf8, 0xea, 0x2a, 0x7e, 0x0f, 0x24, 0x32, 0x02, 0xfe, 0xbe, 0x3e, 0xdc, 0x90, 0x51, 0xaf, 0x86, 0x43, 0xa5, 0x87, 0x7f, 0x0f, 0x15, 0x5e, 0x88, 0x25, 0xc8, 0xa7, 0x12, 0x36, 0x27, 0x63, 0xc0, 0x83, 0xe6, 0xdd, 0xc4, 0x56, 0x23, 0x85, 0x07, 0xd1, 0x27, 0x3a, 0x8e, 0xb6, 0xb2, 0x65, 0x33, 0xe7, 0x8f, 0x33, 0xa7, 0x8e, 0x23, 0x65, 0xe7, 0x77, 0x38, 0x79, 0xf4, 0xb0, 0xaa, 0x60, 0x21, 0x3b, 0x6a, 0x29, 0x5b, 0x3e, 0x78, 0xa3, 0x73, 0xb9, 0x44, 0xd0, 0xf0, 0xc8, 0xbe, 0xaa, 0x75, 0xb5, 0x98, 0x7e, 0xbd, 0x31, 0x61, 0x74, 0x22, 0x02, 0x02, 0xfc, 0xed, 0xa0, 0xb0, 0x73, 0xd1, 0xd7, 0x94, 0x00, 0x52, 0x75, 0x4a, 0x52, 0x62, 0x1b, 0xbf, 0x96, 0x70, 0xf4, 0xe8, 0x51, 0x54, 0xf1, 0x30, 0x6e, 0xcb, 0xf7, 0xdb, 0x90, 0x4f, 0x5d, 0x89, 0x46, 0xb2, 0x03, 0x8a, 0xda, 0xa9, 0x44, 0x10, 0x1b, 0xe0, 0xf5, 0x5c, 0xb1, 0x48, 0x2f, 0x17, 0x5b, 0x93, 0x3e, 0x64, 0x57, 0x41, 0x81, 0xfe, 0xa0, 0x18, 0x1d, 0x0d, 0x07, 0x0e, 0x45, 0xf4, 0xc0, 0x18, 0x2a, 0x0d, 0x7a, 0x91, 0x6d, 0x15, 0xaa, 0xba, 0x16, 0xa7, 0x39, 0x69, 0x8f, 0x1e, 0x9d, 0xac, 0xea, 0x1e, 0x95, 0x71, 0x14, 0x35, 0xb2, 0x0c, 0xe9, 0xc9, 0xde, 0x3e, 0xbe, 0xf0, 0xe6, 0x11, 0x85, 0x9b, 0x57, 0x0f, 0x4e, 0xca, 0x7a, 0x5a, 0x83, 0x6b, 0xe0, 0xf7, 0x5b, 0xaa, 0x48, 0xec, 0xaa, 0x4b, 0xc7, 0x1a, 0xd4, 0x1e, 0xeb, 0xc9, 0xa3, 0x0d, 0x53, 0x6d, 0x15, 0xe2, 0x62, 0x22, 0xd4, 0x45, 0x80, 0x1c, 0x06, 0x76, 0xa5, 0xbb, 0xe6, 0x04, 0xd0, 0x1a, 0x77, 0xf2, 0xd4, 0x49, 0x75, 0x19, 0x9a, 0x47, 0x16, 0x53, 0x58, 0x58, 0x04, 0x91, 0x14, 0x2d, 0xa6, 0x2e, 0x94, 0x9c, 0x8e, 0xca, 0xaa, 0x48, 0xd8, 0x83, 0x1b, 0x97, 0xa8, 0x72, 0x48, 0x27, 0x8a, 0x2c, 0xde, 0xde, 0xbe, 0xea, 0x01, 0x5d, 0x4d, 0x55, 0x05, 0xaa, 0x49, 0x28, 0x13, 0x0f, 0xe5, 0x4a, 0x09, 0x1f, 0x40, 0x3b, 0x82, 0xaf, 0xbd, 0xf6, 0x0a, 0x26, 0x4e, 0x98, 0xa8, 0x65, 0x4d, 0x44, 0xd7, 0xd1, 0x2c, 0x5f, 0xbe, 0xaa, 0x62, 0x24, 0xcb, 0x51, 0x4f, 0x4e, 0xc8, 0x1c, 0x20, 0xaa, 0x6a, 0x52, 0x35, 0x47, 0x5a, 0x0d, 0x97, 0xad, 0x32, 0xe2, 0x1a, 0xa8, 0x7a, 0xd4, 0x83, 0x3b, 0xe3, 0x9b, 0x27, 0x4c, 0xe2, 0xbe, 0xe3, 0xff, 0xc1, 0x24, 0x6c, 0xc6, 0xd0, 0x65, 0x8f, 0x8c, 0x86, 0xad, 0x5b, 0xb7, 0xe2, 0x8d, 0xbf, 0xff, 0x1d, 0x94, 0xcd, 0xa4, 0x26, 0x5c, 0x05, 0x72, 0xa8, 0xd6, 0x27, 0x47, 0xd5, 0xc1, 0x62, 0xa4, 0x91, 0x3b, 0x57, 0x99, 0xac, 0x0d, 0x94, 0x20, 0x77, 0x27, 0x12, 0xc5, 0x54, 0x82, 0x5c, 0xd2, 0xd4, 0x92, 0x65, 0xb9, 0x93, 0x95, 0xfc, 0xc7, 0xa2, 0x85, 0x5c, 0xcf, 0xc7, 0xa9, 0x87, 0x75, 0xcd, 0xf3, 0xb6, 0x7c, 0x97, 0x72, 0xe4, 0x27, 0xac, 0x4b, 0xd8, 0xa0, 0x10, 0x58, 0xe6, 0x01, 0x31, 0xc5, 0x69, 0x30, 0x78, 0x5a, 0x9e, 0x44, 0x58, 0x26, 0xeb, 0x12, 0xff, 0x75, 0x33, 0x02, 0x2c, 0x5b, 0x27, 0xbd, 0x5d, 0x7a, 0xec, 0xaa, 0x4f, 0x3e, 0xc1, 0xfe, 0x03, 0x07, 0xd4, 0xd5, 0x8b, 0x07, 0x11, 0x5e, 0x45, 0xdd, 0xcc, 0x32, 0x6a, 0x7f, 0x38, 0x51, 0x85, 0x5d, 0x56, 0x4a, 0x72, 0x35, 0x10, 0xd3, 0xbf, 0x1f, 0x16, 0x3c, 0xf8, 0x00, 0xc2, 0xc2, 0x42, 0x19, 0x7e, 0x75, 0xd6, 0xee, 0x96, 0x75, 0xed, 0xac, 0xff, 0xba, 0x24, 0x40, 0x67, 0x1b, 0x75, 0x23, 0xa5, 0xbf, 0x74, 0xd8, 0x71, 0x23, 0xd5, 0xf8, 0x5f, 0xac, 0xae, 0xdd, 0x04, 0xb8, 0xc6, 0x04, 0xed, 0x26, 0x40, 0x37, 0x01, 0xae, 0x1d, 0x06, 0x64, 0x05, 0x64, 0x4f, 0xd1, 0xaa, 0xab, 0x6b, 0x77, 0xc3, 0x8e, 0x80, 0x0b, 0x17, 0x2e, 0xa8, 0xab, 0x23, 0x59, 0xf9, 0x34, 0xff, 0xd1, 0x02, 0xaf, 0x5d, 0xbc, 0x89, 0xc9, 0x1a, 0x31, 0xfc, 0x20, 0xf6, 0x13, 0x1e, 0x7a, 0xe8, 0x21, 0xbb, 0xf0, 0x5d, 0x05, 0xd0, 0xb5, 0xdb, 0xbc, 0xae, 0xaa, 0x35, 0xf3, 0x0d, 0x0d, 0x0d, 0xc5, 0x4b, 0x2f, 0xbd, 0xc4, 0xe3, 0xea, 0x7d, 0xea, 0x01, 0xdd, 0x37, 0xdf, 0x7c, 0xa3, 0x6e, 0xb8, 0xa4, 0x48, 0xd9, 0x41, 0xdb, 0x73, 0xa2, 0x41, 0xfa, 0xe5, 0x97, 0x5f, 0xaa, 0x60, 0xad, 0x6a, 0x93, 0xda, 0xcb, 0xa8, 0x93, 0xf1, 0xed, 0x5e, 0x86, 0x52, 0x81, 0x4d, 0x55, 0xb5, 0x2d, 0xa2, 0xc5, 0x7a, 0x39, 0x4e, 0x96, 0x63, 0x5f, 0x39, 0x1a, 0x4e, 0x4e, 0x4e, 0x56, 0x91, 0x62, 0xab, 0x3e, 0x72, 0x22, 0x79, 0xee, 0xdc, 0x39, 0x35, 0x4a, 0x4e, 0x2f, 0x45, 0x9d, 0xd7, 0xd2, 0xf1, 0x7b, 0x8b, 0x6a, 0x5e, 0x12, 0x26, 0xa6, 0x4a, 0xe4, 0x88, 0xb9, 0x35, 0x27, 0x9b, 0x28, 0x49, 0x23, 0xb0, 0xa2, 0xd6, 0x2b, 0x4e, 0xbe, 0x03, 0x25, 0x7b, 0x07, 0x71, 0x42, 0x14, 0x7e, 0xfc, 0x48, 0xf5, 0x37, 0xff, 0x93, 0xcb, 0x1f, 0xf9, 0x02, 0x99, 0xec, 0x35, 0x34, 0x27, 0x69, 0xe5, 0xd7, 0x56, 0x27, 0xd6, 0x4a, 0x64, 0xa7, 0x2e, 0xb8, 0x90, 0xfc, 0x44, 0xb7, 0x94, 0x56, 0x9c, 0x61, 0x4b, 0x75, 0xd8, 0x6e, 0x9e, 0xac, 0x88, 0x5d, 0xc7, 0x23, 0x60, 0x85, 0x5b, 0x7c, 0xa9, 0x71, 0x93, 0x1f, 0x11, 0xd0, 0xe4, 0x5d, 0xe2, 0xc5, 0xf0, 0xb7, 0xa5, 0xfb, 0xfd, 0xef, 0x7f, 0x6f, 0x05, 0xc3, 0xdd, 0xae, 0x19, 0x24, 0x2a, 0x2a, 0xaa, 0x49, 0xbc, 0x66, 0x9e, 0xda, 0x0c, 0x40, 0x0f, 0xc5, 0x55, 0x94, 0x05, 0x0b, 0x16, 0x34, 0x81, 0x6b, 0x5e, 0x17, 0xcb, 0x77, 0x12, 0xc0, 0x32, 0xb9, 0xd9, 0xff, 0xe9, 0xa7, 0x9f, 0xda, 0xcc, 0xa3, 0xb9, 0xc6, 0xaf, 0x39, 0x81, 0x85, 0x67, 0xf7, 0xee, 0xdd, 0x0a, 0x3b, 0x9b, 0xcd, 0xf4, 0x96, 0x65, 0x8b, 0xed, 0xee, 0x77, 0xdf, 0x7d, 0xd7, 0x22, 0x65, 0xeb, 0x5e, 0xe9, 0x09, 0x6d, 0x76, 0x9c, 0xb0, 0x6c, 0xc2, 0xbe, 0xfc, 0xf2, 0xcb, 0xe6, 0x8a, 0xf5, 0xe9, 0xd3, 0xc7, 0x26, 0x8c, 0x66, 0xcc, 0x5c, 0x2a, 0x6b, 0x49, 0x00, 0x01, 0xd6, 0x54, 0xa6, 0x25, 0xae, 0x39, 0x01, 0x44, 0x69, 0x57, 0x53, 0xe8, 0x95, 0x78, 0xf6, 0x32, 0x25, 0x3d, 0x3d, 0xdd, 0x5c, 0xc6, 0xc9, 0x93, 0x27, 0x15, 0x29, 0xd3, 0x12, 0x09, 0xb6, 0x14, 0x7b, 0xcd, 0x09, 0x2e, 0x7b, 0x34, 0x9b, 0xe2, 0x92, 0xce, 0x1e, 0x01, 0xa8, 0x35, 0xdd, 0x24, 0xff, 0xe7, 0x9f, 0x7f, 0xbe, 0x79, 0x76, 0x8a, 0x7c, 0x2d, 0x95, 0x73, 0x91, 0x19, 0x6e, 0xcc, 0x98, 0x31, 0x56, 0x30, 0xb6, 0x02, 0xda, 0x44, 0x00, 0xae, 0x16, 0x94, 0xbb, 0xef, 0xbe, 0xbb, 0x49, 0x01, 0x96, 0x0d, 0xb6, 0xf4, 0x5b, 0x1a, 0x60, 0xb7, 0x2c, 0xb0, 0xa3, 0x04, 0x78, 0xee, 0xb9, 0xe7, 0xcc, 0x8d, 0x92, 0x72, 0xc8, 0xca, 0x2c, 0xb3, 0x55, 0xfd, 0x64, 0x85, 0x4d, 0x60, 0x1c, 0x49, 0x00, 0x9e, 0x17, 0xa9, 0x46, 0xe5, 0xb5, 0x36, 0x8a, 0x1e, 0x7e, 0x4b, 0xee, 0x8d, 0x37, 0xde, 0x68, 0x52, 0x0f, 0xde, 0x77, 0xb4, 0x04, 0x6a, 0x0e, 0xb7, 0xbb, 0x0a, 0x22, 0xa4, 0xca, 0x1f, 0xe5, 0x73, 0xe8, 0xe2, 0xa7, 0xf2, 0xbd, 0xfa, 0x14, 0xbf, 0xf6, 0xdb, 0xbb, 0x77, 0x2f, 0xeb, 0xd7, 0xba, 0x93, 0xd5, 0x86, 0xe6, 0xc4, 0xa4, 0x82, 0xa5, 0xe3, 0x08, 0xb0, 0x7c, 0x6d, 0xe2, 0x97, 0xdb, 0x30, 0x4b, 0x97, 0x9a, 0x9a, 0x6a, 0xf9, 0xaa, 0xfa, 0xe5, 0x62, 0xa7, 0xab, 0x9c, 0xdc, 0x15, 0xf0, 0xd3, 0x7f, 0xe6, 0xec, 0x69, 0xa8, 0xbe, 0xc5, 0xa5, 0xab, 0xa5, 0x79, 0x09, 0x99, 0x9b, 0x12, 0x12, 0x12, 0xcc, 0xe9, 0x5a, 0xf4, 0x10, 0x89, 0x76, 0x9d, 0x25, 0xff, 0x97, 0x1e, 0xfe, 0xc8, 0x23, 0x8f, 0x28, 0x1f, 0xd0, 0x0e, 0x00, 0x0d, 0xf8, 0x29, 0x13, 0x27, 0x4e, 0x6c, 0x42, 0x75, 0x16, 0xa4, 0xc8, 0xf0, 0x6b, 0x4e, 0xfd, 0xfb, 0xf9, 0xa9, 0x02, 0x89, 0xd3, 0x7e, 0xb1, 0xb1, 0xb1, 0xaa, 0x39, 0x08, 0x4e, 0xb8, 0xe6, 0x30, 0x89, 0xe3, 0x25, 0x89, 0x72, 0xd3, 0x4d, 0x37, 0x35, 0x61, 0x33, 0xcd, 0x59, 0x80, 0xb0, 0xa1, 0x85, 0x0b, 0x17, 0x2a, 0x8b, 0x16, 0x2d, 0x52, 0xe4, 0xa3, 0x12, 0x5a, 0x9e, 0xda, 0xb3, 0x7f, 0xff, 0xfe, 0x0a, 0x3b, 0x4c, 0x93, 0x76, 0x09, 0x9b, 0x93, 0xaf, 0x12, 0x86, 0x87, 0x87, 0xab, 0x3f, 0x61, 0x75, 0x1a, 0xbc, 0xb0, 0x0e, 0x2d, 0x3c, 0x32, 0x32, 0x52, 0x35, 0x67, 0xc1, 0x63, 0x71, 0x73, 0x7a, 0x4e, 0xb4, 0x0a, 0x91, 0x69, 0x86, 0x97, 0x74, 0xf2, 0xe9, 0x04, 0xf9, 0xf2, 0xe1, 0x53, 0x4f, 0x3d, 0xa5, 0xb6, 0x57, 0xcb, 0x4b, 0x9e, 0xf2, 0x9d, 0xe4, 0xb6, 0x6a, 0xda, 0x4b, 0x2f, 0xb6, 0xeb, 0xb8, 0xea, 0x50, 0xbf, 0xe7, 0x68, 0xc9, 0x46, 0xa4, 0x20, 0xb1, 0x45, 0x20, 0x26, 0x31, 0x68, 0x93, 0x4c, 0xf9, 0xe8, 0xa3, 0x8f, 0xf8, 0x85, 0xdc, 0x1e, 0x6a, 0x25, 0xfb, 0xf5, 0xeb, 0xa7, 0x6c, 0xd9, 0xb2, 0xc5, 0x2a, 0xdf, 0x55, 0xab, 0x56, 0xa9, 0xfc, 0x56, 0xab, 0xac, 0xa4, 0x9f, 0x3d, 0x7b, 0xb6, 0xc2, 0x15, 0x95, 0xf9, 0x0b, 0x1c, 0x12, 0xc7, 0x55, 0x92, 0x42, 0x71, 0x94, 0x26, 0xe9, 0x85, 0x0d, 0x3e, 0xfb, 0xec, 0xb3, 0x4a, 0xf3, 0x3a, 0x08, 0xe2, 0xa4, 0x2c, 0xe1, 0xcb, 0x1a, 0x0f, 0x96, 0xaf, 0x79, 0xd0, 0x56, 0x5a, 0x93, 0xf4, 0xef, 0xbf, 0xff, 0xbe, 0x3a, 0x7f, 0x68, 0x65, 0xb7, 0xf4, 0x94, 0x3c, 0xc4, 0x06, 0x83, 0x2d, 0x04, 0x8a, 0xc9, 0x8d, 0x17, 0x5f, 0x7c, 0x51, 0x9d, 0xa7, 0x9a, 0xa7, 0x97, 0x7a, 0x49, 0xc7, 0x94, 0xb6, 0xb4, 0xc7, 0xb5, 0x7b, 0x19, 0xca, 0x82, 0xbb, 0x9d, 0x03, 0x31, 0x60, 0x77, 0x0e, 0x70, 0x60, 0x59, 0xdd, 0x59, 0xd9, 0xc0, 0x40, 0x37, 0x01, 0x6c, 0x20, 0xe5, 0x6a, 0x06, 0x75, 0x13, 0xe0, 0x6a, 0x62, 0xdb, 0x46, 0x59, 0xdd, 0x04, 0xb0, 0x81, 0x94, 0xab, 0x19, 0xd4, 0x4d, 0x80, 0xab, 0x89, 0x6d, 0x1b, 0x65, 0x75, 0x13, 0xc0, 0x06, 0x52, 0xae, 0x66, 0x50, 0x37, 0x01, 0xae, 0x26, 0xb6, 0x6d, 0x94, 0xd5, 0x4d, 0x00, 0x1b, 0x48, 0xb9, 0x9a, 0x41, 0xff, 0x07, 0xaf, 0xf3, 0x8d, 0x60, 0xda, 0x1b, 0x41, 0x87, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXJSIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x32, 0x3a, 0x38, 0x38, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x0e, 0x0a, 0xaa, 0x03, 0x00, 0x00, 0x05, 0x39, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x58, 0x7d, 0x48, 0x5b, 0x57, 0x14, 0xcf, 0x8b, 0x2f, 0x31, 0xd1, 0x1a, 0x4d, 0xb4, 0x06, 0xc7, 0xfc, 0x88, 0x55, 0x59, 0x97, 0x5a, 0xf1, 0xb3, 0x43, 0x83, 0xba, 0x32, 0x97, 0x0d, 0x3a, 0x0b, 0x2b, 0x08, 0x75, 0x65, 0x82, 0x8c, 0xc1, 0xdc, 0x1f, 0xab, 0x48, 0xdd, 0x64, 0x62, 0x19, 0x6e, 0x73, 0xc2, 0xa6, 0x83, 0x6e, 0x38, 0x46, 0xff, 0x28, 0xc3, 0x75, 0x6b, 0x51, 0x36, 0x54, 0x56, 0x23, 0x88, 0x3a, 0x3b, 0xa1, 0x22, 0xd4, 0xd8, 0xc9, 0x28, 0x9b, 0x1f, 0x0c, 0xad, 0x4c, 0xa3, 0xb5, 0x89, 0x71, 0xd1, 0x7c, 0xb8, 0x63, 0x9f, 0xbc, 0x3c, 0x93, 0x98, 0x9b, 0xbc, 0x7b, 0xcd, 0x10, 0xde, 0x23, 0x84, 0x73, 0xcf, 0xfb, 0xdd, 0x73, 0x7e, 0xbf, 0x73, 0xee, 0xbb, 0x2f, 0xb9, 0xd4, 0xce, 0xce, 0x8e, 0xe8, 0x28, 0x5f, 0xe2, 0xa3, 0x4c, 0x7e, 0x97, 0xbb, 0x20, 0xe0, 0xff, 0xee, 0xa0, 0xd0, 0x01, 0xa1, 0x03, 0x98, 0x15, 0x10, 0x96, 0x10, 0x66, 0x01, 0xb1, 0xa7, 0xd3, 0xd8, 0x11, 0xf6, 0x02, 0xac, 0x8f, 0x19, 0xcc, 0x13, 0xbf, 0xba, 0xec, 0xdb, 0x30, 0x56, 0x16, 0xe9, 0xa3, 0x5f, 0x78, 0xc9, 0x23, 0xf2, 0xfc, 0x17, 0x57, 0x18, 0x8f, 0x34, 0xfe, 0x99, 0xf8, 0x73, 0x97, 0x68, 0xe5, 0x71, 0x0f, 0x00, 0xbf, 0x21, 0x19, 0x01, 0xb3, 0x1f, 0xbf, 0xb3, 0x74, 0xeb, 0x1b, 0x96, 0x41, 0x58, 0x64, 0x94, 0xb7, 0x80, 0xc5, 0x1b, 0x9f, 0xb3, 0x80, 0x85, 0x6f, 0x3f, 0xc9, 0xfc, 0xee, 0xae, 0x5c, 0xf3, 0x1c, 0xeb, 0xe1, 0x6d, 0x10, 0x78, 0x06, 0x2c, 0xc6, 0x31, 0x2e, 0xfb, 0x40, 0xa8, 0xd8, 0xd7, 0x57, 0xe7, 0x5a, 0x2f, 0x07, 0x82, 0x44, 0x62, 0x08, 0x74, 0xc0, 0x32, 0x39, 0xc6, 0xa6, 0x49, 0xfd, 0xe0, 0x4b, 0xa5, 0xee, 0x55, 0x5a, 0x19, 0xc7, 0x7a, 0x58, 0x23, 0xf7, 0x97, 0x3f, 0x1d, 0xe6, 0xf5, 0xbf, 0xae, 0xbe, 0x65, 0x7d, 0x68, 0x04, 0x27, 0x77, 0x16, 0x8b, 0xe1, 0x61, 0x10, 0xe8, 0x80, 0x63, 0xc3, 0xcc, 0x24, 0xa6, 0x15, 0x31, 0x09, 0x6f, 0xbc, 0x27, 0x4b, 0xce, 0xa0, 0x15, 0x2a, 0x6f, 0x2a, 0xb2, 0xc4, 0xb4, 0x63, 0xda, 0xbc, 0xf8, 0xf3, 0x55, 0xcc, 0x2d, 0x87, 0xd5, 0xe2, 0x8d, 0xe1, 0xe1, 0x21, 0x20, 0x80, 0xcd, 0x2a, 0x0e, 0x97, 0xb3, 0xf6, 0x41, 0x46, 0x20, 0x98, 0x83, 0xe6, 0xfa, 0xf4, 0x93, 0x14, 0xe0, 0x33, 0xc1, 0x61, 0x3b, 0x05, 0x01, 0x22, 0x91, 0x63, 0x7d, 0x95, 0x29, 0x33, 0x45, 0x4b, 0x90, 0xf5, 0x16, 0x4b, 0xdc, 0x18, 0x87, 0x79, 0x0d, 0x89, 0x47, 0x02, 0x70, 0x3b, 0xb0, 0xbd, 0xf2, 0x68, 0x75, 0xf0, 0x27, 0x26, 0x4d, 0xc4, 0x89, 0xe7, 0x91, 0xf9, 0xe4, 0xa9, 0x6e, 0xcc, 0xd2, 0xcd, 0xaf, 0x90, 0x78, 0x24, 0x00, 0x4b, 0xc0, 0xc2, 0xf5, 0x4f, 0xef, 0x97, 0x9f, 0xdc, 0x5e, 0x59, 0x82, 0x34, 0x61, 0x32, 0x79, 0xe2, 0xdb, 0x1f, 0x22, 0xf3, 0x45, 0x9d, 0x3e, 0xa3, 0x2a, 0x7d, 0x8d, 0x81, 0xfd, 0xfd, 0xf5, 0xd5, 0xa9, 0x8b, 0xf9, 0x5b, 0x0b, 0xb3, 0xc8, 0x59, 0x7e, 0x00, 0x58, 0x02, 0x36, 0xa6, 0xee, 0x31, 0x7b, 0xa8, 0x44, 0x75, 0x3c, 0xe3, 0xb3, 0xce, 0xa8, 0x6c, 0x9d, 0x9f, 0x4c, 0x7b, 0xb7, 0x28, 0x71, 0x46, 0xeb, 0xf7, 0xb1, 0x2f, 0x96, 0x33, 0x43, 0xcb, 0xef, 0x13, 0xf6, 0x27, 0x7b, 0x2b, 0x10, 0x3d, 0xd7, 0x17, 0x02, 0x4b, 0x40, 0xac, 0xbe, 0x22, 0x32, 0x23, 0x13, 0xc2, 0xda, 0xd7, 0x56, 0xfe, 0xb8, 0x7c, 0xc1, 0x74, 0xe7, 0x07, 0x5f, 0x29, 0xf6, 0xf9, 0x5c, 0x5b, 0xff, 0x3e, 0x78, 0x53, 0xb7, 0x3a, 0xd4, 0x03, 0x5e, 0xb1, 0x34, 0x5c, 0xfd, 0x7a, 0x75, 0xb8, 0xfa, 0xd9, 0x7d, 0x88, 0x60, 0x07, 0x70, 0x2a, 0x81, 0x73, 0xb9, 0xb6, 0x6d, 0xf7, 0x2f, 0x64, 0xdd, 0x3d, 0x25, 0x82, 0xcf, 0xd4, 0xa5, 0x42, 0x64, 0xa8, 0xc7, 0xbf, 0xf5, 0x33, 0x60, 0xf8, 0x36, 0x19, 0x6e, 0x23, 0xf1, 0x48, 0x00, 0x56, 0x07, 0xa0, 0x58, 0x94, 0x24, 0x5c, 0xa9, 0x7b, 0x85, 0xa9, 0x9a, 0x6d, 0x71, 0x0e, 0x59, 0x3e, 0xdb, 0x82, 0x1b, 0xa3, 0x3a, 0x7b, 0x1e, 0x89, 0x47, 0x02, 0x70, 0x05, 0x40, 0x02, 0x4a, 0x22, 0x45, 0xa6, 0xf1, 0x09, 0xa0, 0x68, 0x9e, 0x13, 0xb9, 0xd1, 0x08, 0x08, 0xe0, 0x86, 0x0b, 0xbd, 0x2d, 0x08, 0xe0, 0xd4, 0xdc, 0x69, 0x35, 0x8b, 0x5c, 0x4e, 0x8e, 0xc3, 0x87, 0xe9, 0x34, 0x3f, 0xf6, 0xe1, 0xc5, 0x70, 0x11, 0xf8, 0x3f, 0x20, 0x8d, 0x55, 0x33, 0x04, 0x9c, 0x9b, 0x56, 0xf8, 0xb9, 0x1f, 0x53, 0xf8, 0x72, 0x64, 0xc6, 0x69, 0xf9, 0x09, 0xad, 0x07, 0x2b, 0x53, 0xff, 0x8f, 0x4e, 0xcb, 0x93, 0x47, 0x37, 0xaf, 0x31, 0x7e, 0x69, 0x6c, 0xbc, 0x07, 0x80, 0xdf, 0x90, 0x80, 0x80, 0xe8, 0x33, 0x67, 0x29, 0x9a, 0xde, 0x71, 0x38, 0x80, 0xc1, 0x3f, 0x3f, 0xdf, 0x80, 0x4f, 0xd2, 0xbb, 0x1f, 0x25, 0x7a, 0x09, 0x78, 0x78, 0xe5, 0x22, 0x97, 0x62, 0x4c, 0xa1, 0x9e, 0x3b, 0xe4, 0x6d, 0x13, 0x78, 0x06, 0xe4, 0x9a, 0x93, 0xa9, 0x0d, 0xd7, 0xe0, 0xad, 0x14, 0x38, 0x89, 0xa8, 0x53, 0x79, 0x9a, 0xf7, 0xdb, 0x03, 0xc7, 0xfb, 0x41, 0x52, 0xf0, 0xa6, 0xf0, 0x73, 0x3b, 0xf0, 0x5b, 0xdb, 0xcb, 0x8b, 0x1b, 0x0f, 0xee, 0x31, 0xa7, 0x12, 0x91, 0xe9, 0x99, 0x3e, 0x96, 0x90, 0xe1, 0x16, 0x13, 0x4d, 0x1a, 0x97, 0xa0, 0xc8, 0xd1, 0x89, 0x28, 0x02, 0xb5, 0x83, 0x80, 0xc4, 0x04, 0x04, 0x2e, 0x95, 0x2c, 0x92, 0x4c, 0x19, 0xc8, 0x72, 0x0a, 0x2a, 0x9a, 0x20, 0x20, 0xa8, 0x72, 0x1d, 0x02, 0x58, 0xe8, 0xc0, 0x21, 0x14, 0x35, 0xa8, 0x90, 0x84, 0x3b, 0x70, 0xfd, 0xe9, 0x15, 0x14, 0x03, 0x4c, 0x30, 0xc9, 0x6d, 0x74, 0x6b, 0x6b, 0x2b, 0x22, 0x22, 0x22, 0x2c, 0x2c, 0x6c, 0x73, 0x73, 0x93, 0xa6, 0x09, 0xbc, 0xe3, 0x03, 0xd1, 0x46, 0x38, 0x4d, 0x74, 0x74, 0xb4, 0x5c, 0x2e, 0x0f, 0x19, 0x7b, 0x50, 0x48, 0xb2, 0x03, 0x10, 0xce, 0xe5, 0x72, 0xc1, 0xb7, 0x58, 0x8c, 0x5e, 0x99, 0x80, 0x0c, 0x04, 0x86, 0x6c, 0x02, 0x81, 0x0e, 0x8c, 0x8e, 0x8e, 0x36, 0x35, 0x35, 0xc1, 0xb2, 0x61, 0x93, 0x55, 0x54, 0x54, 0xd4, 0xd5, 0xd5, 0xb1, 0x43, 0x30, 0x7a, 0x7b, 0x7b, 0xbb, 0xba, 0xba, 0xfa, 0xfb, 0xfb, 0x2d, 0x16, 0x8b, 0x56, 0xab, 0x85, 0xc5, 0x96, 0x94, 0x94, 0xd4, 0xd7, 0xd7, 0xc7, 0xc5, 0xf0, 0xb3, 0xc9, 0x08, 0x18, 0x1e, 0x1e, 0xe6, 0xa6, 0x4f, 0x4b, 0x4b, 0xe3, 0x0e, 0x07, 0x07, 0x07, 0xcb, 0xcb, 0x77, 0xcf, 0x51, 0xf4, 0x7a, 0xbd, 0xd3, 0xe9, 0x84, 0x21, 0xfc, 0x00, 0xb3, 0xdb, 0xed, 0x5c, 0x0c, 0x6f, 0x9b, 0x80, 0x80, 0xda, 0xda, 0xda, 0xfc, 0xfc, 0x7c, 0xab, 0xd5, 0x0a, 0x24, 0x3a, 0x3b, 0x3b, 0xbb, 0xbb, 0xbb, 0x3d, 0xd8, 0x18, 0x0c, 0x06, 0xf0, 0x40, 0xe1, 0xa1, 0x03, 0x60, 0xcc, 0xcf, 0xcf, 0x37, 0x37, 0x37, 0x83, 0x12, 0x0f, 0x18, 0xbf, 0x21, 0x01, 0x01, 0xf0, 0xd4, 0x96, 0x95, 0x95, 0x31, 0xe9, 0xc7, 0xc7, 0xc7, 0xbd, 0x79, 0x14, 0x14, 0x14, 0x80, 0x73, 0x7a, 0x7a, 0x3a, 0x2b, 0x2b, 0xab, 0xa4, 0xa4, 0x24, 0x3b, 0x3b, 0xbb, 0xb1, 0xb1, 0x51, 0xa3, 0xd1, 0x78, 0x23, 0xf9, 0x78, 0x90, 0x07, 0x2f, 0x41, 0x01, 0x1a, 0x1a, 0x1a, 0x80, 0x44, 0x65, 0x65, 0x25, 0x77, 0x16, 0xac, 0x16, 0x9d, 0x6e, 0xdf, 0xa1, 0x1d, 0x45, 0x51, 0x35, 0x35, 0x35, 0x5c, 0x0c, 0x6f, 0x1b, 0xbd, 0x5d, 0xf0, 0xa9, 0xca, 0xfe, 0x39, 0xb0, 0xab, 0x0e, 0x0c, 0x0c, 0x18, 0x8d, 0xc6, 0xfa, 0xfa, 0xfa, 0xe2, 0xe2, 0x62, 0x89, 0x44, 0x02, 0x74, 0x3b, 0x3a, 0x3a, 0xe6, 0xe6, 0xdc, 0x67, 0x44, 0xfb, 0x67, 0x04, 0x31, 0x0a, 0x85, 0x80, 0xb6, 0xb6, 0x36, 0xd8, 0x73, 0x60, 0x9b, 0x6a, 0x6d, 0x6d, 0x1d, 0x19, 0x19, 0x99, 0x99, 0x99, 0x91, 0xc9, 0x64, 0xa0, 0x61, 0x68, 0x68, 0x28, 0x08, 0xa6, 0x07, 0x40, 0x09, 0x3c, 0x03, 0x07, 0x44, 0x76, 0xbb, 0xa1, 0xd2, 0x26, 0x93, 0xa9, 0xb4, 0xb4, 0x14, 0x96, 0x4d, 0x6e, 0x6e, 0xee, 0xe4, 0xe4, 0xa4, 0xcd, 0x66, 0x83, 0x97, 0x00, 0x3c, 0xfa, 0x6e, 0x10, 0x5f, 0x8b, 0xb0, 0x00, 0x60, 0xe6, 0xcd, 0x44, 0xa1, 0x50, 0xc0, 0xa2, 0x87, 0x85, 0xd4, 0xde, 0xbe, 0xf7, 0x3f, 0x38, 0x2e, 0x2e, 0xae, 0xa5, 0xa5, 0x25, 0x33, 0x73, 0xf7, 0x60, 0x18, 0xf3, 0x22, 0x2c, 0x00, 0xb6, 0x48, 0x20, 0x94, 0x90, 0x90, 0xc0, 0xa5, 0x05, 0x4f, 0x76, 0x75, 0x75, 0x75, 0x4a, 0x4a, 0xca, 0xec, 0xec, 0xec, 0xda, 0xda, 0x9a, 0x4a, 0xa5, 0x4a, 0x4e, 0x4e, 0x96, 0x4a, 0x09, 0x9c, 0x2b, 0x42, 0x16, 0x02, 0x02, 0x7a, 0x7a, 0x7a, 0x26, 0x26, 0x26, 0xd4, 0x6a, 0x35, 0xec, 0xa1, 0xcc, 0xcb, 0xb5, 0xa8, 0xa8, 0x88, 0x2b, 0xe0, 0xd8, 0xd3, 0x0b, 0x3c, 0xe9, 0xe9, 0xe9, 0x5c, 0x3f, 0x19, 0x9b, 0xf7, 0xfe, 0xc5, 0x4e, 0x64, 0xde, 0xb2, 0x2c, 0x9b, 0xaa, 0xaa, 0x2a, 0xf6, 0x56, 0x08, 0x0c, 0x02, 0x3f, 0xe6, 0x96, 0x97, 0x97, 0x61, 0x3f, 0x81, 0xc5, 0xa3, 0x54, 0x2a, 0xf3, 0xf2, 0xf2, 0x72, 0x72, 0x72, 0x58, 0x31, 0x21, 0x30, 0x08, 0x08, 0x08, 0x01, 0x4b, 0x3f, 0x29, 0x42, 0xf1, 0x1e, 0xf0, 0x93, 0x1e, 0xff, 0x96, 0x20, 0x00, 0xbf, 0x86, 0x78, 0x11, 0x84, 0x0e, 0xe0, 0xd5, 0x0f, 0x7f, 0xb6, 0xd0, 0x01, 0xfc, 0x1a, 0xe2, 0x45, 0x38, 0xf2, 0x1d, 0xf8, 0x0f, 0x1c, 0x65, 0x73, 0xb3, 0xdd, 0xbe, 0x50, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXJSIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0b, 0x56, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x6b, 0x6c, 0x14, 0xc7, 0xf9, 0xbb, 0xf3, 0xf9, 0x89, 0x0d, 0x7e, 0xc7, 0x02, 0x0a, 0x85, 0x26, 0xa9, 0x04, 0x49, 0x4a, 0x69, 0x6d, 0x41, 0x84, 0x30, 0xd8, 0x16, 0x21, 0xe1, 0x51, 0x89, 0xd6, 0x32, 0x8f, 0x52, 0x35, 0xf4, 0x87, 0x8d, 0x94, 0x54, 0xfd, 0x51, 0x54, 0x14, 0xaa, 0x2a, 0x15, 0x34, 0xa8, 0x85, 0x16, 0x45, 0xa8, 0x56, 0x5c, 0x29, 0x45, 0x40, 0x91, 0x6a, 0x70, 0x48, 0x7e, 0x14, 0xa2, 0xd0, 0x40, 0x51, 0xc1, 0x55, 0x15, 0x5a, 0x09, 0x1c, 0x6a, 0x51, 0xd3, 0x18, 0x81, 0x0d, 0xc6, 0x36, 0x06, 0xe3, 0xf7, 0xe3, 0x7c, 0xdb, 0xf9, 0xd6, 0xec, 0xdc, 0x3e, 0x66, 0x6f, 0x5f, 0xb3, 0x77, 0x7b, 0x64, 0x47, 0xb2, 0xf7, 0x9b, 0x6f, 0xbe, 0xf7, 0xb7, 0x33, 0x3b, 0xbb, 0x3b, 0xb3, 0x17, 0x10, 0x48, 0x01, 0x0b, 0x25, 0x68, 0x81, 0x56, 0x24, 0x65, 0x32, 0x74, 0x7f, 0x2b, 0x20, 0x36, 0x4a, 0x47, 0x85, 0x50, 0x34, 0x49, 0xaf, 0x4c, 0x75, 0x77, 0x68, 0x9a, 0x40, 0x8d, 0xb9, 0xbf, 0x54, 0x89, 0x52, 0xd7, 0x95, 0xad, 0x2a, 0x6e, 0x96, 0x06, 0xa6, 0x0f, 0xc3, 0xf5, 0x3f, 0x17, 0xcd, 0x1e, 0x6d, 0x7a, 0x4f, 0x61, 0x3e, 0x56, 0x02, 0x28, 0x54, 0x83, 0x8d, 0x81, 0x60, 0x6a, 0x88, 0x41, 0x0f, 0x4c, 0x86, 0x40, 0x60, 0x3a, 0xac, 0xd2, 0x51, 0x2e, 0x80, 0xc9, 0x20, 0x11, 0xb0, 0xac, 0x65, 0x32, 0xb0, 0x08, 0x25, 0x21, 0x09, 0x70, 0xfa, 0xf1, 0xae, 0xef, 0x4a, 0xda, 0x21, 0xf2, 0xa0, 0x8b, 0xc2, 0x14, 0xc0, 0x3c, 0xc8, 0x8b, 0xfc, 0x54, 0x90, 0xc3, 0x12, 0x8d, 0xc6, 0xe9, 0xac, 0xcd, 0x6f, 0x52, 0x61, 0x29, 0x05, 0xcf, 0x50, 0x98, 0x02, 0x12, 0xa7, 0x74, 0x94, 0x4b, 0x95, 0xc3, 0x52, 0xbb, 0x36, 0x4a, 0x93, 0xe3, 0x00, 0xa9, 0xe9, 0x54, 0xa0, 0x1a, 0xd0, 0x32, 0xa8, 0x29, 0x54, 0x75, 0x8d, 0x0f, 0xaa, 0x76, 0x4d, 0x55, 0x97, 0x61, 0xd5, 0xaa, 0x55, 0x10, 0x0a, 0x85, 0x34, 0x0c, 0x9a, 0x0e, 0xb4, 0x75, 0xeb, 0x56, 0xc9, 0x3f, 0xe6, 0xd1, 0xb2, 0x0f, 0x96, 0x19, 0xb4, 0x36, 0xc6, 0xc6, 0xe8, 0xfa, 0x1c, 0x9b, 0xcd, 0x7c, 0xab, 0x29, 0x05, 0xac, 0xf1, 0x6f, 0xf0, 0x37, 0x6f, 0x00, 0x0b, 0xaf, 0x56, 0x6d, 0x4a, 0x81, 0x9a, 0x09, 0xeb, 0x39, 0x3f, 0x7d, 0x97, 0x85, 0xd6, 0xe0, 0x6c, 0x2b, 0x60, 0xf6, 0x44, 0x8d, 0x78, 0x60, 0x8f, 0x61, 0x0c, 0x3a, 0x18, 0xd8, 0xb3, 0x55, 0x81, 0x7e, 0xb0, 0x61, 0x81, 0xa2, 0xae, 0x57, 0xf1, 0xce, 0x59, 0xc4, 0x1a, 0x91, 0xf5, 0xac, 0x96, 0xe3, 0x4d, 0xe5, 0x60, 0xfd, 0xfa, 0xf5, 0x72, 0x1e, 0x40, 0x65, 0xa3, 0xa3, 0xa3, 0xb0, 0x61, 0xc3, 0x06, 0x05, 0x9e, 0x59, 0x61, 0xf6, 0x57, 0x06, 0x92, 0x30, 0x53, 0x2c, 0xc2, 0x69, 0x69, 0x69, 0xc2, 0xd2, 0xa5, 0x4b, 0x29, 0x4e, 0x0f, 0xf0, 0x4e, 0x0e, 0x98, 0xee, 0x9b, 0x40, 0x26, 0xbf, 0x07, 0xa6, 0xce, 0x22, 0x13, 0x91, 0xd0, 0x25, 0xf1, 0x86, 0x02, 0x1c, 0x35, 0x7b, 0x4a, 0x53, 0xa8, 0x95, 0x58, 0x37, 0x33, 0x92, 0x22, 0x83, 0xa1, 0x07, 0x3d, 0xcb, 0xa6, 0x2f, 0xd1, 0xc5, 0x57, 0xa6, 0xa8, 0x82, 0x67, 0xfe, 0x6d, 0x7e, 0x92, 0x67, 0xa8, 0x40, 0x98, 0x9c, 0xa0, 0x82, 0xed, 0x00, 0x86, 0x0a, 0x50, 0x68, 0x20, 0x33, 0x8b, 0x29, 0x7b, 0xe2, 0x1f, 0x1f, 0x33, 0xf1, 0x0a, 0xa4, 0x5e, 0x17, 0x47, 0xfc, 0x78, 0xf3, 0x59, 0x81, 0x35, 0xdd, 0xc1, 0x36, 0x9c, 0x52, 0xeb, 0xb5, 0x61, 0xbb, 0x54, 0xa2, 0x03, 0x8c, 0x84, 0x51, 0x1d, 0x51, 0x08, 0x4b, 0x10, 0xe2, 0x22, 0x43, 0x8f, 0x55, 0xd4, 0xda, 0xaa, 0xa9, 0x10, 0x29, 0x5c, 0x96, 0x55, 0x02, 0x33, 0x66, 0xca, 0x6a, 0x6c, 0xd0, 0x50, 0x41, 0xe8, 0xd9, 0x17, 0x44, 0xce, 0xe1, 0xf7, 0x7e, 0x41, 0x25, 0x48, 0x37, 0x12, 0x14, 0x11, 0x03, 0x30, 0x54, 0x50, 0xd0, 0xf8, 0x39, 0x64, 0xd7, 0xbe, 0xad, 0x11, 0x91, 0xfb, 0xbb, 0x8f, 0x34, 0x38, 0x16, 0xe2, 0x4b, 0x32, 0xd8, 0x91, 0x73, 0x83, 0xe5, 0xbd, 0x29, 0x9c, 0x61, 0x0e, 0x50, 0x8a, 0xdd, 0x0b, 0x3e, 0xf2, 0x1a, 0x2a, 0xd8, 0xbb, 0x77, 0x2f, 0xe0, 0x45, 0x3f, 0x23, 0x23, 0x03, 0xe9, 0xa9, 0xb2, 0xec, 0xec, 0x6c, 0xb1, 0x6e, 0xf4, 0xcf, 0x74, 0x92, 0x51, 0xc1, 0xd8, 0xd8, 0x98, 0x28, 0x4f, 0xf2, 0xc8, 0x4c, 0xe8, 0x0c, 0x3d, 0x50, 0x5b, 0x28, 0x09, 0x57, 0xe3, 0xf5, 0xea, 0x8c, 0x7b, 0x12, 0x25, 0xe9, 0xf2, 0xe5, 0xcb, 0x61, 0x60, 0x60, 0x00, 0xea, 0xeb, 0xeb, 0xc5, 0x06, 0x33, 0x56, 0xcb, 0x25, 0x98, 0x0e, 0x91, 0x9c, 0xc9, 0x0a, 0x6c, 0x39, 0x44, 0x56, 0x84, 0x23, 0xad, 0xeb, 0x0a, 0x5c, 0x0f, 0x91, 0x55, 0x8f, 0xad, 0xd2, 0xbb, 0x1e, 0x21, 0xab, 0x06, 0x59, 0xa5, 0xf7, 0x1d, 0xb0, 0x1a, 0x31, 0xde, 0xf4, 0x86, 0xe3, 0x90, 0x19, 0x85, 0xc3, 0xf5, 0x7b, 0x60, 0xe8, 0xfd, 0x77, 0x28, 0x69, 0x20, 0x18, 0x04, 0xf9, 0x34, 0x53, 0x6a, 0x50, 0xcf, 0x65, 0xad, 0x4c, 0x3f, 0x25, 0x19, 0xea, 0x23, 0x97, 0x4e, 0x2c, 0x37, 0xcc, 0xc8, 0xa8, 0x48, 0x77, 0x07, 0xf4, 0xbe, 0x36, 0x4f, 0xb4, 0xc3, 0x88, 0x56, 0x6d, 0x2c, 0xab, 0x1e, 0xff, 0x3e, 0xf0, 0xe4, 0x21, 0x2b, 0xcb, 0x18, 0x3b, 0x38, 0x47, 0x0e, 0x4c, 0xdd, 0x6d, 0x87, 0xde, 0x95, 0xd1, 0x89, 0x69, 0xde, 0xef, 0x3f, 0x31, 0xb4, 0x21, 0x58, 0x3c, 0x97, 0xce, 0x18, 0x30, 0x73, 0x83, 0xef, 0xd4, 0x19, 0xf2, 0xc4, 0x22, 0x70, 0xe4, 0x40, 0xca, 0x9c, 0x85, 0x90, 0xfb, 0xee, 0x5f, 0xa8, 0xfc, 0x91, 0x3f, 0xfd, 0x96, 0xc2, 0xb1, 0x00, 0xe9, 0x7a, 0x1e, 0xcc, 0x2b, 0x84, 0x9c, 0xb7, 0xb4, 0x4f, 0xe0, 0x63, 0xf1, 0xaa, 0xdb, 0xe2, 0xdf, 0x07, 0x7a, 0x3a, 0xa1, 0xf7, 0xd5, 0xaf, 0x88, 0x76, 0x24, 0x65, 0x1f, 0x10, 0x46, 0x86, 0xd4, 0x41, 0x74, 0x54, 0xe7, 0x92, 0x81, 0xc8, 0x83, 0x7b, 0xd0, 0xfb, 0xca, 0x1c, 0x6a, 0x88, 0x99, 0x61, 0x14, 0x1f, 0xe9, 0x17, 0x9e, 0xbb, 0x4f, 0x79, 0xec, 0x02, 0x5c, 0x1c, 0xb0, 0xab, 0x9c, 0x07, 0x9f, 0xa3, 0x4e, 0xcc, 0xc3, 0x00, 0xa7, 0x32, 0x7c, 0x07, 0xe4, 0x11, 0xec, 0xe8, 0xe8, 0x10, 0xc7, 0xf8, 0xc1, 0xc1, 0x41, 0x39, 0xda, 0x55, 0x98, 0x4b, 0x06, 0xf0, 0x16, 0x16, 0x6f, 0x06, 0xe7, 0xcd, 0x9b, 0x9e, 0x22, 0xcc, 0x9f, 0x3f, 0x5f, 0x61, 0xf4, 0xad, 0x5b, 0xb7, 0xc4, 0x76, 0xa4, 0xc1, 0x3f, 0x7c, 0x3d, 0xb5, 0x69, 0xd3, 0x26, 0x05, 0x8d, 0xed, 0x0a, 0xb9, 0xa8, 0x70, 0x2d, 0xc4, 0x10, 0x21, 0x3d, 0x3d, 0x5d, 0x21, 0x13, 0x71, 0xf8, 0x47, 0x0c, 0x17, 0x36, 0x6f, 0xde, 0x2c, 0x5c, 0xbe, 0x7c, 0x59, 0xd1, 0xee, 0xa4, 0x62, 0xf8, 0x00, 0xcd, 0xaa, 0x70, 0x3d, 0x07, 0x4e, 0x9d, 0x3a, 0x45, 0x45, 0xe5, 0xe6, 0xe6, 0x8a, 0x0e, 0x51, 0x84, 0x03, 0x80, 0xcb, 0x74, 0x9a, 0x18, 0xad, 0x28, 0x73, 0xe7, 0xce, 0x55, 0xd4, 0xcf, 0x9f, 0x3f, 0x0f, 0x15, 0x15, 0x15, 0x70, 0xf1, 0xe2, 0x45, 0xc0, 0xfe, 0x71, 0xe7, 0xce, 0x1d, 0xc8, 0xc9, 0xc9, 0x51, 0xd0, 0xd8, 0xae, 0x38, 0x70, 0x9e, 0xb2, 0xce, 0x9c, 0x39, 0x53, 0x8c, 0x28, 0x31, 0x42, 0x38, 0x74, 0xe8, 0x10, 0xc5, 0xc7, 0x03, 0xf0, 0x2f, 0x64, 0xb6, 0x53, 0xcf, 0x89, 0x91, 0xcb, 0x30, 0xca, 0xc9, 0x16, 0x5b, 0x62, 0xfc, 0x53, 0xc8, 0x56, 0xd8, 0x38, 0x32, 0x25, 0xfd, 0x29, 0xe4, 0x3b, 0xc0, 0xf1, 0x6c, 0xb0, 0x25, 0xca, 0xcf, 0x80, 0xad, 0xb0, 0x71, 0x64, 0x4a, 0xfa, 0x0c, 0x70, 0x99, 0xcc, 0xa9, 0x6f, 0xea, 0x59, 0x8f, 0x4b, 0xe4, 0x8f, 0x1f, 0x33, 0xaa, 0xbe, 0x07, 0xb3, 0x7e, 0x7d, 0x8a, 0x4b, 0x1e, 0xb8, 0x64, 0x40, 0xfe, 0x44, 0xc2, 0x8c, 0x55, 0x63, 0x9f, 0x36, 0x81, 0xfc, 0xed, 0xb0, 0x19, 0x1e, 0x3d, 0x1a, 0x0e, 0x0e, 0xe0, 0x44, 0x74, 0xba, 0x64, 0x6d, 0xf9, 0x31, 0xb0, 0xa2, 0x8f, 0xad, 0x88, 0x97, 0xb7, 0x0d, 0x1f, 0x3b, 0xf0, 0x84, 0xcb, 0xd9, 0xc1, 0xb9, 0x03, 0x91, 0x08, 0xb5, 0x60, 0xc6, 0x0f, 0x7f, 0x46, 0x61, 0x23, 0x40, 0x18, 0x9f, 0x7e, 0xe9, 0x69, 0x44, 0x67, 0xd4, 0xee, 0xdc, 0x01, 0x85, 0x86, 0xe9, 0xe5, 0xbd, 0x0a, 0x94, 0xcb, 0x15, 0xce, 0x0e, 0xb8, 0x6c, 0x2d, 0x43, 0xbc, 0x63, 0x07, 0x26, 0x9a, 0xa3, 0x4b, 0x63, 0x82, 0xac, 0x15, 0xc0, 0x2a, 0xa5, 0x81, 0x8c, 0x4c, 0x15, 0xc6, 0x59, 0xd5, 0x91, 0x03, 0xf8, 0x6c, 0xff, 0xd1, 0x4f, 0xa6, 0x97, 0x01, 0xe2, 0xf3, 0x50, 0x08, 0x18, 0x8b, 0xcb, 0x3d, 0xf0, 0x01, 0xb5, 0xb8, 0xb7, 0x7c, 0x16, 0x4c, 0x75, 0xdc, 0xa4, 0x75, 0x3b, 0x80, 0xb1, 0xc6, 0x18, 0x52, 0x47, 0x3e, 0x68, 0x10, 0x5b, 0x53, 0x0a, 0x4b, 0xa0, 0xf0, 0xe3, 0x8e, 0x18, 0x94, 0xd1, 0xa6, 0xb4, 0x97, 0x5f, 0x85, 0xac, 0x9a, 0x37, 0x44, 0x44, 0x64, 0x68, 0x00, 0xc2, 0xed, 0xad, 0xd1, 0x46, 0x1b, 0x90, 0xe3, 0x1b, 0x9a, 0xde, 0xca, 0x42, 0x88, 0xf4, 0xf7, 0x89, 0xaa, 0xe5, 0xc3, 0xa4, 0x9e, 0x2d, 0xfd, 0x3b, 0xab, 0x60, 0xfc, 0xb3, 0xf3, 0x62, 0x73, 0xfe, 0xfb, 0x97, 0x20, 0x75, 0xc9, 0x0a, 0x3d, 0x52, 0x53, 0x78, 0x47, 0x19, 0x40, 0x0d, 0x85, 0x1f, 0xb6, 0x51, 0x45, 0x66, 0x56, 0xf7, 0x4a, 0xc6, 0x23, 0x93, 0x53, 0xe3, 0x51, 0x86, 0x63, 0x07, 0x02, 0xd9, 0xb3, 0x50, 0x4e, 0xc2, 0x8a, 0x63, 0x07, 0x12, 0x66, 0xf9, 0x13, 0xc5, 0x5c, 0x1d, 0x10, 0x46, 0xf9, 0xbe, 0x3e, 0x32, 0x13, 0x1c, 0xe7, 0x0e, 0x04, 0xa3, 0x2b, 0x4b, 0xfb, 0xaa, 0x17, 0x83, 0xde, 0xb2, 0x3d, 0xc4, 0x3f, 0x7e, 0x6b, 0x0b, 0xb5, 0x29, 0xf5, 0xf9, 0x6f, 0x50, 0xd8, 0x09, 0xe0, 0x78, 0x14, 0x42, 0xe5, 0xbd, 0x55, 0xc5, 0x10, 0x79, 0xd4, 0x4b, 0xed, 0x60, 0x8d, 0x46, 0xf2, 0xe9, 0x34, 0x12, 0x16, 0x9d, 0xeb, 0x82, 0x60, 0x41, 0x09, 0xe5, 0xb1, 0x0b, 0x70, 0xb9, 0x1f, 0x28, 0xfa, 0xb4, 0x07, 0xc6, 0x2f, 0x7e, 0x04, 0xe1, 0xd6, 0x7f, 0xe9, 0xda, 0x91, 0xfd, 0xa3, 0x3d, 0xd3, 0x6d, 0xa9, 0x69, 0x30, 0xe3, 0xf5, 0xdd, 0x00, 0xa1, 0x34, 0x5d, 0x5a, 0x2b, 0x0d, 0x5c, 0x32, 0x60, 0x45, 0x21, 0x6f, 0x5a, 0xe7, 0x7d, 0x80, 0xb7, 0x45, 0x16, 0xe5, 0xf9, 0x0e, 0x58, 0x0c, 0x18, 0x77, 0x72, 0x3f, 0x03, 0xf2, 0x90, 0xae, 0x58, 0xb1, 0x02, 0x96, 0x2d, 0x5b, 0x26, 0x47, 0xb9, 0x0e, 0x73, 0x1d, 0x85, 0xa4, 0x75, 0xb7, 0xe4, 0xd5, 0x92, 0xeb, 0x86, 0x4b, 0x0a, 0xb8, 0x5c, 0x07, 0x24, 0x61, 0xf1, 0x34, 0x5c, 0xd2, 0xe9, 0xb8, 0x0f, 0x48, 0x6f, 0xe7, 0xa5, 0x97, 0xd8, 0x52, 0x16, 0x24, 0x05, 0x78, 0x5c, 0xbd, 0x7a, 0xb5, 0xe2, 0x45, 0x37, 0x8b, 0x46, 0x4e, 0x6f, 0x09, 0x76, 0xfa, 0x26, 0x71, 0x72, 0x72, 0x52, 0xd8, 0xb8, 0x71, 0xa3, 0xb0, 0x6e, 0xdd, 0x3a, 0xa1, 0xb4, 0xb4, 0x54, 0xf3, 0xfe, 0xf7, 0xd2, 0xa5, 0x4b, 0x22, 0xee, 0xcc, 0x99, 0x33, 0xa2, 0xaa, 0xfd, 0xfb, 0xf7, 0x6b, 0x68, 0x9c, 0xd8, 0xc0, 0xf5, 0x45, 0xf7, 0xd9, 0xb3, 0x67, 0x35, 0xc6, 0x5d, 0xbd, 0x7a, 0x55, 0xc4, 0x91, 0xa8, 0x8a, 0xbb, 0xdc, 0x0e, 0x1e, 0x3c, 0x28, 0x84, 0xc3, 0x61, 0x27, 0x36, 0x2b, 0x78, 0x5d, 0x77, 0x00, 0xb5, 0x35, 0x35, 0x35, 0x09, 0x64, 0x05, 0x3f, 0x75, 0x04, 0x9d, 0x69, 0x6e, 0x6e, 0x56, 0x18, 0x62, 0xb7, 0xe2, 0xb8, 0x0f, 0x18, 0x9d, 0xaf, 0x57, 0xae, 0x5c, 0x81, 0xed, 0xdb, 0xb7, 0x8b, 0x1b, 0x19, 0x89, 0x91, 0x70, 0xf7, 0xee, 0x5d, 0x91, 0xe5, 0xe8, 0xd1, 0xa3, 0x46, 0xac, 0xa6, 0xda, 0xb9, 0x8e, 0x42, 0x11, 0xd9, 0x63, 0x46, 0x49, 0xfb, 0x8d, 0x1b, 0x37, 0x44, 0xe3, 0xb1, 0xe3, 0xe2, 0x2a, 0x15, 0x72, 0xfa, 0x88, 0x4d, 0xe4, 0x8d, 0xbe, 0x44, 0xe2, 0xe8, 0xc8, 0xd5, 0x01, 0x34, 0x56, 0x5d, 0xb6, 0x6d, 0xdb, 0x26, 0x5e, 0xdc, 0x32, 0x33, 0x33, 0x01, 0xb3, 0xb1, 0x60, 0xc1, 0x02, 0x58, 0xb2, 0x64, 0x89, 0x9a, 0xcc, 0x7e, 0xdd, 0xee, 0xb9, 0x27, 0xf1, 0x9d, 0x3e, 0x7d, 0x5a, 0xa8, 0xac, 0xac, 0x14, 0x76, 0xec, 0xd8, 0x41, 0xcf, 0x71, 0xa9, 0x2d, 0x1e, 0x47, 0x2e, 0x19, 0xc0, 0xd5, 0x28, 0x58, 0x4a, 0x4a, 0x4a, 0xe0, 0xf6, 0xed, 0xdb, 0xf6, 0xa3, 0x69, 0x83, 0x93, 0xeb, 0x54, 0xc2, 0x86, 0x7e, 0xc7, 0x2c, 0xae, 0x8f, 0x42, 0x8e, 0x2d, 0x34, 0x10, 0xe0, 0x3b, 0x60, 0x10, 0x20, 0xd7, 0x9b, 0xfd, 0x0c, 0xb8, 0x1e, 0x62, 0x03, 0x05, 0x49, 0x3f, 0x0a, 0x19, 0xf8, 0xe7, 0xf9, 0xe6, 0xa4, 0xef, 0x02, 0x9e, 0x8f, 0xb0, 0x81, 0x81, 0x7e, 0x02, 0x0c, 0x02, 0xe4, 0x76, 0xb3, 0x9f, 0x00, 0xb7, 0x23, 0x6c, 0x20, 0xdf, 0x4f, 0x80, 0x41, 0x80, 0xdc, 0x6e, 0xf6, 0x13, 0xe0, 0x76, 0x84, 0x0d, 0xe4, 0xfb, 0x09, 0x30, 0x08, 0x90, 0xdb, 0xcd, 0x5c, 0xee, 0xe6, 0x79, 0x1b, 0x89, 0xbb, 0xa5, 0xc3, 0x37, 0x5b, 0x40, 0x08, 0x4f, 0x46, 0x45, 0x93, 0x07, 0x4a, 0xe9, 0xe5, 0xdf, 0x89, 0xd6, 0x63, 0x40, 0xf8, 0xaa, 0x54, 0x5e, 0x02, 0xa1, 0x54, 0x08, 0x91, 0x17, 0xd2, 0xb8, 0x07, 0xd8, 0x6b, 0xc5, 0x3b, 0xf7, 0x01, 0x63, 0x23, 0xd0, 0xf7, 0x83, 0x32, 0x08, 0x7f, 0xf1, 0x1f, 0x66, 0x8c, 0xf4, 0x36, 0x96, 0xb2, 0x88, 0xd5, 0x2f, 0xd3, 0xe5, 0x34, 0xa9, 0xcf, 0xbd, 0x04, 0xf9, 0x47, 0xff, 0x09, 0x90, 0xce, 0x77, 0xc5, 0x98, 0x5c, 0x87, 0x15, 0xd8, 0x33, 0x3d, 0xe0, 0x61, 0x5d, 0x05, 0x33, 0xf8, 0xa1, 0xf9, 0xcf, 0x03, 0x6e, 0x18, 0x07, 0x5c, 0xc9, 0x66, 0xb2, 0xa4, 0xbf, 0xbc, 0x56, 0xa4, 0x9c, 0xea, 0xfc, 0x02, 0xc2, 0x77, 0x94, 0x2b, 0xd9, 0x26, 0x49, 0xcf, 0x7a, 0x44, 0xd6, 0x5a, 0xe5, 0xfd, 0xb1, 0xd9, 0xa4, 0x34, 0x77, 0xc9, 0xbc, 0xd1, 0x03, 0x22, 0x53, 0xd0, 0x5d, 0xaa, 0x3d, 0x17, 0x0a, 0x1a, 0x5b, 0x20, 0xf4, 0xec, 0x8b, 0x8e, 0x22, 0x10, 0x6e, 0xbb, 0x0a, 0x7d, 0x5b, 0xbe, 0xa9, 0x91, 0xc1, 0x5a, 0x86, 0xa2, 0x21, 0x8a, 0x03, 0xc2, 0xfc, 0x69, 0x15, 0x07, 0x63, 0xd4, 0x2a, 0x82, 0xb9, 0x85, 0x6a, 0x94, 0xe5, 0x3a, 0x0f, 0x19, 0x96, 0x95, 0x5a, 0x60, 0xf0, 0x74, 0x02, 0x2c, 0xf8, 0x91, 0xb4, 0xa4, 0x9e, 0x48, 0x80, 0x30, 0x3c, 0xc0, 0x0e, 0x20, 0x99, 0xf9, 0x38, 0x2e, 0x3a, 0x32, 0x74, 0x75, 0x3a, 0x56, 0x68, 0x4d, 0x40, 0xc2, 0x13, 0x10, 0x79, 0xd8, 0x0d, 0x0f, 0xb7, 0x97, 0x69, 0xac, 0x4e, 0x7b, 0x69, 0x39, 0x97, 0x65, 0x80, 0xc1, 0xa2, 0x39, 0x90, 0xfa, 0x82, 0x56, 0xfe, 0xc3, 0xef, 0x7f, 0x1b, 0x84, 0xfe, 0xe8, 0xd2, 0x44, 0x8d, 0x01, 0x71, 0x42, 0x24, 0xec, 0x22, 0x8c, 0xdf, 0xe0, 0x7c, 0xf4, 0xe6, 0x6b, 0x1a, 0x37, 0x53, 0x9f, 0x7b, 0x11, 0x66, 0xec, 0xdc, 0x6b, 0x7a, 0xce, 0xaf, 0x11, 0xa0, 0x83, 0x18, 0xff, 0xdb, 0x87, 0x30, 0x44, 0xbe, 0x43, 0x19, 0xfe, 0xdf, 0x75, 0x0d, 0x05, 0x7e, 0x2e, 0xd2, 0xec, 0x3d, 0x86, 0x86, 0xd9, 0x21, 0x22, 0x61, 0x3d, 0x20, 0x98, 0x57, 0x04, 0xa1, 0xaf, 0x2d, 0xd6, 0x98, 0x3f, 0x79, 0xf3, 0x73, 0x18, 0x78, 0xfb, 0x75, 0xc7, 0x1b, 0x21, 0xe4, 0x82, 0xa7, 0x6e, 0xff, 0x17, 0x06, 0x7e, 0xb9, 0x83, 0x19, 0x7c, 0xb4, 0x21, 0x98, 0xcf, 0xf8, 0xd6, 0xbc, 0x5c, 0x80, 0x8b, 0x70, 0xc2, 0x7a, 0x80, 0xdc, 0xa7, 0xf1, 0xbf, 0x9e, 0x84, 0xfe, 0xdd, 0x35, 0x72, 0x14, 0x04, 0xb3, 0xb2, 0xa1, 0xe8, 0xef, 0x8f, 0x4d, 0xed, 0x62, 0x51, 0x30, 0xaa, 0x2b, 0x53, 0x61, 0xe8, 0x21, 0x3b, 0x5b, 0x84, 0xd1, 0x11, 0x45, 0x4b, 0xee, 0x81, 0x26, 0x48, 0xaf, 0x88, 0xfe, 0xa4, 0x80, 0xa2, 0x31, 0x8e, 0x95, 0x84, 0xf5, 0x00, 0xb9, 0x8f, 0xe9, 0x95, 0xda, 0x40, 0x44, 0xc8, 0x77, 0x9f, 0x22, 0x7d, 0xdd, 0x72, 0x32, 0x5b, 0x70, 0xa4, 0xef, 0xbe, 0x26, 0xf8, 0x28, 0xc8, 0x0b, 0xc1, 0x47, 0x3b, 0x3c, 0x91, 0x00, 0x34, 0xe4, 0xcb, 0x5a, 0xfc, 0x04, 0x24, 0x38, 0xf3, 0xde, 0x48, 0x00, 0x99, 0xab, 0x8b, 0xbb, 0x16, 0x55, 0xc1, 0x18, 0x3b, 0xd7, 0xa8, 0xc2, 0x58, 0xaf, 0x8e, 0x7d, 0xf2, 0x67, 0x0d, 0x53, 0x20, 0x25, 0xba, 0xc5, 0x47, 0xd3, 0x18, 0x67, 0x84, 0x27, 0x2e, 0xc2, 0xe8, 0xf3, 0xe0, 0xaf, 0x6a, 0x61, 0xe4, 0xf4, 0x1f, 0x74, 0xdd, 0xe7, 0xf5, 0x34, 0x14, 0x15, 0x64, 0x55, 0xef, 0x84, 0x9c, 0xdd, 0xf5, 0xba, 0xba, 0xe2, 0xd9, 0xe0, 0x8d, 0x1e, 0x40, 0x3c, 0xce, 0xd9, 0xd3, 0x00, 0x05, 0xe4, 0x31, 0x31, 0x3e, 0xfd, 0x74, 0xab, 0x84, 0xbe, 0xfa, 0x75, 0x28, 0x38, 0xfe, 0x99, 0x67, 0x82, 0x8f, 0x7e, 0x7a, 0xa6, 0x07, 0xb8, 0x15, 0x74, 0xaf, 0xcb, 0xf5, 0x4c, 0x0f, 0xf0, 0x7a, 0xa0, 0xdc, 0xb2, 0xcf, 0x4f, 0x80, 0x5b, 0x91, 0x35, 0x29, 0xd7, 0x4f, 0x80, 0xc9, 0x40, 0xb9, 0x45, 0xe6, 0x27, 0xc0, 0xad, 0xc8, 0x9a, 0x94, 0xeb, 0xf9, 0x04, 0xf4, 0xf7, 0xf7, 0x9b, 0x74, 0x25, 0x39, 0xc9, 0x3c, 0x9b, 0x80, 0x7b, 0xf7, 0xee, 0x89, 0x1b, 0x14, 0xf3, 0xf2, 0xf2, 0xc4, 0x63, 0x3c, 0x3f, 0x35, 0x1c, 0xcf, 0x54, 0x7a, 0x36, 0x01, 0xb3, 0x67, 0xcf, 0x86, 0xb5, 0x6b, 0xd7, 0x42, 0x7e, 0x7e, 0xbe, 0xf8, 0x6b, 0x40, 0xdc, 0xbe, 0x61, 0x1b, 0xcf, 0xe8, 0x9a, 0xd0, 0xe5, 0x99, 0xfb, 0x00, 0xb2, 0xeb, 0x15, 0xda, 0xda, 0xda, 0x60, 0x6a, 0x2a, 0xfa, 0x13, 0x70, 0x72, 0xfb, 0x8b, 0x8b, 0x8b, 0xc5, 0x6d, 0x5c, 0x72, 0x1c, 0x0b, 0x6e, 0x69, 0x69, 0x81, 0xeb, 0xd7, 0xaf, 0x43, 0x67, 0x67, 0x27, 0x90, 0x9f, 0x51, 0x14, 0x79, 0xca, 0xca, 0xca, 0x20, 0x85, 0x3c, 0x7e, 0x38, 0x72, 0xe4, 0x08, 0x54, 0x57, 0x57, 0xc3, 0xe2, 0xc5, 0xda, 0xf7, 0x10, 0x2c, 0x59, 0x71, 0xc1, 0xc5, 0x63, 0x33, 0x9d, 0x91, 0x8e, 0x0b, 0x17, 0x2e, 0xd0, 0x4d, 0x7e, 0xc4, 0x69, 0x26, 0xac, 0xfe, 0xd6, 0xb6, 0x5c, 0x26, 0xf9, 0x41, 0x4c, 0x81, 0x04, 0x95, 0xf2, 0x05, 0x83, 0x41, 0x61, 0xd1, 0xa2, 0x45, 0xc2, 0xca, 0x95, 0x2b, 0x05, 0xe9, 0x13, 0xd6, 0x92, 0x5c, 0xb2, 0xad, 0x5f, 0xce, 0x9a, 0x70, 0x98, 0xeb, 0x6e, 0x61, 0xde, 0xde, 0x48, 0xdb, 0xa7, 0x31, 0x78, 0xb1, 0x12, 0x70, 0xec, 0xd8, 0x31, 0x1a, 0x7c, 0xa4, 0x25, 0x3f, 0x62, 0x26, 0x90, 0x5f, 0xe8, 0x52, 0x98, 0x43, 0x36, 0x40, 0x0a, 0xfb, 0xf6, 0xed, 0x13, 0xae, 0x5d, 0xbb, 0xa6, 0xc0, 0x27, 0xba, 0xf2, 0x54, 0x24, 0x00, 0x83, 0x58, 0x57, 0x57, 0xa7, 0x48, 0x82, 0x74, 0xc6, 0xcb, 0x8f, 0x0b, 0x17, 0x2e, 0x14, 0x1a, 0x1b, 0x1b, 0x13, 0x1d, 0x73, 0x85, 0x7e, 0xcf, 0x5e, 0x84, 0x49, 0xe0, 0x4c, 0x17, 0xe2, 0x91, 0xf8, 0x5b, 0x05, 0xf8, 0x1b, 0x06, 0x08, 0x8f, 0x8f, 0x8f, 0x03, 0xf9, 0xd0, 0x00, 0x90, 0x9e, 0x01, 0x64, 0x0f, 0x33, 0xe0, 0x05, 0x1d, 0x4b, 0x7b, 0x7b, 0x3b, 0xd4, 0xd4, 0xd4, 0xc0, 0xae, 0x5d, 0xbb, 0x4c, 0xcb, 0x76, 0x9d, 0x50, 0x91, 0x0e, 0x8f, 0x55, 0xcc, 0x0e, 0x41, 0xc7, 0x8f, 0x1f, 0xa7, 0x67, 0x3f, 0x8e, 0xff, 0xb5, 0xb5, 0xb5, 0x42, 0x6b, 0x6b, 0xab, 0x30, 0x31, 0x31, 0x21, 0x90, 0x9f, 0x39, 0x14, 0x4e, 0x9e, 0x3c, 0x29, 0x90, 0xed, 0xfa, 0x94, 0xa6, 0xa1, 0xa1, 0xc1, 0x33, 0x9e, 0x7a, 0x7a, 0x08, 0x3a, 0x71, 0xe2, 0x04, 0x0d, 0x1a, 0x7e, 0x71, 0x42, 0xaf, 0x90, 0x9f, 0x5c, 0x10, 0xe9, 0xc8, 0x4c, 0x47, 0x20, 0x1b, 0xde, 0x29, 0x0f, 0x39, 0x7b, 0x29, 0x4c, 0x3e, 0x37, 0x20, 0x0e, 0x53, 0x5d, 0x5d, 0x5d, 0x7a, 0x62, 0x12, 0x82, 0xd7, 0xae, 0x88, 0x75, 0xbd, 0xcf, 0x99, 0x53, 0x80, 0xd3, 0xd2, 0xc3, 0x87, 0x0f, 0x53, 0x62, 0xf2, 0x49, 0x04, 0x0a, 0xab, 0x81, 0xf2, 0xf2, 0x72, 0x71, 0xe8, 0x51, 0xe3, 0x93, 0xa2, 0x9e, 0x90, 0xb4, 0xab, 0x94, 0xae, 0x59, 0xb3, 0x86, 0x9e, 0xa9, 0x24, 0x68, 0x1a, 0xb8, 0xaa, 0xaa, 0x4a, 0x20, 0x5f, 0x03, 0x51, 0x71, 0x3d, 0x1d, 0x55, 0xcf, 0xdc, 0x88, 0xe1, 0xa7, 0x54, 0xf0, 0x46, 0x0c, 0x9f, 0xfd, 0xe0, 0x4f, 0xd4, 0x92, 0x19, 0x0b, 0x90, 0x39, 0x7c, 0x52, 0x9c, 0xc4, 0x4e, 0x8c, 0xf4, 0x4c, 0x02, 0x9c, 0x38, 0x91, 0xcc, 0xbc, 0x4f, 0xc5, 0x34, 0xd4, 0x4f, 0x40, 0x32, 0x47, 0x20, 0xc1, 0xb6, 0xfb, 0x3d, 0xc0, 0x4f, 0x40, 0x82, 0x23, 0x90, 0x60, 0xf5, 0x7e, 0x0f, 0x48, 0x70, 0x02, 0xfe, 0x0f, 0x63, 0xba, 0xa3, 0x5c, 0x3e, 0x86, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXPlistIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x32, 0x3a, 0x33, 0x35, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0xc8, 0x4f, 0xd5, 0xc2, 0x00, 0x00, 0x06, 0xa8, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x58, 0x7b, 0x4c, 0x53, 0x57, 0x18, 0xb7, 0xef, 0x77, 0x91, 0x87, 0x82, 0x20, 0x68, 0x01, 0xd1, 0xf8, 0x20, 0x6e, 0x09, 0xd1, 0x44, 0xc1, 0xcc, 0xa0, 0xce, 0x4c, 0x0d, 0xfc, 0xb7, 0x11, 0xa6, 0x93, 0xb0, 0x65, 0x53, 0x18, 0x31, 0x41, 0xcd, 0x4c, 0x4c, 0x96, 0x25, 0xfe, 0xb3, 0xa0, 0x46, 0x8c, 0x61, 0x03, 0x5f, 0x7f, 0xb0, 0x31, 0x46, 0x74, 0x31, 0x6e, 0xcb, 0x62, 0x80, 0x6c, 0x73, 0x8a, 0x12, 0xc5, 0xa0, 0x19, 0xbe, 0x98, 0x48, 0x43, 0x71, 0x56, 0x94, 0xd2, 0x07, 0xa5, 0x94, 0xb6, 0xfb, 0xe1, 0x71, 0xb7, 0x87, 0xdb, 0xf6, 0xda, 0x7b, 0x5b, 0x75, 0x66, 0xbd, 0x69, 0x9a, 0xef, 0x7c, 0x8f, 0xdf, 0xf9, 0x5e, 0xe7, 0x3b, 0xed, 0x15, 0xf9, 0x7c, 0xbe, 0x69, 0xaf, 0xf3, 0x23, 0x7e, 0x9d, 0x9d, 0x9f, 0xf4, 0x3d, 0x16, 0xc0, 0xab, 0xae, 0x60, 0xac, 0x02, 0xb1, 0x0a, 0x44, 0x98, 0x81, 0x58, 0x0b, 0x45, 0x98, 0xc0, 0x88, 0xcd, 0x63, 0x15, 0x88, 0x38, 0x85, 0x11, 0x02, 0xc4, 0x2a, 0x10, 0x61, 0x02, 0x23, 0x36, 0x8f, 0x55, 0x20, 0x58, 0x0a, 0x87, 0xdc, 0xde, 0x40, 0xb6, 0xcd, 0xe3, 0xf3, 0x04, 0x72, 0x23, 0xe6, 0x44, 0xb9, 0x02, 0x5d, 0xd6, 0xf1, 0x0f, 0xba, 0x1f, 0xcf, 0x6b, 0x37, 0x05, 0x3a, 0xf6, 0xfb, 0xe3, 0xb1, 0xcc, 0x36, 0xd3, 0x97, 0x7f, 0x59, 0x87, 0x83, 0x85, 0x17, 0xa8, 0x1f, 0x26, 0x47, 0x14, 0x95, 0xff, 0x03, 0x48, 0xed, 0x0f, 0x0f, 0x46, 0x0f, 0xf5, 0xd9, 0xfe, 0x78, 0x32, 0x86, 0x8d, 0x95, 0x12, 0x91, 0x73, 0x7d, 0x06, 0xcb, 0x83, 0x9f, 0xcc, 0xce, 0x0d, 0x9d, 0x66, 0x30, 0xd5, 0x12, 0xf1, 0xfb, 0xb3, 0x35, 0x9f, 0x1a, 0x74, 0x0b, 0xb5, 0x32, 0x96, 0x8e, 0x80, 0xa5, 0x54, 0x80, 0x0d, 0x6d, 0xf2, 0xc4, 0xed, 0x3d, 0x6a, 0xb4, 0x1f, 0xb9, 0x6f, 0x33, 0x3a, 0x27, 0x68, 0x3e, 0x07, 0x3d, 0xea, 0xf1, 0x7e, 0xdd, 0x6f, 0xc3, 0xa7, 0x70, 0x86, 0xaa, 0xca, 0xa0, 0x7b, 0x67, 0xa6, 0x4a, 0xc4, 0xa1, 0xfd, 0x3c, 0x91, 0xf0, 0x00, 0xfe, 0xb4, 0xbb, 0x6b, 0xfb, 0x6c, 0x8d, 0x03, 0x0e, 0x38, 0x44, 0xef, 0x32, 0x4b, 0x29, 0xdd, 0x9a, 0xae, 0xa1, 0x39, 0x84, 0xce, 0xd5, 0xcb, 0x4b, 0x67, 0x6b, 0x4f, 0x3d, 0x18, 0x75, 0xfe, 0xab, 0xdf, 0xfa, 0xc8, 0x89, 0x4f, 0xb6, 0x46, 0x56, 0x31, 0x57, 0xb7, 0x35, 0x5d, 0xab, 0x97, 0x0a, 0x09, 0x84, 0x77, 0x0b, 0xc1, 0xd9, 0x9f, 0xcd, 0x4e, 0x74, 0x0b, 0xf6, 0xa6, 0xbd, 0x94, 0x89, 0x45, 0x1b, 0x92, 0x55, 0x65, 0xe9, 0xda, 0xf5, 0x33, 0x55, 0x12, 0x5a, 0x30, 0x95, 0xb6, 0x4c, 0xf8, 0xbe, 0x35, 0x39, 0x8e, 0x1a, 0x6d, 0xd7, 0x46, 0xc6, 0x69, 0x89, 0x4e, 0x2a, 0x46, 0x0c, 0x95, 0x06, 0x5d, 0xb6, 0x9a, 0x5f, 0x4e, 0x79, 0x07, 0xb0, 0xbe, 0xd3, 0xfc, 0x8b, 0x79, 0x8a, 0xeb, 0x8b, 0xf5, 0x72, 0xf8, 0x5d, 0x9a, 0xa6, 0x99, 0x21, 0xe7, 0x31, 0x12, 0xae, 0x59, 0xdd, 0x08, 0x03, 0xc1, 0x58, 0xa8, 0x33, 0x2d, 0x12, 0x89, 0xea, 0x73, 0x13, 0xca, 0xd3, 0xb5, 0x74, 0x6c, 0xdc, 0x34, 0xbf, 0x70, 0x81, 0x35, 0x34, 0xee, 0x6f, 0x98, 0xe2, 0x59, 0xea, 0xcf, 0xb2, 0xe3, 0xf2, 0xe2, 0xe4, 0xdc, 0x7b, 0x04, 0x95, 0xbe, 0xa1, 0x97, 0x1d, 0x59, 0x9c, 0x50, 0xb3, 0x30, 0x1e, 0x4d, 0xf5, 0xc5, 0xdd, 0x91, 0xbb, 0x76, 0x37, 0xd4, 0x30, 0x51, 0xcc, 0x2e, 0x3f, 0x7e, 0x50, 0x43, 0x16, 0x93, 0x77, 0x00, 0xb4, 0xfd, 0x8f, 0x0f, 0x9d, 0xe2, 0x69, 0xa2, 0x8f, 0xe6, 0x68, 0xd7, 0x24, 0x29, 0x05, 0xf4, 0xaf, 0xd3, 0xeb, 0xfb, 0x7e, 0x70, 0x14, 0x33, 0x80, 0x78, 0x4f, 0x23, 0x87, 0x4f, 0xf3, 0x0e, 0xe0, 0xc3, 0x0c, 0xad, 0xd9, 0xe5, 0x21, 0x33, 0xc7, 0xed, 0xf5, 0x9d, 0x7a, 0xe0, 0xc0, 0x67, 0xae, 0x5a, 0x5a, 0x9e, 0x81, 0x83, 0xa8, 0x49, 0x55, 0x70, 0xf4, 0xbf, 0xdf, 0x2b, 0xd2, 0x3f, 0xdf, 0x98, 0x1c, 0x23, 0x54, 0xff, 0x40, 0x5c, 0x90, 0xa8, 0x5c, 0x33, 0x43, 0xe9, 0xd7, 0x0b, 0x83, 0xe2, 0x7d, 0x06, 0x80, 0xc9, 0x9a, 0xfa, 0xcc, 0x2e, 0x52, 0xb1, 0x08, 0x33, 0x11, 0x11, 0xbe, 0x1d, 0xe2, 0x1c, 0x5b, 0x27, 0x7c, 0x4d, 0x83, 0x8e, 0x06, 0xa3, 0xfd, 0xaa, 0xc5, 0xc5, 0x58, 0x81, 0x90, 0x8b, 0x45, 0xef, 0xa5, 0x69, 0xaa, 0x0c, 0x7a, 0xf4, 0x15, 0xcd, 0x0f, 0x87, 0x16, 0x12, 0x00, 0x83, 0x7b, 0x75, 0x64, 0x1c, 0xe3, 0xa8, 0x79, 0xd0, 0x31, 0xee, 0x9d, 0xf2, 0x76, 0x2c, 0x4b, 0x23, 0xeb, 0x7d, 0x2b, 0x95, 0x51, 0x23, 0x44, 0x87, 0x65, 0xbc, 0xb0, 0xe3, 0x21, 0x6b, 0xe6, 0x26, 0x2b, 0x24, 0x9f, 0xcc, 0xd5, 0x7d, 0x3c, 0x47, 0x97, 0xcc, 0x67, 0x00, 0xd0, 0xc8, 0x11, 0x05, 0x40, 0x80, 0xfe, 0x76, 0x79, 0xbf, 0xea, 0xb7, 0xd5, 0xf5, 0xdb, 0xd0, 0x5a, 0x84, 0xc3, 0x7d, 0x13, 0x13, 0x9d, 0x37, 0xe3, 0x14, 0xb8, 0xc5, 0xde, 0x4d, 0xd3, 0xc8, 0x05, 0x9c, 0x1e, 0x2a, 0x02, 0xde, 0x67, 0x80, 0xb2, 0x7d, 0x46, 0xa6, 0x28, 0xc4, 0x9f, 0xe7, 0xc4, 0x61, 0x1c, 0x7d, 0x37, 0xe8, 0x38, 0xd4, 0x67, 0x65, 0x0d, 0x78, 0x96, 0xbe, 0x44, 0x24, 0x2a, 0x4a, 0x51, 0xc3, 0xf5, 0xfc, 0x04, 0x05, 0x4b, 0x24, 0x70, 0x89, 0xc9, 0x15, 0xdd, 0xe7, 0xb7, 0xc7, 0x63, 0x25, 0x5d, 0x8f, 0x02, 0x31, 0xc1, 0xaf, 0xee, 0x19, 0xbe, 0x3f, 0x3a, 0x11, 0x28, 0x8a, 0x84, 0x13, 0x85, 0x16, 0x12, 0x98, 0xb9, 0x28, 0x99, 0xf1, 0xb8, 0x3b, 0xa3, 0xb4, 0x63, 0x94, 0x61, 0x62, 0x01, 0x44, 0x39, 0xa1, 0xbc, 0xe1, 0x62, 0x15, 0xe0, 0x9d, 0xb2, 0x28, 0x1b, 0xc4, 0x2a, 0x10, 0xe5, 0x84, 0xf2, 0x86, 0xfb, 0x7f, 0x57, 0x60, 0xe2, 0xe9, 0xe3, 0xf5, 0x86, 0xfc, 0x0b, 0x02, 0xf9, 0x81, 0x03, 0x07, 0x4e, 0x9f, 0x3e, 0xcd, 0x3b, 0xb1, 0xe1, 0x1b, 0x08, 0xbb, 0xc6, 0x7b, 0x7b, 0x7b, 0x13, 0x12, 0x12, 0xc8, 0x2e, 0xd3, 0xa7, 0x4f, 0x0f, 0x05, 0x72, 0xfb, 0xf6, 0x6d, 0xe8, 0x64, 0x66, 0x66, 0x86, 0x52, 0xe0, 0xe0, 0xbb, 0xdd, 0xee, 0xf6, 0xf6, 0xf6, 0xc1, 0xc1, 0x41, 0x0e, 0x1d, 0x88, 0x04, 0xb6, 0x90, 0x4e, 0xa7, 0x9b, 0x3f, 0x7f, 0x7e, 0x6a, 0xea, 0xe4, 0x6f, 0x66, 0x87, 0xc3, 0x11, 0x2a, 0x5f, 0x72, 0xb9, 0x5c, 0xa1, 0x50, 0xa4, 0xa5, 0xa5, 0x85, 0x52, 0xe0, 0xe0, 0x9f, 0x3b, 0x77, 0x6e, 0xf5, 0xea, 0xd5, 0xe5, 0xe5, 0xe5, 0x1c, 0x3a, 0x93, 0x22, 0xee, 0xf8, 0xb8, 0xa5, 0x57, 0xae, 0x5c, 0x01, 0x82, 0x4c, 0x26, 0xe3, 0x50, 0x43, 0x22, 0x39, 0xa4, 0xb4, 0xc8, 0xe3, 0xf1, 0xd0, 0xcb, 0x93, 0x27, 0x4f, 0x02, 0x3c, 0x2f, 0x2f, 0x8f, 0x66, 0x06, 0xd2, 0x41, 0x2a, 0xb0, 0x77, 0xef, 0xde, 0x65, 0xcb, 0x96, 0xad, 0x5d, 0xbb, 0xb6, 0xb3, 0xb3, 0x73, 0xf9, 0xf2, 0xe5, 0x6a, 0xb5, 0x7a, 0xc9, 0x92, 0x25, 0x35, 0x35, 0x35, 0x1c, 0xbd, 0x8e, 0x9d, 0x02, 0x9f, 0xcd, 0x9b, 0x37, 0x03, 0x67, 0xc5, 0x8a, 0x15, 0x2b, 0x57, 0xae, 0xb4, 0x58, 0x2c, 0x2c, 0x85, 0xb3, 0x67, 0xcf, 0x6e, 0xd9, 0xb2, 0x25, 0x39, 0x39, 0x19, 0xf8, 0xf0, 0x32, 0x37, 0x37, 0x77, 0xd3, 0xa6, 0x4d, 0x8c, 0xce, 0xcd, 0x9b, 0x37, 0x6f, 0xdd, 0xba, 0x85, 0xa5, 0xcd, 0x66, 0xfb, 0xf5, 0xe9, 0x03, 0x67, 0x70, 0xa2, 0x18, 0x05, 0x3f, 0x11, 0x18, 0x93, 0xc1, 0x60, 0x20, 0x62, 0x8d, 0x66, 0xca, 0xfb, 0xa9, 0xd2, 0xd2, 0x52, 0x96, 0x32, 0x47, 0x05, 0xb0, 0xb1, 0x54, 0xea, 0xff, 0xb3, 0xd1, 0xd7, 0xd7, 0x47, 0xdb, 0xb6, 0xb6, 0xb6, 0x92, 0x2d, 0xd6, 0xad, 0x5b, 0x57, 0x58, 0x58, 0x88, 0xb7, 0x29, 0x58, 0x2e, 0x58, 0xb0, 0x80, 0xe8, 0x74, 0x75, 0x75, 0xf9, 0xfd, 0xa3, 0xa8, 0x7d, 0xfb, 0xf6, 0xd1, 0x20, 0x84, 0x0e, 0xd2, 0x42, 0xf7, 0xee, 0xdd, 0x43, 0x62, 0x60, 0x18, 0x1f, 0x1f, 0x5f, 0x5f, 0x5f, 0x7f, 0xfd, 0xfa, 0xf5, 0xe2, 0xe2, 0x62, 0x82, 0x83, 0x8d, 0x69, 0x08, 0x8e, 0x00, 0xa0, 0x76, 0xe3, 0xc6, 0x8d, 0xe6, 0xe6, 0x66, 0x62, 0xc8, 0x0a, 0x60, 0xe7, 0xce, 0x9d, 0xe0, 0x2f, 0x5a, 0xb4, 0x88, 0xa0, 0x41, 0x5a, 0x56, 0x56, 0x86, 0x82, 0x90, 0xa5, 0xd9, 0x6c, 0xce, 0xcf, 0xcf, 0x27, 0x3e, 0xe0, 0x08, 0xcd, 0x7b, 0xfa, 0x2c, 0x5d, 0xba, 0x14, 0xa7, 0x82, 0x28, 0xd0, 0xdf, 0x41, 0x02, 0x80, 0x18, 0x73, 0x03, 0x1b, 0x6c, 0xdb, 0xb6, 0x8d, 0xa8, 0x0e, 0x0d, 0x0d, 0x49, 0x24, 0x93, 0xaf, 0x1b, 0x76, 0xef, 0xde, 0x4d, 0x1b, 0x73, 0x07, 0x00, 0x4d, 0xa7, 0xf3, 0xd9, 0x2b, 0x30, 0x56, 0x00, 0x2d, 0x2d, 0x2d, 0x24, 0x30, 0x74, 0x4e, 0x65, 0x65, 0xe5, 0xf1, 0xe3, 0xc7, 0x91, 0x35, 0x1a, 0x19, 0xb4, 0xf0, 0x33, 0x40, 0xa0, 0xf1, 0xbd, 0x6a, 0xd5, 0x2a, 0x42, 0x27, 0x26, 0x26, 0x22, 0x5b, 0xa0, 0x07, 0x06, 0x06, 0x18, 0x69, 0x24, 0x44, 0x51, 0x51, 0x11, 0x0e, 0x06, 0x10, 0x50, 0xde, 0xc3, 0x87, 0x0f, 0x23, 0xfd, 0x59, 0x59, 0x59, 0xdb, 0xb7, 0x6f, 0x17, 0x80, 0x19, 0xe4, 0x10, 0x33, 0x28, 0x17, 0x2e, 0x5c, 0x20, 0xf4, 0xf0, 0xf0, 0x70, 0x4f, 0x4f, 0x0f, 0x68, 0xe6, 0x78, 0x30, 0x3a, 0xc2, 0x08, 0x1c, 0x0f, 0xf4, 0x43, 0x77, 0x77, 0xf7, 0xae, 0x5d, 0xbb, 0x0a, 0x0a, 0x0a, 0xc8, 0x1c, 0xab, 0xab, 0xab, 0x43, 0xa1, 0x18, 0x40, 0xb1, 0x78, 0xd2, 0x37, 0xbb, 0xdd, 0xce, 0x70, 0x82, 0x13, 0xac, 0xc2, 0x91, 0x25, 0x69, 0xa1, 0xa4, 0xa4, 0xa4, 0xa6, 0xa6, 0x26, 0x14, 0xb7, 0xa4, 0xa4, 0x84, 0x18, 0x9f, 0x3f, 0x7f, 0x9e, 0xd1, 0xc7, 0x7c, 0xbc, 0x7c, 0xf9, 0x32, 0xf8, 0xd8, 0x1e, 0x34, 0x46, 0x04, 0x23, 0x22, 0x04, 0x38, 0x38, 0xca, 0xc4, 0x10, 0x17, 0x1f, 0x3d, 0x4f, 0xf7, 0xef, 0xdf, 0x0f, 0xf0, 0x8e, 0x8e, 0x0e, 0xa2, 0x69, 0x34, 0x1a, 0x95, 0xca, 0xc9, 0xf7, 0x59, 0xc7, 0x8e, 0x1d, 0x63, 0x40, 0xce, 0x9c, 0x39, 0x03, 0x0e, 0x5a, 0x17, 0x0e, 0x8c, 0x8d, 0x8d, 0xb5, 0xb5, 0xb5, 0xa1, 0xa5, 0xd1, 0x57, 0x8c, 0x02, 0x21, 0xb8, 0xce, 0x00, 0xd9, 0x9b, 0xf9, 0xc6, 0x58, 0x24, 0x36, 0x56, 0xab, 0x35, 0xe8, 0xdd, 0x84, 0xc9, 0xcb, 0xa0, 0x63, 0x32, 0x32, 0x86, 0x0c, 0x91, 0x9d, 0x9d, 0xed, 0x72, 0xb9, 0xa0, 0x53, 0x51, 0x51, 0x01, 0x26, 0x0e, 0xe8, 0x8e, 0x1d, 0x3b, 0x1a, 0x1b, 0x1b, 0xab, 0xab, 0xab, 0xb1, 0x44, 0xca, 0xd1, 0x51, 0x0c, 0x02, 0xb9, 0xc5, 0x09, 0x5f, 0xa5, 0x52, 0x11, 0x10, 0x7a, 0x0b, 0xa2, 0xc9, 0x15, 0xc0, 0x9e, 0x3d, 0x7b, 0x30, 0x0d, 0x52, 0x52, 0x52, 0x70, 0x1b, 0x1c, 0x3c, 0x78, 0x10, 0xf7, 0x00, 0xb1, 0x41, 0x00, 0x19, 0x19, 0x19, 0x04, 0x91, 0xfe, 0xc6, 0x4c, 0x64, 0xb6, 0xc7, 0x0d, 0x40, 0x8b, 0x08, 0x9d, 0x93, 0x93, 0x83, 0x5c, 0x42, 0x07, 0xc8, 0x18, 0x9d, 0xf4, 0x98, 0x46, 0x41, 0x1a, 0x1a, 0x1a, 0x18, 0x73, 0x42, 0xd4, 0xd6, 0xd6, 0xe2, 0xca, 0x87, 0x2d, 0x62, 0xc3, 0x90, 0xc5, 0xad, 0x6c, 0x32, 0x99, 0x58, 0x3a, 0x5c, 0x01, 0x5c, 0xbc, 0x78, 0x91, 0xa5, 0x1d, 0xad, 0x25, 0x5a, 0x0b, 0x4d, 0x85, 0x1e, 0xbb, 0x73, 0xe7, 0xce, 0xa5, 0x4b, 0x97, 0xf0, 0x4d, 0x2a, 0x13, 0x88, 0x8f, 0xac, 0xf5, 0xf7, 0xf7, 0x23, 0x65, 0x81, 0x22, 0xc2, 0x61, 0x07, 0x80, 0x2b, 0x13, 0x8d, 0x88, 0xb1, 0x83, 0xb8, 0xab, 0xaa, 0xaa, 0x4e, 0x9c, 0x38, 0xc1, 0x61, 0x1c, 0x0a, 0xf4, 0x65, 0xf2, 0xd9, 0x01, 0xe0, 0xe6, 0x62, 0x95, 0x3e, 0xf0, 0xdc, 0xbc, 0x4c, 0xff, 0x9e, 0xbb, 0x97, 0xff, 0xb6, 0x27, 0x7e, 0x63, 0x42, 0xe3, 0xd7, 0x25, 0x33, 0x3d, 0xf4, 0x7a, 0xfd, 0xc6, 0x8d, 0x1b, 0x59, 0x21, 0xfd, 0xa7, 0x96, 0xb1, 0x37, 0x73, 0xaf, 0xba, 0x1c, 0x5c, 0x37, 0xf1, 0xab, 0xf6, 0x2d, 0xac, 0xfd, 0x63, 0x01, 0x84, 0x95, 0xa6, 0x17, 0xa8, 0x14, 0xab, 0xc0, 0x0b, 0x4c, 0x6e, 0x58, 0xd0, 0xb1, 0x0a, 0x84, 0x95, 0xa6, 0x17, 0xa8, 0xf4, 0x0f, 0x7b, 0x3c, 0x70, 0xd0, 0xa5, 0xfc, 0x34, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXPlistIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x10, 0xc5, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x09, 0x90, 0x15, 0xc5, 0xf5, 0xb1, 0x17, 0xbb, 0x2c, 0xcb, 0x1e, 0x20, 0xec, 0xc2, 0x1e, 0xb2, 0x1c, 0xe1, 0x46, 0x08, 0x8b, 0x20, 0x97, 0x68, 0x20, 0x80, 0x10, 0x90, 0x80, 0x80, 0xd1, 0xa4, 0x62, 0x45, 0x41, 0x28, 0x83, 0x5c, 0x52, 0x42, 0x52, 0xa9, 0x04, 0x04, 0x2a, 0x12, 0x10, 0xa5, 0x10, 0xca, 0x12, 0x15, 0x85, 0x0a, 0xa7, 0x84, 0x08, 0x28, 0xe7, 0x22, 0x87, 0x01, 0xe4, 0x94, 0x4b, 0xc0, 0x05, 0xf6, 0x3e, 0x60, 0xd9, 0x5d, 0xf6, 0xbe, 0x26, 0xef, 0xf5, 0xff, 0xdd, 0x3b, 0x33, 0x7f, 0x66, 0x76, 0xfe, 0xbd, 0xc8, 0xef, 0xaa, 0xff, 0xa7, 0x8f, 0x77, 0xf5, 0x7b, 0x3d, 0xdd, 0x3d, 0xd3, 0xdd, 0x6f, 0x1a, 0x49, 0x18, 0xc0, 0x8e, 0xe0, 0x67, 0x07, 0x2c, 0x03, 0x75, 0x1e, 0xa1, 0xd1, 0x57, 0x77, 0x18, 0xa5, 0xa2, 0x6a, 0x8b, 0xa4, 0x3c, 0x2d, 0x24, 0xa1, 0x3a, 0xf0, 0x00, 0xff, 0xbd, 0xcd, 0xa3, 0x92, 0x6e, 0x5c, 0x40, 0x58, 0x23, 0x3f, 0x95, 0x54, 0xa9, 0xb3, 0x14, 0xe9, 0x46, 0x94, 0x12, 0xec, 0x4c, 0x44, 0x5c, 0x50, 0xe9, 0x46, 0x8d, 0x34, 0xf9, 0x3c, 0xff, 0xfc, 0xf3, 0x2c, 0x5f, 0xc1, 0x21, 0x35, 0x35, 0x55, 0x00, 0x37, 0xb2, 0x22, 0xf2, 0xab, 0x28, 0xe0, 0x35, 0x9a, 0x31, 0x63, 0x06, 0x8f, 0x1a, 0x5e, 0xed, 0xae, 0xb4, 0xdd, 0x08, 0x8a, 0x3a, 0x08, 0x39, 0x0d, 0x22, 0xce, 0x21, 0x5c, 0x28, 0xaa, 0x82, 0x7b, 0x55, 0xb5, 0x8c, 0x7e, 0xe2, 0xa1, 0x4c, 0x08, 0xde, 0x5b, 0xa7, 0x35, 0xc1, 0x94, 0xab, 0xa4, 0xd9, 0xd7, 0xa9, 0x3c, 0xaa, 0xb8, 0xbe, 0x7f, 0xab, 0x48, 0x91, 0x06, 0x45, 0xca, 0x9a, 0xe0, 0x0d, 0x2f, 0x66, 0x7f, 0xba, 0x4d, 0x71, 0x43, 0xd5, 0x12, 0xb5, 0x97, 0x25, 0x4b, 0x96, 0x08, 0x45, 0xc8, 0x23, 0xea, 0xb6, 0xc4, 0xea, 0x40, 0x99, 0x58, 0x3b, 0x39, 0x9c, 0x6e, 0x9c, 0x19, 0xce, 0x2c, 0x30, 0x51, 0xb1, 0x5b, 0x4b, 0xba, 0xac, 0x75, 0x0a, 0xec, 0x6e, 0x4a, 0x3a, 0x74, 0x74, 0xb3, 0x1d, 0x62, 0x60, 0xd3, 0xb5, 0xe9, 0x92, 0xc7, 0x02, 0x9b, 0xa6, 0x65, 0xcd, 0xa0, 0xd6, 0xd7, 0xfd, 0x48, 0xa6, 0x4d, 0x31, 0x6f, 0x95, 0xf2, 0x02, 0xca, 0x9b, 0x7b, 0xe5, 0xbe, 0x3c, 0x4b, 0xc4, 0x75, 0x19, 0x10, 0x04, 0x21, 0x3e, 0xb6, 0x2f, 0xad, 0x0e, 0x58, 0xd6, 0xef, 0xf2, 0x4c, 0x82, 0x99, 0x70, 0x26, 0x8f, 0x27, 0x6d, 0xae, 0x86, 0x0c, 0xe4, 0xd0, 0x1d, 0x0f, 0x67, 0x48, 0xb9, 0x15, 0x35, 0xf2, 0x2c, 0x53, 0x71, 0xef, 0xb7, 0xa2, 0xc3, 0x87, 0x0f, 0x2b, 0x4c, 0x18, 0x15, 0x15, 0xa5, 0x48, 0xd7, 0x9b, 0x90, 0xd7, 0x93, 0x6c, 0x3e, 0x75, 0xea, 0x54, 0xba, 0xe5, 0x44, 0xb6, 0x3c, 0x5e, 0x5c, 0x5c, 0xac, 0x59, 0x16, 0x1a, 0x1a, 0xca, 0xe0, 0x09, 0x96, 0xff, 0x38, 0x01, 0x85, 0x8a, 0xe4, 0xf7, 0x39, 0x02, 0x80, 0xbd, 0x69, 0x24, 0x2e, 0x70, 0x6a, 0x6b, 0x6b, 0x59, 0x5c, 0xc1, 0x80, 0x00, 0x5c, 0x1d, 0x1c, 0xba, 0xd1, 0xec, 0x11, 0xe2, 0xe1, 0xaf, 0x81, 0xdb, 0x55, 0xf4, 0x08, 0x32, 0x38, 0x71, 0xbf, 0x02, 0x7e, 0x77, 0xee, 0xae, 0xf9, 0x86, 0xc4, 0xef, 0x38, 0xad, 0x2b, 0xf5, 0x94, 0xd8, 0xf7, 0x2b, 0x8a, 0x28, 0x8f, 0x42, 0x19, 0xf6, 0x7b, 0x14, 0xff, 0x26, 0xaf, 0x4c, 0x51, 0xae, 0x4e, 0xd4, 0xf5, 0x09, 0xb2, 0x92, 0x15, 0x29, 0x45, 0x8a, 0x69, 0xb0, 0xac, 0x48, 0xf2, 0x57, 0x31, 0x7c, 0xf6, 0xbb, 0x1c, 0x5d, 0x58, 0xc2, 0xd3, 0x64, 0x40, 0x92, 0xbd, 0x7d, 0xcd, 0x76, 0x00, 0xe1, 0xd2, 0xcb, 0x19, 0x32, 0x22, 0x08, 0x1f, 0xb8, 0x5b, 0x59, 0x53, 0x0e, 0xa3, 0xd9, 0x8a, 0xa4, 0xd1, 0x09, 0x50, 0x8c, 0x33, 0x7f, 0x1a, 0x1a, 0xa9, 0xf7, 0xd2, 0x0b, 0x1d, 0x93, 0x33, 0x2d, 0x30, 0x08, 0x5f, 0x39, 0x2a, 0x5e, 0x1b, 0x8c, 0x73, 0xd2, 0xbb, 0x06, 0x59, 0x25, 0xd3, 0x92, 0xf0, 0x54, 0x41, 0x85, 0x1e, 0x9a, 0xc8, 0xd7, 0xac, 0x81, 0x5c, 0x94, 0x0a, 0xab, 0x64, 0x55, 0xb5, 0xb6, 0x75, 0x49, 0x0a, 0x0f, 0x92, 0x83, 0x6a, 0xc6, 0x7d, 0x7d, 0x91, 0xa6, 0x5a, 0xe4, 0x99, 0x86, 0x36, 0xa0, 0xf1, 0x38, 0x27, 0x27, 0x47, 0x0e, 0x0f, 0x9f, 0x7d, 0xf6, 0x99, 0x22, 0x5d, 0x6f, 0x42, 0x98, 0x5b, 0x27, 0x32, 0x62, 0xc4, 0x08, 0x9d, 0x12, 0xdb, 0x6c, 0x64, 0x66, 0x93, 0x29, 0x6a, 0xb0, 0x66, 0xcd, 0x1a, 0x36, 0x86, 0x2e, 0x5a, 0xb4, 0x48, 0x8c, 0xab, 0x6a, 0xe9, 0xc6, 0x8c, 0x19, 0x03, 0x11, 0x11, 0x11, 0x22, 0x9b, 0xc6, 0xec, 0xb2, 0xb2, 0x32, 0xa0, 0xfc, 0xfd, 0xfb, 0xf7, 0xb3, 0x7c, 0xca, 0x3b, 0x73, 0xe6, 0x8c, 0x80, 0x51, 0xb0, 0x1c, 0x34, 0x68, 0x10, 0x93, 0x00, 0x4b, 0x85, 0x24, 0xea, 0x1a, 0x84, 0x87, 0x87, 0x8b, 0xb2, 0x21, 0x43, 0x86, 0xb0, 0x59, 0x84, 0xbf, 0xbf, 0xbf, 0x0d, 0x1e, 0x07, 0xaa, 0xa3, 0x84, 0x39, 0x61, 0x61, 0x61, 0x0c, 0xe1, 0xe2, 0xc5, 0x8b, 0x02, 0x81, 0x98, 0x71, 0x86, 0x3c, 0x2e, 0x4f, 0x07, 0x05, 0x05, 0x49, 0xbd, 0x7b, 0xf7, 0x66, 0xf0, 0xf1, 0xf1, 0xf1, 0x0c, 0x96, 0x97, 0x53, 0xa6, 0x60, 0x30, 0x6d, 0xda, 0x34, 0xa9, 0x7d, 0xfb, 0xf6, 0x0c, 0xd0, 0x95, 0x7f, 0xbe, 0x1b, 0x0d, 0xd5, 0x6d, 0x1c, 0xdc, 0xae, 0x22, 0x63, 0xf6, 0xce, 0x97, 0x8a, 0xfb, 0xcc, 0x79, 0x52, 0xde, 0xa1, 0xe0, 0xab, 0x80, 0x77, 0xf4, 0x5e, 0xc7, 0xd5, 0x63, 0x16, 0xb8, 0x55, 0x56, 0x5d, 0xc7, 0xd5, 0x85, 0x31, 0xa7, 0x2b, 0x30, 0x05, 0xe7, 0xc0, 0xf5, 0xbd, 0xb6, 0x08, 0xd8, 0x9d, 0x0a, 0x89, 0x07, 0x33, 0x14, 0x62, 0x13, 0xce, 0xd3, 0xdf, 0x29, 0x47, 0x62, 0x05, 0x80, 0xc9, 0x84, 0x43, 0xbd, 0x50, 0x02, 0x0a, 0x93, 0x6a, 0xd5, 0xe8, 0xea, 0xee, 0x51, 0x30, 0x23, 0x21, 0x4c, 0x97, 0x5d, 0xdb, 0x43, 0x19, 0x70, 0xbb, 0xb4, 0x1a, 0x8a, 0x46, 0xc6, 0x43, 0x98, 0xbf, 0xf2, 0xad, 0xec, 0x97, 0xd9, 0x65, 0x30, 0xfe, 0xfb, 0x5c, 0x86, 0x1b, 0x11, 0xe8, 0x07, 0xf9, 0xbf, 0x8e, 0x03, 0x25, 0x84, 0x2e, 0x59, 0x51, 0x60, 0xba, 0x02, 0xfe, 0xa8, 0xc5, 0x5a, 0x1c, 0xfa, 0x28, 0x64, 0x0e, 0x8b, 0x85, 0x98, 0xc6, 0xfe, 0x82, 0x88, 0x5e, 0x64, 0xf2, 0xd9, 0xbb, 0xb0, 0x39, 0xb3, 0x04, 0x8e, 0x0d, 0x88, 0x86, 0x01, 0x91, 0x8d, 0xf5, 0xc0, 0x58, 0xfe, 0x83, 0x1a, 0x09, 0xa2, 0xbe, 0x49, 0x83, 0x6a, 0xeb, 0xdc, 0x94, 0x66, 0xde, 0x66, 0x82, 0xe9, 0x0a, 0x10, 0xb1, 0xff, 0x15, 0x54, 0x42, 0xff, 0x63, 0x59, 0x82, 0xee, 0xc5, 0x21, 0xad, 0xa1, 0x7b, 0x58, 0xa0, 0x48, 0xcb, 0x23, 0x1f, 0xdc, 0x7e, 0x00, 0x7f, 0xbe, 0x94, 0x0f, 0xcb, 0x3a, 0x47, 0xc2, 0xfc, 0x76, 0xcd, 0xe4, 0x45, 0x8a, 0x78, 0x46, 0x79, 0x0d, 0xc4, 0x1e, 0x48, 0x17, 0x79, 0x5f, 0x26, 0xb5, 0x84, 0x71, 0xad, 0x42, 0x44, 0xba, 0xbe, 0x88, 0x5d, 0x15, 0x50, 0x13, 0x8b, 0xc7, 0xa6, 0x94, 0x66, 0x6d, 0x4a, 0x47, 0x9e, 0x8a, 0x86, 0xc1, 0x51, 0x16, 0x2d, 0x9f, 0x29, 0xac, 0x84, 0x3e, 0x47, 0xb3, 0x60, 0x4c, 0xab, 0x26, 0xb0, 0x2b, 0xe9, 0x31, 0x35, 0x1a, 0x64, 0x56, 0xd4, 0x40, 0x9b, 0xfd, 0x16, 0xa1, 0xc3, 0xb1, 0xe9, 0xdc, 0x1d, 0x1e, 0x07, 0x01, 0xf6, 0xb6, 0x1d, 0x2b, 0x55, 0xa7, 0x2a, 0x60, 0x23, 0x19, 0x66, 0x94, 0x62, 0x13, 0x08, 0xdd, 0x93, 0x0a, 0xd1, 0xc1, 0xfe, 0x90, 0xf5, 0xab, 0x58, 0x2d, 0x10, 0x97, 0xe6, 0xb9, 0xbc, 0x02, 0x24, 0xdd, 0xd6, 0xac, 0x52, 0x98, 0x18, 0xd3, 0xc4, 0xa5, 0x82, 0xea, 0x11, 0x73, 0x4b, 0x05, 0xf4, 0x98, 0xb9, 0x23, 0xdf, 0xe9, 0x71, 0xc0, 0x1d, 0x42, 0xd9, 0x43, 0xf3, 0xd1, 0xad, 0xc0, 0xac, 0x59, 0xb3, 0x2c, 0x6f, 0x5a, 0xf1, 0x29, 0x6f, 0xe4, 0xc8, 0x91, 0xba, 0x4a, 0xa3, 0xa7, 0xc0, 0xd7, 0x5e, 0x7b, 0x4d, 0xb7, 0xdc, 0xa8, 0x20, 0x24, 0x24, 0x04, 0xbe, 0xf8, 0xe2, 0x0b, 0x23, 0x10, 0xd9, 0xe3, 0xa3, 0xec, 0x41, 0x2c, 0x36, 0x36, 0x96, 0x3d, 0xbc, 0xe1, 0xa3, 0xa8, 0x78, 0x88, 0xa3, 0x07, 0x42, 0x7c, 0x3d, 0x2c, 0x83, 0xb2, 0x44, 0x91, 0xba, 0xa4, 0x7e, 0xf2, 0xa4, 0x92, 0x0b, 0x17, 0x2e, 0x08, 0xdc, 0xe8, 0xe8, 0x68, 0x1b, 0xbc, 0x94, 0x94, 0x14, 0x51, 0x4e, 0x34, 0xe8, 0xc9, 0x14, 0xd7, 0x49, 0x19, 0x5c, 0x75, 0x75, 0xb5, 0xb4, 0x72, 0xe5, 0x4a, 0x56, 0xde, 0xad, 0x5b, 0x37, 0x69, 0xc5, 0x8a, 0x15, 0xd2, 0xaa, 0x55, 0xab, 0x6c, 0x68, 0x50, 0x86, 0x78, 0xa8, 0x54, 0x97, 0x12, 0x51, 0xf9, 0x43, 0x26, 0x2d, 0xa1, 0x52, 0x5e, 0x5e, 0x9e, 0x72, 0x71, 0x43, 0xaf, 0x02, 0x9c, 0x1e, 0x95, 0xcb, 0x1f, 0xa5, 0xe5, 0xf9, 0x54, 0x16, 0x10, 0x10, 0x20, 0x4d, 0x9e, 0x3c, 0x59, 0x3a, 0x76, 0xec, 0x18, 0x2f, 0x12, 0x57, 0x2a, 0x1f, 0x37, 0x6e, 0x9c, 0x48, 0x6b, 0x45, 0x0c, 0x2b, 0x40, 0x04, 0x6e, 0xdf, 0xbe, 0x2d, 0x95, 0x96, 0x96, 0x0a, 0x6d, 0xa9, 0x89, 0x10, 0x8c, 0x96, 0x05, 0x38, 0x1c, 0x95, 0xeb, 0x55, 0x60, 0xeb, 0xd6, 0xad, 0x1c, 0x4c, 0xc2, 0x17, 0x12, 0x8c, 0x87, 0xc8, 0xc0, 0x08, 0xe1, 0x92, 0xe5, 0x29, 0x5c, 0xbb, 0x76, 0x4d, 0x4a, 0x4a, 0x4a, 0x62, 0x71, 0xf9, 0x9f, 0x61, 0x05, 0x5e, 0x7f, 0xfd, 0x75, 0x39, 0xac, 0x4b, 0xe3, 0x07, 0x0f, 0x1e, 0x64, 0xf4, 0xf0, 0xfd, 0x90, 0xb4, 0x6b, 0xd7, 0x2e, 0xa9, 0xa8, 0x48, 0xb9, 0xcc, 0xcb, 0x99, 0x51, 0x73, 0x22, 0xeb, 0x94, 0x94, 0x94, 0xf0, 0x2c, 0xc5, 0x55, 0x73, 0x1c, 0x90, 0x2f, 0xa3, 0xa0, 0x16, 0xa8, 0x92, 0x74, 0x69, 0x90, 0x21, 0x40, 0x4b, 0xaa, 0x86, 0x2c, 0xb0, 0x5a, 0xde, 0x47, 0x77, 0x1c, 0x50, 0x6b, 0xc2, 0x5b, 0x69, 0xcd, 0x7b, 0xc0, 0x5b, 0xc2, 0x38, 0xc2, 0xd7, 0xd7, 0x84, 0x1c, 0xd1, 0x9a, 0x2b, 0x71, 0x7c, 0x16, 0x70, 0xa5, 0x36, 0x1d, 0xa1, 0xe5, 0xb3, 0x80, 0x23, 0x5a, 0x73, 0x25, 0x8e, 0xcf, 0x02, 0xae, 0xd4, 0xa6, 0x23, 0xb4, 0x3c, 0x62, 0x81, 0xb3, 0x45, 0x95, 0x8e, 0xc8, 0x66, 0x0a, 0xc7, 0x23, 0x23, 0x31, 0x7f, 0xf9, 0x6b, 0xf6, 0x75, 0xa1, 0x29, 0xc9, 0xad, 0x40, 0x4e, 0x59, 0x20, 0xbb, 0x02, 0x77, 0x20, 0xe1, 0x5b, 0xe6, 0x85, 0x3f, 0x16, 0xe8, 0xf2, 0xbc, 0x5d, 0x56, 0xc3, 0xca, 0x06, 0x46, 0x05, 0x0b, 0x98, 0x4d, 0x99, 0xa5, 0x0c, 0xef, 0x6a, 0xb1, 0xf3, 0xaf, 0xdc, 0x1d, 0xae, 0x40, 0xff, 0xe3, 0xd9, 0x10, 0xb3, 0x3f, 0x8d, 0x09, 0x95, 0x8b, 0x15, 0xd1, 0x0b, 0x3d, 0xbf, 0xcd, 0x64, 0x45, 0xc9, 0x4f, 0xb5, 0x12, 0x20, 0x3f, 0x16, 0x57, 0xb1, 0x78, 0x97, 0xe4, 0x0c, 0xe8, 0x70, 0xd8, 0x52, 0x2e, 0x0a, 0xed, 0x8c, 0xd8, 0xdd, 0x84, 0x76, 0xe5, 0x94, 0xc1, 0xd8, 0xd3, 0x75, 0xaf, 0xc4, 0xef, 0xe1, 0x2b, 0x71, 0x3d, 0x2d, 0x90, 0xee, 0x03, 0xd0, 0x42, 0xcd, 0xf0, 0xfd, 0x67, 0x21, 0xc2, 0xa9, 0x83, 0xfc, 0x35, 0xfd, 0x87, 0x3d, 0x9a, 0xc3, 0xb4, 0xf8, 0xa6, 0x6a, 0x90, 0x7a, 0xd3, 0xa6, 0x2b, 0x40, 0xc2, 0x44, 0x7c, 0x9d, 0x86, 0x9b, 0x4d, 0x2c, 0xda, 0x3e, 0x8a, 0xaf, 0xcc, 0x07, 0xd6, 0xf3, 0xca, 0x7c, 0xc0, 0x89, 0x1c, 0x38, 0x91, 0x5f, 0x0e, 0xb7, 0x9e, 0x8d, 0x85, 0xc7, 0x43, 0xb4, 0x5f, 0xc7, 0x5f, 0x2b, 0xa9, 0x86, 0xce, 0x87, 0x2d, 0x8b, 0x1f, 0x01, 0x7e, 0x8d, 0xa0, 0x00, 0x2b, 0x1a, 0xaa, 0x5a, 0x47, 0x30, 0xaa, 0x85, 0x9e, 0xf2, 0x14, 0x38, 0x7f, 0xc1, 0x36, 0x4e, 0x9a, 0x24, 0xe1, 0xa7, 0xb4, 0x09, 0x05, 0xba, 0x19, 0xeb, 0x13, 0x9e, 0x08, 0x90, 0xf0, 0x14, 0xf4, 0x84, 0xa7, 0xb2, 0x4e, 0xa1, 0x01, 0x8c, 0xde, 0x1c, 0x7c, 0x05, 0x4f, 0x6b, 0x03, 0x4d, 0x71, 0xdf, 0xf4, 0x84, 0x33, 0xe6, 0x77, 0x3e, 0x99, 0xaa, 0x40, 0x6c, 0x48, 0xdd, 0x93, 0xe7, 0x8e, 0xec, 0x52, 0x28, 0xc1, 0xc5, 0x88, 0xfa, 0xc2, 0x5b, 0x57, 0x2d, 0x37, 0xf6, 0xa6, 0xde, 0xb6, 0xaf, 0xd7, 0xd5, 0xb8, 0x64, 0xdd, 0x8f, 0x53, 0x8b, 0x45, 0x76, 0x62, 0x93, 0x3a, 0x7e, 0x22, 0x53, 0x27, 0x62, 0xba, 0x09, 0x11, 0x7e, 0xd2, 0xb1, 0x6c, 0xf8, 0xbe, 0xa0, 0x82, 0x91, 0x1a, 0xd2, 0x3c, 0x18, 0x92, 0xfb, 0xd7, 0xdd, 0x98, 0x6a, 0xfa, 0x66, 0xbb, 0x4e, 0x5a, 0x63, 0xfb, 0x77, 0x46, 0x09, 0x43, 0x6f, 0xdb, 0x24, 0x10, 0x52, 0x9e, 0x69, 0xad, 0x26, 0x65, 0x98, 0x36, 0x65, 0x01, 0x4e, 0xe1, 0xf4, 0xc0, 0x68, 0xb8, 0x3f, 0x22, 0x1e, 0xfc, 0xf1, 0x75, 0xe1, 0x91, 0x7b, 0xe5, 0xac, 0x2b, 0xfc, 0x38, 0xad, 0x4e, 0x73, 0x1c, 0x6e, 0x27, 0xde, 0xe8, 0x14, 0x66, 0x3c, 0xae, 0xbf, 0x32, 0xb3, 0x27, 0xd7, 0x82, 0xcf, 0x85, 0xbf, 0x83, 0x6b, 0x09, 0xf6, 0x0a, 0x4f, 0x3c, 0xec, 0xb2, 0x00, 0x21, 0xf0, 0xb0, 0x05, 0xd7, 0x00, 0x26, 0x9d, 0xc9, 0x63, 0x49, 0x7a, 0x0d, 0x53, 0xfb, 0x5c, 0xdd, 0xce, 0xb0, 0xfa, 0xb4, 0x1f, 0x82, 0xed, 0xbc, 0xdc, 0xda, 0x0c, 0xdf, 0xeb, 0x1a, 0x05, 0x33, 0xdb, 0xea, 0x2f, 0x12, 0x72, 0x7e, 0xba, 0x57, 0xc5, 0x5b, 0x22, 0x07, 0x12, 0xbf, 0x39, 0x9d, 0x2b, 0x5d, 0x2d, 0xae, 0x3b, 0xa6, 0x92, 0x5e, 0x86, 0x7b, 0xe2, 0x70, 0x2f, 0xdd, 0x13, 0xdf, 0xda, 0x6e, 0xf4, 0xe6, 0xe4, 0x0b, 0x70, 0x97, 0x59, 0xdf, 0x63, 0x59, 0x3c, 0xe9, 0xd4, 0xd5, 0x61, 0x0b, 0xe8, 0x69, 0xa4, 0xf9, 0xbe, 0x74, 0xc8, 0xaf, 0xac, 0x81, 0x8a, 0xe7, 0x12, 0x20, 0xc8, 0xc1, 0x75, 0x2f, 0x3d, 0xda, 0x5a, 0xf9, 0x76, 0xdd, 0x03, 0x5a, 0x04, 0xe4, 0x79, 0xd4, 0x37, 0x91, 0xf0, 0xc1, 0xd8, 0x8f, 0x7b, 0x42, 0x78, 0xe2, 0xed, 0x72, 0x0b, 0xd0, 0x30, 0x77, 0x13, 0x07, 0xa7, 0x8e, 0xd8, 0xbf, 0x7b, 0x22, 0xb8, 0xbc, 0x02, 0x9e, 0x10, 0x5a, 0xce, 0xc3, 0xa5, 0x4d, 0x48, 0x4e, 0xd8, 0x53, 0x71, 0x5f, 0x05, 0x3c, 0xa5, 0x69, 0x3d, 0x3e, 0x3e, 0x0b, 0xe8, 0x69, 0xc6, 0x53, 0xf9, 0x0e, 0x5b, 0x00, 0xd7, 0xce, 0x80, 0xb6, 0x0f, 0xab, 0x8f, 0xf4, 0xc8, 0x05, 0xdf, 0xb3, 0x67, 0x0f, 0xd0, 0x52, 0xa9, 0x5b, 0x83, 0xa3, 0xe3, 0x38, 0x0a, 0xc5, 0x16, 0xe1, 0xe8, 0xaa, 0x17, 0x66, 0xce, 0x9c, 0xc9, 0x60, 0xf4, 0xca, 0x8d, 0xf2, 0x93, 0x93, 0x93, 0xd9, 0x0a, 0xa6, 0x11, 0x0c, 0x95, 0xe9, 0x73, 0xaf, 0x0f, 0x13, 0xcb, 0x67, 0xcf, 0x9e, 0xed, 0xb0, 0x80, 0xf5, 0x91, 0xc7, 0x85, 0x74, 0x53, 0xb4, 0x6d, 0x9a, 0x10, 0xae, 0x0a, 0x8a, 0x15, 0xf8, 0xe1, 0xc3, 0x87, 0x8b, 0x38, 0xed, 0xdf, 0xb6, 0x27, 0x50, 0xd3, 0xa1, 0x59, 0xaa, 0x7a, 0xc1, 0x90, 0xd3, 0x18, 0x3a, 0x74, 0xa8, 0x28, 0x57, 0xc3, 0x1d, 0x38, 0x70, 0x00, 0xce, 0x9e, 0x3d, 0xcb, 0x40, 0x71, 0xc1, 0x1b, 0xe8, 0x77, 0xf4, 0xe8, 0x51, 0x8e, 0xaa, 0xbc, 0x6a, 0x69, 0x82, 0x16, 0xb8, 0x11, 0x4a, 0xf2, 0xf3, 0xf3, 0x93, 0x36, 0x6c, 0xd8, 0x20, 0xe1, 0x51, 0x35, 0x96, 0xc6, 0xed, 0xf1, 0x0a, 0x70, 0x23, 0x0b, 0x2c, 0x5c, 0xb8, 0x50, 0xea, 0xd3, 0xa7, 0x8f, 0xa6, 0x16, 0x51, 0x18, 0x96, 0xbf, 0x7b, 0xf7, 0x6e, 0x46, 0x6f, 0xe9, 0xd2, 0xa5, 0x0a, 0x38, 0xe2, 0xad, 0xf5, 0x53, 0x30, 0xb7, 0x26, 0x34, 0x9b, 0xd0, 0x9c, 0x39, 0x73, 0x14, 0x04, 0x09, 0x96, 0x13, 0x94, 0x13, 0x31, 0xaa, 0x00, 0xc1, 0xd1, 0xfa, 0x2f, 0xe1, 0xa9, 0xc3, 0xf9, 0xf3, 0xe7, 0x05, 0x3d, 0xda, 0x9b, 0xbe, 0x7c, 0xf9, 0x72, 0x89, 0xd6, 0x83, 0xe5, 0xc1, 0xe1, 0x26, 0x84, 0x0c, 0xdd, 0x1e, 0x7a, 0xf6, 0xec, 0x09, 0xdb, 0xb6, 0x6d, 0x83, 0xe0, 0xe0, 0x60, 0xd6, 0x54, 0xe6, 0xce, 0x9d, 0x0b, 0xb8, 0xe5, 0x00, 0x4e, 0x9c, 0x38, 0x61, 0x37, 0x6f, 0x9b, 0x7b, 0x40, 0x4e, 0xa1, 0x5f, 0xbf, 0x7e, 0x2c, 0x89, 0x9b, 0xf3, 0xd9, 0x75, 0xfa, 0xf4, 0xe9, 0xf2, 0x62, 0x87, 0xe3, 0xa7, 0x4f, 0x9f, 0x86, 0x97, 0x5f, 0x7e, 0x99, 0x9d, 0x6f, 0x40, 0xad, 0x43, 0x46, 0x86, 0xe5, 0xb5, 0x8a, 0xfc, 0x04, 0x07, 0x6e, 0x31, 0x60, 0xf4, 0xef, 0xdc, 0xb9, 0xc3, 0xae, 0xef, 0xbe, 0xfb, 0x2e, 0xbc, 0xf2, 0xca, 0x2b, 0xb6, 0x3c, 0xe5, 0x66, 0xe3, 0x71, 0xde, 0x84, 0x10, 0x5a, 0x98, 0xba, 0x7b, 0xf7, 0xee, 0xbc, 0x98, 0xed, 0x7d, 0x90, 0x97, 0xf1, 0x38, 0x1e, 0xc0, 0x67, 0x30, 0x78, 0xf3, 0x0b, 0x3c, 0x5e, 0x46, 0xd7, 0xcf, 0x3f, 0xff, 0x9c, 0x95, 0xd3, 0x7d, 0xc5, 0xf3, 0x69, 0xb3, 0x07, 0x8f, 0xcb, 0xb7, 0x13, 0x94, 0x97, 0x97, 0x8b, 0x7c, 0x5e, 0x3e, 0x6a, 0xd4, 0x28, 0x21, 0x03, 0x8f, 0xd8, 0x36, 0x50, 0x2c, 0xe1, 0x15, 0xe0, 0x40, 0xae, 0xbe, 0xd6, 0xd4, 0xd4, 0x48, 0xd7, 0xaf, 0x5f, 0x97, 0xd2, 0xd2, 0xd2, 0xa4, 0x1d, 0x3b, 0x76, 0x48, 0xe7, 0xce, 0x9d, 0xd3, 0x65, 0x41, 0x70, 0xf4, 0xd3, 0x0b, 0x36, 0x15, 0xa0, 0xcd, 0x17, 0x9d, 0x3a, 0x75, 0x62, 0xb5, 0xa7, 0x6d, 0x30, 0xc7, 0x8f, 0x1f, 0xd7, 0xc3, 0x6d, 0x10, 0xf9, 0x36, 0x15, 0x18, 0x36, 0x6c, 0x98, 0xc2, 0x74, 0x7c, 0x13, 0x52, 0x83, 0x90, 0x56, 0x43, 0x08, 0xdf, 0x13, 0x19, 0xde, 0x60, 0x5e, 0x0d, 0x86, 0xdd, 0xa8, 0x57, 0x25, 0x33, 0xc9, 0xdc, 0x57, 0x01, 0x93, 0x8a, 0x72, 0x1b, 0xd8, 0x43, 0x6f, 0x81, 0x87, 0xbe, 0x17, 0x72, 0x9b, 0x69, 0x3d, 0x44, 0xf8, 0xa1, 0x6f, 0x41, 0x1e, 0xd2, 0x93, 0xdb, 0xd8, 0xf8, 0x0c, 0xe0, 0x36, 0xd5, 0x9a, 0x23, 0xec, 0x33, 0x80, 0x39, 0x3d, 0xb9, 0x0d, 0xca, 0x67, 0x00, 0xb7, 0xa9, 0xd6, 0x1c, 0x61, 0x9f, 0x01, 0xcc, 0xe9, 0xc9, 0x6d, 0x50, 0x3e, 0x03, 0xb8, 0x4d, 0xb5, 0xe6, 0x08, 0xfb, 0x0c, 0x60, 0x4e, 0x4f, 0x6e, 0x83, 0xf2, 0x19, 0xc0, 0x6d, 0xaa, 0x35, 0x47, 0xf8, 0x67, 0x65, 0x80, 0x27, 0x71, 0x03, 0x25, 0xf9, 0x1d, 0x98, 0x88, 0x27, 0xe0, 0x4f, 0xe2, 0xa1, 0xf3, 0x87, 0x21, 0x34, 0x08, 0x03, 0xec, 0xbb, 0x5b, 0x0e, 0x23, 0x4f, 0xe5, 0x42, 0xb9, 0xfe, 0xa6, 0xd3, 0x7a, 0x75, 0x49, 0x4a, 0x3f, 0x85, 0x0e, 0xd2, 0x6a, 0xf0, 0x25, 0xfd, 0x36, 0x74, 0x3f, 0x80, 0xdb, 0x1e, 0x35, 0xc3, 0x4b, 0xe7, 0xef, 0xc1, 0xa7, 0xe9, 0x96, 0x8d, 0x6d, 0x9a, 0x00, 0x1e, 0xce, 0xf4, 0xca, 0xab, 0x08, 0xd2, 0xf3, 0xba, 0x3b, 0xc5, 0x80, 0x6e, 0xd4, 0xa0, 0xd0, 0xea, 0x9f, 0x96, 0xea, 0x1d, 0x88, 0x5a, 0x3b, 0x3b, 0x28, 0x06, 0xba, 0xe9, 0xb8, 0x47, 0xd0, 0xd3, 0xcd, 0xec, 0x2b, 0xf7, 0x61, 0x65, 0x4a, 0x91, 0x28, 0xde, 0xde, 0xa7, 0x25, 0x8c, 0x8f, 0x56, 0x2e, 0x4a, 0xe7, 0x23, 0x9f, 0xce, 0xe8, 0x31, 0x0d, 0xbd, 0x6a, 0x0a, 0xb8, 0x20, 0xe4, 0xf7, 0x56, 0xbb, 0x70, 0x58, 0xd8, 0x21, 0x1c, 0x82, 0xbd, 0xd4, 0x14, 0x3d, 0x66, 0x00, 0xda, 0x5f, 0xfa, 0xb7, 0xeb, 0x05, 0xf0, 0x5e, 0xca, 0x03, 0xd6, 0x4a, 0x85, 0x16, 0x30, 0xd2, 0x3b, 0xbc, 0x31, 0xac, 0x41, 0xaf, 0x20, 0x4f, 0x46, 0x04, 0xc9, 0xb3, 0x4d, 0xc5, 0x57, 0xde, 0x7a, 0x00, 0xb3, 0x2f, 0xe7, 0x0b, 0xd8, 0x95, 0xb8, 0x71, 0xf0, 0x4d, 0x83, 0x8d, 0x83, 0x3f, 0xa1, 0x67, 0x91, 0x37, 0xd0, 0xc7, 0xc5, 0xde, 0x5c, 0xcb, 0x6e, 0x4a, 0x81, 0x88, 0x11, 0xda, 0xc8, 0x4b, 0xf8, 0xad, 0x82, 0x3c, 0x67, 0x0d, 0xb7, 0x1a, 0x20, 0x1d, 0x1d, 0x75, 0x50, 0x65, 0x77, 0xe2, 0x06, 0x5f, 0x79, 0xa0, 0xb5, 0xec, 0xdf, 0xc7, 0x86, 0xc2, 0x3f, 0xd1, 0xd1, 0x47, 0x4b, 0x27, 0x2a, 0xbb, 0x1d, 0xe9, 0x4e, 0xf8, 0xde, 0xb2, 0xdb, 0x93, 0xe8, 0xcf, 0x4a, 0x6c, 0x06, 0x2b, 0xba, 0x44, 0xca, 0x59, 0x19, 0xc6, 0xc9, 0xed, 0xc5, 0xa2, 0xeb, 0x85, 0xf0, 0x2f, 0xbc, 0x7b, 0xd4, 0x1e, 0xfa, 0xfa, 0xe1, 0x56, 0xf0, 0xb5, 0xdd, 0x9b, 0x43, 0xcf, 0x66, 0xda, 0xce, 0x4a, 0x0c, 0x09, 0xdb, 0x51, 0xe8, 0x72, 0x03, 0x9c, 0x46, 0x47, 0x24, 0x53, 0x2f, 0xde, 0x83, 0x73, 0x78, 0x95, 0x87, 0xb0, 0x00, 0x3f, 0xf8, 0xfb, 0x2f, 0x22, 0xe0, 0x8d, 0xc7, 0xc3, 0x1c, 0x76, 0x3c, 0x22, 0xa7, 0x77, 0x0a, 0x07, 0xd9, 0x27, 0x65, 0x9e, 0x5d, 0x7e, 0x1b, 0x13, 0x0a, 0xdb, 0x7e, 0xd9, 0x42, 0x0e, 0x62, 0x77, 0x7c, 0x33, 0x6e, 0xdf, 0x9d, 0x7d, 0xf9, 0x3e, 0x64, 0x96, 0x2b, 0x8f, 0x62, 0xc4, 0xe3, 0x6e, 0x71, 0xf2, 0xdb, 0x33, 0xa6, 0xa5, 0xb2, 0x5b, 0xb3, 0x9b, 0x81, 0x06, 0x82, 0x4b, 0x0d, 0xd0, 0x0b, 0x3d, 0xc0, 0x9c, 0x57, 0x29, 0xbe, 0x4b, 0x58, 0x10, 0xfc, 0x07, 0x3d, 0xc2, 0xb4, 0xb7, 0x63, 0x0b, 0xba, 0x86, 0x9c, 0x8a, 0xac, 0x34, 0xbc, 0xb3, 0x12, 0xd1, 0x51, 0x11, 0xf7, 0xdd, 0xd3, 0x17, 0x5b, 0xeb, 0x49, 0x3c, 0xc0, 0xe0, 0xaa, 0x90, 0x57, 0x59, 0x0b, 0x2f, 0xe1, 0x2e, 0xf4, 0x7d, 0x79, 0xca, 0x6e, 0x8a, 0xc6, 0x28, 0x5d, 0x6f, 0x9b, 0x0e, 0x32, 0x77, 0xa9, 0x01, 0x48, 0x86, 0x1c, 0x14, 0x7e, 0xfe, 0xd5, 0xfb, 0xb0, 0x01, 0x67, 0x1a, 0xb8, 0x0a, 0xaa, 0x10, 0x8b, 0xce, 0x1d, 0x2c, 0xc7, 0x2e, 0x62, 0x92, 0x13, 0x8e, 0x62, 0x8a, 0x71, 0x2c, 0x89, 0x43, 0xff, 0x43, 0xb8, 0xdf, 0x9a, 0xd1, 0x8e, 0x43, 0x9a, 0x29, 0xcf, 0xb4, 0x71, 0xfa, 0xae, 0x3a, 0x92, 0x5f, 0xc1, 0xba, 0xcb, 0x1f, 0x34, 0x8e, 0x88, 0x0d, 0x7f, 0x2c, 0x04, 0x56, 0x75, 0x8b, 0x62, 0x87, 0x33, 0x14, 0x15, 0x72, 0x41, 0xc2, 0xe5, 0x06, 0x90, 0xcb, 0x44, 0x2a, 0x5a, 0x8f, 0xc7, 0x09, 0x16, 0x5c, 0x2b, 0x80, 0x3c, 0xd9, 0xec, 0x83, 0x60, 0xe8, 0xc8, 0xc1, 0x74, 0xec, 0x8e, 0xfe, 0x81, 0xdd, 0x52, 0x84, 0x49, 0x67, 0x48, 0x44, 0xaf, 0x23, 0x1e, 0x72, 0xc2, 0x6f, 0x38, 0x10, 0x09, 0x08, 0xc5, 0x6e, 0x2d, 0xed, 0xd9, 0x36, 0x10, 0x89, 0x67, 0x8a, 0xec, 0x0d, 0x95, 0xd8, 0x36, 0xd0, 0x63, 0x2e, 0x2c, 0xbe, 0x51, 0x08, 0x25, 0xd6, 0x63, 0x40, 0x9c, 0x46, 0x88, 0xbf, 0x1f, 0xbc, 0xdd, 0xbe, 0x19, 0xcc, 0xc3, 0x19, 0x92, 0xbb, 0x67, 0x47, 0x6e, 0x35, 0x00, 0xaf, 0x10, 0xbf, 0x5e, 0x7a, 0x50, 0x05, 0x6f, 0xe0, 0x8c, 0x25, 0x19, 0xe7, 0xfd, 0xea, 0x90, 0x14, 0xd1, 0x18, 0x3e, 0xc0, 0x56, 0x66, 0x34, 0x13, 0x1a, 0x82, 0x4e, 0xdc, 0xbe, 0xc5, 0x73, 0x22, 0x14, 0x68, 0x20, 0xff, 0xf1, 0xe9, 0xd6, 0xd0, 0xc1, 0x8e, 0x2d, 0xe5, 0x34, 0x03, 0x7a, 0x13, 0xfb, 0xf8, 0xaf, 0x72, 0x94, 0x93, 0x02, 0xa2, 0xd7, 0x15, 0xbb, 0xca, 0xf7, 0xbb, 0x45, 0xc2, 0x33, 0x78, 0x90, 0xc6, 0x93, 0xc1, 0xa3, 0x06, 0x90, 0x57, 0x8c, 0x66, 0x20, 0x4b, 0xb0, 0xf5, 0x2d, 0xc7, 0x56, 0x88, 0x0e, 0xef, 0x59, 0xd1, 0x20, 0xac, 0xfc, 0x11, 0x3c, 0x45, 0xa4, 0xf5, 0x0c, 0x45, 0x0f, 0x50, 0x1b, 0xd3, 0xeb, 0x0e, 0xe7, 0x1c, 0xea, 0x1f, 0x0d, 0x43, 0x9b, 0x1b, 0xfb, 0x79, 0x93, 0xf3, 0xa3, 0xf8, 0xb4, 0x1f, 0xf2, 0xf1, 0xf9, 0xe3, 0x01, 0xcb, 0x26, 0x03, 0xfe, 0x31, 0x2e, 0x14, 0x96, 0x76, 0x72, 0x6e, 0x26, 0xa6, 0xe6, 0x61, 0x6f, 0xda, 0x6b, 0x06, 0x50, 0x0b, 0x9a, 0x82, 0xad, 0x53, 0xef, 0xac, 0xd8, 0xdb, 0xd8, 0x85, 0x2d, 0xbb, 0x59, 0x28, 0x50, 0x3e, 0x7d, 0xa2, 0x05, 0xfc, 0x01, 0xa7, 0xb1, 0x8e, 0x04, 0x3a, 0x88, 0x9a, 0x80, 0x27, 0x08, 0xb5, 0x8c, 0xec, 0x08, 0x3d, 0x67, 0x71, 0x1a, 0x8c, 0x01, 0xf4, 0x2a, 0xf2, 0x21, 0x3e, 0x31, 0x4f, 0xff, 0xe1, 0x9e, 0x28, 0xa6, 0x31, 0xe3, 0xaf, 0xf8, 0xe4, 0xfa, 0x73, 0x09, 0xf6, 0x8f, 0x5e, 0x1e, 0xac, 0x39, 0xbd, 0x34, 0xa0, 0xf1, 0xb9, 0x43, 0x53, 0xcb, 0xc3, 0xd0, 0x9f, 0xe2, 0xc3, 0x7e, 0x56, 0xca, 0x27, 0x55, 0x36, 0xf8, 0x3b, 0xc0, 0x83, 0xf6, 0xf6, 0x0a, 0xab, 0x06, 0x7d, 0x07, 0x78, 0x45, 0x23, 0x1e, 0x66, 0xea, 0x33, 0x80, 0x87, 0x15, 0xae, 0x66, 0xe7, 0x33, 0x80, 0x5a, 0x23, 0x1e, 0x4e, 0xfb, 0x0c, 0xe0, 0x61, 0x85, 0xab, 0xd9, 0xf9, 0x0c, 0xa0, 0xd6, 0x88, 0x87, 0xd3, 0x0f, 0xbd, 0x01, 0xaa, 0xaa, 0xaa, 0x00, 0x5d, 0x9e, 0x7a, 0x58, 0x6d, 0xae, 0x63, 0xe7, 0x71, 0x03, 0xac, 0x5e, 0xbd, 0xda, 0xe6, 0x90, 0x21, 0xbd, 0x16, 0x18, 0x3b, 0x76, 0xac, 0xdd, 0xb5, 0xc2, 0xe3, 0x48, 0x80, 0x9f, 0x72, 0x01, 0xfc, 0x02, 0x9d, 0xc3, 0xae, 0x87, 0xed, 0x66, 0x6a, 0x45, 0x20, 0xa3, 0x17, 0x14, 0xe8, 0xbb, 0x7e, 0x32, 0x4b, 0xd7, 0xe3, 0x06, 0xa0, 0x2f, 0xf7, 0xcc, 0x9f, 0x3f, 0x1f, 0x26, 0x4c, 0x98, 0x00, 0x1d, 0x3a, 0x74, 0x10, 0x72, 0x56, 0x56, 0x2a, 0x17, 0x70, 0x44, 0x81, 0x41, 0x84, 0x4e, 0xa3, 0xb6, 0x6d, 0xdb, 0x96, 0xfd, 0x28, 0xee, 0xa9, 0xb0, 0x60, 0xc1, 0x02, 0x66, 0xf4, 0xc8, 0xc8, 0x48, 0x78, 0xf1, 0xc5, 0x17, 0x9d, 0x62, 0x6b, 0xda, 0x3b, 0x05, 0x7d, 0x72, 0xf0, 0xc6, 0x8d, 0x1b, 0x50, 0x51, 0x51, 0xc1, 0xce, 0x38, 0x76, 0xec, 0xd8, 0x11, 0x28, 0xef, 0xf2, 0xe5, 0xcb, 0x70, 0xea, 0xd4, 0x29, 0x20, 0x05, 0xa2, 0x0f, 0x68, 0xa0, 0x73, 0x90, 0xcd, 0x9a, 0xe9, 0x7b, 0xad, 0x49, 0x48, 0x48, 0x80, 0x65, 0xcb, 0x96, 0x31, 0xa1, 0xa9, 0x05, 0xa3, 0xcf, 0x68, 0xbb, 0x2b, 0x80, 0xa7, 0xf8, 0x98, 0x2c, 0x31, 0x31, 0x31, 0xb0, 0x73, 0xe7, 0x4e, 0x86, 0xdf, 0xaa, 0x95, 0xbe, 0x2b, 0x20, 0x2d, 0x06, 0xf8, 0xc1, 0x24, 0xb8, 0x74, 0xe9, 0x12, 0xa4, 0xa7, 0xa7, 0xb3, 0xbb, 0x08, 0x7d, 0x5f, 0x43, 0xdf, 0xbe, 0x7d, 0x01, 0xfd, 0x5b, 0xc3, 0x27, 0x9f, 0x7c, 0x02, 0x13, 0x27, 0x4e, 0x84, 0xae, 0x5d, 0xbb, 0x6a, 0xa1, 0x02, 0xf1, 0xe7, 0x81, 0x3e, 0x07, 0xe5, 0x54, 0xd0, 0x38, 0x3a, 0x66, 0x93, 0x95, 0x95, 0x95, 0x25, 0x61, 0x37, 0xa1, 0x38, 0xff, 0x86, 0x4a, 0x56, 0xa4, 0x51, 0x08, 0x45, 0xfa, 0x85, 0x17, 0x5e, 0xb0, 0xa1, 0xa3, 0xce, 0xe0, 0xa7, 0x75, 0x09, 0xd7, 0xc8, 0x35, 0xb5, 0x1c, 0x6f, 0xcb, 0x96, 0x2d, 0x0a, 0x3e, 0x9c, 0xaf, 0x96, 0xeb, 0x6a, 0x39, 0x1e, 0xc5, 0x51, 0x59, 0x12, 0x2a, 0x55, 0xe0, 0xd3, 0x71, 0xe8, 0x2e, 0x5d, 0xba, 0x48, 0x83, 0x07, 0x0f, 0x16, 0x6e, 0xad, 0x39, 0xbd, 0x81, 0x03, 0x07, 0x2a, 0xd0, 0xf7, 0xee, 0xdd, 0x2b, 0xf0, 0x38, 0x8c, 0xd1, 0xf5, 0xe4, 0xc9, 0x93, 0x0a, 0x7c, 0xbd, 0x84, 0xcd, 0x41, 0x43, 0x3d, 0x40, 0xca, 0xe7, 0x47, 0x40, 0x39, 0xe3, 0xf5, 0xeb, 0xd7, 0x2b, 0xc0, 0x6f, 0xde, 0xbc, 0x29, 0x35, 0x69, 0xd2, 0x44, 0x08, 0x1a, 0x17, 0x17, 0xa7, 0x28, 0x57, 0x27, 0x1c, 0x31, 0x80, 0x9c, 0x06, 0x3f, 0x4e, 0x4d, 0xf2, 0x98, 0x31, 0x80, 0xfc, 0x8c, 0x2c, 0xe1, 0xe0, 0xc7, 0xcf, 0x6c, 0xdc, 0x71, 0xe3, 0xe1, 0x5e, 0x69, 0xf1, 0xe2, 0xc5, 0xcc, 0x33, 0xbc, 0x9c, 0x17, 0x1d, 0x4f, 0x3d, 0x74, 0xe8, 0x10, 0xf3, 0xf8, 0x4e, 0x06, 0xe3, 0x3a, 0xe0, 0x9e, 0xdf, 0xc9, 0xfb, 0x3b, 0xf7, 0x00, 0xaf, 0xe5, 0x64, 0x5d, 0x4e, 0x4b, 0x1e, 0x77, 0xd8, 0x00, 0x38, 0x68, 0xca, 0xe9, 0x88, 0xf8, 0xda, 0xb5, 0x6b, 0x85, 0x70, 0x24, 0xa4, 0x91, 0x30, 0x9e, 0x36, 0x00, 0x09, 0x49, 0x9f, 0x55, 0xe3, 0xca, 0xd3, 0xbb, 0x26, 0x26, 0x26, 0x4a, 0x9b, 0x37, 0x6f, 0x16, 0x75, 0x52, 0x47, 0xf8, 0x59, 0x78, 0xc2, 0xaf, 0xcf, 0x73, 0xbd, 0x1a, 0x57, 0x9d, 0x76, 0x78, 0x10, 0xde, 0xb7, 0x6f, 0x1f, 0xc8, 0x3f, 0x8f, 0x8e, 0xc2, 0xb0, 0x35, 0xe0, 0x8f, 0x3e, 0xfa, 0x88, 0xa2, 0x2c, 0xd0, 0x58, 0x30, 0x60, 0xc0, 0x00, 0x9e, 0xf4, 0xfa, 0x15, 0x2b, 0x0f, 0xe3, 0xc7, 0x8f, 0x87, 0x5b, 0xb7, 0x6e, 0x31, 0x59, 0x69, 0x3c, 0x43, 0xe7, 0x03, 0x80, 0x77, 0x06, 0x3b, 0xca, 0xde, 0xba, 0x75, 0x6b, 0x26, 0x23, 0x7e, 0xf7, 0x00, 0x26, 0x4d, 0x9a, 0x04, 0xf3, 0xe6, 0xcd, 0xd3, 0x94, 0x99, 0xc6, 0x09, 0x1e, 0x9c, 0x9e, 0x09, 0xa9, 0x2d, 0x62, 0x94, 0x56, 0x77, 0x41, 0x28, 0x04, 0x6b, 0x4d, 0xf4, 0x31, 0x88, 0xc0, 0xc0, 0x40, 0x45, 0xcb, 0x6a, 0xd3, 0xa6, 0x8d, 0x54, 0x58, 0x58, 0xa8, 0x20, 0xf7, 0xce, 0x3b, 0xef, 0x48, 0x9d, 0x3b, 0x77, 0x96, 0x08, 0x9e, 0x7e, 0x4d, 0x9b, 0x36, 0x15, 0x38, 0xe8, 0xbc, 0x40, 0xe4, 0xb7, 0x6b, 0xd7, 0x4e, 0x7a, 0xf5, 0xd5, 0x57, 0x15, 0xb8, 0x94, 0xd8, 0xbe, 0x7d, 0x3b, 0xfb, 0xbc, 0x00, 0xc7, 0x6f, 0xd1, 0xa2, 0x85, 0xc0, 0xa7, 0x31, 0x8a, 0xe7, 0xd3, 0x97, 0x05, 0x47, 0x8f, 0x1e, 0x2d, 0x65, 0x67, 0x67, 0x2b, 0x68, 0xd0, 0x71, 0x7d, 0x2e, 0x33, 0xf5, 0xff, 0xf4, 0xd1, 0xe5, 0x2b, 0x57, 0xae, 0x48, 0x38, 0x81, 0x90, 0xe8, 0x78, 0x3e, 0x8d, 0x2f, 0xf2, 0xb1, 0x6e, 0xdd, 0xba, 0x75, 0x0a, 0x7c, 0x9e, 0xd8, 0xb8, 0x71, 0xa3, 0xa0, 0x43, 0xf4, 0x88, 0x16, 0x7d, 0xf6, 0x00, 0x27, 0x02, 0x8a, 0x7c, 0x2a, 0x23, 0xfa, 0x46, 0xc1, 0xe1, 0x2e, 0x88, 0x3e, 0x9b, 0x40, 0xae, 0x59, 0x68, 0xb0, 0xed, 0xd1, 0xa3, 0x07, 0xfb, 0x90, 0x05, 0xb6, 0x2e, 0xe6, 0xbe, 0x82, 0x2a, 0xa3, 0x15, 0xa8, 0xcb, 0xe1, 0x9f, 0x6d, 0xe4, 0x8a, 0xd0, 0xba, 0xd2, 0x67, 0x1a, 0x49, 0x81, 0xea, 0x80, 0xb3, 0x13, 0x09, 0x67, 0x3e, 0x36, 0x95, 0x54, 0xd3, 0x20, 0x25, 0xf6, 0xea, 0xd5, 0x4b, 0xe2, 0xfe, 0x13, 0x38, 0x1d, 0x92, 0x97, 0x60, 0xe9, 0x4b, 0x1e, 0xf4, 0xc5, 0x0f, 0x35, 0x1e, 0xa5, 0xd1, 0x59, 0x0a, 0xeb, 0xa6, 0x68, 0xe2, 0x61, 0x14, 0x72, 0x73, 0x73, 0xa5, 0x29, 0x53, 0xa6, 0xd8, 0x34, 0x3c, 0x6a, 0x88, 0xe4, 0xaf, 0x61, 0xd3, 0xa6, 0x4d, 0x46, 0xe8, 0xa2, 0xcc, 0x29, 0x03, 0x08, 0x2a, 0xbe, 0x88, 0xc3, 0x1a, 0x30, 0xb5, 0x20, 0x43, 0x0f, 0x4c, 0x38, 0xc3, 0xc1, 0x06, 0xa2, 0x1d, 0xf0, 0x0e, 0x00, 0xfc, 0x9e, 0x8c, 0x76, 0xa1, 0x2f, 0xd7, 0x50, 0x03, 0xa6, 0x0c, 0xc0, 0x1f, 0xb8, 0xf0, 0x0b, 0x2a, 0x36, 0xc4, 0xe8, 0x69, 0x10, 0xfb, 0x75, 0x5d, 0xff, 0x46, 0x36, 0x08, 0xbe, 0x0c, 0x85, 0x06, 0x4c, 0x19, 0x40, 0x81, 0xe1, 0x4b, 0xb8, 0x54, 0x03, 0x0e, 0x4f, 0x43, 0x5d, 0x2a, 0xc5, 0x23, 0x4c, 0xcc, 0x67, 0x00, 0x2f, 0x1b, 0xdf, 0x67, 0x00, 0x9f, 0x01, 0xbc, 0xac, 0x01, 0x2f, 0xb3, 0xf7, 0xdd, 0x01, 0x5e, 0x36, 0xc0, 0xff, 0x01, 0x60, 0x99, 0xd3, 0x96, 0x0f, 0xf3, 0xae, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXTextIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x32, 0x3a, 0x38, 0x33, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0xd4, 0x6c, 0xf8, 0x31, 0x00, 0x00, 0x05, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x59, 0x5b, 0x2c, 0x6c, 0x57, 0x18, 0x9e, 0x61, 0xce, 0x5c, 0x5c, 0xe2, 0x5a, 0xd7, 0x10, 0x97, 0x53, 0xd7, 0xba, 0x3d, 0xd0, 0x3e, 0x20, 0x2a, 0x82, 0x88, 0x22, 0x1e, 0x08, 0xaa, 0x24, 0x08, 0x21, 0x12, 0x11, 0x91, 0xf4, 0xa9, 0x69, 0x1a, 0xf5, 0x26, 0x22, 0xc1, 0x83, 0x04, 0x91, 0xa6, 0x09, 0x5e, 0x2a, 0x8d, 0x34, 0x91, 0xa0, 0xe1, 0xd4, 0x83, 0x07, 0xd2, 0x70, 0x8a, 0xe8, 0x11, 0xe2, 0x1a, 0x8a, 0xc1, 0x30, 0x86, 0x41, 0xbf, 0xb1, 0xcc, 0xea, 0x9c, 0x33, 0xec, 0xd9, 0x33, 0xb3, 0x9c, 0x46, 0xb2, 0x57, 0x64, 0xe7, 0x5f, 0xff, 0xff, 0xfd, 0xf7, 0xb5, 0xd6, 0xac, 0xbd, 0x89, 0xef, 0xee, 0xee, 0x44, 0x2f, 0x79, 0xd8, 0xbc, 0xe4, 0xe0, 0x75, 0xb1, 0x0b, 0x09, 0xfc, 0xdf, 0x1d, 0x14, 0x3a, 0x20, 0x74, 0xc0, 0xca, 0x0a, 0x08, 0x4b, 0xc8, 0xca, 0x02, 0x5a, 0xad, 0x2e, 0x74, 0xc0, 0xea, 0x12, 0x5a, 0x69, 0x40, 0xe8, 0x80, 0x95, 0x05, 0xb4, 0x5a, 0x5d, 0x62, 0xb5, 0x05, 0x9d, 0x01, 0xad, 0xe8, 0x6e, 0x4a, 0xa5, 0x7c, 0xa7, 0x51, 0xdf, 0x9a, 0xba, 0x1a, 0xda, 0x88, 0xc5, 0xaf, 0x65, 0x8a, 0x24, 0x07, 0x67, 0x89, 0x48, 0xcc, 0xc4, 0x35, 0x9b, 0x04, 0x7e, 0x3d, 0xf9, 0x27, 0xff, 0xdd, 0x9f, 0xfc, 0x03, 0xfa, 0x25, 0x38, 0x26, 0xd7, 0xe9, 0x13, 0xfe, 0x78, 0x0e, 0x24, 0x9b, 0x3d, 0xb0, 0xae, 0x51, 0x73, 0xf8, 0x30, 0x16, 0xad, 0x99, 0x89, 0x37, 0xb6, 0x40, 0x39, 0x6c, 0x3a, 0x70, 0x7e, 0x7b, 0x43, 0x2c, 0xba, 0x49, 0xa4, 0x9e, 0xaf, 0xa4, 0xa0, 0x8f, 0xb5, 0xd7, 0xbb, 0xd7, 0x1a, 0xc2, 0xf4, 0x91, 0xca, 0x9c, 0x6d, 0x5f, 0x81, 0xde, 0xbb, 0xd6, 0x1c, 0x69, 0xaf, 0x41, 0x5c, 0xdc, 0xde, 0x12, 0x91, 0xf5, 0x4f, 0x36, 0x1d, 0x50, 0xeb, 0x03, 0xaa, 0x76, 0xf7, 0x7d, 0x1b, 0xfe, 0x05, 0xfe, 0xbe, 0xf7, 0x09, 0xa6, 0xc1, 0xfd, 0xe0, 0x1d, 0x4c, 0x98, 0x95, 0xee, 0xbe, 0x84, 0xa9, 0xbe, 0x7b, 0x48, 0x98, 0x62, 0x2c, 0x26, 0x18, 0x25, 0x70, 0xf7, 0x50, 0x51, 0x99, 0x0d, 0x97, 0x41, 0xb9, 0xf8, 0x41, 0x7a, 0xa9, 0x4f, 0xd8, 0xe2, 0xb8, 0xa9, 0x22, 0x97, 0x3f, 0x0a, 0x32, 0x49, 0xa8, 0xf5, 0x4b, 0x88, 0x86, 0xf8, 0xa8, 0x8a, 0x42, 0x9f, 0x1e, 0xed, 0xd8, 0xa3, 0x30, 0xb3, 0x98, 0x6c, 0x12, 0x08, 0x95, 0xdb, 0x13, 0xaf, 0x81, 0x32, 0x05, 0x87, 0xfb, 0x68, 0x85, 0x23, 0x91, 0x86, 0xc8, 0xed, 0x38, 0x60, 0x66, 0x89, 0xd8, 0x6c, 0xe2, 0x6f, 0x5c, 0xbd, 0x4f, 0x6e, 0xb4, 0x38, 0xda, 0xbf, 0x72, 0x72, 0xe7, 0x70, 0xff, 0xa5, 0xa3, 0xcb, 0x8f, 0xbe, 0xaf, 0x51, 0xfe, 0xaf, 0x5d, 0xbd, 0x38, 0x60, 0x66, 0x89, 0xd8, 0x24, 0xe0, 0x6a, 0x2b, 0xf9, 0xce, 0x2b, 0xd0, 0xa4, 0x63, 0x85, 0xd8, 0xe6, 0x5b, 0xcf, 0x00, 0x93, 0x30, 0xb3, 0x00, 0x6c, 0x96, 0x90, 0x59, 0x2e, 0xd9, 0x82, 0x85, 0x04, 0xd8, 0xd6, 0xd3, 0x7c, 0x6b, 0x2f, 0xbe, 0x03, 0x6c, 0x36, 0x31, 0x2d, 0xdc, 0xd1, 0x8d, 0xf6, 0xaf, 0x4b, 0x15, 0xa6, 0x7f, 0x6b, 0x2e, 0x28, 0x73, 0x55, 0x73, 0xf1, 0xe6, 0x5c, 0x29, 0x16, 0x89, 0xc3, 0xe5, 0xf6, 0xd8, 0xee, 0x94, 0xcf, 0x84, 0x10, 0x33, 0xfc, 0x36, 0xfa, 0xf3, 0xf1, 0x5e, 0xe9, 0xfa, 0x5b, 0x8e, 0x1b, 0x35, 0xee, 0xd2, 0x3f, 0x05, 0x7c, 0x56, 0xe4, 0xe2, 0xc9, 0x24, 0x74, 0x62, 0x84, 0xe5, 0x12, 0xfa, 0x43, 0x75, 0xc2, 0x11, 0x3d, 0xfc, 0x41, 0xfa, 0x46, 0xa5, 0x64, 0x18, 0x3d, 0x4c, 0xb1, 0x4c, 0xa0, 0xc4, 0xd5, 0x2b, 0x4a, 0xe1, 0xc0, 0x11, 0x1f, 0xa4, 0xc0, 0x70, 0x00, 0x2c, 0x10, 0xb1, 0x5c, 0x42, 0x16, 0xb8, 0xb7, 0x5e, 0x85, 0x65, 0x07, 0xac, 0x8f, 0xc6, 0x02, 0x0b, 0x42, 0x02, 0x16, 0x14, 0x8d, 0xa9, 0x8a, 0xd0, 0x01, 0xa6, 0xe5, 0xb4, 0xc0, 0x98, 0xd0, 0x01, 0x0b, 0x8a, 0xc6, 0x54, 0x85, 0x6f, 0x07, 0xb4, 0x5a, 0xed, 0xe4, 0xe4, 0xe4, 0xee, 0xee, 0x2e, 0x53, 0xef, 0xef, 0x19, 0xdb, 0xd8, 0xd8, 0x98, 0x9a, 0x9a, 0x32, 0xfb, 0x6a, 0x03, 0x05, 0x3e, 0x63, 0x74, 0x74, 0x14, 0xde, 0xb2, 0xb2, 0xb2, 0xf8, 0x80, 0x2d, 0xc3, 0xc4, 0xc7, 0xc7, 0xc3, 0xc5, 0xec, 0xec, 0xac, 0x59, 0xea, 0x7c, 0x3b, 0x70, 0x70, 0x70, 0x00, 0xeb, 0xe4, 0xf9, 0x5e, 0xdd, 0xd8, 0x4d, 0x2c, 0x73, 0xc1, 0x2b, 0x81, 0xa5, 0xa5, 0xa5, 0xe5, 0xe5, 0x65, 0x84, 0x7a, 0x76, 0x76, 0xf6, 0xfb, 0xfd, 0x40, 0x9d, 0xb0, 0xa8, 0x0c, 0x83, 0x3f, 0x3d, 0x3d, 0x6d, 0x6c, 0x6c, 0x8c, 0x89, 0x89, 0x71, 0x70, 0x70, 0x88, 0x8c, 0x8c, 0x6c, 0x6e, 0x6e, 0x3e, 0x3f, 0x3f, 0xa7, 0x80, 0xfa, 0xfa, 0xfa, 0xcf, 0xef, 0x47, 0x45, 0x45, 0x05, 0x0a, 0x5c, 0x54, 0x54, 0x84, 0x59, 0x62, 0x62, 0xe2, 0xe0, 0xe0, 0x20, 0x30, 0x2a, 0x95, 0x6a, 0x7a, 0x7a, 0xfa, 0xf2, 0xf2, 0x12, 0xf4, 0xc2, 0xc2, 0x02, 0x71, 0xb1, 0xb3, 0xb3, 0x43, 0xd5, 0xb9, 0x08, 0x93, 0xfd, 0x9a, 0x9b, 0x9b, 0x7b, 0x54, 0xbf, 0xa5, 0xa5, 0x85, 0xea, 0x62, 0x6f, 0xf8, 0xf9, 0xf9, 0x11, 0x98, 0x9b, 0x9b, 0x1b, 0x21, 0x22, 0x22, 0x22, 0x90, 0x03, 0x30, 0x48, 0xd5, 0xde, 0xfe, 0xe1, 0xbb, 0x8b, 0x4c, 0x26, 0x53, 0x2a, 0x95, 0x36, 0xfa, 0x0f, 0x44, 0x25, 0x25, 0x25, 0x00, 0xe4, 0xe4, 0xe4, 0x18, 0xbb, 0xb0, 0xb3, 0xb3, 0xbb, 0xba, 0xba, 0xa2, 0x2e, 0x9e, 0x22, 0x44, 0x4f, 0x09, 0x28, 0x7f, 0x7f, 0x7f, 0x3f, 0x29, 0x29, 0xc9, 0xd3, 0x53, 0x77, 0x89, 0x87, 0xfb, 0x4f, 0xef, 0x47, 0x6c, 0x6c, 0xec, 0xd8, 0xd8, 0x18, 0xc5, 0xe4, 0xe5, 0xe5, 0x41, 0x9a, 0x96, 0x96, 0xb6, 0xb5, 0xb5, 0x05, 0xe6, 0xfc, 0xfc, 0xbc, 0xaf, 0xaf, 0xee, 0x2b, 0x62, 0x53, 0x53, 0x13, 0xc1, 0xa0, 0x81, 0x65, 0x65, 0x65, 0xe0, 0x24, 0x24, 0x24, 0x1c, 0x1f, 0x1f, 0x87, 0x87, 0x87, 0x4b, 0xa5, 0xd2, 0xde, 0xde, 0xde, 0xa3, 0xa3, 0x23, 0x00, 0x3a, 0x3a, 0x3a, 0xc2, 0xc2, 0xc2, 0x24, 0x12, 0xdd, 0xbb, 0x8e, 0x8f, 0x8f, 0x0f, 0x71, 0x51, 0x58, 0x58, 0x48, 0xed, 0x73, 0x10, 0xa6, 0x13, 0x20, 0xca, 0xfd, 0xfd, 0xfd, 0xb0, 0x8e, 0x7d, 0x66, 0x6c, 0x4b, 0xad, 0x56, 0xdb, 0xda, 0xda, 0x42, 0xda, 0xde, 0xde, 0xfe, 0x9b, 0x7e, 0x60, 0xa9, 0x80, 0x13, 0x1d, 0x1d, 0x4d, 0xf1, 0xe8, 0x43, 0x66, 0x66, 0x26, 0x98, 0x5e, 0x5e, 0xba, 0x1b, 0x75, 0x5f, 0x5f, 0x1f, 0x15, 0x11, 0x22, 0x20, 0x20, 0x00, 0x7c, 0x9c, 0x16, 0x1f, 0xf0, 0xb9, 0xa7, 0x0c, 0x5e, 0xf0, 0x56, 0x57, 0x57, 0x6f, 0x6e, 0x74, 0x1f, 0x6b, 0x1b, 0x1a, 0x1a, 0xf0, 0x34, 0x1c, 0x17, 0x17, 0xff, 0xbd, 0x58, 0x22, 0xc9, 0xa1, 0xa1, 0xa1, 0xc0, 0xc0, 0xc0, 0xbd, 0xbd, 0xbd, 0x94, 0x94, 0x94, 0xf2, 0xf2, 0x72, 0x43, 0xa4, 0xc5, 0x34, 0xdf, 0x04, 0xc8, 0xaa, 0xc5, 0x6e, 0x33, 0xf6, 0x84, 0xa6, 0x13, 0x66, 0x4f, 0x4f, 0x4f, 0x54, 0x54, 0x14, 0x05, 0x20, 0xe2, 0x90, 0x90, 0x10, 0x3a, 0x05, 0xd1, 0xd6, 0xd6, 0x76, 0x78, 0x78, 0x28, 0x97, 0xcb, 0xb1, 0x4d, 0x3b, 0x3b, 0x3b, 0xeb, 0xea, 0xea, 0x0c, 0xa5, 0x1c, 0x2e, 0x0c, 0x61, 0x1f, 0xd2, 0xdc, 0x0d, 0xa2, 0xd2, 0x91, 0x91, 0x11, 0x68, 0x22, 0xa6, 0xb5, 0xb5, 0x35, 0x1c, 0x17, 0xe3, 0xe3, 0xe3, 0xb5, 0xb5, 0xb5, 0x58, 0x57, 0x04, 0x10, 0x1a, 0x1a, 0x0a, 0x69, 0x7a, 0x7a, 0x3a, 0xd9, 0xb5, 0x60, 0x2e, 0x2e, 0x2e, 0x56, 0x57, 0x57, 0xb7, 0xb6, 0xb6, 0x52, 0x0b, 0xdd, 0xdd, 0xdd, 0xc0, 0xe0, 0xe4, 0x81, 0xc8, 0xd9, 0xd9, 0x19, 0xe1, 0x0e, 0x0f, 0x0f, 0x53, 0x29, 0x88, 0xb8, 0xb8, 0x38, 0x00, 0xaa, 0xaa, 0xaa, 0xb0, 0xd8, 0xd6, 0xd7, 0xd7, 0xbb, 0xba, 0xba, 0x4a, 0x4b, 0x4b, 0x91, 0xb0, 0x21, 0xc6, 0x98, 0xe6, 0xbb, 0x07, 0x56, 0x56, 0x56, 0x60, 0x1d, 0x03, 0x8e, 0x15, 0x8a, 0x87, 0x2f, 0xb8, 0x88, 0x98, 0x58, 0xc4, 0x8f, 0xb4, 0x58, 0xac, 0xfb, 0x9f, 0x17, 0x44, 0xa9, 0xa9, 0xa9, 0x64, 0x07, 0x63, 0x5a, 0x50, 0x50, 0x40, 0x00, 0xd8, 0xdf, 0x3a, 0x65, 0x91, 0x28, 0x3f, 0x3f, 0x1f, 0x9b, 0xd8, 0xdb, 0xdb, 0x9b, 0x4c, 0x33, 0x32, 0x32, 0x68, 0x4c, 0xc5, 0xc5, 0xc5, 0x84, 0x49, 0x8f, 0x2c, 0x4c, 0x67, 0x66, 0x66, 0x28, 0xe0, 0x51, 0x82, 0x6f, 0x02, 0x50, 0xc6, 0x59, 0xe1, 0xe8, 0xa8, 0xfb, 0xbc, 0x8c, 0x1c, 0x70, 0x68, 0x54, 0x56, 0x56, 0x6e, 0x6f, 0x6f, 0x53, 0xa3, 0x13, 0x13, 0x13, 0x38, 0x9a, 0x48, 0x04, 0x00, 0xe0, 0x9c, 0xa9, 0xa9, 0xa9, 0x21, 0x87, 0x0c, 0x30, 0xf8, 0x09, 0x27, 0xa2, 0xdc, 0xdc, 0x5c, 0x24, 0xe0, 0xe1, 0xe1, 0x81, 0x29, 0x72, 0xce, 0xce, 0xce, 0xa6, 0x16, 0x50, 0xf5, 0xe4, 0xe4, 0x64, 0x02, 0x73, 0x72, 0x72, 0x42, 0x21, 0x06, 0x06, 0x06, 0xa8, 0xf4, 0x29, 0xc2, 0xbc, 0x77, 0x62, 0x58, 0xd9, 0xdc, 0xdc, 0x74, 0x71, 0x71, 0x21, 0x99, 0x10, 0x67, 0x86, 0x4f, 0xfc, 0x9c, 0xe1, 0x4a, 0x13, 0x14, 0x14, 0x64, 0x58, 0x45, 0x43, 0x80, 0x49, 0x1a, 0xdb, 0x0c, 0xcb, 0xc6, 0xdf, 0xdf, 0x9f, 0xb4, 0xd4, 0x24, 0xde, 0xbc, 0x04, 0x4c, 0x9a, 0xfb, 0xf8, 0x00, 0x5e, 0x57, 0x89, 0x8f, 0x1f, 0x16, 0x7f, 0x8f, 0x42, 0x02, 0xfc, 0x6b, 0xf5, 0x3c, 0x48, 0xa1, 0x03, 0xcf, 0x53, 0x57, 0xfe, 0x56, 0x85, 0x0e, 0xf0, 0xaf, 0xd5, 0xf3, 0x20, 0x85, 0x0e, 0x3c, 0x4f, 0x5d, 0xf9, 0x5b, 0x15, 0x3a, 0xc0, 0xbf, 0x56, 0xcf, 0x83, 0xfc, 0x17, 0xab, 0x70, 0xa9, 0x05, 0xf0, 0x5c, 0xd1, 0x77, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXTextIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0b, 0x9d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x59, 0x6c, 0x14, 0xc9, 0xf5, 0xf9, 0x02, 0xdb, 0xd8, 0x66, 0x8d, 0xd6, 0xf6, 0x2e, 0xc1, 0x46, 0x59, 0x41, 0x62, 0x2c, 0xc2, 0x8d, 0x20, 0x80, 0x04, 0x24, 0x8a, 0x11, 0x88, 0x0f, 0x20, 0xc1, 0x12, 0x20, 0x21, 0x81, 0x58, 0x89, 0x0f, 0x40, 0x80, 0x20, 0x04, 0x14, 0xe0, 0x0b, 0x3e, 0x88, 0xb8, 0x43, 0x40, 0x09, 0x87, 0x10, 0xe7, 0x86, 0x2b, 0x8a, 0x84, 0x20, 0xe6, 0x10, 0x26, 0x2c, 0x87, 0x82, 0x62, 0x90, 0x02, 0xd8, 0x60, 0x0e, 0x1f, 0xd8, 0x6b, 0x7b, 0x85, 0xef, 0x03, 0x7b, 0x3c, 0x9d, 0x7a, 0x65, 0x57, 0x4d, 0xf7, 0x74, 0x4f, 0x4f, 0x77, 0xbb, 0x0f, 0x1f, 0x55, 0x52, 0x4f, 0x55, 0xbd, 0x7a, 0xf7, 0xab, 0xea, 0xee, 0xe9, 0xae, 0xaa, 0x8e, 0x90, 0x48, 0x02, 0x13, 0x29, 0xd2, 0x04, 0x2e, 0x45, 0x8d, 0x0e, 0x26, 0xb8, 0x93, 0xfd, 0x9a, 0x82, 0x06, 0x26, 0x47, 0x43, 0x44, 0x54, 0x04, 0x4c, 0x3f, 0xff, 0x53, 0x25, 0x0a, 0xaa, 0x14, 0x9c, 0xca, 0xff, 0x55, 0x47, 0x41, 0xb7, 0x7f, 0x53, 0x28, 0xe1, 0x21, 0x4f, 0x61, 0x55, 0xfa, 0x75, 0xee, 0xcf, 0x14, 0x12, 0xc2, 0x12, 0x54, 0x7f, 0xdf, 0xa8, 0x20, 0x88, 0x40, 0x71, 0x0a, 0x48, 0x98, 0x4a, 0x58, 0x09, 0xc1, 0xf4, 0x86, 0x08, 0x9e, 0x3f, 0x7f, 0xce, 0xe9, 0x34, 0x09, 0x16, 0x2e, 0x5c, 0x48, 0x11, 0x22, 0x22, 0x22, 0x38, 0x22, 0x2b, 0x38, 0x6f, 0x83, 0xf3, 0x12, 0x14, 0x46, 0x63, 0x3f, 0x62, 0x07, 0x1a, 0x99, 0xff, 0xfb, 0x32, 0x66, 0x6b, 0x20, 0x97, 0xf7, 0x13, 0x2c, 0xb3, 0xbe, 0xa3, 0xd5, 0x8f, 0xb0, 0x5d, 0x21, 0x21, 0xc0, 0x06, 0x00, 0xfb, 0x50, 0xc2, 0xf0, 0x81, 0x72, 0x10, 0x2d, 0x87, 0x24, 0xc0, 0xd6, 0xc6, 0xe2, 0xcf, 0x2a, 0x02, 0x17, 0xbd, 0xb4, 0x71, 0xe3, 0x46, 0x95, 0x78, 0x04, 0xd4, 0xd7, 0xd7, 0x2b, 0xe0, 0x2a, 0x1b, 0xb0, 0xff, 0xb0, 0xbe, 0xa4, 0xc0, 0xec, 0xaa, 0x38, 0x6f, 0x83, 0x69, 0x09, 0x5a, 0x6a, 0xea, 0xc1, 0x54, 0x36, 0xeb, 0x21, 0x5b, 0x69, 0x33, 0x24, 0x00, 0xfb, 0x6f, 0x45, 0xae, 0x32, 0x1a, 0x08, 0x7b, 0xf2, 0x6d, 0x71, 0x58, 0x99, 0x86, 0x04, 0x84, 0xe5, 0xa2, 0x83, 0xd0, 0x73, 0x05, 0xe0, 0x60, 0xf4, 0x35, 0xf9, 0xe9, 0xf0, 0xd7, 0x31, 0x00, 0xfa, 0x68, 0x2f, 0xc2, 0xe1, 0xc9, 0x0e, 0x34, 0x1f, 0xcb, 0xef, 0xdf, 0xbf, 0xa7, 0x39, 0xd6, 0xa3, 0xa3, 0x3b, 0x2f, 0xba, 0x03, 0x07, 0x76, 0x9e, 0xaf, 0x18, 0x2e, 0xe6, 0xaa, 0x84, 0x67, 0xbd, 0xe0, 0xd4, 0xda, 0xda, 0x8a, 0x97, 0x3b, 0x7a, 0x60, 0xdb, 0xe0, 0xc1, 0x83, 0x15, 0x75, 0x6c, 0xc3, 0xc4, 0x72, 0x56, 0x96, 0xd7, 0x29, 0x02, 0xf9, 0xe9, 0xfd, 0x31, 0xe8, 0xfd, 0x16, 0xf4, 0xdc, 0x91, 0x4c, 0x7a, 0x8c, 0xa1, 0xa4, 0xba, 0x8b, 0x64, 0x54, 0x78, 0xb6, 0xd4, 0x4a, 0xec, 0x9e, 0xef, 0xfb, 0xa5, 0xef, 0xd5, 0x77, 0x98, 0x5a, 0x04, 0xac, 0xbf, 0x86, 0xca, 0x0b, 0x8f, 0x54, 0xf1, 0xbb, 0x07, 0x39, 0x0e, 0xbb, 0xa3, 0x90, 0xc3, 0xb4, 0xca, 0x96, 0x62, 0xc0, 0xac, 0x63, 0xb9, 0x96, 0xe2, 0x0c, 0x66, 0x49, 0x00, 0x73, 0x13, 0xcb, 0x19, 0x33, 0xad, 0xdc, 0x92, 0x00, 0x2d, 0x46, 0xa1, 0x60, 0x3d, 0x5b, 0xc0, 0x9b, 0xbf, 0x56, 0x43, 0x7b, 0x5d, 0x47, 0x28, 0xe5, 0x29, 0xdc, 0xb2, 0x05, 0x3f, 0x5f, 0x93, 0x0a, 0x25, 0x97, 0x6b, 0xa0, 0xb5, 0xda, 0xa7, 0x2b, 0x40, 0x9c, 0x8b, 0x74, 0xdd, 0x83, 0x8d, 0x96, 0x63, 0x10, 0x96, 0x73, 0x17, 0x82, 0xa6, 0x00, 0xcd, 0x6b, 0xab, 0x0e, 0x47, 0x3d, 0x7c, 0xd5, 0xc9, 0x8e, 0x21, 0x63, 0x4e, 0xce, 0x2d, 0xf0, 0xe8, 0xd1, 0x23, 0x38, 0x78, 0xf0, 0x20, 0x24, 0x25, 0x25, 0xc1, 0x90, 0x21, 0x43, 0x60, 0xdc, 0xb8, 0x71, 0xb0, 0x64, 0xc9, 0x12, 0x88, 0x89, 0x89, 0x81, 0x93, 0x27, 0x4f, 0xc2, 0xd1, 0xa3, 0x47, 0xa9, 0x68, 0x86, 0xaf, 0xd2, 0x43, 0xeb, 0x04, 0x45, 0x90, 0x38, 0x18, 0xcb, 0x0b, 0x16, 0x2c, 0xa0, 0xc7, 0xce, 0x9d, 0x3b, 0x29, 0x3c, 0x27, 0x27, 0x47, 0x4a, 0x4b, 0x4b, 0x53, 0xe0, 0xf0, 0x4a, 0x50, 0x41, 0x65, 0x01, 0xd3, 0x00, 0x35, 0x9a, 0x37, 0x6f, 0x1e, 0xb5, 0x82, 0x59, 0x35, 0x74, 0xe8, 0x50, 0xc8, 0xcc, 0xcc, 0x84, 0xc2, 0xc2, 0x42, 0x8a, 0x26, 0xd7, 0x1a, 0xcb, 0x89, 0x89, 0x89, 0xaa, 0xbf, 0x2c, 0x62, 0x1c, 0x30, 0x8f, 0x86, 0xcc, 0x35, 0xbb, 0x69, 0x48, 0x6c, 0x0b, 0x0d, 0x8e, 0xc7, 0xc0, 0x82, 0x4e, 0xa6, 0x48, 0x1c, 0xf7, 0x90, 0x29, 0x6d, 0x2c, 0x20, 0x0b, 0x03, 0x2c, 0x38, 0xcd, 0x56, 0x92, 0x90, 0x27, 0x3a, 0x3d, 0x29, 0x46, 0xee, 0xe6, 0x18, 0x3d, 0x3e, 0x25, 0x9d, 0xf1, 0xdd, 0x37, 0xac, 0xca, 0xff, 0xf2, 0xe2, 0xb3, 0xa8, 0x29, 0x7f, 0x1b, 0xce, 0xe1, 0x56, 0x0b, 0xb6, 0x0c, 0xe2, 0xd7, 0x7f, 0xa9, 0x86, 0xd2, 0x7f, 0xd4, 0x50, 0x1d, 0xb2, 0x36, 0x7d, 0x05, 0x5f, 0x67, 0x27, 0xa9, 0xf4, 0xd1, 0x33, 0xda, 0xc8, 0xdd, 0xa7, 0x8a, 0x61, 0x17, 0xc0, 0x52, 0x04, 0x42, 0x31, 0xd3, 0x83, 0x8f, 0xde, 0xf6, 0x35, 0x6d, 0xfe, 0xdf, 0xee, 0x0a, 0x9a, 0xc7, 0xa6, 0xc4, 0xc0, 0x88, 0x6f, 0xbf, 0xd4, 0x23, 0x31, 0xd4, 0xe6, 0x9a, 0x01, 0x69, 0xb3, 0x12, 0x15, 0x06, 0x44, 0xc7, 0x47, 0x02, 0x83, 0x19, 0xd2, 0x34, 0x04, 0x92, 0x38, 0x0b, 0x85, 0x70, 0x4c, 0x58, 0x70, 0xeb, 0x8f, 0xfa, 0x77, 0xfb, 0x61, 0x19, 0x74, 0x21, 0xb8, 0x1e, 0x81, 0xf8, 0x9f, 0x0c, 0xa0, 0xa2, 0x7d, 0x4d, 0x1d, 0xfc, 0x19, 0x7c, 0x7d, 0x41, 0xab, 0x51, 0x7d, 0x55, 0x78, 0xb6, 0x9c, 0x85, 0x54, 0x5c, 0x5d, 0x04, 0xb8, 0x1e, 0x01, 0xbb, 0x6d, 0x13, 0x06, 0xd8, 0xed, 0x51, 0xb3, 0xfc, 0x0c, 0x47, 0x20, 0x2f, 0x2f, 0x8f, 0xfe, 0xd3, 0x33, 0x2b, 0xc0, 0x0c, 0xbe, 0xfc, 0x3f, 0x98, 0x61, 0xba, 0xa0, 0x3f, 0x81, 0x9a, 0xd5, 0x5b, 0xb7, 0x6e, 0x49, 0x33, 0x67, 0xce, 0xa4, 0x8f, 0x8e, 0xf7, 0xed, 0xdb, 0x27, 0xe1, 0x71, 0xff, 0xfe, 0x7d, 0x05, 0xee, 0xb3, 0x67, 0xcf, 0xf8, 0xe3, 0x66, 0x22, 0x5c, 0x3a, 0x7c, 0xf8, 0x30, 0x6f, 0x9f, 0x32, 0x65, 0x0a, 0x6f, 0x43, 0x20, 0xb6, 0xe3, 0x31, 0x71, 0xe2, 0x44, 0x8a, 0x53, 0x52, 0x52, 0x22, 0xed, 0xde, 0xbd, 0x9b, 0xc2, 0x36, 0x6d, 0xda, 0x44, 0xf9, 0x9f, 0x39, 0x73, 0x86, 0xd3, 0xeb, 0x15, 0x02, 0xff, 0x7f, 0xf5, 0xb0, 0x48, 0xdb, 0x86, 0x0d, 0x1b, 0xa8, 0x00, 0x2d, 0x34, 0xa6, 0xd0, 0xe9, 0xd3, 0xa7, 0x25, 0x76, 0x44, 0x46, 0x46, 0xaa, 0xf0, 0x11, 0x2f, 0x2a, 0x2a, 0x4a, 0x05, 0x47, 0x9e, 0x75, 0x75, 0x75, 0x14, 0x8e, 0x8e, 0x30, 0x93, 0x0c, 0x1b, 0xb0, 0x63, 0xc7, 0x0e, 0x2a, 0xe0, 0xc3, 0x87, 0x0f, 0x94, 0xff, 0x9e, 0x3d, 0x7b, 0xa4, 0x15, 0x2b, 0x56, 0xd0, 0xf2, 0xf4, 0xe9, 0xd3, 0x69, 0xdb, 0xcb, 0x97, 0x2f, 0xb9, 0xec, 0xf3, 0xe7, 0xcf, 0x73, 0x0f, 0x23, 0x70, 0xd9, 0xb2, 0x65, 0x5c, 0x71, 0x34, 0x64, 0xd8, 0xb0, 0x61, 0x1c, 0x17, 0x0b, 0x3e, 0x9f, 0x8f, 0xb6, 0x93, 0x37, 0x90, 0x14, 0x7e, 0xfd, 0xfa, 0x75, 0x05, 0xbd, 0x02, 0x59, 0x56, 0x31, 0x7d, 0x1d, 0x78, 0xf3, 0xe6, 0x0d, 0x91, 0x0f, 0x30, 0x72, 0xe4, 0x48, 0x9a, 0xcb, 0x7f, 0x08, 0x5f, 0xb8, 0x7d, 0xfb, 0x36, 0x90, 0x87, 0x16, 0x30, 0x66, 0xcc, 0x18, 0x79, 0x93, 0xe1, 0x72, 0x4d, 0x4d, 0x0d, 0x10, 0x27, 0xc1, 0xf8, 0xf1, 0xe3, 0x0d, 0xd1, 0x98, 0x36, 0xc0, 0x10, 0x57, 0x17, 0x91, 0x0c, 0x9f, 0x85, 0x5c, 0xd4, 0xc9, 0x94, 0x28, 0x61, 0x80, 0x29, 0x77, 0x39, 0x80, 0x2c, 0xc6, 0x80, 0x03, 0x4e, 0x35, 0xc5, 0x52, 0x8c, 0x01, 0x53, 0xee, 0x72, 0x00, 0x59, 0x44, 0xc0, 0x01, 0xa7, 0x9a, 0x62, 0x29, 0x22, 0x60, 0xca, 0x5d, 0x0e, 0x20, 0x9b, 0x7e, 0xb0, 0xe5, 0x6b, 0xf0, 0x43, 0xde, 0x6f, 0x8b, 0x0c, 0xab, 0x22, 0x7f, 0x6c, 0xf8, 0xfc, 0x8f, 0xe5, 0xd0, 0xf8, 0xae, 0x73, 0xba, 0x9d, 0x6a, 0x46, 0xa9, 0x61, 0x8e, 0x4a, 0x44, 0xd3, 0x06, 0x28, 0xc9, 0xcd, 0xd5, 0xda, 0xea, 0x7c, 0xd0, 0xfa, 0x63, 0xbb, 0x39, 0xa2, 0x30, 0xd8, 0xa6, 0x0d, 0x88, 0x4e, 0x8c, 0xa4, 0x33, 0x1d, 0xe5, 0x7c, 0xe5, 0x0f, 0x6e, 0xe5, 0x1e, 0x97, 0xe3, 0x38, 0x55, 0x36, 0x6d, 0x80, 0x15, 0x45, 0x8a, 0xff, 0x5e, 0x03, 0x45, 0xc7, 0xab, 0x15, 0xa4, 0x76, 0x19, 0xdd, 0xeb, 0xcf, 0x42, 0xae, 0x44, 0x20, 0xe5, 0x97, 0x09, 0x10, 0x9b, 0x1a, 0x0d, 0x05, 0xfb, 0x2b, 0xc1, 0xd7, 0xe2, 0xa7, 0x91, 0x60, 0x8f, 0xdb, 0x15, 0x61, 0xb1, 0x50, 0x71, 0xc5, 0x80, 0xf8, 0xf4, 0x18, 0xc0, 0xa3, 0xf0, 0xcf, 0x55, 0x00, 0x2d, 0x9d, 0x5a, 0xda, 0xf1, 0x68, 0x1d, 0x39, 0xf5, 0xfa, 0x2e, 0x24, 0x0c, 0xb0, 0xd0, 0x6d, 0x6d, 0x25, 0x11, 0x11, 0x30, 0xe3, 0xce, 0xc8, 0x18, 0x8d, 0x69, 0xb2, 0x66, 0x18, 0x68, 0xe0, 0xba, 0x1a, 0x81, 0x2f, 0x7e, 0x11, 0xc7, 0x55, 0xc8, 0x5b, 0x50, 0x04, 0x3f, 0xdc, 0xaa, 0x87, 0xca, 0x7b, 0x0d, 0x1c, 0x66, 0xa5, 0xe0, 0xaa, 0x01, 0x59, 0x9b, 0xbf, 0xe2, 0x3a, 0xfa, 0x9a, 0xfd, 0xf0, 0xe2, 0x4f, 0x3f, 0x00, 0x7b, 0xed, 0xca, 0x1b, 0x4c, 0x16, 0x5c, 0x35, 0x00, 0xbb, 0xd0, 0xaf, 0xae, 0x8f, 0x84, 0xc1, 0xa3, 0xe2, 0xe8, 0x1a, 0x27, 0xd4, 0xb5, 0xbb, 0xdd, 0x4a, 0x3c, 0x56, 0x31, 0x19, 0x71, 0xdb, 0xd1, 0x5d, 0xed, 0x42, 0xb6, 0x6b, 0x8f, 0x5d, 0xd0, 0x09, 0xa6, 0x6e, 0xf2, 0x14, 0x06, 0xb8, 0xe9, 0x6d, 0x2d, 0x59, 0x22, 0x02, 0x5a, 0x5e, 0x71, 0x13, 0x66, 0x38, 0x02, 0x71, 0x71, 0x71, 0x70, 0xf6, 0xec, 0x59, 0xc7, 0x74, 0x9b, 0x3b, 0x77, 0x2e, 0x64, 0x65, 0x65, 0x99, 0xe7, 0x2f, 0x7b, 0xe1, 0xa7, 0x5b, 0x24, 0x9c, 0xe9, 0x0c, 0x64, 0x5d, 0xa4, 0x6e, 0x34, 0x22, 0x7f, 0x3c, 0xcc, 0xa6, 0xb0, 0x11, 0xe8, 0xe8, 0xe8, 0x80, 0x03, 0x07, 0x0e, 0x50, 0xcf, 0x14, 0x15, 0x15, 0xc1, 0xfe, 0xfd, 0xfb, 0xe1, 0xd0, 0xa1, 0x43, 0xb4, 0x2e, 0xff, 0x21, 0x2f, 0xc2, 0xf9, 0x42, 0xae, 0xe4, 0xe4, 0x64, 0x40, 0x3a, 0x96, 0xf0, 0x0d, 0x3c, 0x1e, 0x63, 0xc7, 0x8e, 0x85, 0x63, 0xc7, 0x8e, 0x71, 0x3c, 0xd6, 0x7e, 0xea, 0xd4, 0x29, 0x56, 0xa4, 0xfc, 0x51, 0x46, 0x65, 0x65, 0x25, 0x87, 0xe9, 0x16, 0xc2, 0x59, 0x9c, 0x9b, 0x9b, 0x4b, 0x3d, 0x43, 0x98, 0x28, 0xf2, 0xa7, 0x4f, 0x9f, 0x72, 0x52, 0xb2, 0xca, 0x8c, 0xb6, 0xe1, 0xbb, 0xe1, 0xcb, 0x97, 0x2f, 0xab, 0x5e, 0x66, 0x93, 0x05, 0xa4, 0xb4, 0x7d, 0xed, 0xda, 0xb5, 0x52, 0x7e, 0x7e, 0x3e, 0x2d, 0x2f, 0x5a, 0xb4, 0x88, 0xd3, 0x07, 0xf3, 0xc6, 0xfa, 0xfc, 0xf9, 0xf3, 0x79, 0xbb, 0x5e, 0xc1, 0x70, 0xcc, 0x90, 0x29, 0x4e, 0x64, 0x0f, 0x4e, 0xe4, 0xbd, 0x2e, 0x55, 0x08, 0x27, 0xb7, 0xb3, 0xb7, 0xf4, 0x24, 0x42, 0x14, 0x46, 0xde, 0x19, 0x73, 0x74, 0x9c, 0x9e, 0xc0, 0x14, 0x25, 0xb3, 0xea, 0x39, 0x9c, 0x15, 0x58, 0x1b, 0xab, 0x1b, 0xcd, 0x0d, 0xdf, 0xcc, 0x61, 0x17, 0x20, 0x06, 0xc0, 0xb5, 0x6b, 0xd7, 0x88, 0xac, 0x40, 0x7a, 0xf8, 0xf0, 0x21, 0x90, 0x37, 0xf5, 0x30, 0x67, 0xce, 0x1c, 0xc0, 0x81, 0xce, 0x12, 0x2e, 0x01, 0x38, 0x71, 0xe2, 0x04, 0x9d, 0x34, 0x8f, 0x30, 0xec, 0x52, 0x6c, 0x3d, 0xe0, 0xab, 0x57, 0xaf, 0xe8, 0x44, 0x7b, 0x86, 0x8b, 0x39, 0xf2, 0xc7, 0x44, 0x14, 0xa7, 0xb9, 0xe1, 0x1f, 0xa3, 0x96, 0x22, 0x6f, 0x32, 0x83, 0x9f, 0xa2, 0x17, 0x14, 0x14, 0x48, 0x93, 0x27, 0x4f, 0xe6, 0xa4, 0xd8, 0x16, 0x1b, 0x1b, 0xcb, 0xeb, 0x0d, 0x0d, 0x0d, 0xb4, 0xbd, 0xad, 0xad, 0x8d, 0xc2, 0x88, 0xf2, 0xd4, 0xfb, 0xcb, 0x97, 0x2f, 0x97, 0xd6, 0xac, 0x59, 0x43, 0xcb, 0xef, 0xde, 0xbd, 0xe3, 0xf8, 0x58, 0x20, 0x06, 0x50, 0x38, 0x4e, 0x39, 0x40, 0x7a, 0x9c, 0x9a, 0x70, 0xef, 0xde, 0x3d, 0x05, 0x8e, 0x56, 0xc5, 0x70, 0x17, 0xca, 0xc8, 0xc8, 0xa0, 0x02, 0x50, 0x59, 0x76, 0xf8, 0xfd, 0x7e, 0xca, 0xf3, 0xca, 0x95, 0x2b, 0x1c, 0xc6, 0xda, 0x30, 0x67, 0x89, 0xc1, 0xa6, 0x4e, 0x9d, 0x2a, 0x91, 0x65, 0x0f, 0x1c, 0xf7, 0xc2, 0x85, 0x0b, 0x0c, 0x45, 0xc2, 0x6e, 0xc5, 0xf0, 0x58, 0x8e, 0xb3, 0x64, 0xc2, 0xa5, 0x80, 0x94, 0x70, 0x98, 0xa4, 0x1d, 0xbd, 0xf3, 0xe0, 0xc1, 0x03, 0xa9, 0xa9, 0xa9, 0x49, 0x13, 0xbb, 0xb8, 0xb8, 0x58, 0xc2, 0x41, 0x8f, 0x33, 0x4f, 0xac, 0xa6, 0xc7, 0x8f, 0x1f, 0x4b, 0xe5, 0xe5, 0xe5, 0x86, 0xc9, 0x0d, 0x8f, 0x01, 0xe2, 0x95, 0x1e, 0x99, 0xc2, 0x5e, 0x07, 0x7a, 0xa4, 0xd6, 0x32, 0xa5, 0x84, 0x01, 0x32, 0x67, 0x78, 0x52, 0x14, 0x11, 0xf0, 0xc4, 0xed, 0x32, 0xa1, 0x22, 0x02, 0x32, 0x67, 0x78, 0x52, 0xec, 0xf5, 0xd7, 0x01, 0x4f, 0xbc, 0x66, 0xa3, 0xd0, 0x5e, 0x3f, 0x04, 0x6c, 0xf4, 0x85, 0x27, 0xac, 0x44, 0x00, 0x3c, 0x71, 0x7b, 0x40, 0xa8, 0x08, 0x40, 0xc0, 0x17, 0x9e, 0x94, 0x44, 0x00, 0x3c, 0x71, 0x7b, 0x40, 0xa8, 0x08, 0x40, 0xc0, 0x17, 0x9e, 0x94, 0x44, 0x00, 0x3c, 0x71, 0x7b, 0x40, 0xa8, 0x2b, 0x13, 0x85, 0x70, 0x8a, 0xe3, 0xfd, 0xdf, 0xbd, 0x0d, 0x48, 0xb5, 0xb1, 0x34, 0x60, 0x48, 0x14, 0xcc, 0xb8, 0xf0, 0x8d, 0x8d, 0x1c, 0xdd, 0x65, 0xe5, 0x4a, 0x00, 0xd0, 0x24, 0xf2, 0x84, 0xca, 0x19, 0xcb, 0x3a, 0x67, 0xae, 0x69, 0xf2, 0xfe, 0xcf, 0xda, 0x12, 0xa8, 0x2f, 0x54, 0xae, 0x90, 0xb5, 0x6b, 0x0d, 0xbf, 0xa6, 0x40, 0x0b, 0x40, 0xd7, 0x02, 0x10, 0x6e, 0x2a, 0x84, 0x44, 0x5e, 0x83, 0x48, 0x7e, 0x75, 0x90, 0x22, 0xc9, 0x76, 0xb1, 0x7a, 0x2f, 0xb1, 0x23, 0x1c, 0x98, 0xb9, 0x63, 0xc1, 0x8f, 0x96, 0x49, 0x5c, 0x09, 0x00, 0xce, 0xf1, 0x9d, 0x4d, 0xe6, 0xa6, 0xe8, 0x25, 0xf9, 0x86, 0x0e, 0x72, 0xbc, 0xcc, 0x0d, 0x69, 0x9a, 0x9b, 0x3b, 0xc8, 0x71, 0x58, 0xd9, 0x4f, 0xf6, 0x10, 0xf4, 0xb5, 0x06, 0x86, 0x04, 0x06, 0x35, 0x38, 0xf9, 0xc9, 0xf3, 0xfd, 0xb6, 0x5a, 0x75, 0xc3, 0x80, 0x2f, 0xa2, 0x82, 0x51, 0x5d, 0xa9, 0xbb, 0x12, 0x00, 0x57, 0x2c, 0x21, 0x42, 0x4a, 0xff, 0x59, 0xab, 0x9a, 0xd7, 0x1c, 0x2c, 0xbb, 0xb9, 0xac, 0x0d, 0xfe, 0x9d, 0xa3, 0xbe, 0x1e, 0xb9, 0x3d, 0xd1, 0x9b, 0xe9, 0x25, 0xee, 0x82, 0x98, 0x27, 0x3c, 0xca, 0xfb, 0xd4, 0x08, 0x18, 0x9e, 0x93, 0x0c, 0x78, 0xb0, 0x84, 0x77, 0x5e, 0xed, 0xf5, 0xca, 0xd3, 0x4d, 0x4f, 0xbb, 0x08, 0x8b, 0x11, 0xc0, 0xa2, 0xe5, 0x51, 0x2e, 0x02, 0xe0, 0x91, 0xe3, 0x99, 0x58, 0x11, 0x00, 0xe6, 0x09, 0x8f, 0x72, 0x11, 0x00, 0x8f, 0x1c, 0xcf, 0xc4, 0xf6, 0xbb, 0x00, 0x38, 0xf6, 0x8f, 0x9c, 0x79, 0xd4, 0x64, 0xde, 0xa7, 0x03, 0xc0, 0x36, 0xef, 0x92, 0xfb, 0xa4, 0xa9, 0xa4, 0x0d, 0x3e, 0xfd, 0xb7, 0x59, 0x0e, 0xf2, 0xb4, 0xdc, 0xa7, 0x6e, 0x43, 0x83, 0x3d, 0x99, 0x3a, 0x23, 0x01, 0xea, 0x5e, 0x75, 0x2d, 0x13, 0x94, 0x35, 0xe6, 0xff, 0xa1, 0x4c, 0x56, 0xeb, 0x2c, 0x4e, 0x3e, 0x94, 0x01, 0x49, 0x99, 0xb1, 0x2a, 0xb8, 0xd3, 0x80, 0x3e, 0x3d, 0x02, 0x32, 0x16, 0x27, 0xc3, 0xc4, 0x7d, 0xe9, 0x10, 0xfb, 0x65, 0x8c, 0xd3, 0x7e, 0xb4, 0xcc, 0xbf, 0x7f, 0xce, 0x8a, 0x20, 0xcf, 0xfc, 0xe8, 0xf3, 0x20, 0x92, 0x47, 0xc5, 0x46, 0x40, 0x14, 0xd9, 0xa2, 0xd2, 0xab, 0xd4, 0x3f, 0x03, 0xe0, 0x95, 0xb7, 0x35, 0xe4, 0x7a, 0x17, 0x7a, 0x0d, 0x65, 0xfa, 0x23, 0x48, 0x04, 0xc0, 0xe3, 0xa8, 0x8b, 0x00, 0x88, 0x00, 0x78, 0xec, 0x01, 0x8f, 0xc5, 0x8b, 0x11, 0x20, 0x02, 0xe0, 0xb1, 0x07, 0x3c, 0x16, 0xef, 0xc8, 0x08, 0x68, 0x6e, 0x6e, 0x86, 0xda, 0xda, 0x5a, 0x8f, 0x4d, 0xeb, 0x9e, 0xf8, 0x8a, 0x8a, 0x0a, 0xe7, 0x66, 0x72, 0xc8, 0x54, 0xb3, 0x3d, 0x00, 0xdb, 0xb6, 0x6d, 0x83, 0x41, 0x83, 0x06, 0x01, 0xae, 0xf8, 0x5c, 0xba, 0x74, 0xa9, 0x4c, 0x54, 0xef, 0x28, 0xb6, 0xb4, 0xb4, 0x00, 0xd9, 0xb9, 0x18, 0xf0, 0xbb, 0x3d, 0x64, 0x67, 0x63, 0x78, 0xfd, 0x5a, 0xfb, 0x13, 0x6c, 0x76, 0x59, 0x63, 0xfb, 0xb3, 0x20, 0xf2, 0x79, 0x43, 0xae, 0x1b, 0x1a, 0x63, 0x35, 0x7d, 0xfc, 0xf8, 0x11, 0xc8, 0x92, 0x46, 0xea, 0x80, 0x84, 0x84, 0x04, 0x18, 0x35, 0x6a, 0x14, 0x5d, 0x10, 0x1d, 0x1f, 0x1f, 0x1f, 0x92, 0xe5, 0xdb, 0xb7, 0x6f, 0x81, 0x2c, 0xc2, 0xa3, 0xed, 0xe8, 0x44, 0xfc, 0xf0, 0x11, 0xe6, 0x98, 0xca, 0xca, 0xca, 0xe0, 0xd3, 0xa7, 0x4f, 0xb4, 0x8c, 0x3c, 0x46, 0x8c, 0x18, 0x41, 0xcb, 0xc1, 0x3f, 0xed, 0xed, 0xed, 0x40, 0x56, 0x20, 0x72, 0x70, 0x77, 0x6c, 0xe0, 0x4c, 0xf4, 0x0a, 0x86, 0x97, 0xf4, 0xe9, 0x20, 0xde, 0xb8, 0x71, 0x03, 0x27, 0xf4, 0x18, 0x3e, 0x9e, 0x3c, 0x79, 0xa2, 0xc9, 0x8d, 0xac, 0xab, 0xe6, 0x4b, 0x8d, 0xf5, 0xf8, 0xcd, 0x9e, 0x3d, 0x5b, 0x22, 0x8e, 0xe2, 0x3c, 0xf0, 0x9b, 0x96, 0x64, 0xa1, 0xac, 0x4a, 0x3e, 0x2e, 0x63, 0xfe, 0xfc, 0xf9, 0xb3, 0xb4, 0x7a, 0xf5, 0x6a, 0x55, 0x1b, 0xf9, 0x80, 0xa6, 0x44, 0x9c, 0xcb, 0x79, 0xc8, 0x37, 0x1f, 0xd7, 0x93, 0x8d, 0x6d, 0x64, 0x75, 0x3c, 0xa7, 0xeb, 0x6e, 0xc1, 0x96, 0x47, 0x11, 0xd8, 0x63, 0x70, 0xeb, 0x78, 0xfc, 0x8a, 0x34, 0xae, 0x26, 0x26, 0xbb, 0xae, 0x13, 0x3d, 0x01, 0x46, 0x8f, 0x1e, 0x0d, 0x2b, 0x57, 0xae, 0xa4, 0x65, 0xfc, 0xc1, 0xde, 0x48, 0x76, 0x53, 0xa7, 0xab, 0x8b, 0x39, 0xb0, 0xab, 0x80, 0xb8, 0x2f, 0x5e, 0xbc, 0xa0, 0x35, 0x5c, 0x79, 0x7c, 0xf3, 0xe6, 0xcd, 0x60, 0x14, 0x58, 0xb7, 0x6e, 0x1d, 0x90, 0x1d, 0xdb, 0x29, 0x1c, 0x57, 0x17, 0xe3, 0x16, 0xda, 0xe4, 0x03, 0xa2, 0x0a, 0x3c, 0xfc, 0x48, 0xd9, 0xaa, 0x55, 0xab, 0xe8, 0xf9, 0x1b, 0x3f, 0xef, 0x75, 0xe7, 0xce, 0x1d, 0xd8, 0xbc, 0x79, 0x33, 0xd5, 0x0f, 0x4f, 0x29, 0x17, 0x2f, 0x5e, 0x84, 0xc5, 0x8b, 0x17, 0x2b, 0x68, 0xb0, 0x42, 0x96, 0x97, 0x02, 0x59, 0xa6, 0x0a, 0x78, 0xee, 0xc7, 0xd3, 0x28, 0x4b, 0x64, 0xa7, 0x77, 0x7a, 0x3a, 0x62, 0xf5, 0x94, 0x94, 0x14, 0xba, 0x32, 0x1a, 0x73, 0x5b, 0x52, 0x77, 0x23, 0x18, 0x4c, 0xcf, 0x76, 0x90, 0x27, 0xca, 0x69, 0xae, 0x4f, 0x0f, 0xc6, 0xc7, 0xfa, 0xdd, 0xbb, 0x77, 0x55, 0x3d, 0x14, 0xe9, 0xc3, 0x1d, 0xf2, 0xc5, 0xc6, 0xc1, 0x7c, 0xb7, 0x6f, 0xdf, 0xae, 0xa0, 0x27, 0xc1, 0x0b, 0x46, 0xd1, 0xac, 0xb3, 0x1d, 0xea, 0x99, 0x6c, 0xb3, 0x3b, 0xd5, 0x6b, 0x32, 0xd5, 0x01, 0x9a, 0x5a, 0xec, 0xac, 0xc3, 0x87, 0x37, 0xe1, 0xb7, 0x01, 0x98, 0xf2, 0xb3, 0x66, 0xcd, 0xe2, 0x70, 0xbd, 0x42, 0x63, 0x63, 0xa3, 0xc4, 0xb6, 0xf2, 0x47, 0x5a, 0xf6, 0x65, 0x3c, 0x3d, 0x9a, 0x50, 0x6d, 0xb8, 0x20, 0x9b, 0x7c, 0xb2, 0x8f, 0xeb, 0x30, 0x6d, 0xda, 0x34, 0x5e, 0x26, 0x17, 0x56, 0x89, 0x6c, 0x6c, 0x1f, 0x8a, 0x94, 0xc2, 0x51, 0x17, 0xa6, 0x3f, 0xe6, 0x46, 0x96, 0xbc, 0xeb, 0x32, 0x0c, 0xd3, 0x68, 0x7b, 0x00, 0xce, 0x9d, 0x3b, 0xa7, 0x30, 0x00, 0x1d, 0x8b, 0x7b, 0x03, 0xe0, 0x27, 0x06, 0xe5, 0x86, 0x61, 0x59, 0xfe, 0x29, 0x02, 0x72, 0xb7, 0x21, 0x91, 0x8b, 0x2d, 0xc7, 0xc1, 0xf5, 0xff, 0x13, 0x26, 0x4c, 0x90, 0xb6, 0x6e, 0xdd, 0x2a, 0x6d, 0xd9, 0xb2, 0x45, 0xc2, 0x73, 0x34, 0xdb, 0x9a, 0x02, 0x69, 0xb1, 0x9d, 0x5c, 0x58, 0xb9, 0x79, 0x47, 0x8e, 0x1c, 0x91, 0xd2, 0xd3, 0xd3, 0x15, 0xf4, 0x6c, 0x84, 0xe0, 0x67, 0x0a, 0xc8, 0x06, 0x0a, 0xbc, 0x8d, 0x5c, 0x84, 0xa5, 0xec, 0xec, 0x6c, 0xa9, 0xba, 0xba, 0x9a, 0xd3, 0xcb, 0x0b, 0xe4, 0xab, 0x03, 0x1c, 0x17, 0x65, 0xa5, 0xa6, 0xa6, 0x4a, 0x93, 0x26, 0x4d, 0x52, 0xc8, 0x47, 0x38, 0x7e, 0x3a, 0xb1, 0xbb, 0xc9, 0xf6, 0x00, 0xa0, 0x42, 0x55, 0x55, 0x55, 0xb4, 0x17, 0xca, 0x8d, 0x46, 0x85, 0xb1, 0x8e, 0x86, 0xe3, 0xde, 0x1e, 0xa1, 0x12, 0xd9, 0x6c, 0x44, 0x22, 0xd7, 0x0d, 0x09, 0x9d, 0x84, 0x34, 0xf2, 0x03, 0xe9, 0xc9, 0xf5, 0x41, 0xba, 0x7a, 0xf5, 0xaa, 0x8a, 0x7c, 0xd7, 0xae, 0x5d, 0x12, 0xb9, 0xf5, 0xe5, 0xf8, 0x48, 0xbf, 0x77, 0xef, 0x5e, 0x8a, 0x77, 0xfc, 0xf8, 0x71, 0xba, 0x59, 0x04, 0xe3, 0x85, 0x9d, 0x02, 0xbf, 0xec, 0xa1, 0xb7, 0xad, 0xc0, 0xa5, 0x4b, 0x97, 0x24, 0xad, 0x3d, 0x1a, 0x10, 0xb6, 0x7e, 0xfd, 0x7a, 0xa9, 0xb4, 0xb4, 0x54, 0xa5, 0x83, 0x15, 0x80, 0x2d, 0x17, 0x61, 0x62, 0x98, 0x48, 0x16, 0x3d, 0x60, 0xfb, 0x1f, 0x31, 0x8b, 0x7a, 0xf4, 0x5b, 0x32, 0x11, 0x00, 0x8f, 0x43, 0x2f, 0x02, 0x20, 0x02, 0xe0, 0xb1, 0x07, 0x3c, 0x16, 0x2f, 0x46, 0x80, 0x08, 0x80, 0xc7, 0x1e, 0xf0, 0x58, 0xbc, 0x18, 0x01, 0x22, 0x00, 0x1e, 0x7b, 0xc0, 0x63, 0xf1, 0xff, 0x07, 0x81, 0x2f, 0x8c, 0xaf, 0x25, 0xdb, 0xd9, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXVideoIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x32, 0x3a, 0x37, 0x39, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0xcc, 0x4b, 0x33, 0xb9, 0x00, 0x00, 0x09, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x59, 0x7b, 0x4c, 0x54, 0xd9, 0x19, 0x9f, 0x99, 0x7b, 0x67, 0x18, 0x9e, 0x83, 0xc0, 0xcc, 0x30, 0x83, 0x2c, 0xa2, 0x80, 0x3c, 0x15, 0x96, 0xe2, 0xae, 0x8f, 0xd4, 0xf5, 0x11, 0x13, 0xb7, 0xb6, 0x31, 0xa6, 0xd5, 0x08, 0x6a, 0xb4, 0xba, 0x3e, 0x6a, 0x1a, 0x13, 0x63, 0xac, 0x35, 0x21, 0x9a, 0xfe, 0x63, 0x62, 0x8d, 0xb1, 0x35, 0x46, 0x8d, 0xd1, 0xac, 0xa6, 0x35, 0xd1, 0xa6, 0x66, 0xdd, 0x9a, 0xad, 0x31, 0x0d, 0x6e, 0xa3, 0x8d, 0x6f, 0x08, 0x6f, 0x99, 0x05, 0x11, 0x81, 0x85, 0x79, 0xc0, 0xbc, 0x98, 0xc7, 0xbd, 0x33, 0xf7, 0x4e, 0x7f, 0x77, 0xce, 0xee, 0x75, 0x32, 0x28, 0x8f, 0x19, 0x08, 0x6b, 0x32, 0x07, 0xb8, 0x7c, 0xe7, 0x9c, 0xef, 0x3b, 0xe7, 0xfb, 0x7d, 0xdf, 0x77, 0xce, 0x77, 0xee, 0xb9, 0xd2, 0x40, 0x20, 0x20, 0xf9, 0x90, 0x8b, 0xec, 0x43, 0x56, 0x5e, 0xd0, 0x3d, 0x06, 0x60, 0xa6, 0x3d, 0x18, 0xf3, 0x40, 0xcc, 0x03, 0x51, 0x5a, 0x60, 0x86, 0x43, 0xe8, 0xd9, 0xb3, 0x67, 0x83, 0x83, 0x83, 0xd1, 0x60, 0x98, 0x19, 0x00, 0x0c, 0xc3, 0xb4, 0xb6, 0x77, 0x9d, 0xf9, 0xeb, 0xc5, 0x23, 0x7f, 0xfc, 0xc3, 0xed, 0xdb, 0x5f, 0x45, 0x03, 0x80, 0x8e, 0x46, 0x78, 0xb2, 0xb2, 0x83, 0x83, 0x16, 0xe3, 0x90, 0xcd, 0x60, 0xe8, 0xf9, 0xde, 0x62, 0x4d, 0x4e, 0x4e, 0xee, 0x1d, 0xb4, 0xf5, 0xf5, 0x7e, 0xff, 0xe2, 0xc5, 0x8b, 0xc9, 0x8e, 0x13, 0xca, 0x1f, 0x15, 0x80, 0xbb, 0x77, 0xef, 0x32, 0x5e, 0x6f, 0x5c, 0x42, 0xb2, 0xcb, 0xed, 0x95, 0x4a, 0xa5, 0x18, 0x97, 0xa6, 0x69, 0xa5, 0x52, 0x91, 0x9c, 0x94, 0xa4, 0xce, 0x48, 0xcb, 0xd4, 0x6a, 0x92, 0x92, 0x92, 0xd0, 0xd8, 0xdf, 0x6f, 0x6c, 0x6c, 0xed, 0xec, 0x37, 0x59, 0x19, 0x2f, 0x23, 0x95, 0xd1, 0x1f, 0x65, 0xeb, 0xca, 0xb4, 0x6a, 0x9e, 0x93, 0x98, 0x4c, 0x96, 0x21, 0xab, 0xfd, 0xf1, 0xe3, 0xc7, 0x0e, 0x87, 0x23, 0x25, 0x25, 0x25, 0x54, 0xad, 0x89, 0xd3, 0x51, 0x01, 0x18, 0x1a, 0x1a, 0xea, 0xea, 0xfc, 0x4e, 0x9d, 0x53, 0x2e, 0x97, 0xcb, 0xe7, 0xcc, 0xce, 0xc0, 0xa1, 0x84, 0x65, 0x39, 0x37, 0xe3, 0xb3, 0xd8, 0x6c, 0xcd, 0x86, 0x01, 0x9b, 0xfd, 0x89, 0x67, 0xc4, 0xe9, 0xb0, 0x19, 0xfb, 0x8c, 0xb6, 0xb4, 0x0c, 0x5d, 0x41, 0x5e, 0xee, 0xec, 0xac, 0xcc, 0xa4, 0xe4, 0x44, 0xc0, 0x18, 0x30, 0x5a, 0xad, 0x56, 0x67, 0xf7, 0xeb, 0x3e, 0xa9, 0x4c, 0x3e, 0x38, 0x68, 0xec, 0xea, 0xea, 0xaa, 0xa8, 0xa8, 0x98, 0xb8, 0xd2, 0xa1, 0x9c, 0x51, 0x01, 0x50, 0x05, 0x8b, 0x5e, 0xa7, 0xcb, 0xc9, 0xd6, 0xe6, 0xe7, 0xea, 0x29, 0x19, 0xdc, 0x00, 0x3f, 0x08, 0x0f, 0x9e, 0x97, 0x70, 0x5c, 0x80, 0xf1, 0xb1, 0x76, 0xa7, 0xbb, 0x7f, 0xc0, 0xdc, 0xfa, 0xb2, 0xeb, 0xf5, 0xab, 0xae, 0xde, 0xbe, 0x3e, 0xf4, 0x26, 0x26, 0x26, 0x3b, 0x9c, 0xae, 0xef, 0xda, 0x5b, 0xf4, 0x9a, 0xa4, 0x3f, 0x1d, 0xaf, 0x8d, 0x53, 0x50, 0x5a, 0xad, 0x36, 0x54, 0xa7, 0x49, 0xd1, 0x51, 0x01, 0xa8, 0xab, 0xab, 0x63, 0x3d, 0x9e, 0xf8, 0xb4, 0x5c, 0x4d, 0xba, 0xca, 0x6e, 0x77, 0xd0, 0x32, 0x4a, 0x4a, 0x41, 0x7b, 0x19, 0x0d, 0x24, 0x40, 0x23, 0x91, 0xc4, 0xc9, 0xa9, 0xcc, 0x8c, 0x94, 0x2c, 0xad, 0x6a, 0x51, 0x79, 0x01, 0xc3, 0xfa, 0x7b, 0x7a, 0x07, 0x9a, 0x5a, 0x0d, 0xf7, 0x1f, 0xd4, 0x1b, 0xda, 0x1b, 0xaa, 0x7f, 0xfd, 0xf9, 0x6f, 0xb7, 0x57, 0xc3, 0x75, 0x93, 0x52, 0x77, 0x34, 0x73, 0x54, 0x00, 0x1a, 0x1a, 0x1a, 0x1c, 0x56, 0x2b, 0x9d, 0xf2, 0x51, 0x6e, 0x4e, 0xae, 0xd5, 0xee, 0xa2, 0x64, 0x28, 0xd0, 0x5f, 0x22, 0x10, 0xf8, 0x93, 0x0a, 0x0f, 0x50, 0x14, 0x25, 0x21, 0xf4, 0x6c, 0x9d, 0x7a, 0xb6, 0x5e, 0xf3, 0xf3, 0x25, 0x95, 0x4f, 0xea, 0x5f, 0xb6, 0xb7, 0xb5, 0x3e, 0x7b, 0xd1, 0xb4, 0xe4, 0xd3, 0xca, 0xd1, 0x3a, 0x4d, 0xaa, 0x25, 0x2a, 0x00, 0x1e, 0x8f, 0x47, 0x4a, 0xd1, 0x0c, 0xcb, 0xf5, 0xf4, 0x9b, 0x38, 0xde, 0x27, 0xa7, 0x69, 0xb9, 0x9c, 0xa2, 0x69, 0x4a, 0x4e, 0x09, 0x4f, 0xc1, 0x17, 0x14, 0x01, 0x22, 0x38, 0xe6, 0x47, 0x54, 0x52, 0x84, 0xd8, 0xa7, 0x1f, 0x17, 0x16, 0xe4, 0xcd, 0xfe, 0xe6, 0xde, 0xa3, 0xaf, 0xbf, 0xf9, 0x0f, 0xcd, 0x3b, 0xf7, 0xed, 0xdb, 0x97, 0x95, 0x95, 0x35, 0x29, 0xbd, 0x45, 0xe6, 0xa8, 0x00, 0x64, 0x66, 0x66, 0x42, 0x43, 0x9b, 0xd5, 0xf2, 0x8f, 0x7f, 0xfe, 0xab, 0xb4, 0xa4, 0x38, 0x0b, 0xb1, 0x92, 0x9c, 0xa8, 0x4a, 0x49, 0x50, 0x25, 0x27, 0xc4, 0xc7, 0x2b, 0x04, 0x00, 0xf8, 0x09, 0x16, 0xc1, 0x1d, 0x32, 0xf8, 0x41, 0x08, 0x2c, 0x50, 0x41, 0x5a, 0xb6, 0xf8, 0x93, 0x8a, 0x53, 0xa7, 0xeb, 0x92, 0x65, 0xce, 0xa6, 0xa6, 0xa6, 0x99, 0x01, 0x70, 0xf2, 0xe4, 0x49, 0xa4, 0xa4, 0x53, 0xa7, 0xff, 0xd2, 0xd3, 0xd1, 0xa8, 0xd5, 0xaa, 0x0b, 0x8b, 0xe7, 0x27, 0x26, 0xd0, 0x71, 0x71, 0x4a, 0xa8, 0xe8, 0xf7, 0x07, 0xa4, 0xd2, 0x80, 0xa0, 0x2e, 0x60, 0xc8, 0xa4, 0x3c, 0x4f, 0xd0, 0x70, 0xa8, 0x52, 0x94, 0x54, 0xc6, 0x07, 0x38, 0xce, 0xef, 0xf3, 0xb1, 0x0a, 0x05, 0x9d, 0xae, 0x4a, 0x17, 0x82, 0x2c, 0xd2, 0x12, 0x95, 0x07, 0x0a, 0x0a, 0x0a, 0x30, 0x6f, 0xda, 0xac, 0x59, 0x99, 0x5a, 0xb5, 0x32, 0x4e, 0x9e, 0xa8, 0xa4, 0x52, 0x92, 0xe2, 0x3b, 0x0d, 0x2f, 0x47, 0x1c, 0xc3, 0x89, 0x49, 0x49, 0x64, 0x3f, 0x12, 0x00, 0x08, 0x19, 0x22, 0xe8, 0x08, 0x81, 0x90, 0xb8, 0xdd, 0x6e, 0x8d, 0x36, 0xb3, 0xb0, 0xa8, 0x94, 0xe3, 0x78, 0xaf, 0xcb, 0x5b, 0xf7, 0xfc, 0xf1, 0x2f, 0xd6, 0xfd, 0x52, 0xe8, 0x88, 0xa8, 0x44, 0x05, 0x80, 0xcc, 0x18, 0x90, 0x04, 0x54, 0xaa, 0xd4, 0x00, 0xcf, 0xf9, 0x18, 0x6c, 0x9b, 0x72, 0x1f, 0xeb, 0x5b, 0xbe, 0x7c, 0x79, 0x5a, 0x5a, 0x3a, 0x72, 0x1c, 0xb4, 0x86, 0xe6, 0x34, 0x25, 0xc3, 0x53, 0x22, 0x81, 0x43, 0x64, 0xf1, 0x4a, 0x65, 0x6f, 0x7f, 0x7f, 0x73, 0x6b, 0x1b, 0x03, 0xfb, 0xe3, 0x97, 0xf5, 0x19, 0x4d, 0xc6, 0x68, 0x5e, 0x6b, 0xa7, 0x00, 0x00, 0x76, 0xcf, 0x0c, 0xb5, 0x9a, 0xe7, 0x39, 0xb7, 0x87, 0x89, 0x53, 0xca, 0x19, 0x96, 0x0d, 0x48, 0xa9, 0xcb, 0x97, 0x2e, 0x74, 0xb4, 0x37, 0x05, 0xfd, 0x20, 0xc5, 0x82, 0x86, 0xa2, 0x00, 0x33, 0x3c, 0x6c, 0x59, 0xf7, 0xab, 0xdf, 0xfc, 0x6c, 0xd1, 0x12, 0x96, 0xf5, 0xb3, 0x00, 0xcb, 0xf8, 0x59, 0x24, 0x0a, 0xbb, 0x23, 0x1a, 0x00, 0x53, 0x70, 0x98, 0xc3, 0x8e, 0xa3, 0xd5, 0x68, 0xfc, 0x7e, 0x0e, 0xeb, 0xc1, 0xeb, 0x15, 0xdc, 0xe0, 0x74, 0xba, 0xe5, 0x0a, 0x79, 0x4d, 0xcd, 0xe6, 0x3d, 0xbb, 0xbf, 0x58, 0xb7, 0xee, 0x73, 0x43, 0xc7, 0x4b, 0xb3, 0xd9, 0x58, 0x55, 0x55, 0x69, 0x36, 0x19, 0x5f, 0xf7, 0xf4, 0xb0, 0x1c, 0x0f, 0x3c, 0x5e, 0x2f, 0xcb, 0xb0, 0x3e, 0x9e, 0x27, 0x77, 0x0a, 0x91, 0x5f, 0x2c, 0x4c, 0x01, 0x00, 0xb9, 0x9c, 0xd6, 0x68, 0xb5, 0x7e, 0xbf, 0x1f, 0x1e, 0xf0, 0x7a, 0xa0, 0x16, 0xeb, 0x74, 0x8e, 0x28, 0xe4, 0xf2, 0xfc, 0xfc, 0xfc, 0xd2, 0x92, 0x12, 0xbd, 0x5e, 0xf7, 0xaa, 0xfb, 0xd5, 0x67, 0x9f, 0x2d, 0x37, 0x18, 0x0c, 0x26, 0x8b, 0xd9, 0xef, 0xe7, 0x3d, 0x6e, 0xaf, 0x00, 0x80, 0x65, 0xbd, 0x8c, 0x8f, 0xf7, 0x73, 0x11, 0x45, 0xfe, 0x5b, 0xa1, 0x29, 0x08, 0x21, 0x8a, 0xa6, 0x34, 0x6a, 0x35, 0xc7, 0x75, 0xc2, 0x01, 0x0c, 0x93, 0x80, 0xf0, 0xc0, 0xd9, 0xce, 0x64, 0xb6, 0x7c, 0xfb, 0x6d, 0x9d, 0x5e, 0x9f, 0xdd, 0xd2, 0xda, 0x1c, 0x17, 0x17, 0x67, 0xe8, 0x30, 0x48, 0xa4, 0x92, 0x8a, 0xf2, 0x72, 0x97, 0xdb, 0xed, 0x72, 0x7b, 0x58, 0x9f, 0x1f, 0xda, 0xc3, 0x09, 0x7e, 0xde, 0xff, 0x56, 0x97, 0x88, 0xa8, 0x29, 0x00, 0x40, 0x53, 0x74, 0x7a, 0x3a, 0xb6, 0xc2, 0x60, 0xfa, 0x52, 0x20, 0x97, 0xd1, 0xd0, 0xaf, 0xec, 0xe3, 0xa5, 0x76, 0xbb, 0xb5, 0xcf, 0xcc, 0xa4, 0xeb, 0x8a, 0x0e, 0xd7, 0xfe, 0x19, 0x51, 0x8e, 0x83, 0x2a, 0x96, 0xad, 0x5a, 0xa3, 0x19, 0x71, 0xb9, 0x20, 0x82, 0xbd, 0x15, 0x8d, 0x3c, 0xcf, 0xc3, 0x75, 0x08, 0xbf, 0x88, 0x94, 0x17, 0x84, 0xa6, 0x00, 0x00, 0x96, 0xaf, 0x5e, 0xa7, 0x4d, 0x49, 0x55, 0x69, 0x35, 0x50, 0x2f, 0xdd, 0xed, 0x1c, 0x52, 0xa9, 0x52, 0x32, 0x34, 0x6a, 0xa4, 0x01, 0x21, 0x7f, 0x61, 0xdf, 0xa7, 0x29, 0x09, 0x94, 0x0d, 0xf0, 0xd8, 0x8b, 0xc0, 0x3c, 0x64, 0x36, 0xa5, 0xce, 0x52, 0xcd, 0x52, 0xa5, 0x3a, 0xdd, 0x01, 0x5a, 0xae, 0x28, 0x2a, 0x2a, 0x8a, 0xf8, 0x2c, 0x1d, 0x2d, 0x00, 0x28, 0x85, 0xbd, 0x25, 0x5e, 0x19, 0xff, 0xdf, 0xff, 0x3d, 0xf1, 0x30, 0xfc, 0x90, 0xc3, 0x23, 0xa1, 0xdd, 0x43, 0x76, 0x37, 0x6d, 0xb4, 0x8f, 0xb0, 0x32, 0xf4, 0x06, 0x01, 0x08, 0x89, 0x80, 0xc7, 0x5e, 0x8b, 0xe5, 0x8a, 0x7f, 0x52, 0xc9, 0x88, 0xc3, 0x6e, 0x32, 0xdb, 0xe8, 0x04, 0x4b, 0x7b, 0x5b, 0xdb, 0xc1, 0xdf, 0x7f, 0xb1, 0x64, 0xd1, 0x02, 0x8d, 0x46, 0x13, 0xb1, 0x07, 0x04, 0x3f, 0x46, 0x26, 0xec, 0xf3, 0xf9, 0x90, 0x92, 0x46, 0x46, 0x46, 0xcc, 0x66, 0x4b, 0x43, 0x63, 0xd3, 0xc9, 0x93, 0xa7, 0xcc, 0x96, 0x21, 0xe4, 0x54, 0xa4, 0x58, 0xc1, 0xee, 0x38, 0x2d, 0x08, 0x23, 0x23, 0x0f, 0x08, 0xc3, 0x07, 0xa9, 0xe0, 0x3f, 0xa1, 0xc6, 0x23, 0x85, 0xc1, 0x1b, 0xc5, 0x85, 0x05, 0x7f, 0xff, 0xdb, 0x97, 0x19, 0x19, 0x6a, 0x9c, 0x49, 0x11, 0x60, 0x42, 0xcf, 0xe4, 0x4b, 0x84, 0x62, 0x64, 0x22, 0x44, 0x30, 0xc7, 0x71, 0x0a, 0x85, 0x1c, 0x47, 0xb3, 0xdf, 0xed, 0xdd, 0x69, 0x32, 0x99, 0x84, 0xa0, 0x41, 0xce, 0x22, 0x56, 0x79, 0x8f, 0x65, 0xd0, 0x8f, 0x13, 0x2a, 0xf8, 0xf2, 0xf2, 0xf3, 0x81, 0xd0, 0xeb, 0xf5, 0x46, 0xac, 0x3d, 0xd4, 0x88, 0xdc, 0x03, 0x10, 0x86, 0x9e, 0xc0, 0x80, 0x02, 0x02, 0x56, 0x14, 0x54, 0x9f, 0x64, 0x01, 0xfe, 0x1f, 0x30, 0x4f, 0x52, 0x50, 0x64, 0x8f, 0x0a, 0x80, 0x38, 0xca, 0x0c, 0x12, 0x53, 0x90, 0xc8, 0x66, 0x50, 0x7b, 0x4c, 0x1d, 0x03, 0x30, 0xb3, 0xf6, 0x8f, 0x79, 0x60, 0xa6, 0xed, 0x1f, 0xf3, 0xc0, 0x87, 0xec, 0x01, 0x9c, 0x21, 0x4f, 0x9f, 0x3e, 0x7d, 0xeb, 0xd6, 0xad, 0xb1, 0x41, 0x20, 0xcd, 0x05, 0xcf, 0x9b, 0xd1, 0x1e, 0x9b, 0xdf, 0x3b, 0x0b, 0x92, 0x68, 0x64, 0xa5, 0xa3, 0xa3, 0x03, 0x83, 0xce, 0x9d, 0x3b, 0x77, 0x0c, 0xf1, 0xa3, 0x47, 0x8f, 0x8a, 0x37, 0x0e, 0xb8, 0xfc, 0x19, 0x83, 0x33, 0xe2, 0xae, 0xc8, 0xf3, 0x80, 0x42, 0xa1, 0xc0, 0x9b, 0xca, 0xd8, 0xf7, 0x39, 0x39, 0x39, 0x39, 0xf3, 0xe6, 0xcd, 0x4b, 0x4b, 0x4b, 0x03, 0x54, 0x5c, 0x41, 0xbf, 0xd7, 0x8a, 0xd1, 0x74, 0x44, 0x0c, 0x1d, 0x82, 0x38, 0x90, 0x4e, 0x44, 0x1c, 0x91, 0x06, 0x0d, 0xb7, 0x6c, 0xd9, 0x32, 0x11, 0xe6, 0xc9, 0xf2, 0x84, 0x7b, 0xe0, 0xc8, 0x91, 0x23, 0x9f, 0x04, 0xcb, 0x9a, 0x35, 0x6b, 0xda, 0xda, 0xda, 0x9e, 0x3f, 0x7f, 0xbe, 0x6a, 0xd5, 0x2a, 0x34, 0x2c, 0x5d, 0xba, 0xf4, 0xda, 0xb5, 0x6b, 0xa2, 0xa5, 0xb6, 0x6d, 0xdb, 0x46, 0x1a, 0x97, 0x2d, 0x5b, 0x66, 0xb3, 0xd9, 0xc4, 0x76, 0x91, 0xb8, 0x71, 0xe3, 0xc6, 0xd6, 0xad, 0x5b, 0x31, 0xc8, 0xf1, 0xe3, 0xc7, 0x47, 0xdb, 0x1e, 0x2d, 0x07, 0x0f, 0x1e, 0x5c, 0xb8, 0x70, 0x21, 0x3e, 0x20, 0x94, 0x94, 0x94, 0x1c, 0x3e, 0x7c, 0xd8, 0xe5, 0x72, 0x89, 0xb2, 0x20, 0xfa, 0xfb, 0xfb, 0x77, 0xec, 0xd8, 0x51, 0x58, 0x58, 0x08, 0x86, 0xf2, 0xf2, 0xf2, 0xd1, 0x0c, 0x6f, 0x99, 0xc3, 0x10, 0xeb, 0x74, 0x3a, 0xb1, 0xef, 0xea, 0xd5, 0xab, 0x67, 0xcf, 0x9e, 0x15, 0xab, 0x1b, 0x36, 0x6c, 0x20, 0xcc, 0x4e, 0xa7, 0x33, 0xf4, 0x00, 0xdc, 0xdd, 0xdd, 0x1d, 0x36, 0xc8, 0xae, 0x5d, 0xbb, 0x44, 0x29, 0x10, 0xe4, 0x94, 0x2a, 0x7a, 0x60, 0x60, 0x60, 0x20, 0x3b, 0x3b, 0x9b, 0x30, 0xe0, 0x5d, 0x94, 0x10, 0xc5, 0xc5, 0xc5, 0xc0, 0x40, 0xc6, 0xc1, 0x37, 0x9b, 0xd1, 0xef, 0x68, 0xb9, 0xb9, 0xb9, 0x16, 0x8b, 0x25, 0x6c, 0x22, 0x54, 0x85, 0x23, 0x71, 0x68, 0xe9, 0xec, 0xec, 0x2c, 0x2d, 0x2d, 0xc5, 0xa0, 0xbb, 0x77, 0xef, 0x46, 0x84, 0xe0, 0xa6, 0x64, 0xf3, 0xe6, 0xcd, 0xa8, 0x6e, 0xda, 0xb4, 0x29, 0x54, 0xbe, 0xb9, 0xb9, 0x19, 0x36, 0x26, 0x73, 0x87, 0x01, 0xb8, 0x7d, 0xfb, 0x36, 0x69, 0x87, 0x73, 0xae, 0x5c, 0xb9, 0xb2, 0x62, 0xc5, 0x0a, 0x52, 0xad, 0xa9, 0xa9, 0x21, 0x13, 0xad, 0x5f, 0xbf, 0x1e, 0x2d, 0xab, 0x57, 0xaf, 0xee, 0xeb, 0xeb, 0x43, 0x0b, 0xae, 0xb8, 0xc9, 0x42, 0x3a, 0x74, 0xe8, 0x10, 0xaa, 0x98, 0x94, 0x28, 0x00, 0x53, 0x9e, 0x3b, 0x77, 0x0e, 0xfa, 0x1c, 0x3b, 0x76, 0x2c, 0x3e, 0x3e, 0x1e, 0x22, 0xd5, 0xd5, 0xd5, 0xa1, 0xaa, 0x12, 0x3a, 0x1c, 0x00, 0x5a, 0x2f, 0x5e, 0xbc, 0x08, 0x6e, 0x7c, 0x32, 0x01, 0x8d, 0xf3, 0x3a, 0x16, 0x22, 0x4c, 0x88, 0x3d, 0x27, 0x4c, 0x18, 0x57, 0xd3, 0x44, 0xb3, 0x30, 0x00, 0x44, 0x3f, 0xbc, 0x25, 0x42, 0x96, 0x88, 0x60, 0x04, 0x70, 0x12, 0x00, 0x90, 0x22, 0xfb, 0xd2, 0x99, 0x33, 0x67, 0xfe, 0xfd, 0x63, 0xd9, 0xb9, 0x73, 0x27, 0x18, 0x16, 0x2c, 0x58, 0x00, 0x7e, 0x5c, 0xf4, 0x92, 0x61, 0xb1, 0x72, 0xc4, 0x19, 0x21, 0x8b, 0x46, 0xa5, 0x52, 0x89, 0x1d, 0x59, 0x6c, 0x24, 0xc4, 0x3b, 0x00, 0x20, 0x42, 0xf0, 0x05, 0x0e, 0x02, 0xf5, 0xf5, 0xf5, 0x77, 0xee, 0xdc, 0x01, 0xb1, 0x76, 0xed, 0xda, 0x30, 0x31, 0x54, 0xdf, 0x07, 0x00, 0x81, 0x0b, 0x91, 0xed, 0xdb, 0xb7, 0x8b, 0x22, 0xfb, 0xf7, 0xef, 0x47, 0x0b, 0x01, 0x20, 0xea, 0x87, 0x96, 0xb0, 0x92, 0x97, 0x97, 0x07, 0x91, 0x9b, 0x37, 0x6f, 0x92, 0x76, 0x7c, 0x77, 0x12, 0x47, 0x10, 0x1b, 0xe1, 0x10, 0xb1, 0x91, 0x10, 0xef, 0x78, 0xa5, 0xc4, 0xba, 0x41, 0xbc, 0x9e, 0x3f, 0x7f, 0xfe, 0xf2, 0xe5, 0xcb, 0x3d, 0x3d, 0x3d, 0x18, 0xee, 0xc0, 0x81, 0x03, 0x61, 0x93, 0x8d, 0x51, 0x85, 0x38, 0x7a, 0xe1, 0x16, 0x91, 0x27, 0x94, 0xd6, 0xeb, 0xf5, 0xa4, 0xfd, 0xd2, 0xa5, 0x4b, 0x65, 0x65, 0x65, 0x22, 0x0f, 0xdc, 0x42, 0xae, 0x8a, 0xc5, 0x45, 0x88, 0x4f, 0xc8, 0x48, 0x32, 0x84, 0x01, 0x34, 0x08, 0x04, 0x02, 0x2e, 0xf4, 0x45, 0x91, 0x1f, 0x88, 0x30, 0x40, 0xa4, 0xda, 0xd8, 0xd8, 0x88, 0x6e, 0xf8, 0x01, 0xef, 0x7b, 0xb0, 0x28, 0x79, 0x69, 0x0c, 0xe5, 0x84, 0x2b, 0xe1, 0x28, 0x32, 0x04, 0xac, 0x12, 0xba, 0x9f, 0xee, 0xd9, 0xb3, 0x07, 0xed, 0x50, 0xe8, 0xe1, 0xc3, 0x87, 0x10, 0x79, 0xfa, 0xf4, 0x29, 0xd2, 0x05, 0x5a, 0xc4, 0x35, 0x30, 0x7f, 0xfe, 0x7c, 0x54, 0xb1, 0x41, 0x89, 0xab, 0xb6, 0xa5, 0xa5, 0x05, 0x52, 0x27, 0x4e, 0x9c, 0x00, 0xbf, 0xdd, 0x6e, 0x27, 0xfe, 0xc7, 0xbe, 0xf7, 0xe6, 0xcd, 0x1b, 0xb4, 0x60, 0x91, 0x10, 0xd8, 0x24, 0xaa, 0x43, 0xd5, 0x00, 0xfd, 0x8e, 0x10, 0x22, 0x1c, 0x8b, 0x17, 0x2f, 0x26, 0xfa, 0x61, 0x25, 0x85, 0xc9, 0x54, 0x55, 0x55, 0x91, 0xae, 0xd0, 0x27, 0x02, 0x00, 0x2b, 0x1e, 0x9c, 0x98, 0x35, 0x31, 0x31, 0x91, 0x74, 0x91, 0x70, 0x22, 0x34, 0xec, 0x57, 0x5b, 0x5b, 0x0b, 0x86, 0xfb, 0xf7, 0xef, 0x93, 0x7d, 0x09, 0x4b, 0x73, 0xe5, 0xca, 0x95, 0x62, 0x2a, 0xdc, 0xb8, 0x71, 0x23, 0x99, 0xe8, 0xc2, 0x85, 0x0b, 0x44, 0x04, 0x7b, 0x1d, 0xb6, 0x5a, 0x42, 0xc3, 0x22, 0x8f, 0x1e, 0x3d, 0x0a, 0xd3, 0x04, 0xd5, 0xf7, 0x02, 0xb8, 0x77, 0xef, 0x5e, 0x6a, 0x6a, 0x2a, 0xd6, 0x1f, 0x2e, 0x4e, 0xc2, 0xc4, 0x90, 0x01, 0xc8, 0xa0, 0xa1, 0x4f, 0x04, 0x00, 0xee, 0x17, 0x08, 0x27, 0x66, 0xc2, 0x75, 0x15, 0xe9, 0x45, 0x48, 0x60, 0xbd, 0xc2, 0x93, 0xd0, 0x86, 0x00, 0x00, 0x0f, 0xbe, 0x0e, 0x62, 0x77, 0x27, 0x0c, 0xe8, 0x02, 0xf3, 0xde, 0xbd, 0x7b, 0x87, 0x87, 0x87, 0xc5, 0x89, 0xae, 0x5f, 0xbf, 0x4e, 0x96, 0x3e, 0xe1, 0xa9, 0xac, 0xac, 0x7c, 0xf0, 0xe0, 0x81, 0xd8, 0x1b, 0x4a, 0x8c, 0xf5, 0x52, 0x8f, 0xcf, 0xc0, 0x30, 0x15, 0x39, 0x08, 0x84, 0xea, 0x3a, 0x41, 0x1a, 0xfb, 0x3d, 0x82, 0x04, 0x47, 0x09, 0x62, 0xef, 0xd1, 0x52, 0x48, 0x67, 0x58, 0x63, 0x08, 0x74, 0xd1, 0x63, 0x61, 0x3c, 0xd8, 0xb8, 0x31, 0xc8, 0x9c, 0x39, 0x73, 0x48, 0x50, 0x85, 0xf5, 0x92, 0xea, 0x58, 0x00, 0xde, 0x29, 0xf0, 0x53, 0x6b, 0x0c, 0x3f, 0x4a, 0xfc, 0xd4, 0xf4, 0x1b, 0x57, 0x9f, 0x18, 0x80, 0x71, 0x4d, 0x34, 0xcd, 0x0c, 0x31, 0x0f, 0x4c, 0xb3, 0x81, 0xc7, 0x1d, 0x3e, 0xe6, 0x81, 0x71, 0x4d, 0x34, 0xcd, 0x0c, 0x31, 0x0f, 0x4c, 0xb3, 0x81, 0xc7, 0x1d, 0x3e, 0xe6, 0x81, 0x71, 0x4d, 0x34, 0xcd, 0x0c, 0xff, 0x07, 0x71, 0xef, 0x64, 0x50, 0x13, 0xcd, 0x1c, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXVideoIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x1d, 0x7b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x5d, 0x09, 0x5c, 0x55, 0xd5, 0xd6, 0xff, 0x33, 0x5f, 0x66, 0x10, 0x70, 0x46, 0xc0, 0x11, 0x4d, 0x02, 0x51, 0xeb, 0xe5, 0x94, 0xa5, 0x69, 0x8e, 0x5f, 0x45, 0xa9, 0x2f, 0xfd, 0x5e, 0x59, 0xaf, 0xb9, 0xac, 0x34, 0x9b, 0x9e, 0x59, 0x9f, 0xf6, 0x9e, 0xcd, 0xbe, 0xcc, 0x29, 0x2d, 0x7d, 0x95, 0xe6, 0x33, 0x87, 0xd4, 0xd2, 0x1c, 0x32, 0x07, 0x1c, 0x02, 0x19, 0x84, 0x00, 0x11, 0x04, 0x19, 0x45, 0xe6, 0x79, 0x1e, 0xce, 0xb7, 0xd6, 0xbe, 0xf7, 0x1c, 0xef, 0xbd, 0x70, 0x91, 0x0b, 0xc8, 0xd5, 0xf7, 0xd8, 0xfc, 0x2e, 0xe7, 0xec, 0x79, 0xaf, 0xb5, 0xf6, 0xb8, 0xf6, 0x5a, 0xeb, 0x98, 0x49, 0xe4, 0x60, 0x84, 0x33, 0x6f, 0x2e, 0xed, 0xd5, 0xac, 0x2b, 0xd8, 0xb4, 0x6e, 0xb5, 0x4e, 0x12, 0x33, 0xfd, 0x1a, 0x96, 0xbf, 0xb5, 0x08, 0x92, 0xb9, 0x25, 0xac, 0xa8, 0xa8, 0x32, 0x95, 0x2b, 0x8a, 0x2e, 0x9e, 0xc3, 0x9a, 0x6f, 0x77, 0x29, 0x99, 0x2c, 0x95, 0x37, 0xcd, 0xcb, 0x98, 0xb1, 0x77, 0xa3, 0xa8, 0xb0, 0x10, 0xc5, 0x6e, 0x5e, 0x48, 0x4f, 0x4a, 0xc2, 0xc2, 0x25, 0x2b, 0x74, 0x92, 0x34, 0x6a, 0xd2, 0xee, 0xed, 0xdf, 0x21, 0x22, 0xbb, 0x14, 0x97, 0x62, 0xa2, 0x71, 0xcf, 0x6d, 0x03, 0x91, 0x96, 0x99, 0xa9, 0x93, 0xa1, 0x51, 0x93, 0x38, 0xf6, 0x8d, 0xbf, 0x7f, 0x0c, 0xa9, 0xa1, 0x01, 0x97, 0x92, 0x93, 0xb1, 0x7b, 0xf3, 0x97, 0x3a, 0x19, 0x1a, 0x35, 0x89, 0x63, 0x33, 0x2f, 0xc5, 0xc3, 0xc1, 0xc9, 0x05, 0x9e, 0x0e, 0xd6, 0x3a, 0x89, 0xd9, 0xd3, 0x64, 0x0d, 0x8d, 0x52, 0x69, 0x05, 0x34, 0x82, 0x41, 0x2b, 0xae, 0xc9, 0x57, 0x25, 0xc3, 0xe2, 0xc5, 0x8b, 0x45, 0x02, 0x33, 0x33, 0x33, 0xbc, 0xfe, 0xfa, 0xeb, 0x4a, 0xe2, 0x07, 0x1f, 0x7c, 0x10, 0x0d, 0x04, 0x8f, 0xec, 0x6e, 0x7c, 0x93, 0xda, 0xaf, 0x86, 0x2d, 0xff, 0xda, 0x84, 0x2b, 0x57, 0xae, 0xc8, 0x4d, 0x57, 0x9e, 0x8d, 0xe8, 0x50, 0x5a, 0x52, 0x0c, 0x47, 0x27, 0x67, 0x1c, 0x3f, 0xb8, 0x17, 0x31, 0x99, 0x79, 0xe8, 0x52, 0x99, 0x87, 0xd7, 0xdf, 0xff, 0xa8, 0xe9, 0x0c, 0xc7, 0x0f, 0xee, 0x47, 0x68, 0x42, 0x0a, 0x02, 0xc6, 0x4f, 0x84, 0xca, 0x27, 0x00, 0xdf, 0xfd, 0x73, 0x19, 0xd2, 0xb3, 0x4b, 0x94, 0xc4, 0xfc, 0xa2, 0xa0, 0x95, 0x3d, 0xfb, 0xf6, 0xec, 0x42, 0x69, 0x3d, 0x10, 0x7c, 0xe4, 0x30, 0x1c, 0xa9, 0xbb, 0xbe, 0xbc, 0x78, 0x29, 0xcc, 0xcd, 0x75, 0x92, 0xe8, 0x66, 0xf8, 0x6c, 0xfd, 0x26, 0xd4, 0x56, 0x57, 0xa1, 0xa6, 0xaa, 0x12, 0xc5, 0xb5, 0x12, 0xe2, 0x32, 0x72, 0xb8, 0x1c, 0x1d, 0xd7, 0x08, 0x86, 0xe4, 0xb8, 0x68, 0xd8, 0x58, 0x58, 0xc2, 0xce, 0xd1, 0x19, 0x36, 0x35, 0x95, 0x3a, 0x89, 0xd9, 0xd3, 0x7e, 0x68, 0xe5, 0x2e, 0xd2, 0x94, 0x13, 0x4d, 0xe2, 0x48, 0xed, 0xb9, 0x40, 0xc6, 0xbf, 0xb7, 0xb7, 0x37, 0x86, 0x0d, 0x1b, 0x06, 0x2b, 0x2b, 0x2b, 0xec, 0xd8, 0xb1, 0x43, 0xa4, 0x69, 0xbf, 0x26, 0x35, 0xd5, 0x1c, 0x0e, 0x33, 0xba, 0x06, 0x43, 0x05, 0x19, 0x0a, 0x6f, 0x44, 0x06, 0x43, 0x09, 0xe5, 0xf0, 0x7d, 0xfb, 0xf6, 0xe1, 0xfb, 0xcd, 0x5f, 0xc0, 0xab, 0x97, 0x17, 0x60, 0x61, 0x81, 0xb2, 0xd2, 0x7c, 0xdc, 0x3f, 0x73, 0x2e, 0x66, 0x3c, 0xf0, 0xa0, 0x9c, 0x44, 0xe7, 0xa9, 0xdb, 0x4d, 0x74, 0xa2, 0x80, 0x9f, 0xf7, 0xed, 0xc1, 0xd1, 0x23, 0x87, 0x44, 0xe8, 0x3b, 0x8b, 0x17, 0x20, 0x37, 0x37, 0x07, 0x71, 0x51, 0x61, 0x88, 0x88, 0x8c, 0x86, 0x95, 0x8f, 0x3f, 0xf2, 0xf2, 0x8a, 0x10, 0x7b, 0x21, 0x1e, 0xb1, 0x11, 0x21, 0x7a, 0x39, 0xaf, 0x79, 0x5b, 0x84, 0xa2, 0x27, 0x5e, 0xfb, 0x1b, 0x06, 0xfa, 0x0e, 0x42, 0x75, 0x45, 0x05, 0x88, 0x74, 0xc8, 0x4e, 0x4b, 0xc1, 0x50, 0xff, 0x40, 0x24, 0x44, 0x9d, 0xc3, 0x6b, 0xaf, 0xbe, 0x82, 0xde, 0xbd, 0x7b, 0x5f, 0x2b, 0x51, 0xef, 0xed, 0xba, 0x28, 0xaa, 0xab, 0xab, 0x43, 0x8f, 0x1e, 0xdd, 0x50, 0x52, 0x50, 0x40, 0x59, 0x69, 0x19, 0xa1, 0x0a, 0x12, 0xe2, 0x62, 0x10, 0x7b, 0x3e, 0x02, 0x66, 0x0d, 0x75, 0xc8, 0xcd, 0xc9, 0x69, 0x5b, 0x05, 0x96, 0x96, 0x96, 0xc8, 0xce, 0x48, 0x47, 0x55, 0x59, 0x39, 0x15, 0x2f, 0xc1, 0xa3, 0x7b, 0x0f, 0xf4, 0xf7, 0x0b, 0xc0, 0xe3, 0x0f, 0x4d, 0xc7, 0xd6, 0x1f, 0x0f, 0x60, 0x58, 0x60, 0xa0, 0x5e, 0x9b, 0x75, 0xbd, 0x2d, 0x42, 0x91, 0x6e, 0x16, 0xe3, 0x7c, 0xcd, 0x12, 0x99, 0x8b, 0xf2, 0xf3, 0xf3, 0xd3, 0x29, 0xf1, 0xe4, 0xc9, 0x93, 0x38, 0x77, 0xee, 0x9c, 0x4e, 0x58, 0xb3, 0x1e, 0x5e, 0x3d, 0xd9, 0xc5, 0xc4, 0xc4, 0xf0, 0x3a, 0x2d, 0xe5, 0xe5, 0xe5, 0x89, 0x27, 0x87, 0xcd, 0x9b, 0x37, 0x4f, 0x79, 0x67, 0x3f, 0xc7, 0xdb, 0xd9, 0xd9, 0x29, 0x61, 0xec, 0x8f, 0x8c, 0x8c, 0xd4, 0xf1, 0x7f, 0xfd, 0xf5, 0xd7, 0x8a, 0x5f, 0xe4, 0xe1, 0x7f, 0xda, 0x8e, 0x33, 0xc9, 0x8e, 0xdf, 0xe5, 0x1f, 0x87, 0xc9, 0xef, 0x72, 0x1a, 0x43, 0xfe, 0x2e, 0x5d, 0xba, 0xc8, 0x45, 0x48, 0xa6, 0xa7, 0x01, 0xb5, 0xb2, 0x4d, 0xee, 0xd6, 0x87, 0xe0, 0xba, 0xdd, 0x54, 0x1f, 0x3f, 0xbc, 0x71, 0xf8, 0xe0, 0x9d, 0xc5, 0x28, 0x2a, 0xc8, 0xc7, 0x8a, 0x25, 0xaf, 0xa1, 0xbe, 0x9e, 0x96, 0xc1, 0x66, 0x9c, 0xd1, 0x15, 0x14, 0xe6, 0xe7, 0x22, 0x22, 0x2a, 0x0a, 0x2f, 0x3c, 0x35, 0x17, 0x97, 0x93, 0x13, 0x11, 0x1e, 0x16, 0xd6, 0x4c, 0xf1, 0x7a, 0xeb, 0xb2, 0x7e, 0xca, 0xed, 0x5b, 0xb7, 0x20, 0x35, 0xe5, 0x32, 0xb2, 0xae, 0x64, 0xe2, 0xeb, 0xd5, 0x9f, 0x89, 0xd6, 0x2e, 0xfb, 0xdb, 0x62, 0x5a, 0x55, 0x81, 0x51, 0x53, 0x1f, 0x85, 0x54, 0x5f, 0x87, 0x1d, 0xff, 0x5a, 0xa7, 0x9f, 0x4d, 0xc7, 0x6f, 0x10, 0x82, 0xb3, 0x21, 0xa1, 0x98, 0x3d, 0x77, 0x1e, 0xb6, 0x7f, 0xfb, 0x0d, 0x4d, 0xdb, 0xfb, 0xf0, 0xe4, 0x8b, 0x0b, 0xf1, 0xf5, 0xda, 0xcf, 0xe1, 0xd6, 0xb3, 0x0f, 0x06, 0x4e, 0x9c, 0x85, 0xac, 0xc2, 0x22, 0xc4, 0x24, 0x26, 0x62, 0x80, 0xdf, 0x30, 0x9d, 0x02, 0xf5, 0x3d, 0x06, 0x67, 0xd3, 0x43, 0xbb, 0xb6, 0xa2, 0xaa, 0x20, 0x07, 0xc5, 0x36, 0x4e, 0x48, 0x4e, 0x4a, 0x47, 0x8f, 0xe3, 0x67, 0x61, 0x33, 0x20, 0x10, 0xde, 0x03, 0xcd, 0x90, 0x18, 0x1b, 0x83, 0x72, 0xda, 0xe3, 0x4c, 0x1c, 0x35, 0x1a, 0xe9, 0xf1, 0x71, 0xfa, 0x65, 0xea, 0xf8, 0x0d, 0x42, 0x50, 0x48, 0x7b, 0xe7, 0x7b, 0xa6, 0x4c, 0x47, 0x41, 0x72, 0x1c, 0x26, 0x3f, 0xf0, 0x30, 0xce, 0x9d, 0x3f, 0x8f, 0xcb, 0x09, 0xf1, 0x48, 0xa1, 0x5f, 0x17, 0x7b, 0x3b, 0x54, 0xe4, 0xe7, 0xa0, 0xd2, 0xd2, 0x01, 0xcb, 0x57, 0xe9, 0xee, 0x7a, 0x75, 0x4a, 0x27, 0x8f, 0x41, 0x08, 0x3e, 0xdf, 0xf8, 0x2d, 0xe2, 0xe2, 0x13, 0x30, 0x60, 0xe4, 0x28, 0x44, 0x86, 0x9e, 0x15, 0x5b, 0x04, 0xd4, 0x4b, 0x68, 0xa0, 0x2d, 0xc5, 0xb0, 0xdb, 0x06, 0x21, 0x3f, 0x23, 0x09, 0xdd, 0xbb, 0xf6, 0xd0, 0x2f, 0xaf, 0x91, 0xdf, 0x20, 0x04, 0x9c, 0x72, 0x88, 0xef, 0x40, 0xd4, 0xd6, 0xd6, 0xa2, 0x9e, 0x7f, 0x75, 0xfc, 0xab, 0x13, 0xbf, 0xb7, 0xdf, 0x7b, 0x0f, 0x47, 0x0e, 0x1f, 0x41, 0xf0, 0xa9, 0x93, 0x8d, 0x0a, 0xd4, 0x0f, 0x30, 0x08, 0x81, 0x9c, 0x50, 0x5d, 0x68, 0xad, 0x1a, 0x02, 0x9a, 0xee, 0x1a, 0xa4, 0x06, 0xb8, 0x79, 0x7a, 0x43, 0x65, 0x67, 0x8f, 0x6f, 0xa9, 0x67, 0x5d, 0xcf, 0x5d, 0xb7, 0x82, 0xea, 0xea, 0x6a, 0x64, 0x5c, 0x4e, 0xe1, 0x0d, 0x0e, 0x1a, 0xea, 0x1b, 0xd0, 0xb5, 0x67, 0x4f, 0x8c, 0xf6, 0xf3, 0x45, 0x45, 0x55, 0x95, 0xe8, 0xbe, 0xde, 0x3e, 0x7d, 0x9b, 0xad, 0xe3, 0xba, 0x15, 0xf4, 0xed, 0xea, 0x81, 0xa4, 0x98, 0x18, 0xf0, 0x76, 0xce, 0x82, 0x66, 0xec, 0x2a, 0xea, 0x3d, 0x61, 0xf1, 0xe9, 0x00, 0x55, 0x76, 0xbd, 0xc2, 0xb9, 0xe6, 0xff, 0xf0, 0xc9, 0x6e, 0xf2, 0xe4, 0xc9, 0x8d, 0xf0, 0x7b, 0xec, 0xd8, 0xb1, 0x46, 0x61, 0xcd, 0x05, 0x18, 0x8d, 0xa2, 0x6e, 0xdd, 0xba, 0x21, 0x3b, 0x3b, 0xbb, 0xb9, 0x32, 0x75, 0xe3, 0xe4, 0xc5, 0x93, 0x42, 0xa5, 0x23, 0x47, 0x8e, 0x48, 0xb4, 0xf7, 0x96, 0xa8, 0x95, 0x22, 0xd8, 0xc9, 0xc9, 0x49, 0x8e, 0x16, 0xeb, 0xf1, 0x84, 0x09, 0x13, 0xc4, 0x93, 0x03, 0xa7, 0x4f, 0x9f, 0x2e, 0x2d, 0x5f, 0xbe, 0x5c, 0x1a, 0x3a, 0x74, 0xa8, 0x94, 0x9e, 0x9e, 0x2e, 0x79, 0x79, 0x79, 0x49, 0x53, 0xa6, 0x4c, 0x91, 0xe8, 0xd0, 0x22, 0x45, 0x45, 0x45, 0x5d, 0xcb, 0xa7, 0xbc, 0xd1, 0x8b, 0x8f, 0x8f, 0x8f, 0x52, 0x00, 0x87, 0x07, 0x06, 0x06, 0x2a, 0xd1, 0xdc, 0x00, 0x76, 0xd4, 0x9b, 0xc4, 0x93, 0xfd, 0x0f, 0x3c, 0xf0, 0x80, 0xf8, 0x1d, 0x3c, 0x78, 0x50, 0x27, 0x9f, 0x9c, 0x96, 0x13, 0x5e, 0xdb, 0x42, 0xb0, 0x47, 0x53, 0x88, 0x88, 0xa0, 0x77, 0xf6, 0xcb, 0x61, 0x23, 0x47, 0x8e, 0x14, 0xef, 0xdc, 0x88, 0xbb, 0xee, 0xba, 0x4b, 0xaa, 0xa8, 0xa8, 0x50, 0xe2, 0xa3, 0xa3, 0xa3, 0xa5, 0xf8, 0xf8, 0x78, 0xc5, 0xcf, 0xf9, 0x65, 0x67, 0x34, 0x0d, 0xa8, 0x42, 0xa3, 0x5c, 0xb3, 0x73, 0x91, 0x51, 0x25, 0x19, 0x48, 0x7c, 0xeb, 0x57, 0x70, 0xc3, 0x69, 0x60, 0x00, 0x73, 0xed, 0x16, 0x7c, 0xdd, 0xc9, 0xb4, 0xdd, 0x6a, 0xa2, 0x82, 0x78, 0xed, 0x3a, 0xfd, 0xdb, 0x6f, 0x28, 0xc9, 0xbf, 0x0a, 0x6b, 0x07, 0x17, 0x8c, 0x9f, 0x34, 0x09, 0x2a, 0x95, 0x6d, 0x9b, 0xaa, 0xe8, 0x10, 0x0a, 0x7c, 0xf2, 0x8f, 0x65, 0xe8, 0x41, 0x6b, 0x60, 0x7e, 0x5a, 0x12, 0xce, 0x45, 0xfe, 0x0e, 0xda, 0xdc, 0xc3, 0x8c, 0xb8, 0x20, 0x7d, 0x3d, 0xfb, 0xa1, 0xab, 0x77, 0x7f, 0x24, 0x44, 0x47, 0x61, 0xe9, 0x87, 0x9f, 0xb6, 0x0a, 0x90, 0x36, 0x03, 0x90, 0x97, 0x97, 0x8f, 0x90, 0x93, 0xbf, 0x21, 0xeb, 0xea, 0x55, 0xb8, 0x51, 0xc3, 0xfc, 0x47, 0xde, 0x89, 0xbe, 0xfd, 0xfa, 0x2b, 0x8d, 0xf9, 0xe8, 0xef, 0xef, 0xc2, 0xad, 0x6b, 0x37, 0xfc, 0xb0, 0x75, 0x13, 0x2a, 0xca, 0xcb, 0x30, 0xe6, 0x8e, 0xd1, 0xa8, 0xb1, 0xb4, 0x81, 0x3d, 0xea, 0x71, 0x3e, 0x2e, 0x0a, 0xf6, 0xb4, 0xff, 0x09, 0x7a, 0xf4, 0x09, 0x84, 0x1e, 0x3f, 0x86, 0x8f, 0xd6, 0x7e, 0xad, 0xe4, 0x6b, 0xe9, 0x4b, 0xab, 0x47, 0xf1, 0x87, 0x7f, 0x5f, 0x86, 0x0b, 0xb4, 0xbb, 0x5b, 0xbf, 0xf2, 0x03, 0x0c, 0x1f, 0x35, 0x16, 0x7f, 0x7d, 0xfe, 0x25, 0x3c, 0x38, 0x67, 0x2e, 0x7e, 0xda, 0xf6, 0x2d, 0xce, 0x9e, 0x0e, 0xc6, 0xab, 0xcf, 0xfd, 0x55, 0xb4, 0xa1, 0xe0, 0xea, 0x15, 0x1c, 0xfa, 0x69, 0x2f, 0xac, 0xad, 0xec, 0xd0, 0xd3, 0x6b, 0x30, 0x6a, 0x7b, 0xfa, 0x22, 0x9e, 0x98, 0x58, 0x59, 0x92, 0x3d, 0xcc, 0xea, 0x24, 0xd4, 0x55, 0xd5, 0x62, 0xdf, 0xf6, 0xad, 0xb0, 0x6e, 0x65, 0x4b, 0x5a, 0x45, 0x81, 0xb7, 0x16, 0xbd, 0x84, 0x15, 0x9f, 0x7e, 0xa1, 0x20, 0xe9, 0xad, 0x97, 0x9f, 0xc5, 0x80, 0xa1, 0xc4, 0x55, 0x38, 0x1f, 0x82, 0x65, 0x2b, 0xd7, 0xc1, 0xda, 0xda, 0x1a, 0x89, 0xa9, 0x99, 0xd8, 0xb1, 0x6b, 0x37, 0x2c, 0x89, 0x57, 0x32, 0x62, 0xe4, 0x08, 0xec, 0xf9, 0xe5, 0x20, 0x54, 0xf6, 0x8e, 0xb0, 0xa2, 0xb8, 0xf4, 0xc4, 0x78, 0xa4, 0x47, 0x06, 0xa3, 0x9b, 0xbb, 0x1b, 0x72, 0x8b, 0xf2, 0x28, 0xee, 0x14, 0xf1, 0x56, 0x36, 0xe2, 0x99, 0x05, 0x0b, 0x95, 0x32, 0x5b, 0xfa, 0xd2, 0x2a, 0x00, 0xde, 0x5e, 0xf0, 0x34, 0xfa, 0xf4, 0x1d, 0x48, 0x75, 0xd0, 0x52, 0x45, 0x67, 0x9b, 0xd3, 0x89, 0xe9, 0xc8, 0x49, 0xbd, 0x0c, 0x67, 0xda, 0xd4, 0x4f, 0x0f, 0x9a, 0x85, 0xea, 0x1a, 0xda, 0x8e, 0xa5, 0xa5, 0x51, 0xbc, 0x9a, 0x63, 0xc6, 0xbb, 0xa9, 0x8b, 0x91, 0x61, 0x50, 0x39, 0xbb, 0xc0, 0xd6, 0xde, 0x01, 0x76, 0xb4, 0x1f, 0xec, 0xef, 0xe3, 0x8d, 0xac, 0xf0, 0xa3, 0xb0, 0x25, 0xc6, 0xd8, 0xa2, 0xf7, 0x3f, 0x23, 0x9e, 0x10, 0x1d, 0x37, 0x5a, 0xe1, 0x5a, 0x05, 0xc0, 0x49, 0xda, 0xb3, 0x98, 0x99, 0x9b, 0x61, 0xec, 0xdd, 0xe3, 0x71, 0xfc, 0xf8, 0x6f, 0xf8, 0xd7, 0xfa, 0xb5, 0x98, 0xf9, 0xc2, 0xeb, 0x88, 0x08, 0x0d, 0x11, 0x18, 0x67, 0x6e, 0x09, 0x37, 0x9e, 0xd6, 0x4b, 0x5e, 0x7d, 0xc5, 0x80, 0xb5, 0xa0, 0x2d, 0x60, 0xd0, 0x43, 0x8f, 0x50, 0x43, 0xad, 0x50, 0x55, 0x98, 0x0e, 0x07, 0x02, 0xc4, 0xd1, 0xc9, 0x11, 0x39, 0xb4, 0x35, 0xd1, 0xe7, 0x53, 0x18, 0x03, 0x47, 0xab, 0x00, 0xe0, 0x0a, 0xf8, 0x94, 0xb9, 0xe2, 0xbd, 0xbf, 0x21, 0x37, 0x35, 0x11, 0x7d, 0xc6, 0x4e, 0x43, 0x51, 0x69, 0x05, 0x6d, 0x8a, 0xe5, 0x93, 0xa6, 0x06, 0x04, 0x6e, 0x3f, 0xfd, 0x99, 0x9b, 0x99, 0xe3, 0xfc, 0xd9, 0xd3, 0xe8, 0xd3, 0xa7, 0x0f, 0x54, 0xb6, 0x76, 0xf8, 0xf5, 0x00, 0x8d, 0x09, 0x6b, 0x1b, 0x01, 0xdc, 0x9b, 0x6f, 0xbe, 0x81, 0xa0, 0xa0, 0x20, 0x63, 0xda, 0xac, 0x93, 0xb6, 0xd5, 0x00, 0x68, 0x97, 0xf2, 0xd1, 0xba, 0xf5, 0x28, 0x2a, 0xab, 0xa2, 0xee, 0xc4, 0x3c, 0x77, 0xc6, 0xfe, 0xb5, 0x87, 0xa0, 0x00, 0x01, 0x10, 0x71, 0xe6, 0x18, 0xca, 0x68, 0xc0, 0xf2, 0x71, 0x42, 0xa5, 0xb2, 0x81, 0x87, 0x8b, 0x2b, 0x36, 0x7f, 0xb9, 0x46, 0x9d, 0xb6, 0x0d, 0xff, 0xdb, 0x65, 0x21, 0xab, 0xa9, 0xa9, 0x45, 0x2d, 0x1d, 0x43, 0xf8, 0x12, 0x84, 0x1d, 0xed, 0xfa, 0xe8, 0xbf, 0xfa, 0x9d, 0xfb, 0x3f, 0x77, 0x25, 0xdf, 0x80, 0x91, 0x28, 0x2f, 0x2f, 0x27, 0x76, 0x62, 0x39, 0x9c, 0x5c, 0xba, 0xc0, 0x9a, 0xf8, 0xd6, 0x97, 0x2e, 0x5d, 0x42, 0xff, 0xfe, 0xd7, 0xa6, 0x5c, 0x91, 0xd9, 0xc8, 0x7f, 0xed, 0x02, 0x40, 0xf6, 0x95, 0x2c, 0x3a, 0x47, 0x55, 0xa3, 0xa2, 0xb4, 0x54, 0xa9, 0x9e, 0x1b, 0xcd, 0xdd, 0xac, 0x87, 0x67, 0x1f, 0x11, 0x56, 0x49, 0x9c, 0xc4, 0xfb, 0x47, 0xdf, 0x01, 0xef, 0xbe, 0x3e, 0x08, 0x0b, 0x0b, 0x47, 0x44, 0xcc, 0x45, 0xb1, 0xa0, 0x29, 0x19, 0x5a, 0xf9, 0xd2, 0x2e, 0x5d, 0x88, 0xeb, 0x8e, 0x88, 0x3c, 0x8f, 0x6d, 0xff, 0xde, 0x86, 0xec, 0x9c, 0x5c, 0x54, 0x56, 0x6a, 0x6e, 0x03, 0x34, 0x63, 0x40, 0xdd, 0xab, 0xd4, 0x00, 0x75, 0xe9, 0xe2, 0x8a, 0x87, 0x1f, 0x0a, 0xc2, 0xe4, 0xc9, 0x93, 0xc4, 0x59, 0xaf, 0x95, 0xed, 0x56, 0xb2, 0xb5, 0x1b, 0x00, 0x4a, 0x89, 0x1d, 0xfc, 0xd2, 0xca, 0xf5, 0xaf, 0x83, 0x5b, 0xd9, 0x4c, 0x75, 0x6d, 0x02, 0x80, 0x07, 0x28, 0xff, 0x0c, 0xb9, 0xb3, 0x67, 0xcf, 0x8a, 0xf8, 0xeb, 0xa5, 0x33, 0x94, 0xbf, 0x45, 0xe1, 0xf2, 0xe1, 0x4c, 0x7e, 0x7e, 0xfc, 0xf1, 0xc7, 0x3c, 0x0f, 0x8a, 0xdf, 0xa3, 0x8f, 0x3e, 0x2a, 0xfd, 0xfa, 0xeb, 0xaf, 0x8a, 0xff, 0xc9, 0x27, 0x9f, 0x94, 0x93, 0x29, 0x61, 0x9c, 0xb6, 0x29, 0x77, 0xc7, 0x1d, 0x77, 0x88, 0x34, 0x01, 0x01, 0x01, 0x92, 0x87, 0x87, 0x87, 0x78, 0xd7, 0x4e, 0xf7, 0xc1, 0x07, 0x1f, 0xe8, 0x94, 0x71, 0xf1, 0xe2, 0x45, 0xed, 0x68, 0xe9, 0xa9, 0xa7, 0x9e, 0xd2, 0x89, 0x8f, 0x8b, 0x8b, 0xd3, 0x89, 0x97, 0x3d, 0x4d, 0xd6, 0x2e, 0x1f, 0x73, 0xf3, 0xf3, 0xf3, 0x45, 0x3a, 0x6e, 0xe4, 0xc6, 0x8d, 0x1b, 0xe5, 0x3c, 0xca, 0x93, 0x8f, 0xcd, 0xfa, 0x00, 0xec, 0xd9, 0xb3, 0x47, 0x84, 0x6d, 0xda, 0xb4, 0x49, 0x49, 0x27, 0x23, 0x44, 0x0e, 0x60, 0xbf, 0xbf, 0xbf, 0xbf, 0xf4, 0xcd, 0x37, 0xdf, 0x28, 0x3f, 0x0e, 0x1b, 0x33, 0x66, 0x8c, 0x48, 0xc2, 0xef, 0x83, 0x06, 0x0d, 0x92, 0x93, 0x2b, 0x61, 0xcc, 0x13, 0xd0, 0x77, 0x4d, 0x02, 0xc0, 0x89, 0x96, 0x2c, 0x59, 0xa2, 0x60, 0xe0, 0x9d, 0x77, 0xde, 0xd1, 0xcf, 0x27, 0xfc, 0x4d, 0x01, 0x20, 0x1f, 0xbf, 0x7d, 0x7d, 0x7d, 0x45, 0x1a, 0x62, 0x25, 0x2a, 0xe5, 0xc8, 0x85, 0xc8, 0x00, 0x11, 0x8f, 0x58, 0x0e, 0x92, 0x5e, 0x78, 0xe1, 0x05, 0x69, 0xcb, 0x96, 0x2d, 0xc2, 0x2f, 0xc7, 0x13, 0xeb, 0x4b, 0xf8, 0x3f, 0xfd, 0xf4, 0x53, 0x51, 0xc6, 0xd4, 0xa9, 0x53, 0x95, 0xf4, 0xf2, 0x8b, 0x41, 0x00, 0x38, 0x01, 0xb1, 0x50, 0x24, 0xba, 0x95, 0x94, 0xd3, 0x1a, 0xf5, 0xe4, 0xc6, 0x31, 0x25, 0x9b, 0x73, 0xa5, 0xa5, 0xa5, 0xd2, 0x4f, 0x3f, 0xfd, 0x24, 0x25, 0x27, 0x27, 0x37, 0x99, 0x2c, 0x27, 0x27, 0x47, 0x74, 0x61, 0x5a, 0x4f, 0x9a, 0x8c, 0xe7, 0xc0, 0xce, 0x69, 0x94, 0xc8, 0x6d, 0x52, 0xd7, 0xa6, 0x69, 0xd4, 0xa4, 0x2d, 0xd7, 0x54, 0x7e, 0xcb, 0x03, 0xd0, 0x39, 0x06, 0x4c, 0xdd, 0x8d, 0x6e, 0xf9, 0x2e, 0xd4, 0x2e, 0xe7, 0x81, 0x96, 0x50, 0x81, 0xcf, 0x06, 0x97, 0x49, 0xe2, 0xeb, 0x8f, 0xd0, 0x33, 0xbc, 0xf6, 0x60, 0xf0, 0xb0, 0xe1, 0x18, 0x34, 0xf8, 0xb6, 0x6b, 0x7b, 0x29, 0xba, 0x65, 0x02, 0x49, 0x21, 0x18, 0xeb, 0x3a, 0x64, 0x0c, 0x24, 0x51, 0xc3, 0x7f, 0xd9, 0xb1, 0x05, 0x7d, 0x06, 0x0c, 0xc1, 0xb8, 0x7b, 0x27, 0xd0, 0x46, 0x41, 0xc2, 0xb1, 0xdf, 0x7e, 0xc5, 0xd5, 0xc4, 0x58, 0x4c, 0x7a, 0x78, 0x1e, 0xfa, 0xf5, 0x1f, 0x60, 0x6c, 0xbb, 0x95, 0xf4, 0x37, 0x1c, 0x80, 0x98, 0x98, 0x58, 0xe2, 0x87, 0x1e, 0x44, 0xd2, 0xa5, 0x24, 0xba, 0xa8, 0xaa, 0xa1, 0xc3, 0x7f, 0x21, 0xac, 0x6d, 0x6c, 0xe0, 0xe2, 0xec, 0x06, 0x3b, 0x95, 0x3d, 0x31, 0x2f, 0xcc, 0x30, 0x63, 0xd6, 0x3c, 0x0c, 0xb9, 0xed, 0x36, 0xa5, 0x51, 0xc6, 0xbc, 0xdc, 0xf0, 0x31, 0xb0, 0x93, 0x58, 0x8a, 0xbe, 0xb7, 0x07, 0xd0, 0x31, 0x32, 0x18, 0xee, 0x6e, 0x1e, 0xc8, 0x2b, 0x28, 0x44, 0x61, 0x71, 0x19, 0x1c, 0x6d, 0xed, 0x11, 0x13, 0x13, 0x86, 0x29, 0x0f, 0x06, 0xe1, 0x9b, 0x75, 0x2b, 0xa9, 0xcd, 0xbc, 0xdd, 0x31, 0xde, 0xb5, 0x99, 0x02, 0xbf, 0x13, 0x1b, 0x31, 0x25, 0x29, 0x11, 0x75, 0xc4, 0x79, 0xee, 0xe3, 0xe5, 0x83, 0xb1, 0x13, 0xee, 0x53, 0xfa, 0x75, 0x7c, 0xfc, 0x05, 0xba, 0x92, 0xf5, 0xc0, 0xdc, 0xd9, 0xd3, 0x51, 0x57, 0x53, 0x87, 0xba, 0xba, 0x7a, 0x8c, 0xfe, 0xd3, 0x68, 0xd4, 0x9b, 0x59, 0x20, 0x95, 0xba, 0x4f, 0x1d, 0x1d, 0xfc, 0x6d, 0x55, 0x96, 0x78, 0x7b, 0xf9, 0x6a, 0x64, 0x90, 0xcc, 0xd0, 0x3d, 0x13, 0x27, 0x19, 0x0d, 0x81, 0xf1, 0xa3, 0x46, 0x53, 0x45, 0x72, 0x72, 0x0a, 0x8e, 0xee, 0xdf, 0x09, 0xef, 0xfe, 0xbe, 0x98, 0x38, 0x75, 0x06, 0x31, 0xb4, 0x2c, 0x51, 0x90, 0x9f, 0x8b, 0x55, 0x2b, 0xde, 0xc5, 0xf0, 0xd1, 0x13, 0x30, 0xe6, 0xee, 0xbb, 0x71, 0x25, 0x2d, 0x15, 0x27, 0x0e, 0x1f, 0x44, 0x0f, 0x8f, 0x6e, 0xc4, 0xb1, 0x20, 0x0c, 0x93, 0x44, 0x69, 0xbd, 0x47, 0x5f, 0x38, 0x39, 0x3a, 0xc2, 0x8d, 0xce, 0xcd, 0x35, 0x25, 0xb9, 0xb0, 0xb6, 0xb4, 0x40, 0xf0, 0xd1, 0x43, 0x68, 0xa8, 0xad, 0x6a, 0x15, 0x00, 0xad, 0xea, 0x42, 0x99, 0x24, 0xb7, 0xf8, 0xfd, 0xe6, 0xf5, 0x70, 0xec, 0xd2, 0x15, 0x29, 0x74, 0xd3, 0xeb, 0xee, 0xee, 0x01, 0x17, 0x57, 0x57, 0x84, 0x45, 0x44, 0x21, 0x37, 0xbf, 0x10, 0xd5, 0x55, 0xe5, 0x38, 0xb4, 0x7f, 0xbf, 0xa0, 0x4a, 0x4a, 0x4a, 0x12, 0xb2, 0x73, 0x0b, 0x91, 0x44, 0x00, 0xf7, 0x1b, 0x37, 0x1d, 0xa1, 0x27, 0x8e, 0xe2, 0xdc, 0xf1, 0x5f, 0x51, 0x54, 0x54, 0x4c, 0x5c, 0xb9, 0x5c, 0xe2, 0x6a, 0xe7, 0x20, 0x36, 0x3a, 0x42, 0x47, 0x8e, 0xd3, 0x18, 0x32, 0x18, 0x0d, 0x00, 0x4f, 0x87, 0x1b, 0x3f, 0xff, 0x14, 0x4b, 0x96, 0x7f, 0x80, 0x59, 0x7f, 0x9e, 0x87, 0xec, 0xf4, 0x24, 0x92, 0xac, 0x48, 0x11, 0x53, 0x63, 0x6a, 0x6c, 0x18, 0x5e, 0x7d, 0xfb, 0x3d, 0x4c, 0x98, 0x3c, 0x95, 0x38, 0xd7, 0x91, 0x70, 0x76, 0x73, 0xc7, 0x6b, 0x6f, 0x2d, 0x45, 0x5a, 0x46, 0x1a, 0x7a, 0x0d, 0x1d, 0x41, 0xec, 0xf5, 0x0a, 0xdc, 0x39, 0x71, 0x0a, 0xfa, 0x92, 0x80, 0x56, 0x51, 0x56, 0x1a, 0x72, 0x8a, 0x0a, 0x50, 0x52, 0x5e, 0x8c, 0xc5, 0xef, 0xae, 0x80, 0x67, 0xbf, 0x41, 0xc6, 0xb4, 0x5b, 0x49, 0x6b, 0x74, 0x17, 0x4a, 0xcf, 0xc8, 0xc0, 0xd0, 0x61, 0x6a, 0xa9, 0x30, 0x16, 0xd5, 0x1c, 0x1c, 0x70, 0x27, 0x36, 0xaf, 0xfb, 0x02, 0x96, 0x34, 0xb3, 0xb0, 0x04, 0x83, 0x9b, 0x9b, 0x1b, 0x2a, 0x2a, 0xab, 0x30, 0xf0, 0x8e, 0x7b, 0x11, 0x1e, 0x9b, 0x80, 0x90, 0xe8, 0x0b, 0xb8, 0x67, 0xfa, 0x6c, 0xd8, 0x75, 0xf7, 0xa4, 0x1b, 0xf2, 0x06, 0x31, 0x3e, 0x7e, 0xfc, 0x66, 0x0d, 0x1e, 0x21, 0xb6, 0xca, 0xf1, 0x90, 0xb3, 0x98, 0xf3, 0xd8, 0xf3, 0xd8, 0xb2, 0x71, 0x2d, 0x01, 0xfe, 0xae, 0xd2, 0x28, 0x63, 0x5e, 0x8c, 0x06, 0x20, 0x3c, 0xe4, 0x77, 0x14, 0xe7, 0xe5, 0xe2, 0x18, 0x09, 0xab, 0x32, 0x27, 0xae, 0xb0, 0xb8, 0x14, 0x35, 0xce, 0xdd, 0x51, 0x4e, 0x83, 0x58, 0x55, 0x59, 0x84, 0x43, 0xbf, 0x1c, 0x80, 0xa3, 0xef, 0x70, 0xe4, 0x56, 0xd7, 0xc0, 0xbe, 0x97, 0x37, 0xcd, 0x2d, 0x12, 0x7c, 0x6c, 0x9d, 0x91, 0x4a, 0x42, 0x34, 0x4e, 0xd4, 0xcd, 0x98, 0x3b, 0xed, 0x4c, 0x5c, 0xea, 0x33, 0xe1, 0x11, 0x98, 0x3c, 0x61, 0x12, 0xc9, 0xe7, 0x79, 0xa2, 0x57, 0x2f, 0xcf, 0x56, 0x73, 0xa7, 0x8d, 0x06, 0xa0, 0x86, 0x58, 0xe7, 0xa9, 0x29, 0xc9, 0xa8, 0x25, 0x19, 0x0d, 0x9e, 0xf8, 0x32, 0x73, 0xf3, 0x10, 0x12, 0xfe, 0x07, 0xc9, 0x6d, 0xd4, 0xc0, 0x7f, 0xfc, 0xfd, 0xb0, 0xe9, 0x3b, 0x04, 0x87, 0x7f, 0xde, 0xcb, 0xbc, 0x69, 0x35, 0x22, 0x69, 0xd1, 0xaa, 0xa2, 0x9b, 0x99, 0xcc, 0xb4, 0xcb, 0xc4, 0xf0, 0xaa, 0x80, 0x8d, 0xad, 0x2d, 0xa6, 0xcf, 0x7f, 0x19, 0xa3, 0x07, 0x7b, 0xe3, 0xec, 0xcf, 0x3f, 0xd0, 0xec, 0x54, 0x8e, 0x69, 0x0f, 0x3c, 0x62, 0x0c, 0xd2, 0x75, 0xd2, 0x1a, 0x3d, 0x8d, 0xc6, 0xc5, 0xc5, 0xa2, 0xb8, 0x30, 0x1f, 0x77, 0x8d, 0x1e, 0x27, 0x0a, 0x3a, 0x7c, 0xf0, 0x20, 0x7e, 0x3c, 0x7a, 0x1c, 0x6e, 0xbd, 0x7c, 0x30, 0x7c, 0xc4, 0x30, 0x62, 0xb1, 0x9f, 0xa3, 0x1d, 0x81, 0x9a, 0xd7, 0xcf, 0x5b, 0x06, 0x66, 0xa9, 0x94, 0x91, 0x88, 0x8d, 0xb3, 0xbd, 0x3d, 0x9c, 0xbb, 0xb8, 0xe1, 0x4a, 0x7a, 0x0a, 0xca, 0x8a, 0x0b, 0xa1, 0x32, 0xab, 0x47, 0xf7, 0x9e, 0xbd, 0xb1, 0x68, 0xd1, 0x22, 0x9d, 0x06, 0x19, 0xeb, 0x31, 0x9a, 0x02, 0x83, 0x07, 0x0f, 0xc1, 0x82, 0xf9, 0x73, 0x05, 0x00, 0x3c, 0xa0, 0x7f, 0xfe, 0xf7, 0x66, 0x0c, 0xb9, 0x7b, 0x26, 0xea, 0xa9, 0xd1, 0x11, 0xe7, 0x42, 0x85, 0x08, 0xb3, 0x8e, 0x40, 0x17, 0x11, 0xa2, 0x8a, 0xe4, 0x52, 0x1e, 0x7e, 0x68, 0x36, 0x9c, 0x48, 0x98, 0xba, 0xa4, 0x30, 0x1b, 0x6e, 0x2c, 0xd4, 0x4e, 0x80, 0x95, 0x97, 0x11, 0x4b, 0x9e, 0xca, 0xd0, 0x17, 0x7b, 0x36, 0x06, 0x08, 0xa3, 0x01, 0x60, 0x8c, 0xbe, 0xfa, 0xce, 0x32, 0xac, 0xfe, 0x68, 0x05, 0xba, 0xf5, 0xf1, 0x46, 0x15, 0x2c, 0x50, 0x43, 0x97, 0x1d, 0xc5, 0xb9, 0xf9, 0xa2, 0x5e, 0x96, 0xca, 0xe1, 0xbd, 0x8e, 0xec, 0x98, 0x0a, 0x7c, 0xd9, 0x91, 0x9f, 0x16, 0x8f, 0x6a, 0x62, 0xad, 0x5f, 0x22, 0x25, 0x81, 0xec, 0xbc, 0x6c, 0x5a, 0x37, 0xac, 0xa8, 0xe1, 0x66, 0x18, 0x32, 0xc4, 0xb7, 0x4d, 0x00, 0x18, 0xdd, 0x85, 0xe4, 0x86, 0x25, 0x26, 0x5e, 0xc4, 0xa7, 0x4b, 0x5f, 0x47, 0xcf, 0x81, 0x7e, 0xb0, 0xea, 0xd5, 0x0f, 0xe5, 0xcc, 0x99, 0xd6, 0xb4, 0x5b, 0x5c, 0x6f, 0x28, 0x30, 0xd0, 0x18, 0x28, 0x2b, 0xa3, 0x95, 0x37, 0x01, 0x3e, 0xde, 0x3e, 0x48, 0xb8, 0x18, 0x4f, 0xdd, 0xe8, 0x32, 0xcb, 0x1f, 0x89, 0x59, 0xe9, 0x1c, 0x51, 0xad, 0xb5, 0xd7, 0x4b, 0xdc, 0x16, 0xa3, 0x29, 0x20, 0x03, 0x30, 0x60, 0xc0, 0x20, 0xac, 0xfb, 0x7e, 0x0f, 0x7e, 0xd8, 0xb9, 0x03, 0x49, 0xf9, 0xa5, 0xe2, 0x72, 0x43, 0xdd, 0x66, 0x05, 0x0a, 0x4d, 0x52, 0xf5, 0x0d, 0x4d, 0x52, 0xc2, 0x05, 0xe4, 0xd0, 0x8d, 0x65, 0x25, 0xdd, 0x11, 0xa8, 0x68, 0xca, 0x75, 0x74, 0x72, 0x42, 0x57, 0x0f, 0xf7, 0x36, 0x35, 0xbe, 0x4d, 0x00, 0x70, 0x66, 0xee, 0x4e, 0xee, 0x74, 0x07, 0x9c, 0x90, 0x53, 0x24, 0x04, 0xba, 0xd4, 0x14, 0xb8, 0x06, 0x00, 0x53, 0x82, 0xc4, 0xb1, 0x68, 0xba, 0xad, 0x83, 0x05, 0x5d, 0x29, 0x59, 0xd0, 0xf5, 0x92, 0xbb, 0xab, 0x1b, 0x5d, 0x33, 0xa9, 0xf0, 0xf4, 0xbc, 0xd9, 0x98, 0x72, 0xff, 0x54, 0x0d, 0x90, 0xad, 0x7f, 0xb4, 0x9a, 0x02, 0x72, 0x95, 0xd4, 0x07, 0x49, 0xae, 0xb1, 0x4e, 0xdc, 0x56, 0xaa, 0xbb, 0xfe, 0x35, 0x00, 0x38, 0x0d, 0x8f, 0x09, 0x07, 0x6a, 0x74, 0xd7, 0x3e, 0x7d, 0xc5, 0x36, 0x9a, 0x45, 0xa7, 0x2d, 0x2d, 0xad, 0x90, 0x91, 0xc3, 0xb2, 0xd9, 0x6d, 0x77, 0x6d, 0x06, 0x80, 0x9b, 0xdb, 0x40, 0xa7, 0x29, 0xde, 0x69, 0xf2, 0xe0, 0x15, 0xcd, 0x17, 0xff, 0x1a, 0xc4, 0x3b, 0x5f, 0xf0, 0x15, 0x91, 0x20, 0xb9, 0xef, 0xc0, 0x01, 0xe2, 0x46, 0xd2, 0x86, 0xee, 0xc7, 0x9c, 0x1d, 0x9d, 0x70, 0xe9, 0x62, 0x62, 0xdb, 0x5b, 0x4f, 0x25, 0xb4, 0x19, 0x00, 0x96, 0x76, 0xaf, 0xa5, 0x5f, 0x16, 0xed, 0x77, 0x64, 0x46, 0xbb, 0x9a, 0x12, 0x54, 0x38, 0xed, 0x50, 0xdd, 0xba, 0x75, 0x15, 0x8b, 0x5a, 0xfa, 0xe5, 0x64, 0xbc, 0xf4, 0xd4, 0x7c, 0x94, 0x95, 0x94, 0x62, 0xd7, 0xc1, 0xa3, 0x70, 0xa3, 0x2b, 0xd6, 0xf6, 0x70, 0x6d, 0x06, 0x40, 0x65, 0x6d, 0x89, 0x6a, 0xda, 0xfb, 0x88, 0x2b, 0x56, 0x0d, 0x05, 0x18, 0x10, 0xf5, 0x22, 0x26, 0x5e, 0xa8, 0x1b, 0x49, 0xb0, 0xb5, 0xb1, 0x86, 0x0b, 0xdd, 0x4c, 0x3a, 0x38, 0x38, 0x50, 0x77, 0xa3, 0x31, 0x61, 0x46, 0xd3, 0x6d, 0x3b, 0xb8, 0x56, 0x4f, 0xa3, 0x72, 0xdd, 0xdc, 0xd0, 0xff, 0x99, 0x3d, 0x97, 0x4e, 0x58, 0x3c, 0xaf, 0x6b, 0xf0, 0xa1, 0x86, 0x40, 0x5c, 0x6a, 0xf3, 0xde, 0x87, 0x01, 0xa8, 0x2e, 0x2f, 0xa5, 0x15, 0xa3, 0x01, 0x35, 0xb4, 0xa8, 0x55, 0x56, 0xd5, 0x61, 0xf9, 0xfb, 0xef, 0x62, 0xb0, 0xef, 0x60, 0xb9, 0x98, 0x56, 0x3f, 0x8d, 0x07, 0x80, 0xba, 0x8b, 0x3e, 0xf7, 0x80, 0x57, 0xde, 0xb5, 0xeb, 0xd7, 0x23, 0x3a, 0xfa, 0x0f, 0x94, 0xd0, 0x7a, 0xc0, 0xab, 0xab, 0x70, 0x34, 0x16, 0xe4, 0x35, 0x41, 0xa2, 0xc1, 0x6c, 0x61, 0x41, 0xbb, 0xd7, 0xc1, 0x83, 0xb1, 0xf0, 0x95, 0x57, 0x68, 0x43, 0xe7, 0xdc, 0xea, 0x46, 0x6b, 0x67, 0x34, 0x1e, 0x00, 0xed, 0xdc, 0x37, 0xc1, 0xbb, 0xd1, 0x07, 0x9a, 0x9b, 0xa0, 0xcd, 0x3a, 0x4d, 0xe8, 0x04, 0x40, 0x07, 0x1d, 0x26, 0xf0, 0xb4, 0x69, 0x1a, 0xe5, 0xad, 0x04, 0xaf, 0x03, 0x86, 0x36, 0x63, 0x74, 0x85, 0x44, 0xfc, 0x20, 0xb5, 0x2a, 0xc3, 0xf8, 0xf1, 0xe3, 0xc5, 0xd6, 0xa3, 0xdd, 0x61, 0xa4, 0x69, 0xb0, 0x55, 0xae, 0xa0, 0xa0, 0x80, 0xd7, 0x5b, 0x29, 0x33, 0x33, 0xd3, 0x60, 0x7e, 0x8e, 0x97, 0x7f, 0x21, 0x21, 0x21, 0x06, 0xd3, 0xb5, 0x25, 0xa2, 0xd5, 0x14, 0x70, 0xa5, 0xf3, 0x2d, 0x55, 0xdc, 0x2c, 0x42, 0xe5, 0x78, 0xa6, 0x14, 0xff, 0x6e, 0x84, 0x6b, 0x34, 0x88, 0xf9, 0x74, 0xa4, 0x5d, 0x21, 0xcb, 0xbf, 0x69, 0xfb, 0xb9, 0x11, 0x74, 0x01, 0xae, 0x84, 0x7d, 0xf9, 0x65, 0x63, 0x4d, 0x8b, 0x5f, 0x7e, 0xf9, 0x85, 0xc4, 0xca, 0xec, 0xc4, 0x41, 0x65, 0xef, 0xde, 0xbd, 0x8d, 0xda, 0x5d, 0x53, 0x53, 0x03, 0x47, 0x62, 0x6e, 0xc9, 0xe5, 0xea, 0x4b, 0xb2, 0xe7, 0xe6, 0xe6, 0x8a, 0x6e, 0x29, 0xc7, 0x4f, 0x22, 0xf9, 0x52, 0x83, 0x4e, 0x9f, 0x7c, 0x8f, 0x3d, 0xf6, 0x98, 0x20, 0xfb, 0xd8, 0xb1, 0x63, 0x45, 0xd4, 0x2b, 0xaf, 0xbc, 0x22, 0xfc, 0xd3, 0xa6, 0x4d, 0x53, 0x92, 0x1e, 0x3e, 0x7c, 0x58, 0x62, 0x3f, 0x15, 0x2a, 0xad, 0x5c, 0xb9, 0x52, 0x09, 0xe7, 0x97, 0xb5, 0x6b, 0xd7, 0x8a, 0x70, 0x95, 0x4a, 0x25, 0x51, 0xc5, 0xe2, 0x9d, 0xd3, 0x69, 0x77, 0x21, 0xf6, 0x93, 0xf4, 0x96, 0x14, 0x1a, 0x1a, 0x2a, 0x7d, 0xf8, 0xe1, 0x87, 0x22, 0x0d, 0xb1, 0x63, 0x44, 0x39, 0xda, 0xf7, 0xca, 0x6b, 0xd6, 0xac, 0x91, 0xe6, 0xcf, 0x9f, 0xaf, 0x94, 0xa7, 0x53, 0x91, 0xc6, 0xc3, 0xdd, 0xa0, 0x91, 0xe3, 0x0a, 0x6c, 0x6d, 0x6d, 0x45, 0xf8, 0xec, 0xd9, 0xb3, 0x45, 0x01, 0x8d, 0x12, 0x51, 0x40, 0x53, 0x00, 0x70, 0x18, 0xff, 0x64, 0xb7, 0x7b, 0xf7, 0x6e, 0xe1, 0x97, 0x01, 0x90, 0x11, 0xa2, 0x7d, 0x4b, 0x3f, 0x71, 0xe2, 0x44, 0x25, 0xcf, 0xf0, 0xe1, 0xc3, 0xc5, 0xbb, 0xf6, 0x25, 0xf8, 0x9c, 0x39, 0x73, 0x44, 0x98, 0x7c, 0xf1, 0x2d, 0x97, 0xcd, 0xcf, 0x46, 0x5d, 0x88, 0x2a, 0xc7, 0x86, 0x0d, 0x1b, 0x84, 0xcc, 0x0f, 0x6f, 0x09, 0xb6, 0x6f, 0xdf, 0x8e, 0x4f, 0x3e, 0xf9, 0x84, 0x83, 0x5b, 0xe5, 0xf4, 0xbb, 0x47, 0x6c, 0x6c, 0xac, 0x28, 0xe7, 0xc7, 0x1f, 0x7f, 0x84, 0xfc, 0xe3, 0x0d, 0xde, 0x33, 0xcf, 0x3c, 0x23, 0xc2, 0xe9, 0xa6, 0x5f, 0x3c, 0xb5, 0x0f, 0xfa, 0xac, 0xf6, 0xcb, 0x8e, 0x25, 0xbc, 0x1a, 0x39, 0x6d, 0x68, 0xb4, 0xdf, 0x29, 0xa1, 0x64, 0x6f, 0x6f, 0xaf, 0x60, 0x46, 0x3b, 0x4e, 0x7e, 0xe7, 0x34, 0xfa, 0x5d, 0x88, 0xfa, 0xad, 0xc8, 0xc3, 0xb7, 0xec, 0xec, 0x64, 0x71, 0x04, 0x99, 0x02, 0xac, 0x07, 0xc1, 0xf9, 0xb8, 0x6b, 0xc8, 0x8e, 0x05, 0x39, 0xba, 0x77, 0xef, 0x2e, 0xbc, 0x4b, 0x97, 0x2e, 0x15, 0xf1, 0x8f, 0x3f, 0xfe, 0xb8, 0x1c, 0x2d, 0xfc, 0x9c, 0xa7, 0x29, 0xd7, 0x74, 0x28, 0xa5, 0x94, 0xc7, 0xc2, 0x82, 0x05, 0x0b, 0x74, 0xf2, 0x9d, 0x39, 0x73, 0x46, 0x29, 0x90, 0x0b, 0x95, 0x7f, 0xfd, 0xfa, 0xf5, 0x13, 0xe9, 0xca, 0xca, 0xca, 0x94, 0x30, 0x8e, 0xa3, 0x13, 0x98, 0xe2, 0x97, 0x0b, 0xba, 0xef, 0xbe, 0xfb, 0x94, 0x30, 0x39, 0xbf, 0x0c, 0x00, 0xa7, 0x19, 0x30, 0x60, 0x40, 0xa3, 0x78, 0x9a, 0x18, 0xe4, 0xec, 0x3a, 0x4f, 0x83, 0x00, 0x70, 0xaa, 0x43, 0x87, 0x0e, 0xe9, 0x24, 0x36, 0xc6, 0xc3, 0xe2, 0x33, 0x69, 0x69, 0x69, 0xcd, 0x66, 0x09, 0x0f, 0x0f, 0x97, 0x18, 0x21, 0xd4, 0x55, 0x9b, 0x4c, 0x17, 0x1c, 0x1c, 0x2c, 0x34, 0x50, 0x9a, 0x8c, 0xd4, 0x04, 0x76, 0xee, 0x46, 0xa9, 0x0b, 0x98, 0xd4, 0x35, 0x39, 0x0b, 0x99, 0xb4, 0x45, 0x46, 0x56, 0xde, 0x09, 0x80, 0x91, 0x08, 0x6b, 0xf7, 0xe4, 0x9d, 0x14, 0x68, 0x77, 0x94, 0x1a, 0x59, 0xe0, 0x2d, 0x4f, 0x81, 0x5b, 0x7e, 0x1d, 0x30, 0x92, 0x60, 0x37, 0x5d, 0xf2, 0x5b, 0xbe, 0x07, 0xdd, 0x74, 0x18, 0x35, 0xb2, 0x41, 0x9d, 0x04, 0x30, 0x12, 0x61, 0xed, 0x9d, 0xbc, 0xd5, 0x2c, 0x89, 0xf6, 0x6e, 0x48, 0x7b, 0x94, 0x47, 0xdb, 0x6b, 0x30, 0xb7, 0x20, 0x92, 0xf4, 0x5b, 0xe2, 0xff, 0x38, 0x8f, 0xab, 0x99, 0x29, 0xc4, 0xb4, 0x6a, 0x80, 0x25, 0xab, 0x4b, 0xd1, 0x75, 0x28, 0x2b, 0xbc, 0xd5, 0x54, 0x55, 0x88, 0xcb, 0x8a, 0x9e, 0xa4, 0xd8, 0x39, 0x74, 0xd8, 0x48, 0x0c, 0xf1, 0xf3, 0x17, 0xba, 0x60, 0xcc, 0x3d, 0x30, 0x85, 0xfb, 0x8f, 0x58, 0x03, 0xf8, 0xdc, 0x7e, 0x81, 0xac, 0x36, 0x1d, 0xda, 0xb7, 0x1b, 0x52, 0x5d, 0x95, 0x50, 0xc4, 0xe3, 0xdb, 0xae, 0x41, 0x43, 0xfd, 0x90, 0x4a, 0xda, 0xb3, 0xf9, 0xb9, 0x57, 0xc5, 0x0d, 0x2a, 0x13, 0xa2, 0x47, 0x4f, 0x4f, 0x38, 0xd3, 0x7d, 0x5d, 0x66, 0x72, 0x12, 0xcc, 0xea, 0x49, 0xf3, 0xaa, 0xa6, 0x1a, 0xb5, 0xc4, 0x16, 0xb8, 0x9f, 0x44, 0x1d, 0xfc, 0x02, 0x02, 0xda, 0x74, 0x5f, 0xdd, 0x1a, 0x02, 0xde, 0xf2, 0x04, 0x28, 0x20, 0x3b, 0x2c, 0xdf, 0x6f, 0xda, 0x40, 0x36, 0x41, 0x6a, 0x30, 0xff, 0xb9, 0x05, 0x38, 0x73, 0xea, 0x38, 0xbe, 0xfe, 0xf2, 0x0b, 0xf8, 0xf6, 0x1b, 0x48, 0x42, 0x61, 0x0e, 0x64, 0xa0, 0xe4, 0x02, 0x6a, 0xea, 0xaa, 0x49, 0xe3, 0xd7, 0x9e, 0x2e, 0x89, 0x00, 0x1b, 0x6b, 0x15, 0x7a, 0xf7, 0xe8, 0x8d, 0xda, 0xca, 0x32, 0x14, 0x94, 0x14, 0x21, 0x68, 0xde, 0x7c, 0xf8, 0x0e, 0x09, 0xc0, 0x57, 0xab, 0x3e, 0xa1, 0x5b, 0x60, 0x5b, 0x3c, 0x49, 0x0a, 0xb9, 0x2c, 0x2d, 0xd4, 0x51, 0xee, 0xa6, 0x23, 0x00, 0xf3, 0xb9, 0x99, 0x1b, 0xa4, 0xcd, 0x11, 0x32, 0x84, 0x8c, 0x52, 0x12, 0x5e, 0xf8, 0x6a, 0xcd, 0x2a, 0x12, 0xd6, 0x1c, 0x8a, 0x00, 0x52, 0xc1, 0x5e, 0xb1, 0xec, 0x4d, 0x24, 0x5f, 0x4a, 0xa0, 0x1b, 0xb8, 0x2a, 0xe2, 0x68, 0x55, 0x23, 0xd0, 0x7f, 0x38, 0x89, 0x1b, 0x76, 0xc5, 0x05, 0x92, 0x0f, 0xc8, 0x27, 0xd9, 0x1a, 0x96, 0x97, 0xec, 0xef, 0xd3, 0x9f, 0xf4, 0x0d, 0x2b, 0x10, 0x97, 0x10, 0x07, 0x73, 0x2b, 0x4b, 0xb8, 0x38, 0x39, 0xc0, 0xc5, 0xbd, 0x1b, 0x96, 0x2c, 0xfb, 0x0c, 0x1b, 0x56, 0x7f, 0x4e, 0x56, 0xc5, 0xf2, 0xc9, 0xd4, 0xe3, 0x7b, 0x42, 0x24, 0xc5, 0x50, 0xbd, 0xed, 0x19, 0x6e, 0x32, 0x02, 0x14, 0xd3, 0x45, 0x71, 0x64, 0x78, 0x18, 0xd9, 0xe3, 0xb9, 0x40, 0x06, 0x80, 0x2e, 0xc1, 0x82, 0xae, 0xee, 0xbb, 0x93, 0x5d, 0x18, 0x47, 0x07, 0x47, 0x61, 0xe1, 0x26, 0x3b, 0xfb, 0x2a, 0x4a, 0x48, 0xa0, 0xa8, 0xab, 0x47, 0x57, 0x78, 0xf5, 0x1b, 0x80, 0xc1, 0xb7, 0x07, 0x92, 0xc2, 0x65, 0x3f, 0x1d, 0xd8, 0x43, 0xce, 0x9c, 0x42, 0x44, 0x58, 0x28, 0x49, 0x4a, 0x05, 0xe1, 0xe3, 0x15, 0xef, 0x21, 0xea, 0x7c, 0x18, 0xc9, 0x34, 0xf0, 0x5c, 0xde, 0x40, 0x92, 0x86, 0x35, 0x42, 0x14, 0x66, 0xc4, 0x9d, 0xe3, 0xd0, 0xc5, 0xc7, 0x97, 0x64, 0x3b, 0xcd, 0x60, 0x49, 0xb7, 0xed, 0x15, 0xb9, 0x99, 0x88, 0x8f, 0x3c, 0x8d, 0x92, 0xb2, 0x62, 0x61, 0x89, 0x80, 0xe7, 0x7e, 0xbe, 0x23, 0xf6, 0xea, 0x7f, 0x1b, 0x16, 0xbd, 0xf9, 0x1e, 0x36, 0xaf, 0x59, 0x89, 0x6e, 0x3d, 0x7b, 0xe1, 0x09, 0x1a, 0x4d, 0x1d, 0xe1, 0x3a, 0x9c, 0x00, 0xb1, 0x71, 0x71, 0xf8, 0x85, 0x2c, 0xf1, 0x65, 0x5c, 0x4e, 0x84, 0xab, 0xab, 0x8b, 0x30, 0x71, 0x38, 0x3d, 0xe8, 0xcf, 0xb8, 0x3d, 0xa0, 0xb1, 0x29, 0x27, 0xbe, 0xd9, 0x59, 0x4b, 0x92, 0x91, 0x16, 0x34, 0xaf, 0x5f, 0x25, 0xbb, 0x76, 0x2a, 0x12, 0x51, 0xbe, 0xeb, 0x9e, 0xfb, 0x30, 0x6d, 0xc6, 0x4c, 0x71, 0x23, 0x74, 0xf4, 0xd0, 0x01, 0x44, 0x9e, 0x39, 0x41, 0x97, 0xe5, 0xe6, 0x42, 0x4a, 0x80, 0xe5, 0x31, 0x78, 0x21, 0x6e, 0xa0, 0x05, 0xa0, 0xa4, 0xb4, 0x04, 0xb6, 0xbd, 0xfa, 0xa2, 0xc2, 0xdc, 0x0e, 0x25, 0x57, 0xd3, 0x31, 0x6e, 0xfc, 0xbd, 0x34, 0x3a, 0x12, 0x91, 0x45, 0xf2, 0x4c, 0x1e, 0x24, 0x68, 0x65, 0x5d, 0xc6, 0xac, 0x7b, 0x2b, 0x16, 0x61, 0x62, 0x66, 0x25, 0xb1, 0xf8, 0xf9, 0xd6, 0xd8, 0x52, 0xb0, 0x18, 0xbb, 0xd2, 0x3a, 0x31, 0xff, 0xa5, 0xd7, 0x3a, 0x02, 0xff, 0x6d, 0x17, 0x91, 0x68, 0x69, 0x2b, 0x79, 0x77, 0xb2, 0x81, 0xee, 0xc0, 0xf3, 0x68, 0x67, 0xf2, 0xf8, 0xb3, 0x2f, 0x82, 0x0d, 0x13, 0x25, 0x26, 0x5c, 0xc4, 0xde, 0x1d, 0xff, 0xc6, 0xe1, 0xbd, 0x3b, 0x49, 0xfe, 0x23, 0x07, 0x77, 0xdf, 0x7b, 0xaf, 0x20, 0x08, 0x97, 0x19, 0x4f, 0x32, 0x20, 0xfb, 0x77, 0x6d, 0x43, 0x3d, 0x89, 0x93, 0x7a, 0xf4, 0xf3, 0xc5, 0xc2, 0xa5, 0xff, 0x10, 0xc2, 0x30, 0x87, 0x0f, 0xfc, 0x8c, 0x37, 0x5e, 0x7e, 0x0e, 0x73, 0x9f, 0x78, 0x86, 0xe6, 0x6a, 0x77, 0xd8, 0x38, 0xb9, 0x62, 0xca, 0xcc, 0x20, 0x2c, 0x25, 0xe3, 0x5f, 0x31, 0xd1, 0x91, 0xa8, 0x22, 0x3b, 0x9c, 0x36, 0xb6, 0x0e, 0x98, 0x10, 0xf4, 0xbf, 0xe8, 0xd2, 0xdd, 0x07, 0x66, 0x95, 0xe5, 0x28, 0x2d, 0xb2, 0xc5, 0x8e, 0x1f, 0xb6, 0xc3, 0xbd, 0x7b, 0x4f, 0x58, 0x35, 0xd4, 0xe0, 0x44, 0xf0, 0x7e, 0x94, 0x55, 0x94, 0xd0, 0x7a, 0x60, 0x23, 0xa6, 0x3a, 0x6b, 0xb2, 0xa4, 0xd0, 0x7f, 0x90, 0x1f, 0x5e, 0x7f, 0x7b, 0x19, 0xbe, 0xdb, 0xb0, 0x16, 0x2e, 0xbc, 0x82, 0x77, 0x90, 0xeb, 0x90, 0x11, 0xc0, 0xc8, 0x5f, 0xf5, 0xcf, 0x95, 0xa8, 0x26, 0x81, 0xfb, 0xb7, 0xfe, 0xef, 0x1f, 0x3a, 0xf3, 0x7b, 0x16, 0x09, 0x2f, 0x6f, 0xd9, 0xb4, 0x1e, 0x12, 0x09, 0x71, 0xf6, 0x19, 0x70, 0x1b, 0x66, 0x3c, 0xf4, 0x30, 0x22, 0xc2, 0x23, 0x71, 0xea, 0xc8, 0xcf, 0x42, 0x26, 0x72, 0xfc, 0xd4, 0x99, 0xb8, 0x9f, 0x24, 0xb2, 0x65, 0xc7, 0x3d, 0x3c, 0xf6, 0x8f, 0x68, 0x6c, 0xfb, 0x6e, 0x33, 0x86, 0xdf, 0x39, 0x1a, 0x31, 0x64, 0xaf, 0xf2, 0x81, 0x47, 0xe6, 0x90, 0xa1, 0x07, 0x27, 0x3c, 0xfd, 0xf8, 0x1c, 0x58, 0x3a, 0xb8, 0x22, 0x70, 0xfc, 0x14, 0xda, 0x76, 0x9a, 0x8b, 0x6d, 0xa7, 0x19, 0x8d, 0x0e, 0x2b, 0x6b, 0x2b, 0x21, 0xb1, 0x1d, 0x1f, 0x7e, 0x06, 0xaa, 0x0a, 0x9e, 0xd6, 0xdc, 0x11, 0x4d, 0x8b, 0x33, 0xdb, 0x5c, 0xb0, 0x55, 0xd1, 0x68, 0x20, 0x42, 0x6c, 0xfc, 0x76, 0x27, 0xa2, 0xff, 0x88, 0x45, 0xc8, 0xd1, 0xfd, 0x78, 0x76, 0xd1, 0xdb, 0xe8, 0xde, 0xa3, 0xa7, 0x5c, 0xe5, 0x0d, 0x7d, 0x76, 0x08, 0x01, 0x8e, 0x90, 0x39, 0xd9, 0xe0, 0xc3, 0xfb, 0x31, 0x6b, 0xde, 0x13, 0x18, 0x34, 0x84, 0xc4, 0x52, 0x68, 0x37, 0xc2, 0x5c, 0x7f, 0x79, 0xe7, 0x4d, 0x26, 0x1b, 0x71, 0xfc, 0xd4, 0x69, 0xd4, 0x5b, 0xa9, 0xc8, 0x4a, 0x09, 0x84, 0xed, 0xce, 0x82, 0xd4, 0x8b, 0x98, 0x4c, 0x32, 0xf8, 0x93, 0xa7, 0x4d, 0x17, 0x37, 0x80, 0xa5, 0x15, 0x55, 0x08, 0xbb, 0x98, 0x8c, 0xf2, 0x7a, 0x0b, 0x38, 0xd2, 0xb5, 0x28, 0xd9, 0x4d, 0x12, 0x53, 0x0f, 0x8b, 0xbf, 0xb0, 0x71, 0x16, 0x6b, 0x92, 0x1e, 0x49, 0x23, 0xe9, 0xf3, 0xac, 0xcc, 0x34, 0xd8, 0x68, 0x90, 0x6a, 0x4e, 0x06, 0x2a, 0x2c, 0x48, 0x24, 0xac, 0x9c, 0x16, 0xeb, 0xe3, 0xbb, 0xb7, 0x20, 0xe2, 0xd4, 0x61, 0xb8, 0x7b, 0x74, 0xc7, 0xe8, 0x11, 0x81, 0x70, 0x76, 0xb0, 0x47, 0x5c, 0x52, 0x32, 0x86, 0xdf, 0x35, 0x0e, 0xaf, 0x2c, 0x7a, 0x83, 0x2c, 0xdd, 0xed, 0x05, 0x13, 0xe8, 0xb9, 0xc5, 0x4b, 0xe0, 0xd9, 0xc7, 0xeb, 0x86, 0x22, 0x5d, 0xbb, 0xf0, 0x0e, 0x21, 0xc0, 0xce, 0xed, 0xdb, 0x10, 0xf5, 0xfb, 0x49, 0xd8, 0x58, 0x59, 0x93, 0x78, 0x65, 0xbd, 0x98, 0xa7, 0xb9, 0x11, 0xbc, 0x7f, 0x67, 0x31, 0x65, 0x4b, 0xd7, 0x6e, 0xb8, 0x9c, 0x57, 0x02, 0x4b, 0x42, 0xa6, 0x9f, 0xff, 0x30, 0x44, 0x87, 0x87, 0xa2, 0x9e, 0xe6, 0x7b, 0x73, 0xb2, 0xce, 0x6b, 0x59, 0x53, 0x81, 0xb1, 0x0f, 0xcd, 0xc5, 0xe0, 0xc0, 0x61, 0xb8, 0x9c, 0x98, 0x80, 0xcb, 0x49, 0x97, 0x85, 0x40, 0x95, 0xf6, 0xc1, 0x89, 0xb2, 0x09, 0x22, 0x55, 0x14, 0x17, 0x91, 0xc1, 0xea, 0x04, 0xda, 0xdd, 0x58, 0x93, 0xa5, 0x68, 0x67, 0x61, 0xfa, 0x84, 0x89, 0x60, 0x45, 0x7e, 0x7b, 0x92, 0xd1, 0xce, 0xc9, 0x48, 0x45, 0x02, 0x69, 0x8f, 0xb8, 0x39, 0x3b, 0xa1, 0xbb, 0xb3, 0x3d, 0xdc, 0x5c, 0xc8, 0x02, 0xb0, 0x9d, 0x13, 0x0a, 0x0a, 0xf2, 0x10, 0x38, 0x6a, 0x1c, 0x66, 0x3e, 0x4c, 0x23, 0xa8, 0x15, 0xca, 0x47, 0xda, 0x08, 0x35, 0xf6, 0xbd, 0x43, 0x4e, 0xc2, 0x96, 0x74, 0x3f, 0x5e, 0x49, 0x5b, 0xc3, 0xfb, 0xe8, 0xb0, 0x33, 0x76, 0xdc, 0x3d, 0xd7, 0x64, 0xa0, 0xa8, 0xb5, 0x2c, 0x5a, 0xc1, 0x23, 0xa4, 0xf4, 0xd8, 0x49, 0xe4, 0x16, 0x16, 0x63, 0xef, 0x9e, 0x9d, 0xb0, 0xb6, 0x73, 0xc4, 0xd0, 0x11, 0xbe, 0x18, 0x33, 0x7e, 0x3c, 0xec, 0x1d, 0xed, 0x71, 0x81, 0xa6, 0x9c, 0x9f, 0x76, 0xec, 0xa4, 0x59, 0x45, 0x7d, 0xc9, 0x2f, 0x90, 0xcf, 0x58, 0xd7, 0x72, 0x1c, 0x56, 0x57, 0x5b, 0x4d, 0x8b, 0x73, 0x8d, 0xd8, 0x45, 0x99, 0x93, 0x99, 0x16, 0x1b, 0x5a, 0x94, 0x2d, 0xe9, 0x59, 0x4e, 0xf2, 0xe6, 0x39, 0x39, 0x57, 0x89, 0x20, 0x36, 0xf0, 0x1b, 0x33, 0x09, 0x56, 0x15, 0xa4, 0xaa, 0x40, 0x46, 0x0a, 0xeb, 0xad, 0x7b, 0x60, 0xfc, 0x8c, 0x07, 0x11, 0x38, 0x7c, 0xa4, 0x56, 0x49, 0x1d, 0xfb, 0xda, 0x21, 0x04, 0x98, 0x3a, 0x6d, 0x06, 0x22, 0x49, 0xd3, 0x20, 0xf4, 0xe4, 0x09, 0x41, 0x00, 0x6d, 0x79, 0x16, 0x36, 0xa9, 0x74, 0x2e, 0xf8, 0x04, 0x32, 0xa3, 0xce, 0xc2, 0xaa, 0xab, 0x17, 0x66, 0x3d, 0xf5, 0x12, 0xa9, 0x0f, 0x79, 0x22, 0x23, 0xe5, 0x32, 0xa2, 0xce, 0xfd, 0x2e, 0xb4, 0x12, 0xb8, 0x17, 0x9b, 0x6b, 0x58, 0x05, 0xac, 0xa5, 0xa0, 0x83, 0x7a, 0x75, 0xf7, 0xa7, 0x11, 0x60, 0x2e, 0x6e, 0xb3, 0x7b, 0xf7, 0xe9, 0x8f, 0x3b, 0xc8, 0xec, 0x92, 0x03, 0x11, 0xce, 0x82, 0xe4, 0x24, 0x39, 0xbc, 0x24, 0x3f, 0x0b, 0xf6, 0x16, 0x75, 0xb0, 0xe3, 0xc3, 0x18, 0x9f, 0xc6, 0xa8, 0x2c, 0x47, 0x12, 0x72, 0xcb, 0x48, 0xcf, 0x20, 0x02, 0xd9, 0x8a, 0x11, 0xa9, 0x3d, 0xa2, 0x3a, 0x92, 0x04, 0x1d, 0x32, 0x05, 0x31, 0x40, 0xa5, 0xb4, 0x2d, 0xfc, 0xe2, 0x93, 0x0f, 0x49, 0x3d, 0xa9, 0x1a, 0x73, 0x9f, 0x7e, 0x09, 0x3e, 0xde, 0x5e, 0x60, 0xed, 0xa1, 0x6f, 0xbe, 0x5c, 0x83, 0x2b, 0xa9, 0x97, 0xd0, 0xd3, 0x7f, 0x14, 0x3c, 0x7d, 0xfd, 0x68, 0x0e, 0xcf, 0x40, 0x49, 0x51, 0x11, 0xcc, 0x69, 0x4e, 0x67, 0xfe, 0x8d, 0xda, 0x26, 0x8b, 0x06, 0xe5, 0xfc, 0x60, 0x42, 0x68, 0x7a, 0x3f, 0x2f, 0xc8, 0xea, 0x85, 0x44, 0x3d, 0x32, 0xf2, 0xc9, 0x2e, 0x96, 0x17, 0x99, 0xcc, 0x09, 0x7a, 0xe4, 0x61, 0xd8, 0x92, 0x68, 0xbc, 0xb9, 0xb9, 0x05, 0xad, 0x0f, 0x16, 0x48, 0x4b, 0x8c, 0x47, 0x2e, 0xed, 0xbe, 0xd8, 0x06, 0x10, 0x13, 0x93, 0x91, 0xcd, 0xd2, 0x27, 0xa5, 0xc5, 0x25, 0x74, 0x42, 0x56, 0xe1, 0xf6, 0xdb, 0xfd, 0x95, 0xdd, 0x57, 0x47, 0x22, 0x9f, 0xeb, 0xea, 0x30, 0x02, 0x70, 0x65, 0x8c, 0xb0, 0xa8, 0xf3, 0x91, 0xd8, 0xf2, 0xd5, 0x3a, 0x74, 0x71, 0xb0, 0x15, 0xbb, 0x90, 0x22, 0x42, 0x76, 0x17, 0xaf, 0x41, 0x70, 0xa6, 0x9e, 0x5b, 0x4d, 0xd2, 0x99, 0xe5, 0x64, 0x34, 0x46, 0xb6, 0x8a, 0xc3, 0x88, 0x16, 0x38, 0xe7, 0xcc, 0xe4, 0x64, 0x91, 0x4d, 0xb5, 0x4f, 0xf3, 0x5f, 0x43, 0x04, 0x46, 0x36, 0x1b, 0xa3, 0x49, 0xf9, 0x23, 0x12, 0x36, 0xa4, 0x83, 0x66, 0x47, 0x82, 0xe5, 0x3c, 0xd2, 0xb8, 0xac, 0x2c, 0xd2, 0xf1, 0xc9, 0xa5, 0x38, 0x1b, 0x92, 0x92, 0x67, 0xd1, 0x67, 0x3e, 0x65, 0xf3, 0xd4, 0xc4, 0xd4, 0x7b, 0xe6, 0xd9, 0x67, 0x30, 0x63, 0x06, 0xe9, 0xbd, 0x75, 0xf0, 0xdc, 0x2f, 0xc3, 0xd0, 0x21, 0x53, 0x90, 0x5c, 0x19, 0xf7, 0xbc, 0x00, 0xd2, 0xd8, 0xf2, 0x5f, 0xbd, 0x81, 0x6c, 0xaa, 0x97, 0x22, 0x34, 0xe4, 0x0c, 0x19, 0xdc, 0x3f, 0x48, 0xe6, 0xb7, 0x92, 0x61, 0xe1, 0x4a, 0x22, 0xd5, 0x36, 0x76, 0x84, 0xb0, 0x7a, 0xf5, 0x1a, 0xa1, 0xe9, 0xf4, 0x9c, 0x57, 0xfd, 0x4a, 0xe8, 0x97, 0x3b, 0xbc, 0xa6, 0x40, 0x65, 0x04, 0x70, 0x02, 0xfa, 0x35, 0x90, 0x1e, 0x52, 0x83, 0x15, 0x59, 0x7c, 0xa3, 0x83, 0x94, 0x9b, 0xbb, 0xbb, 0xd8, 0x5e, 0x72, 0x8f, 0xaf, 0xa6, 0xd3, 0x76, 0x35, 0xe9, 0x1c, 0xa9, 0x48, 0xdf, 0x88, 0x91, 0xce, 0x32, 0xb7, 0x6c, 0xab, 0xac, 0x92, 0x76, 0x52, 0x0e, 0x34, 0x2d, 0x99, 0x0a, 0xf9, 0x0c, 0x46, 0x87, 0x8e, 0x00, 0xae, 0xb0, 0x29, 0x77, 0x22, 0x38, 0x18, 0x61, 0x71, 0x17, 0x50, 0x55, 0x4f, 0x6a, 0x40, 0x84, 0x14, 0xa2, 0x80, 0x1a, 0xeb, 0x9a, 0xd9, 0x86, 0x15, 0xb8, 0x18, 0xc7, 0xe4, 0x15, 0x8e, 0x09, 0xa1, 0x38, 0xf2, 0x70, 0x38, 0xcf, 0xf5, 0xcc, 0x6a, 0x8e, 0x0e, 0xff, 0x1d, 0xb9, 0xa4, 0xd0, 0xc8, 0x06, 0x0f, 0x2d, 0xa8, 0x97, 0xf3, 0x3a, 0x20, 0x0a, 0xa3, 0x44, 0xd6, 0xb4, 0x1b, 0x72, 0xa2, 0xed, 0xe7, 0x8c, 0xa9, 0x93, 0x31, 0x93, 0x4e, 0xd3, 0x2c, 0x20, 0xed, 0xe2, 0xe2, 0x62, 0x52, 0x02, 0x74, 0xe8, 0x08, 0x50, 0x90, 0xa6, 0xf7, 0xc2, 0xfa, 0x88, 0xb5, 0xb5, 0xf5, 0x64, 0x49, 0x99, 0x50, 0xcd, 0xc8, 0xe7, 0x1f, 0x3b, 0x41, 0x07, 0x35, 0xf2, 0x99, 0x02, 0x9a, 0x37, 0x75, 0x9c, 0x42, 0x04, 0x0a, 0xe5, 0x75, 0x81, 0xf2, 0xd0, 0x4a, 0x40, 0x73, 0xbe, 0x25, 0x6a, 0xe9, 0xdd, 0x42, 0x50, 0x8c, 0xfd, 0x34, 0xdd, 0xd0, 0xf4, 0xc2, 0xd3, 0x11, 0x13, 0xc4, 0x52, 0x65, 0x27, 0x90, 0xce, 0x22, 0x96, 0x37, 0x83, 0xbb, 0x29, 0x08, 0xc0, 0x53, 0x02, 0x9f, 0x0f, 0xd8, 0xa2, 0x9b, 0x50, 0x9a, 0xd0, 0x10, 0xe0, 0x5a, 0x4f, 0x57, 0xb0, 0xad, 0x8b, 0x33, 0x26, 0x8a, 0x48, 0x44, 0x44, 0xa0, 0x77, 0x6b, 0x95, 0x0a, 0x7f, 0xba, 0x67, 0x92, 0x50, 0x89, 0xac, 0xa7, 0xc2, 0xb8, 0x87, 0x73, 0x38, 0x8b, 0xf8, 0x5b, 0x10, 0x21, 0xc4, 0x30, 0x22, 0x7f, 0x58, 0x6c, 0x22, 0x02, 0xfc, 0x53, 0xe0, 0xe5, 0xe5, 0xad, 0x5b, 0x9e, 0x09, 0x7c, 0x37, 0x09, 0x01, 0xd4, 0xc6, 0xce, 0xea, 0x6a, 0x1b, 0x50, 0x40, 0x9f, 0xc8, 0xa8, 0x22, 0xc5, 0x6e, 0x81, 0x39, 0x0d, 0x42, 0xb8, 0x83, 0xcb, 0xc8, 0x23, 0xfc, 0x09, 0x0f, 0x8f, 0x14, 0x4b, 0x9a, 0x52, 0x5c, 0x69, 0xae, 0xe7, 0x45, 0x95, 0x0f, 0x75, 0x15, 0xe5, 0xb5, 0xb4, 0x88, 0x97, 0x22, 0x3f, 0x3f, 0x0f, 0xb5, 0xb4, 0xeb, 0xf2, 0xed, 0xef, 0x03, 0x57, 0x9a, 0x62, 0xb2, 0x49, 0x37, 0xf5, 0x6a, 0x61, 0x09, 0x11, 0x88, 0xec, 0xf1, 0x91, 0xd6, 0x8b, 0x8a, 0xb4, 0x3a, 0x79, 0xd4, 0xdd, 0x0c, 0xee, 0xa6, 0x20, 0x40, 0x2d, 0x69, 0xd0, 0x94, 0x14, 0x17, 0xc3, 0x8c, 0x0e, 0x4a, 0x3c, 0x5d, 0xd8, 0xd2, 0xfc, 0xad, 0xee, 0xd9, 0x6a, 0x14, 0xf1, 0x3b, 0xd3, 0x80, 0x9d, 0xe8, 0xf0, 0xe4, 0x11, 0x53, 0x15, 0xcd, 0xfb, 0x0d, 0x34, 0x72, 0x04, 0x7d, 0x34, 0x08, 0x2d, 0x25, 0x36, 0xb7, 0x05, 0x1d, 0xc6, 0xe6, 0xcc, 0x0e, 0x42, 0xaf, 0xde, 0x9e, 0xe2, 0x8a, 0x92, 0x17, 0xdc, 0x18, 0x92, 0x4c, 0x3d, 0x75, 0x2e, 0x02, 0x55, 0xbc, 0x00, 0x13, 0xfb, 0x59, 0x8c, 0x08, 0x75, 0x91, 0x26, 0xfd, 0x7f, 0x53, 0x10, 0x20, 0x90, 0x58, 0xd1, 0x07, 0x8f, 0x9d, 0xa0, 0xcb, 0x90, 0x22, 0xfa, 0xa4, 0x4a, 0x03, 0x8d, 0x80, 0x0a, 0x42, 0x30, 0xe1, 0x45, 0xee, 0xf9, 0x02, 0x45, 0x72, 0xdf, 0x57, 0xe3, 0x4b, 0x8c, 0x00, 0x42, 0xa4, 0x9d, 0x1d, 0xed, 0x6c, 0xcc, 0x79, 0x04, 0xa9, 0x39, 0x98, 0xb6, 0x36, 0x56, 0xa4, 0x4e, 0x5e, 0x8b, 0xe3, 0x27, 0x83, 0x71, 0xf7, 0xb8, 0x71, 0x74, 0xb1, 0xe2, 0x84, 0xb4, 0xf4, 0x74, 0x84, 0x86, 0x45, 0x90, 0x96, 0x75, 0x2d, 0x2a, 0x4b, 0xcb, 0x70, 0xfb, 0x9d, 0x81, 0xf0, 0xd4, 0xd8, 0x4a, 0x34, 0x29, 0xf6, 0x19, 0x44, 0xea, 0x5d, 0x6a, 0xc8, 0x4c, 0xdc, 0x12, 0xb6, 0x78, 0xf9, 0xe5, 0xc6, 0x8d, 0xd8, 0x4f, 0x26, 0x6c, 0xa9, 0x51, 0xc4, 0x2a, 0xb6, 0x16, 0x97, 0x34, 0xbc, 0xb0, 0x32, 0x0b, 0xe2, 0xda, 0x1e, 0x88, 0x1b, 0xaa, 0x6e, 0xb2, 0x39, 0x19, 0xd2, 0x30, 0x63, 0x2d, 0x71, 0xe1, 0xd5, 0x10, 0x88, 0x1e, 0x75, 0xc4, 0xe5, 0xac, 0x25, 0xb6, 0x34, 0xdf, 0xae, 0xd5, 0x13, 0x6b, 0x99, 0x17, 0x6f, 0x3e, 0x00, 0xfb, 0xf9, 0xfb, 0xe3, 0x89, 0x27, 0xe6, 0x83, 0x4c, 0xc1, 0x89, 0x69, 0xcb, 0xc4, 0x20, 0x8b, 0xea, 0x6f, 0x1a, 0x02, 0xdc, 0x0c, 0xc8, 0x30, 0x45, 0x1b, 0x68, 0x6b, 0xd0, 0xe9, 0x4c, 0x89, 0x81, 0x4e, 0x02, 0x98, 0x12, 0xfb, 0x54, 0x77, 0x27, 0x01, 0xfe, 0x9b, 0x09, 0xc0, 0x8c, 0xb8, 0xff, 0x76, 0x67, 0x92, 0x11, 0xc0, 0xd2, 0x0e, 0xcc, 0x98, 0x63, 0x76, 0x00, 0x3f, 0xe5, 0x8f, 0x41, 0x1a, 0x43, 0x0c, 0x52, 0x33, 0x15, 0x1f, 0x02, 0xe0, 0xfc, 0xfa, 0x3f, 0x52, 0xdc, 0x32, 0xa6, 0x28, 0x93, 0xa6, 0x35, 0xc9, 0x39, 0x80, 0x35, 0x3d, 0xe9, 0x43, 0x3b, 0xe0, 0xef, 0x05, 0xd2, 0x37, 0x72, 0xc0, 0x9f, 0x10, 0x32, 0xd6, 0xf5, 0x24, 0x19, 0x22, 0x52, 0xb4, 0x04, 0x29, 0x5c, 0x8a, 0xed, 0x26, 0xd9, 0xb6, 0x05, 0x7f, 0x7f, 0x8a, 0x1d, 0x29, 0xab, 0x19, 0x5b, 0x9c, 0xc9, 0xd2, 0xb7, 0x68, 0x1b, 0x4a, 0xdf, 0x45, 0xd4, 0xe9, 0xa5, 0xfc, 0x95, 0x12, 0xed, 0x8f, 0x5b, 0xf2, 0x05, 0x79, 0x52, 0x52, 0x92, 0x72, 0x7a, 0x65, 0xd1, 0xbe, 0x5e, 0xbd, 0x7a, 0x35, 0x09, 0x14, 0x7d, 0x3a, 0x09, 0xfc, 0xd1, 0x34, 0xd9, 0xf1, 0xc9, 0x97, 0x4c, 0x30, 0x0b, 0x66, 0x99, 0x1c, 0x76, 0xbd, 0x27, 0xf3, 0x78, 0x52, 0x53, 0x53, 0x41, 0xda, 0x7d, 0xe2, 0x62, 0x85, 0xd3, 0x33, 0x11, 0x49, 0xbd, 0x51, 0x64, 0x65, 0xa2, 0x30, 0x61, 0x0d, 0x39, 0xb6, 0xfd, 0x9e, 0x90, 0x90, 0x00, 0xd6, 0xcb, 0xe4, 0x3c, 0x6c, 0x61, 0x7f, 0xc8, 0x90, 0x21, 0xc4, 0x1b, 0xf2, 0x32, 0x94, 0x45, 0x27, 0x9c, 0xdb, 0xcf, 0x79, 0x49, 0x99, 0x8f, 0xc4, 0x60, 0xaa, 0x44, 0xfb, 0xc9, 0x8a, 0x33, 0xd9, 0x03, 0xeb, 0xa1, 0x93, 0xae, 0x45, 0x1e, 0x3e, 0x88, 0x35, 0xe7, 0xe6, 0xce, 0x9d, 0xcb, 0x27, 0x9c, 0x46, 0x3f, 0xd9, 0x6c, 0xf4, 0xaa, 0x55, 0xab, 0x1a, 0xc5, 0x71, 0x7a, 0x52, 0x30, 0x6d, 0x54, 0x2c, 0x01, 0xd9, 0x64, 0x5a, 0x7d, 0x7d, 0x51, 0xfd, 0x8c, 0xac, 0x89, 0x48, 0xca, 0xac, 0x4d, 0xe6, 0x6d, 0xaa, 0x6d, 0xac, 0x86, 0xac, 0xef, 0x48, 0x87, 0x5a, 0xd8, 0xda, 0x6e, 0x2a, 0xbd, 0x76, 0x18, 0xe9, 0x77, 0x4b, 0x07, 0x0e, 0x1c, 0xd0, 0xcf, 0x2e, 0x34, 0x22, 0x89, 0x75, 0x7d, 0xdd, 0x36, 0xb0, 0xed, 0xee, 0xaf, 0xbe, 0xfa, 0xaa, 0x51, 0x7e, 0x43, 0x01, 0xdc, 0x6b, 0x5b, 0xe4, 0xb4, 0x75, 0xaf, 0xb5, 0x2d, 0xac, 0x73, 0x66, 0x56, 0xa4, 0x95, 0x81, 0x60, 0x3d, 0x53, 0x6d, 0x55, 0x65, 0x43, 0x85, 0xcb, 0x4a, 0xb8, 0x9c, 0xaf, 0x39, 0x02, 0xb0, 0xd2, 0x2e, 0x4d, 0x59, 0x4a, 0xf9, 0xd4, 0xcb, 0xc4, 0x17, 0x4b, 0xe5, 0x72, 0xa9, 0x27, 0x4b, 0xde, 0xde, 0xde, 0x4a, 0x3c, 0x97, 0x27, 0x2b, 0xf6, 0xca, 0x69, 0xe8, 0xe3, 0xc2, 0x4a, 0x3c, 0xad, 0x17, 0xd2, 0xe9, 0xd3, 0xa7, 0xe5, 0x28, 0xe5, 0xc9, 0x6a, 0xa9, 0xb2, 0x72, 0x31, 0x97, 0xb1, 0x70, 0xe1, 0x42, 0x25, 0x8e, 0xf5, 0x75, 0x65, 0xf8, 0xf8, 0xf9, 0xfe, 0xfb, 0xef, 0x2b, 0x71, 0xf2, 0x0b, 0x13, 0x8d, 0xcb, 0x96, 0xd3, 0x8d, 0x1a, 0x35, 0x4a, 0x8e, 0x6a, 0xf6, 0xd9, 0x62, 0x02, 0x70, 0x2f, 0xd4, 0xee, 0xc1, 0x6c, 0x02, 0x9f, 0x1d, 0x4d, 0x05, 0x8a, 0x42, 0x30, 0xcd, 0xcb, 0x52, 0x53, 0x3a, 0xe1, 0x4d, 0xb5, 0xa0, 0xa5, 0x04, 0xe0, 0xaf, 0xbe, 0xc9, 0x40, 0xf1, 0x33, 0x25, 0x25, 0xa5, 0x51, 0x71, 0xc5, 0xc5, 0xc5, 0x3a, 0x69, 0xf4, 0x09, 0x30, 0x6e, 0xdc, 0x38, 0x9d, 0x78, 0xed, 0xf2, 0x0c, 0xbd, 0xcb, 0x96, 0xe9, 0x49, 0xa8, 0x4c, 0xe2, 0x51, 0x21, 0xa7, 0xfb, 0xcb, 0x5f, 0xfe, 0xd2, 0xa8, 0x7e, 0x39, 0x40, 0x7f, 0x36, 0x60, 0x5d, 0xe2, 0xeb, 0xb9, 0x16, 0xef, 0x82, 0x78, 0xa7, 0x41, 0x05, 0x92, 0xd9, 0x4d, 0x77, 0x6a, 0x0b, 0xc0, 0x9f, 0x47, 0xdf, 0xb6, 0x6d, 0x1b, 0x5d, 0x68, 0xdf, 0x2e, 0x16, 0x41, 0xde, 0xd1, 0xf0, 0x9c, 0xc8, 0x26, 0x1a, 0xda, 0xd3, 0xe9, 0xcf, 0xcb, 0x11, 0x11, 0x11, 0x8d, 0x8a, 0xe7, 0x76, 0x35, 0xe7, 0xe8, 0x03, 0x78, 0x4a, 0x34, 0xaf, 0x4f, 0xbc, 0xa6, 0x11, 0x62, 0x9a, 0xfd, 0xc9, 0x7a, 0xfb, 0xfc, 0x79, 0xee, 0x11, 0x23, 0x46, 0x28, 0xf9, 0xe9, 0x5b, 0x0b, 0x42, 0x28, 0x4c, 0x09, 0xd0, 0x7a, 0xd1, 0x36, 0x2f, 0xc1, 0x97, 0xfe, 0xcc, 0x73, 0xba, 0xae, 0xbb, 0x1e, 0x85, 0xf4, 0xe3, 0xb3, 0xb2, 0xb2, 0x74, 0x7a, 0x04, 0x55, 0x20, 0x3e, 0x4e, 0xcc, 0x73, 0xac, 0x21, 0xc7, 0xa3, 0x62, 0xd6, 0xac, 0x59, 0xe2, 0x93, 0x84, 0xb4, 0x78, 0x4b, 0xfc, 0xe3, 0xb9, 0x92, 0xf3, 0xf2, 0x8f, 0xe7, 0x56, 0x39, 0x9c, 0x08, 0x2a, 0xcc, 0x4e, 0x68, 0x97, 0xa5, 0x3f, 0x05, 0xf0, 0x34, 0xf4, 0xdc, 0x73, 0xcf, 0x49, 0x2f, 0xbe, 0xf8, 0xa2, 0xe4, 0xe9, 0xe9, 0xa9, 0x94, 0x23, 0x97, 0x47, 0x8b, 0xaa, 0xb4, 0x75, 0xeb, 0x56, 0xed, 0x22, 0xa4, 0xa7, 0x9f, 0x7e, 0x5a, 0x27, 0x1d, 0x7f, 0xcb, 0x98, 0xdb, 0xc4, 0x1f, 0x99, 0x60, 0xab, 0x12, 0xac, 0xae, 0x2f, 0xe7, 0xe7, 0x27, 0xa7, 0x97, 0x1d, 0x2d, 0xb4, 0xe2, 0x5b, 0x0e, 0xda, 0xf1, 0xbc, 0x06, 0xb2, 0xf5, 0x88, 0x37, 0xdf, 0x7c, 0x53, 0xe2, 0xe9, 0x46, 0x3b, 0x8e, 0xcb, 0xbe, 0x9e, 0xa6, 0xbd, 0x5c, 0x76, 0x8b, 0xa7, 0x20, 0x39, 0x03, 0x3f, 0x13, 0x13, 0x13, 0x25, 0x32, 0x1e, 0x25, 0x2a, 0x65, 0x64, 0x34, 0x87, 0x7c, 0x4e, 0x4f, 0xbb, 0x0e, 0xf1, 0xe1, 0x09, 0xda, 0xf1, 0xe8, 0x34, 0x54, 0xbb, 0xd1, 0xf2, 0x3b, 0x8d, 0x24, 0xf1, 0xb1, 0x49, 0xce, 0xa7, 0xed, 0xe8, 0x43, 0x77, 0xd2, 0xb2, 0x65, 0xcb, 0x74, 0xe6, 0x69, 0xce, 0xc3, 0x84, 0x3b, 0x7a, 0xf4, 0xa8, 0x98, 0x97, 0xe5, 0x39, 0x98, 0xbf, 0xe6, 0xb1, 0x7a, 0xf5, 0x6a, 0xed, 0xec, 0xca, 0xfb, 0xae, 0x5d, 0xbb, 0x0c, 0x2e, 0xc6, 0x6c, 0x47, 0xe4, 0xd5, 0x57, 0x5f, 0x95, 0x64, 0x63, 0x11, 0x4a, 0x26, 0xcd, 0x0b, 0x77, 0x24, 0x26, 0x18, 0xcd, 0x02, 0x8d, 0xe0, 0xe0, 0xf5, 0xe3, 0xf9, 0xe7, 0x9f, 0x97, 0xe4, 0x0f, 0x7a, 0xe8, 0xe7, 0x35, 0xe4, 0x6f, 0xd1, 0x36, 0x94, 0x00, 0xed, 0x74, 0x37, 0x08, 0x03, 0x2d, 0x5e, 0x03, 0x6e, 0x50, 0xfd, 0xff, 0xf5, 0xc5, 0x76, 0x12, 0xc0, 0xc4, 0x5d, 0xa0, 0x93, 0x00, 0x9d, 0x04, 0x30, 0x31, 0x06, 0x4c, 0x5c, 0x7d, 0xe7, 0x08, 0xe8, 0x24, 0x80, 0x89, 0x31, 0x60, 0xe2, 0xea, 0x3b, 0x47, 0x40, 0x27, 0x01, 0x4c, 0x8c, 0x01, 0x13, 0x57, 0xff, 0xff, 0x2d, 0xb1, 0x75, 0xfd, 0xe9, 0xfe, 0xa1, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXXMLIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x32, 0x3a, 0x30, 0x39, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x9d, 0x3c, 0x78, 0xe3, 0x00, 0x00, 0x05, 0x7c, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x58, 0x59, 0x4c, 0x1b, 0x57, 0x14, 0xc5, 0x0b, 0xbb, 0xc1, 0x26, 0xa2, 0x31, 0x56, 0x58, 0x4c, 0xb1, 0x1b, 0x37, 0x15, 0x8e, 0x42, 0x05, 0x94, 0x25, 0x88, 0x25, 0x6c, 0x05, 0x52, 0x14, 0x0a, 0xfd, 0xa8, 0x08, 0x1f, 0x91, 0xe0, 0x83, 0x02, 0xa2, 0x2a, 0x6a, 0xfb, 0xd3, 0x82, 0x00, 0x15, 0x3e, 0x8a, 0x8a, 0x10, 0x42, 0x42, 0x4a, 0xbf, 0x10, 0x12, 0x84, 0x06, 0xd5, 0x12, 0x15, 0x5b, 0x49, 0x0a, 0x51, 0x31, 0x34, 0xdd, 0x00, 0xa5, 0xb5, 0x4d, 0x42, 0x83, 0xa1, 0x24, 0x0a, 0xc4, 0x94, 0x60, 0x03, 0xb6, 0xb1, 0x7b, 0x9d, 0x57, 0xdc, 0x67, 0x9b, 0x24, 0xe3, 0x19, 0x3b, 0x08, 0x69, 0x46, 0x23, 0xfb, 0xbe, 0xf3, 0xee, 0xbd, 0xef, 0xdc, 0x7b, 0xdf, 0x32, 0x33, 0x0c, 0xb3, 0xd9, 0xec, 0x71, 0x9c, 0x2f, 0xe6, 0x71, 0x26, 0x6f, 0xe1, 0x4e, 0x07, 0x70, 0xd4, 0x15, 0xa4, 0x2b, 0x40, 0x57, 0x80, 0x62, 0x06, 0xe8, 0x29, 0x44, 0x31, 0x81, 0x94, 0xcd, 0xe9, 0x0a, 0x50, 0x4e, 0x21, 0x45, 0x07, 0x74, 0x05, 0x28, 0x26, 0x90, 0xb2, 0x39, 0x5d, 0x81, 0xc3, 0x52, 0xa8, 0xff, 0x67, 0xdb, 0x11, 0x36, 0xea, 0x76, 0x3d, 0x4c, 0xae, 0x7f, 0xf2, 0x75, 0x71, 0x05, 0x36, 0x15, 0xcb, 0xb7, 0x9b, 0xbe, 0x1e, 0x2e, 0xfe, 0xd4, 0x31, 0x80, 0xf5, 0xdf, 0x94, 0xdf, 0x15, 0x7d, 0xac, 0xec, 0x19, 0x36, 0x3c, 0xd1, 0x39, 0xf6, 0x92, 0x46, 0xd8, 0xa4, 0x2d, 0x6d, 0x0c, 0x4d, 0xe6, 0xd5, 0x1f, 0x7e, 0x51, 0xf5, 0x8d, 0xad, 0xff, 0xae, 0x02, 0x9c, 0xe5, 0xed, 0x69, 0xd3, 0x7b, 0xd0, 0xd0, 0x3d, 0xd8, 0x98, 0xeb, 0xbc, 0x76, 0xe7, 0xaa, 0x2c, 0x3c, 0x37, 0x41, 0x5c, 0x72, 0x21, 0x40, 0x28, 0x38, 0xe8, 0x21, 0xff, 0x4f, 0x35, 0x00, 0xc3, 0x96, 0x76, 0x49, 0x36, 0xb5, 0xf8, 0xcd, 0x04, 0x90, 0x23, 0xc8, 0xc2, 0xb8, 0xbb, 0x77, 0x6f, 0xf0, 0x26, 0xdc, 0xfc, 0xb8, 0x33, 0xa2, 0x92, 0x4c, 0x41, 0x92, 0x94, 0xa0, 0xe1, 0xa1, 0x6a, 0xe4, 0x03, 0xd8, 0x5a, 0xfa, 0x7b, 0xb1, 0x7f, 0x7c, 0x79, 0x58, 0x0e, 0x84, 0x70, 0xd7, 0xbe, 0xc1, 0x3c, 0x61, 0x7e, 0x32, 0x8e, 0x20, 0x99, 0x2b, 0x0a, 0x8b, 0xc8, 0x49, 0x58, 0xb9, 0xf9, 0xf3, 0xfe, 0xae, 0x1e, 0x21, 0x0f, 0x67, 0xef, 0xc0, 0xcd, 0x09, 0xe3, 0x8b, 0xde, 0x4d, 0x07, 0x13, 0xb6, 0x9f, 0x8f, 0xa3, 0xd5, 0x0b, 0x11, 0x86, 0xd3, 0xaf, 0x94, 0x66, 0xf3, 0xda, 0x8f, 0xf3, 0x8b, 0xfd, 0x63, 0x30, 0x36, 0xee, 0x9d, 0xc9, 0x66, 0x0b, 0x92, 0xa5, 0xc2, 0xfc, 0xf3, 0x82, 0x84, 0x68, 0x0f, 0x26, 0x03, 0xef, 0xc2, 0x65, 0xc3, 0xf6, 0x8e, 0x7a, 0x54, 0x0e, 0x45, 0xd3, 0x28, 0xee, 0xe3, 0xb8, 0xa7, 0xbf, 0xaf, 0x30, 0x2f, 0x49, 0x54, 0x9c, 0xe1, 0x1f, 0x7a, 0x12, 0xc7, 0x5f, 0x28, 0x3b, 0x1d, 0xc0, 0xad, 0x0f, 0xbf, 0x7a, 0x30, 0x3d, 0x8f, 0xfb, 0xe5, 0x46, 0x85, 0x42, 0xfe, 0x20, 0xbb, 0x5e, 0x3c, 0x0e, 0x8e, 0x3f, 0x5f, 0xde, 0x54, 0x2e, 0x43, 0x18, 0x10, 0x8c, 0x1e, 0x5b, 0xd3, 0x0c, 0x06, 0xe3, 0xcd, 0x4f, 0xca, 0x84, 0x17, 0xcf, 0x3f, 0xdf, 0x16, 0xef, 0x75, 0x7a, 0x0a, 0xe9, 0x37, 0xff, 0xdf, 0x22, 0x4f, 0xa5, 0xc6, 0x48, 0x2e, 0xe7, 0x05, 0xbd, 0x2e, 0xc4, 0x3d, 0x12, 0x94, 0x79, 0xaf, 0x85, 0x9f, 0xfb, 0xe8, 0xfd, 0xb3, 0x55, 0x25, 0x30, 0xa9, 0xfe, 0xb8, 0x2a, 0x7b, 0xa2, 0x7e, 0x08, 0x86, 0x30, 0x1d, 0x76, 0x35, 0x5b, 0x04, 0x3d, 0x20, 0x35, 0xa7, 0x03, 0xc0, 0xbd, 0xaf, 0xdd, 0x9a, 0x63, 0x30, 0x98, 0x91, 0x85, 0x29, 0xfc, 0xb8, 0x37, 0x70, 0x9c, 0xa0, 0x6c, 0xda, 0x33, 0xa8, 0xbf, 0xff, 0x69, 0x49, 0x36, 0x89, 0xd8, 0x13, 0xb4, 0xb2, 0x53, 0x73, 0x3a, 0x80, 0xc8, 0x77, 0x52, 0x20, 0x49, 0x68, 0xcf, 0x31, 0x19, 0x8d, 0x2b, 0x37, 0x6e, 0xc3, 0xed, 0x2f, 0x08, 0x06, 0x1c, 0x26, 0xb1, 0x4f, 0x30, 0xcf, 0x6e, 0x80, 0x43, 0x9b, 0x68, 0xfe, 0x2c, 0x8f, 0xc8, 0x0d, 0xdb, 0x36, 0x67, 0xc2, 0x2b, 0xe7, 0x4e, 0x3b, 0x9b, 0x0b, 0xa7, 0xd7, 0x80, 0x85, 0x90, 0xed, 0xae, 0x6f, 0xa5, 0xc8, 0x64, 0xb3, 0x04, 0x89, 0x52, 0x88, 0x24, 0xe4, 0xad, 0xc3, 0xd7, 0x31, 0x1c, 0xc6, 0xea, 0xd1, 0x99, 0x7b, 0xdf, 0x4e, 0x6a, 0xfe, 0xfc, 0xcb, 0x6a, 0x05, 0x02, 0xd3, 0x93, 0x1d, 0x9e, 0x15, 0x2f, 0x2a, 0xb9, 0x00, 0xf3, 0x0a, 0xc7, 0x89, 0xc8, 0xa4, 0x02, 0x38, 0x70, 0xbc, 0xa9, 0xb8, 0xaf, 0xea, 0x1b, 0x57, 0x8f, 0xcf, 0x9a, 0x0c, 0xc6, 0x03, 0xcc, 0xf2, 0xcf, 0x09, 0x3d, 0x99, 0x73, 0xed, 0x0b, 0x1c, 0x01, 0xf9, 0xf1, 0xc2, 0xdd, 0xc9, 0xaa, 0x2f, 0xed, 0xf6, 0x5c, 0x9f, 0x13, 0xdc, 0xa8, 0x4b, 0xa9, 0xaf, 0x5e, 0x4a, 0xf5, 0x0e, 0x0a, 0xb4, 0xd3, 0x27, 0xd8, 0x74, 0x7a, 0x0a, 0xe1, 0x7e, 0x79, 0xa7, 0x23, 0x62, 0x3f, 0xbb, 0x22, 0xfd, 0xa0, 0xf8, 0x2e, 0x1c, 0x4c, 0xd7, 0x6f, 0xec, 0x3e, 0xfe, 0x6f, 0xfd, 0xed, 0x3c, 0xd2, 0xe0, 0x6a, 0x48, 0xd6, 0x6f, 0x69, 0x71, 0xf6, 0x41, 0x92, 0x08, 0x71, 0x49, 0x66, 0x58, 0x66, 0x1c, 0x83, 0xcd, 0x72, 0x54, 0x26, 0x8e, 0x50, 0x0a, 0x00, 0x0d, 0xe3, 0x7d, 0x22, 0xf0, 0xcc, 0x95, 0x8b, 0x92, 0xcb, 0x6f, 0xab, 0xc7, 0x66, 0xe1, 0x68, 0xb3, 0xdb, 0xe0, 0xed, 0xa8, 0x30, 0x58, 0xcc, 0x53, 0x29, 0x31, 0xa2, 0xf7, 0x2e, 0x04, 0x9f, 0x15, 0xdb, 0x75, 0x91, 0x6c, 0xc2, 0xce, 0xe5, 0xda, 0xeb, 0xd1, 0xaf, 0x8a, 0x99, 0xcf, 0xbb, 0x1d, 0x7d, 0x02, 0x3e, 0xd7, 0xd1, 0xaf, 0x5d, 0x5b, 0x77, 0xec, 0xa2, 0x82, 0x50, 0x5a, 0x03, 0x24, 0x73, 0xe6, 0x52, 0x33, 0x17, 0x3f, 0x4e, 0xbb, 0x94, 0x1b, 0x21, 0x67, 0x74, 0x00, 0x84, 0xd2, 0xe4, 0x46, 0x25, 0xba, 0x02, 0x6e, 0x4c, 0x2e, 0x21, 0xd7, 0x74, 0x05, 0x08, 0xa5, 0xc9, 0x8d, 0x4a, 0xc7, 0xbe, 0x02, 0x2e, 0x78, 0x94, 0x20, 0x9d, 0xde, 0xfd, 0xfd, 0x7d, 0x38, 0x83, 0xc1, 0x9c, 0xcd, 0x26, 0x4f, 0xe3, 0xc8, 0x2a, 0x50, 0x5d, 0x5d, 0xed, 0xe5, 0xe5, 0xe5, 0xf9, 0xf4, 0x6a, 0x6f, 0x6f, 0x27, 0x9d, 0x85, 0x23, 0x0b, 0x20, 0x32, 0x32, 0x32, 0x2a, 0x2a, 0xca, 0xd7, 0xd7, 0x17, 0xa8, 0x6b, 0xb5, 0xda, 0xe3, 0x17, 0x40, 0x6d, 0x6d, 0xad, 0x52, 0xa9, 0xcc, 0xc8, 0xc8, 0x20, 0x4d, 0x1d, 0x19, 0x92, 0xa9, 0xc0, 0xf4, 0xf4, 0x74, 0x7a, 0x7a, 0x7a, 0x7c, 0x7c, 0x7c, 0x72, 0x72, 0xf2, 0xd4, 0xd4, 0xd4, 0xc8, 0xc8, 0x48, 0x62, 0x62, 0x22, 0x34, 0x8b, 0x8a, 0x8a, 0x76, 0x76, 0x76, 0x64, 0x32, 0x59, 0x52, 0x52, 0x52, 0x5a, 0x5a, 0x5a, 0x67, 0x67, 0x27, 0x9f, 0xcf, 0x17, 0x8b, 0xc5, 0xbd, 0xbd, 0xbd, 0x31, 0x31, 0x31, 0x21, 0x21, 0x21, 0xf5, 0xf5, 0xf5, 0x7b, 0x7b, 0x36, 0x1f, 0x91, 0x28, 0xb2, 0xb7, 0x98, 0x93, 0x78, 0x94, 0x6d, 0x69, 0x69, 0xb1, 0x0e, 0xdc, 0xd4, 0xd4, 0x54, 0x57, 0x57, 0x67, 0x6d, 0xaa, 0x54, 0xaa, 0xaa, 0xaa, 0x2a, 0x6b, 0xd3, 0x51, 0x80, 0x60, 0xf0, 0x11, 0xf3, 0xf3, 0xf3, 0x41, 0xa7, 0xb9, 0xb9, 0x19, 0x07, 0x9d, 0x92, 0xc9, 0x54, 0xa0, 0xa6, 0xa6, 0x66, 0x70, 0x70, 0x90, 0xc7, 0xb3, 0xbc, 0xbf, 0x4b, 0xa5, 0x52, 0x89, 0x44, 0x02, 0x42, 0x6e, 0x6e, 0xee, 0xcc, 0xcc, 0x8c, 0x48, 0x24, 0x6a, 0x6c, 0x6c, 0x84, 0x6a, 0x00, 0x52, 0x5a, 0x5a, 0x3a, 0x3e, 0x3e, 0x0e, 0x02, 0x5c, 0x43, 0x43, 0x43, 0xe5, 0xe5, 0xe5, 0x20, 0xcc, 0xcf, 0xdb, 0x7c, 0x53, 0x7a, 0xda, 0x49, 0xe9, 0x87, 0xcc, 0xfe, 0xe5, 0xe3, 0xe3, 0x53, 0x58, 0x58, 0xc8, 0xe5, 0x72, 0xb3, 0xb3, 0xb3, 0xcb, 0xca, 0xca, 0x74, 0x3a, 0x5d, 0x74, 0x74, 0x74, 0x5f, 0x5f, 0x5f, 0x40, 0x40, 0x00, 0x70, 0x01, 0x1c, 0x66, 0x0b, 0x08, 0x79, 0x79, 0x79, 0xb1, 0xb1, 0xb1, 0x20, 0xc0, 0x4e, 0x93, 0x95, 0x95, 0xb5, 0xb2, 0xb2, 0x02, 0xb2, 0x46, 0x73, 0xc8, 0xdb, 0x26, 0xe0, 0xa4, 0x2f, 0x32, 0x15, 0x40, 0x83, 0xc1, 0x2c, 0x6f, 0x68, 0x68, 0x00, 0x42, 0x30, 0xad, 0x3b, 0x3a, 0x3a, 0x10, 0x7b, 0x9c, 0x87, 0x9f, 0x9f, 0x1f, 0x7c, 0x69, 0x03, 0x04, 0x02, 0x80, 0x9d, 0x1e, 0xc9, 0x26, 0x93, 0x09, 0xd7, 0xa1, 0x2e, 0x93, 0x0f, 0x00, 0x32, 0xda, 0xd5, 0xd5, 0x05, 0xd5, 0x00, 0x12, 0x15, 0x15, 0x15, 0x1b, 0x1b, 0x44, 0xbf, 0x4e, 0x53, 0x27, 0x8d, 0x7b, 0x20, 0x19, 0x00, 0xd0, 0xcd, 0xc9, 0xc9, 0x59, 0x5d, 0x5d, 0x1d, 0x18, 0x18, 0xa8, 0xac, 0xac, 0x54, 0x28, 0x14, 0x05, 0x05, 0x05, 0xd6, 0xed, 0x1c, 0x56, 0x21, 0x8c, 0x81, 0x7e, 0xf1, 0xc1, 0x70, 0x19, 0x4a, 0x61, 0x34, 0x1a, 0x91, 0x0e, 0x2e, 0xe3, 0x3a, 0x84, 0x64, 0xa7, 0x96, 0x3c, 0x52, 0x86, 0xa5, 0x89, 0x12, 0x0f, 0xc7, 0x90, 0x5c, 0x2e, 0x6f, 0x6d, 0x6d, 0x45, 0x23, 0x41, 0x73, 0x62, 0x62, 0xa2, 0xad, 0xad, 0x0d, 0xcd, 0x96, 0xc0, 0xc0, 0xc0, 0x85, 0x85, 0x05, 0xd4, 0x05, 0x4b, 0xbc, 0xbb, 0xbb, 0x1b, 0x64, 0x16, 0x8b, 0xd5, 0xd3, 0xd3, 0x03, 0x26, 0x8e, 0x8f, 0x0f, 0x1c, 0x0e, 0x67, 0x74, 0x74, 0xd4, 0x59, 0x3e, 0x64, 0x2a, 0x00, 0xfc, 0xe0, 0x29, 0x00, 0xd8, 0x80, 0x00, 0x59, 0xd4, 0xeb, 0xf5, 0x88, 0xb1, 0x15, 0x64, 0x32, 0x2d, 0x6e, 0x81, 0x22, 0x5c, 0xe8, 0xac, 0x05, 0x04, 0x2d, 0x12, 0x08, 0x00, 0x28, 0x82, 0x3e, 0x32, 0x41, 0xe1, 0xa1, 0x5f, 0x47, 0x04, 0xef, 0x7d, 0x96, 0x4c, 0x7f, 0x95, 0x78, 0x56, 0x66, 0x5e, 0x16, 0x4e, 0x66, 0x0a, 0xbd, 0x2c, 0x6e, 0x84, 0xc6, 0xa1, 0x03, 0x20, 0x94, 0x26, 0x37, 0x2a, 0xd1, 0x15, 0x70, 0x63, 0x72, 0x09, 0xb9, 0xa6, 0x2b, 0x40, 0x28, 0x4d, 0x6e, 0x54, 0xa2, 0x2b, 0xe0, 0xc6, 0xe4, 0x12, 0x72, 0x4d, 0x57, 0x80, 0x50, 0x9a, 0xdc, 0xa8, 0xf4, 0x2f, 0x8a, 0xf9, 0x6c, 0x7c, 0x9d, 0x47, 0x95, 0x15, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXXMLIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0e, 0x6d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x1d, 0x69, 0x74, 0x54, 0xd5, 0xf9, 0x9b, 0xcc, 0x64, 0x9f, 0xec, 0x54, 0x16, 0x13, 0xb6, 0x20, 0x02, 0x0a, 0x28, 0x2a, 0x04, 0xb1, 0x2d, 0x50, 0x6c, 0x2d, 0x16, 0xb4, 0x28, 0x76, 0x53, 0xaa, 0xb5, 0x96, 0x9e, 0x23, 0x15, 0x0a, 0x47, 0xa4, 0x2d, 0x82, 0x4b, 0xed, 0xb1, 0xd5, 0x42, 0x6b, 0x51, 0xb1, 0x0b, 0xf0, 0xc3, 0x96, 0xd3, 0x5a, 0x2c, 0x16, 0x5a, 0x6c, 0xd5, 0x42, 0x85, 0xb0, 0x06, 0x01, 0x29, 0x90, 0xb0, 0x19, 0x16, 0xc3, 0x96, 0xc9, 0x64, 0x9b, 0x2c, 0x93, 0x99, 0x79, 0xfd, 0xbe, 0x1b, 0xef, 0xcd, 0xbc, 0x37, 0xf3, 0xde, 0xcc, 0x7b, 0x33, 0xef, 0x91, 0xe0, 0x7b, 0xe7, 0x24, 0xef, 0xdb, 0xee, 0x77, 0xbf, 0xe5, 0x2e, 0x6f, 0xb9, 0xef, 0x8e, 0x43, 0xc2, 0x03, 0x74, 0x1c, 0x29, 0x3a, 0x64, 0x99, 0x68, 0xe2, 0x05, 0x6a, 0xb7, 0x8d, 0x65, 0x9a, 0xa4, 0xa0, 0x8f, 0x9d, 0x39, 0x2e, 0x2c, 0x21, 0x1f, 0xf8, 0x71, 0x69, 0xeb, 0x2d, 0x1c, 0x94, 0xd4, 0x60, 0x07, 0x49, 0x88, 0xd2, 0x08, 0x84, 0xda, 0x6a, 0x20, 0x25, 0xa3, 0x5f, 0x38, 0x49, 0x06, 0x47, 0x14, 0x90, 0x71, 0xa3, 0x20, 0x89, 0x3b, 0xad, 0x54, 0xea, 0x70, 0x38, 0x64, 0xa4, 0x88, 0x1a, 0xb8, 0x00, 0x3f, 0xcb, 0xa4, 0x11, 0x31, 0xdf, 0x07, 0xf3, 0x6b, 0x88, 0x70, 0x5a, 0xe9, 0xa4, 0x12, 0x97, 0x15, 0x08, 0xfa, 0x8e, 0x81, 0x14, 0x68, 0x60, 0x32, 0xde, 0x8a, 0xaf, 0x82, 0x67, 0xfb, 0x6d, 0x4a, 0x79, 0x00, 0xde, 0x78, 0x3c, 0x3b, 0x26, 0x71, 0x50, 0x76, 0x6e, 0xad, 0xf9, 0xb3, 0x0c, 0x17, 0x05, 0xc2, 0xa9, 0xbc, 0xe1, 0x79, 0x76, 0x7d, 0x39, 0x9c, 0xcc, 0xe0, 0xee, 0x1e, 0x25, 0x1e, 0x92, 0x8a, 0x8a, 0x0a, 0x18, 0x36, 0x6c, 0x18, 0x47, 0x65, 0x67, 0xf3, 0x7d, 0xd0, 0x5d, 0x83, 0xcc, 0xbe, 0x38, 0x10, 0x59, 0xcb, 0x88, 0x43, 0x5e, 0xb7, 0x88, 0xa1, 0x0a, 0x22, 0x86, 0x36, 0xad, 0x6a, 0x23, 0x9a, 0xd6, 0x27, 0x04, 0x6a, 0x7d, 0xde, 0x0f, 0xbe, 0x11, 0xc1, 0xe6, 0xad, 0x32, 0x9c, 0x41, 0x34, 0xdf, 0x47, 0xbf, 0x0e, 0x27, 0x09, 0x38, 0x6a, 0xd3, 0xe6, 0x5c, 0x2a, 0xe8, 0xd9, 0xf9, 0x45, 0x8e, 0xca, 0xc6, 0x5a, 0x4e, 0x24, 0x99, 0xc6, 0x23, 0x8b, 0x38, 0x1a, 0x71, 0xd6, 0xac, 0x20, 0x5c, 0xba, 0xae, 0xe2, 0x5e, 0x29, 0xd4, 0xe1, 0x0d, 0x27, 0xc5, 0x05, 0x77, 0xef, 0x56, 0x54, 0x5d, 0x5d, 0x0d, 0x9b, 0x37, 0x6f, 0xd6, 0x4a, 0x71, 0xd7, 0x78, 0x14, 0xcd, 0x5f, 0x2c, 0xc9, 0xc8, 0xd7, 0x5f, 0x7f, 0xbd, 0xf4, 0xec, 0xb3, 0xcf, 0x4a, 0x03, 0x07, 0x0e, 0xa4, 0x69, 0x8d, 0xfd, 0x71, 0x79, 0x2e, 0xc3, 0x71, 0xe5, 0x59, 0x33, 0x07, 0x54, 0x78, 0xf7, 0xee, 0xdd, 0x42, 0x29, 0x57, 0x7e, 0xed, 0xb5, 0xd7, 0x4a, 0x4f, 0x3d, 0xf5, 0x94, 0x8c, 0xae, 0x54, 0xcc, 0xf1, 0xee, 0x9d, 0x03, 0xf4, 0x28, 0xe6, 0xd1, 0xf3, 0x3d, 0x30, 0x34, 0x16, 0xc5, 0x8c, 0x4b, 0x98, 0xc0, 0xa7, 0xb0, 0x82, 0x40, 0xd3, 0x41, 0x68, 0x3e, 0xba, 0x24, 0x2c, 0x08, 0x31, 0x40, 0xde, 0x21, 0xa2, 0x9d, 0x69, 0xa4, 0xc4, 0xb1, 0x5f, 0xc6, 0x22, 0x1a, 0x3b, 0x42, 0xed, 0x6c, 0x74, 0xf5, 0x7b, 0x77, 0xca, 0xf8, 0x4a, 0x24, 0x6a, 0x4f, 0x6e, 0xfd, 0xf8, 0x4f, 0x51, 0x87, 0x66, 0x2a, 0x5c, 0xbb, 0xad, 0x4c, 0xa6, 0xa3, 0xe1, 0xe0, 0xa3, 0xaa, 0xb2, 0x24, 0x18, 0xb5, 0x02, 0xb2, 0xd2, 0x57, 0xfd, 0xb2, 0x4c, 0x11, 0x21, 0xc2, 0x7a, 0x05, 0x87, 0x79, 0x5a, 0x3e, 0x5e, 0x41, 0xed, 0x44, 0xa3, 0x56, 0x40, 0xac, 0xe6, 0x13, 0x2f, 0x46, 0x28, 0x54, 0x56, 0x50, 0x57, 0x71, 0x4f, 0x84, 0x8c, 0xb2, 0x16, 0xd5, 0x0a, 0xb8, 0x60, 0x6d, 0xf9, 0xad, 0x0c, 0xac, 0x8d, 0x62, 0x61, 0x47, 0xd3, 0x61, 0x2e, 0xa6, 0x7a, 0x8e, 0xd9, 0x0f, 0x8a, 0x6e, 0x2d, 0x67, 0xcd, 0x44, 0x0a, 0x05, 0x22, 0x9a, 0x8b, 0xcb, 0x3d, 0x3c, 0x82, 0xa6, 0x24, 0xd8, 0x63, 0x91, 0x32, 0x22, 0x11, 0x78, 0xcc, 0x1c, 0x44, 0x94, 0x08, 0x23, 0xc4, 0x9c, 0x8f, 0x51, 0x36, 0xa1, 0x0a, 0x36, 0x6e, 0xdc, 0x18, 0x56, 0x5d, 0x74, 0x50, 0xb5, 0x82, 0x79, 0xf3, 0xe6, 0x41, 0x6e, 0x6e, 0x2e, 0xd0, 0x3d, 0xde, 0xd9, 0xb3, 0x67, 0xd9, 0x79, 0xcc, 0x98, 0x31, 0x40, 0x74, 0x97, 0xcb, 0x15, 0x5d, 0x5b, 0x34, 0xaa, 0x6a, 0x03, 0x46, 0x86, 0xdb, 0xed, 0x96, 0xe6, 0xce, 0x9d, 0xcb, 0x44, 0xb0, 0xac, 0xf4, 0xc6, 0x1b, 0x6f, 0x08, 0x98, 0x80, 0xf9, 0xf3, 0xe7, 0x33, 0x5c, 0xeb, 0x9f, 0x6a, 0x47, 0x23, 0x85, 0xfc, 0x8f, 0xae, 0x22, 0x78, 0x05, 0x9c, 0x96, 0x97, 0x97, 0x27, 0xf8, 0xa7, 0x4f, 0x9f, 0x56, 0xad, 0xc3, 0xee, 0x07, 0x18, 0x31, 0xed, 0x43, 0xb5, 0x15, 0x69, 0x17, 0x8b, 0x9f, 0x6b, 0x7a, 0x0e, 0xe2, 0x37, 0xc5, 0x98, 0xa4, 0xe9, 0x11, 0x32, 0x66, 0x56, 0xfc, 0xa5, 0x6c, 0x07, 0xe2, 0x8f, 0x95, 0x39, 0x92, 0x96, 0x65, 0x20, 0xd4, 0x5e, 0x63, 0x8a, 0x07, 0x09, 0x3b, 0xd0, 0x54, 0xb5, 0x18, 0x62, 0x3d, 0xb6, 0xf0, 0x94, 0x8f, 0x87, 0xba, 0x3d, 0x77, 0xcb, 0x1c, 0xa0, 0x32, 0x0d, 0x07, 0xbf, 0x2f, 0xa3, 0x19, 0x41, 0x0c, 0x8d, 0x42, 0xde, 0x8a, 0xe9, 0x10, 0x6c, 0x3b, 0xcf, 0xea, 0x73, 0x97, 0x2e, 0x84, 0x8c, 0xbe, 0xf7, 0xaa, 0xd6, 0xed, 0xad, 0xb8, 0x1b, 0x65, 0x6b, 0xa0, 0x68, 0xfc, 0x16, 0x70, 0x38, 0xb3, 0x64, 0x72, 0x7e, 0xcf, 0x16, 0x68, 0x3c, 0xb2, 0x90, 0xd1, 0x52, 0x5c, 0x39, 0x50, 0x58, 0xf6, 0x9e, 0x8c, 0x1f, 0x0f, 0x12, 0xb7, 0x03, 0x9e, 0xf2, 0x32, 0x9c, 0x57, 0x43, 0x4c, 0x67, 0xd1, 0xb8, 0x4d, 0xe0, 0x48, 0x2d, 0x8a, 0xa9, 0xbf, 0xa9, 0xea, 0x27, 0xd0, 0x7e, 0xe9, 0x1d, 0xc8, 0x1f, 0xfd, 0x7b, 0x70, 0xe5, 0x8c, 0xd2, 0x94, 0x97, 0x82, 0x2d, 0x50, 0xb7, 0x73, 0x0a, 0xd6, 0xd1, 0x79, 0x6d, 0xda, 0xeb, 0xb6, 0xdd, 0x9a, 0xf2, 0x9c, 0x19, 0xb7, 0x03, 0x54, 0x20, 0xd0, 0xf4, 0x3f, 0xa8, 0x3f, 0xf0, 0x1d, 0x5e, 0x16, 0x0a, 0xc6, 0xac, 0x05, 0x67, 0x56, 0xa9, 0xc0, 0xc3, 0x81, 0xb6, 0x73, 0x7f, 0x01, 0xbc, 0xf4, 0x87, 0xec, 0x81, 0x73, 0x20, 0xb3, 0x78, 0x56, 0x38, 0x4b, 0x06, 0x87, 0xfc, 0x97, 0xa0, 0x6e, 0xf7, 0x9d, 0x82, 0x96, 0x3b, 0xe2, 0x05, 0x48, 0x2b, 0xfc, 0xbc, 0xc0, 0x63, 0x01, 0xba, 0x1c, 0x50, 0x2a, 0xf3, 0xee, 0x99, 0x06, 0xc1, 0xf6, 0x0b, 0x8c, 0x9c, 0x3f, 0xea, 0x35, 0x70, 0xe5, 0xde, 0xc8, 0xe0, 0x40, 0x73, 0x25, 0xd4, 0xef, 0x9f, 0x05, 0xe9, 0x45, 0x9f, 0x85, 0x9c, 0xe1, 0xbf, 0x54, 0x16, 0x83, 0x90, 0xbf, 0x16, 0x8d, 0x9e, 0xca, 0xe8, 0x29, 0x2e, 0x37, 0x36, 0x9d, 0x77, 0x10, 0x76, 0x46, 0xc8, 0xc5, 0x43, 0x48, 0xc8, 0x81, 0xa8, 0x15, 0x84, 0xda, 0xa1, 0x76, 0xfb, 0x67, 0x21, 0x25, 0xad, 0x17, 0x14, 0x8e, 0xfd, 0x67, 0x54, 0x91, 0x64, 0x12, 0x93, 0xef, 0x00, 0x5a, 0xe7, 0xaf, 0x7d, 0x0f, 0xd2, 0x7a, 0x7d, 0x21, 0x99, 0x76, 0xaa, 0xea, 0x32, 0xc5, 0x01, 0xd5, 0xda, 0x4c, 0x60, 0x24, 0x3c, 0x0f, 0x98, 0x60, 0x93, 0x2e, 0x95, 0x3d, 0xde, 0x01, 0x1d, 0xb7, 0x6f, 0xba, 0x02, 0x13, 0x53, 0x38, 0xfc, 0x8d, 0x21, 0xde, 0x70, 0xc5, 0x94, 0x57, 0x13, 0x30, 0x94, 0x81, 0xb4, 0xb4, 0x34, 0x76, 0x1b, 0xeb, 0x74, 0x3a, 0x21, 0x10, 0x08, 0x30, 0x98, 0x0c, 0x5a, 0xbf, 0x7e, 0x3d, 0x14, 0x15, 0x15, 0x31, 0x7c, 0xd6, 0xac, 0x59, 0xec, 0x3c, 0x63, 0xc6, 0x0c, 0xc1, 0x7f, 0xff, 0xfd, 0xf7, 0x85, 0x1d, 0x64, 0x34, 0xde, 0x95, 0x0a, 0xdc, 0x30, 0xa0, 0x7a, 0xbb, 0x19, 0x83, 0x71, 0xe1, 0xc2, 0x05, 0x76, 0xd7, 0x8a, 0x4e, 0x48, 0x23, 0x46, 0x8c, 0x90, 0x49, 0xa3, 0x31, 0xd2, 0xb6, 0x6d, 0xdb, 0xd8, 0x6d, 0x34, 0xc1, 0x74, 0xf0, 0x3b, 0xdf, 0x70, 0x41, 0xba, 0xad, 0xe6, 0xfc, 0x70, 0xba, 0x1e, 0xd8, 0x50, 0x06, 0x28, 0x5a, 0x43, 0x87, 0x0e, 0x85, 0x9c, 0x9c, 0x1c, 0x38, 0x74, 0xe8, 0x10, 0x1c, 0x3e, 0x7c, 0x18, 0x16, 0x2f, 0x5e, 0x4c, 0x64, 0x71, 0xa4, 0xa7, 0xa7, 0x0b, 0xd8, 0x4c, 0xc0, 0x1e, 0x46, 0xcd, 0x8c, 0x6e, 0x3c, 0xba, 0x0d, 0x37, 0xa1, 0x78, 0x94, 0x5b, 0x21, 0x63, 0x3b, 0x60, 0x45, 0x94, 0xb5, 0xea, 0xb0, 0x3b, 0xb1, 0x56, 0x74, 0xac, 0xe0, 0xd9, 0x7d, 0xc0, 0x8a, 0x28, 0x6b, 0xd5, 0x61, 0x67, 0x40, 0x2b, 0x3a, 0x56, 0xf0, 0xec, 0x0c, 0x58, 0x11, 0x65, 0xad, 0x3a, 0xec, 0x0c, 0x68, 0x45, 0x87, 0xf3, 0x82, 0xbe, 0x2a, 0x0e, 0x26, 0xfd, 0x6c, 0xc9, 0x4c, 0xcc, 0x1f, 0xfe, 0xc6, 0xfb, 0xb8, 0x50, 0x8f, 0x97, 0x09, 0x35, 0x21, 0xa9, 0xc3, 0xc3, 0x9e, 0x4c, 0xb7, 0x9c, 0x7a, 0x55, 0xb5, 0xce, 0x50, 0xfb, 0x39, 0xc6, 0x4b, 0xcd, 0xbb, 0x41, 0xc8, 0xb4, 0x5f, 0xfa, 0x17, 0x2b, 0x17, 0x6c, 0xad, 0x16, 0x34, 0xa3, 0x80, 0xe1, 0x9b, 0xfa, 0x86, 0x0f, 0x1f, 0x86, 0x8e, 0xc6, 0x83, 0xac, 0x5e, 0x5c, 0xfa, 0xa6, 0x5a, 0x7f, 0xfd, 0xbe, 0x6f, 0x31, 0x5e, 0xde, 0xc8, 0x95, 0x42, 0x26, 0xd8, 0x7a, 0x8a, 0xc1, 0xde, 0xbd, 0xf7, 0x81, 0x33, 0xb3, 0x04, 0x0a, 0x6e, 0x5a, 0x27, 0x78, 0x7a, 0x01, 0xdd, 0x4d, 0xc8, 0x5f, 0xb7, 0x15, 0x1a, 0x0f, 0x2f, 0x60, 0xf5, 0x74, 0x3e, 0x12, 0x7f, 0x17, 0x61, 0xf9, 0xe2, 0xe5, 0x2e, 0x23, 0x42, 0x18, 0xe9, 0x32, 0xe8, 0x7c, 0xfe, 0xf9, 0x9f, 0x2e, 0xf2, 0x27, 0x90, 0xec, 0x31, 0xfd, 0x90, 0x45, 0x90, 0xd1, 0x67, 0x46, 0x84, 0x4c, 0x2c, 0x82, 0x0e, 0x07, 0x42, 0xe0, 0xd9, 0x31, 0x19, 0xe8, 0x31, 0x38, 0x1d, 0xf9, 0xa3, 0x7e, 0x87, 0x0f, 0x73, 0x47, 0x6b, 0xea, 0x6f, 0xf8, 0xf0, 0xbb, 0x98, 0xa5, 0x0f, 0xa1, 0xf0, 0x96, 0xb7, 0x20, 0x25, 0xbd, 0x6f, 0x54, 0x59, 0xca, 0x86, 0x77, 0xef, 0x4c, 0xc6, 0x73, 0x38, 0x5c, 0xf8, 0x1e, 0x01, 0x1d, 0x4d, 0xc9, 0x88, 0x2a, 0x1b, 0x8d, 0x18, 0x57, 0x1f, 0x68, 0x39, 0xb5, 0x92, 0x45, 0x92, 0x8c, 0x4f, 0xff, 0xcc, 0x97, 0x80, 0x3a, 0x63, 0x2c, 0xe3, 0xa9, 0x32, 0x32, 0x9e, 0x0e, 0x35, 0xe3, 0x89, 0xe7, 0xcc, 0x1c, 0xc0, 0xf4, 0x65, 0x15, 0xdf, 0xcf, 0xde, 0x0d, 0xd4, 0x6e, 0xff, 0x1c, 0x34, 0x55, 0x2e, 0x22, 0x56, 0x5c, 0x47, 0x5c, 0x0e, 0xa4, 0xa4, 0x5f, 0x25, 0x94, 0xf9, 0x3d, 0x9b, 0x69, 0x4d, 0xbe, 0xc0, 0xd5, 0x80, 0x96, 0xea, 0xdf, 0x30, 0x56, 0xee, 0xb0, 0x9f, 0xaa, 0x89, 0x84, 0xd1, 0x43, 0xd0, 0x76, 0xfe, 0x2d, 0x81, 0x3b, 0x33, 0xae, 0x16, 0x70, 0x2c, 0x40, 0x47, 0x13, 0x02, 0x68, 0x38, 0xf0, 0x20, 0xe0, 0x52, 0x21, 0xa6, 0x33, 0x35, 0x6f, 0x0c, 0x84, 0x77, 0x4c, 0x65, 0x45, 0xf1, 0x0e, 0x9d, 0xf4, 0x8e, 0xad, 0xfd, 0xd2, 0xbf, 0x59, 0x71, 0x32, 0xbc, 0xe0, 0xe6, 0xbf, 0x29, 0x55, 0x69, 0xe2, 0xba, 0x1c, 0x20, 0x4d, 0x52, 0xb0, 0x19, 0x5f, 0x05, 0xdd, 0x8e, 0xe9, 0x0e, 0x32, 0xc5, 0x39, 0xd7, 0x2c, 0x86, 0xf4, 0xde, 0xd3, 0x65, 0x95, 0xf8, 0xeb, 0xfe, 0x8b, 0x1d, 0xfd, 0x71, 0xc8, 0xec, 0x37, 0x13, 0xb2, 0x07, 0x3f, 0x2e, 0xe3, 0x71, 0xa4, 0xc3, 0xbb, 0x1d, 0x1a, 0x0e, 0xcd, 0xe3, 0x28, 0xf6, 0x93, 0x0d, 0xd8, 0xd4, 0x7a, 0x0b, 0x3c, 0x5e, 0x40, 0xb7, 0x03, 0x5c, 0xb1, 0xbf, 0xf6, 0x5d, 0x68, 0xac, 0xfc, 0x31, 0x43, 0xe9, 0xb1, 0x62, 0xd1, 0x84, 0x5d, 0x9c, 0xc5, 0xc6, 0x78, 0x42, 0xd4, 0x26, 0x2e, 0x0f, 0xbe, 0x00, 0x91, 0xf0, 0x45, 0x08, 0x1d, 0xee, 0xc1, 0x0b, 0x20, 0xa3, 0xdf, 0xd7, 0x18, 0x6c, 0xe8, 0x9f, 0x9e, 0xc7, 0x78, 0xd1, 0x64, 0x71, 0x48, 0x95, 0x02, 0x2d, 0xd5, 0x82, 0x15, 0x6c, 0xbf, 0xc8, 0x96, 0xbb, 0x79, 0xf7, 0xdd, 0x2f, 0x68, 0x4a, 0x20, 0x14, 0x68, 0x96, 0xea, 0xf7, 0x3f, 0xa8, 0x24, 0x1b, 0xc2, 0x0d, 0x67, 0x40, 0x2d, 0x5a, 0x75, 0xbb, 0x6e, 0x87, 0x50, 0x47, 0x03, 0xf4, 0x9a, 0xb0, 0x1d, 0xa7, 0x07, 0xc3, 0xf3, 0xa4, 0x9a, 0xfa, 0x08, 0x7a, 0x5c, 0xa3, 0x50, 0x44, 0x29, 0x0d, 0x02, 0x19, 0xef, 0x48, 0xc1, 0xe7, 0xa2, 0x16, 0x18, 0x4f, 0x66, 0x24, 0x3d, 0x03, 0xb4, 0x14, 0x2b, 0xd4, 0x76, 0x06, 0xe7, 0xa2, 0xfe, 0x1a, 0x6e, 0x26, 0x8f, 0x65, 0x82, 0x03, 0xc9, 0x33, 0x2e, 0x1e, 0x4d, 0x49, 0x6f, 0x42, 0xf1, 0x54, 0x9a, 0x4c, 0x19, 0xdb, 0x81, 0x64, 0x46, 0xd3, 0x88, 0x2e, 0x3b, 0x03, 0x46, 0xa2, 0x96, 0xcc, 0x32, 0xe6, 0xcf, 0x34, 0x2a, 0xd6, 0xf2, 0xa5, 0xc5, 0xf8, 0x82, 0x10, 0x7a, 0xf7, 0xd6, 0x7f, 0x0d, 0x24, 0xd4, 0x1a, 0x9a, 0xbf, 0x13, 0x2c, 0x84, 0x2b, 0x8c, 0xd9, 0xdb, 0x49, 0x34, 0x42, 0xba, 0xe3, 0x8e, 0x3b, 0x12, 0xd2, 0x76, 0x59, 0xfa, 0xc0, 0xf2, 0xe5, 0xcb, 0xf1, 0x6a, 0x96, 0x7c, 0x48, 0xfc, 0xd0, 0xed, 0xc0, 0x86, 0x0d, 0x1b, 0xc4, 0x8b, 0xeb, 0x75, 0xeb, 0xd6, 0x41, 0x49, 0x49, 0x09, 0xc3, 0x53, 0x53, 0x53, 0xa1, 0xa5, 0xa5, 0x85, 0xc1, 0xc3, 0x87, 0x0f, 0x17, 0x32, 0xfc, 0x45, 0x37, 0xae, 0x86, 0x4e, 0xdc, 0xda, 0x68, 0x1a, 0xf4, 0xe6, 0xaf, 0xa6, 0xa6, 0x46, 0xc2, 0x77, 0xc4, 0xac, 0x09, 0xe0, 0x67, 0x6b, 0xd2, 0xcc, 0x99, 0x33, 0x19, 0x3c, 0x67, 0xce, 0x1c, 0xa6, 0x2a, 0x3f, 0x3f, 0x9f, 0xe1, 0xab, 0x56, 0xad, 0x62, 0x67, 0xac, 0x53, 0x7a, 0xe6, 0x99, 0x67, 0x18, 0x1c, 0x0c, 0x06, 0x65, 0xd5, 0x11, 0x2f, 0xd1, 0x26, 0x44, 0xa9, 0x34, 0x74, 0x70, 0x27, 0xc8, 0x08, 0x8f, 0xc7, 0x23, 0x74, 0x8c, 0x1d, 0x3b, 0x96, 0x19, 0x4b, 0x84, 0x70, 0x03, 0x09, 0x3e, 0x7a, 0xf4, 0xa8, 0x90, 0x53, 0xf2, 0x65, 0x0c, 0x1d, 0x88, 0xee, 0x26, 0x84, 0x86, 0xb0, 0x63, 0xf5, 0xea, 0xd5, 0x1c, 0x84, 0xc2, 0xc2, 0x42, 0x01, 0x2b, 0x01, 0xb3, 0xdf, 0xd8, 0x1b, 0x72, 0x60, 0xcf, 0x9e, 0x3d, 0x30, 0x61, 0xc2, 0x04, 0xd8, 0xb7, 0x6f, 0x9f, 0x58, 0xdc, 0xa1, 0x34, 0xdc, 0x32, 0x5c, 0x47, 0xb6, 0x98, 0xe8, 0xda, 0xb5, 0x6b, 0x59, 0xd3, 0x40, 0x03, 0xa5, 0xb7, 0xdf, 0x7e, 0x5b, 0xc0, 0x84, 0x1f, 0x3f, 0x7e, 0x5c, 0xe0, 0x8f, 0x3d, 0xf6, 0x98, 0x80, 0x1b, 0x1b, 0x1b, 0x05, 0x4c, 0x4a, 0x48, 0x36, 0xda, 0x9f, 0x5e, 0x5b, 0x48, 0xde, 0xbe, 0x9c, 0xc6, 0x48, 0x5e, 0xd6, 0xc3, 0x50, 0x1f, 0xb8, 0xac, 0x16, 0x2b, 0x2a, 0xb7, 0x1d, 0x50, 0x04, 0xc4, 0x72, 0xd4, 0xce, 0x80, 0xe5, 0x21, 0x57, 0x54, 0x68, 0x67, 0x40, 0x11, 0x10, 0xcb, 0xd1, 0x1e, 0x3f, 0x91, 0x59, 0x1e, 0xb1, 0x24, 0x57, 0xd8, 0xe3, 0xbb, 0x40, 0x92, 0xe3, 0x61, 0xb9, 0x3a, 0x3b, 0x01, 0x96, 0x87, 0x5c, 0x5e, 0xa1, 0x9d, 0x00, 0x79, 0x3c, 0x2c, 0xc7, 0xec, 0x04, 0x58, 0x1e, 0x72, 0x79, 0x85, 0x76, 0x02, 0xe4, 0xf1, 0xb0, 0x1c, 0xb3, 0x13, 0x60, 0x79, 0xc8, 0xe5, 0x15, 0xda, 0x09, 0x90, 0xc7, 0xc3, 0x72, 0xcc, 0x4e, 0x80, 0xe5, 0x21, 0x97, 0x57, 0x78, 0xd9, 0x5e, 0x8d, 0xc9, 0xcd, 0x48, 0x0e, 0xd6, 0x70, 0xe0, 0x21, 0xa0, 0x6f, 0xb0, 0xd3, 0x8a, 0x26, 0xe2, 0xc7, 0xe3, 0xf7, 0x83, 0xcb, 0x7d, 0x5d, 0x72, 0x14, 0x9b, 0xa8, 0xa5, 0x5b, 0xf4, 0x80, 0x8e, 0xfa, 0x5d, 0xd0, 0x48, 0x6b, 0xb6, 0x24, 0xbf, 0x61, 0x57, 0x9b, 0x2a, 0x7f, 0x84, 0x8b, 0xe0, 0x0e, 0xe1, 0x6b, 0x9a, 0x20, 0xb4, 0xe3, 0x67, 0xd8, 0x6a, 0x9b, 0x26, 0x35, 0x1f, 0x5d, 0x0a, 0xed, 0x17, 0x63, 0xef, 0x88, 0x64, 0xd8, 0x10, 0x9d, 0x05, 0x2f, 0xd3, 0xa3, 0x08, 0x09, 0x97, 0x26, 0xbe, 0x09, 0x2d, 0xd5, 0x2f, 0x03, 0x2e, 0xd9, 0x12, 0x26, 0x3b, 0x52, 0x5c, 0x90, 0x7f, 0xc3, 0xeb, 0xb8, 0x3d, 0xc2, 0x60, 0x41, 0x8b, 0x07, 0xf0, 0x7d, 0xf4, 0x2b, 0xc0, 0x7d, 0xdf, 0x84, 0x68, 0xee, 0xf0, 0x5f, 0xb0, 0x5e, 0x20, 0x08, 0x08, 0x48, 0x81, 0x46, 0xf0, 0x7e, 0x70, 0x1f, 0xee, 0x42, 0x50, 0x27, 0xc8, 0x8e, 0x94, 0x54, 0xb6, 0xcd, 0x42, 0x56, 0xc9, 0x43, 0xb8, 0xda, 0x28, 0x4d, 0xd0, 0xad, 0x04, 0xac, 0x4b, 0x00, 0xae, 0x2f, 0x6d, 0x39, 0xfd, 0x5b, 0x0c, 0xd4, 0x5a, 0xd6, 0x4a, 0xc3, 0x9d, 0x74, 0xb9, 0x87, 0x81, 0x7b, 0xc8, 0x13, 0x86, 0x86, 0x8c, 0xb6, 0x9a, 0xb5, 0xd0, 0x7c, 0x72, 0xb9, 0x50, 0xe7, 0x1e, 0x3c, 0x1f, 0x17, 0x0e, 0x7e, 0x5d, 0xe0, 0x4a, 0x20, 0xd4, 0xf6, 0x31, 0xca, 0xbf, 0x00, 0xfe, 0x3a, 0x5c, 0x06, 0xa7, 0x38, 0x68, 0x21, 0xaf, 0x7b, 0xf0, 0x0f, 0x71, 0xa3, 0x0f, 0xf5, 0xf7, 0x7c, 0x8a, 0x22, 0x09, 0xa3, 0xa6, 0x26, 0x20, 0xe4, 0xbf, 0x08, 0x3e, 0xdc, 0xd0, 0xa3, 0x1d, 0xb7, 0x4d, 0x09, 0x3f, 0x68, 0x65, 0x66, 0xfa, 0x55, 0x77, 0xe2, 0x46, 0x1f, 0x3f, 0x40, 0x67, 0x0b, 0xc2, 0x59, 0xba, 0x60, 0x5a, 0x38, 0xdc, 0x78, 0xe4, 0x09, 0x51, 0x26, 0xf3, 0xea, 0x6f, 0x42, 0xf6, 0xa0, 0xae, 0xe5, 0xa7, 0x82, 0xa1, 0x06, 0xe0, 0x6a, 0xcf, 0x96, 0x33, 0x7f, 0xc0, 0x46, 0xf1, 0x3a, 0xae, 0xfc, 0x94, 0xef, 0xd0, 0x97, 0x9a, 0x3b, 0x12, 0xdc, 0xa5, 0x8b, 0xc0, 0x99, 0x7d, 0x8d, 0x5a, 0xe9, 0xa4, 0xd0, 0x93, 0x9e, 0x80, 0x40, 0xf3, 0x11, 0x68, 0x3e, 0xfe, 0x33, 0x9c, 0x0c, 0xe5, 0x1f, 0x9a, 0xa4, 0xb8, 0xb2, 0x21, 0xab, 0xff, 0xf7, 0xb0, 0x75, 0xde, 0x87, 0x86, 0x1b, 0xdb, 0x78, 0x24, 0xdc, 0xe3, 0x40, 0xf3, 0x61, 0xdc, 0xf0, 0xe4, 0x41, 0x41, 0x4a, 0xef, 0x35, 0x19, 0x72, 0x86, 0x3d, 0x2f, 0x70, 0x23, 0x00, 0x2d, 0xdf, 0xf5, 0x7d, 0xb4, 0x1c, 0x37, 0x58, 0xb9, 0x24, 0x2b, 0xee, 0xcc, 0xe8, 0x83, 0x6b, 0x8e, 0x17, 0xe2, 0x2e, 0x2f, 0xb7, 0xc9, 0xe8, 0xc9, 0x40, 0x92, 0x9a, 0x80, 0xfa, 0xfd, 0x0f, 0x44, 0x04, 0xde, 0x85, 0xe3, 0x79, 0xee, 0x88, 0x17, 0x71, 0xdd, 0x65, 0x71, 0x32, 0xec, 0x65, 0x3a, 0xa8, 0x67, 0x79, 0x71, 0x87, 0x25, 0xbe, 0x77, 0x4f, 0x6a, 0xce, 0x75, 0x90, 0x37, 0x7a, 0x75, 0xd2, 0xf4, 0x4b, 0x81, 0x7a, 0x68, 0xaa, 0x5a, 0x02, 0xb8, 0x09, 0xa9, 0x4c, 0x27, 0xcd, 0x51, 0x45, 0xb7, 0x46, 0x0e, 0x5d, 0x32, 0x21, 0x9d, 0x48, 0x52, 0x13, 0x40, 0x75, 0x4b, 0x1d, 0x75, 0xe0, 0xab, 0x5e, 0x81, 0x57, 0x1a, 0xff, 0xa0, 0x85, 0x23, 0x32, 0x73, 0x9c, 0xb8, 0x1a, 0x3d, 0x7b, 0xd0, 0x5c, 0xdc, 0x28, 0x66, 0x8a, 0x8c, 0xae, 0x0b, 0x09, 0xb5, 0xe2, 0x56, 0x3e, 0x5f, 0xc1, 0xc9, 0xbb, 0x89, 0x15, 0x23, 0x9d, 0x05, 0xb7, 0xac, 0x47, 0x38, 0xb1, 0x5e, 0x15, 0x68, 0xdc, 0x87, 0xfb, 0x1f, 0xbd, 0x00, 0x01, 0xdf, 0xf1, 0x08, 0x73, 0xd2, 0x0a, 0xca, 0xb0, 0x07, 0x2c, 0x60, 0x1f, 0x67, 0x44, 0x30, 0x13, 0x24, 0x24, 0x3d, 0x01, 0x72, 0x7b, 0x24, 0x68, 0xbf, 0xf0, 0x77, 0x4c, 0xc8, 0x2b, 0xb8, 0xfe, 0xdb, 0x2b, 0x63, 0x39, 0x1c, 0x4e, 0xb6, 0x15, 0x57, 0xd6, 0x80, 0xd9, 0xb8, 0xd5, 0x96, 0x5b, 0xc6, 0x53, 0x47, 0x24, 0xfc, 0xbc, 0xe6, 0x1e, 0x08, 0xb6, 0x9e, 0x65, 0x22, 0x0e, 0x67, 0x26, 0x7e, 0x66, 0xb0, 0x11, 0x57, 0x65, 0xe7, 0xa8, 0x17, 0x51, 0xe3, 0xe0, 0xce, 0x57, 0xad, 0x1f, 0xff, 0x11, 0xe7, 0x80, 0x55, 0xf8, 0x09, 0x44, 0xab, 0x4c, 0xca, 0xe1, 0xcc, 0x80, 0xac, 0xe2, 0x6f, 0xe3, 0x15, 0xd2, 0x03, 0xa6, 0x5f, 0x1d, 0x99, 0x9c, 0x00, 0x99, 0x5f, 0x10, 0x6c, 0x39, 0x09, 0x3e, 0xba, 0x02, 0xa9, 0xdf, 0x2b, 0x67, 0x20, 0x96, 0x9a, 0x33, 0x02, 0xb2, 0x4b, 0x1f, 0xd7, 0xbc, 0x12, 0xa2, 0x4d, 0xdc, 0x3a, 0x1a, 0x3e, 0x60, 0x65, 0x69, 0x22, 0x2f, 0xb8, 0xe9, 0xaf, 0x38, 0xb4, 0x95, 0x44, 0xe8, 0x52, 0x23, 0x74, 0x5e, 0x01, 0x2d, 0xc3, 0x2b, 0xa0, 0xad, 0x11, 0x22, 0xae, 0xec, 0x52, 0xd6, 0xca, 0x53, 0xf3, 0x6e, 0x8e, 0xe0, 0x99, 0x49, 0xb0, 0x34, 0x01, 0x32, 0x47, 0xd8, 0x15, 0xc8, 0xea, 0x4f, 0xae, 0x40, 0x3a, 0x6f, 0xc0, 0x52, 0xf3, 0x6e, 0xc4, 0xaf, 0x88, 0x5e, 0x93, 0x89, 0x71, 0x84, 0x6e, 0xa0, 0xda, 0x2e, 0x6e, 0xe2, 0x28, 0xca, 0xbd, 0x0a, 0xa9, 0x79, 0x37, 0x09, 0x3c, 0x1e, 0xc0, 0x77, 0xe2, 0x79, 0x68, 0x3d, 0xf7, 0x26, 0x13, 0x65, 0x57, 0x62, 0xbd, 0xa7, 0x41, 0xf6, 0x80, 0x47, 0x13, 0xba, 0x12, 0x8b, 0xa7, 0x5e, 0x2d, 0x99, 0xcb, 0x97, 0x00, 0x85, 0x55, 0x5a, 0x3f, 0x0d, 0xd3, 0x72, 0xea, 0x15, 0x1c, 0x2a, 0xd6, 0x88, 0x12, 0x39, 0x43, 0x97, 0xb2, 0xcb, 0x58, 0x41, 0xd0, 0x01, 0xd0, 0x87, 0xa8, 0x5a, 0x1f, 0xe1, 0xe9, 0x50, 0x95, 0x14, 0xd1, 0x6e, 0x93, 0x00, 0x35, 0x6f, 0xda, 0xce, 0xaf, 0xc3, 0xcb, 0xda, 0x9f, 0x0b, 0x76, 0x36, 0xce, 0x19, 0x99, 0x25, 0x0f, 0x0b, 0xbc, 0xa7, 0x03, 0xdd, 0xe2, 0x59, 0x90, 0x7a, 0x10, 0x43, 0xb8, 0x58, 0xd9, 0x89, 0x57, 0x1f, 0x9d, 0x9f, 0x0e, 0x65, 0xf4, 0xb9, 0xfb, 0x8a, 0x0a, 0x3e, 0xf9, 0xdd, 0xed, 0x7b, 0x80, 0x7a, 0x72, 0xae, 0x0c, 0x4e, 0x37, 0xef, 0x01, 0x57, 0x46, 0x90, 0xb5, 0xbc, 0xb0, 0x13, 0xa0, 0x15, 0x1d, 0x0b, 0x78, 0x76, 0x02, 0x2c, 0x08, 0xb2, 0x56, 0x15, 0x76, 0x02, 0xb4, 0xa2, 0x63, 0x01, 0xcf, 0x4e, 0x80, 0x05, 0x41, 0xd6, 0xaa, 0xc2, 0x4e, 0x80, 0x56, 0x74, 0x2c, 0xe0, 0x7d, 0xaa, 0x12, 0xb0, 0x62, 0xc5, 0x0a, 0xf1, 0x11, 0x24, 0x3d, 0x8a, 0xe0, 0x7f, 0x77, 0xdd, 0x75, 0x97, 0x05, 0xa1, 0x8e, 0x5e, 0xc5, 0x15, 0xb5, 0x2a, 0x22, 0xba, 0x8b, 0x5d, 0xd4, 0x69, 0xd3, 0xa6, 0xb1, 0x5f, 0x16, 0x3a, 0x71, 0xe2, 0x04, 0x1c, 0x38, 0x70, 0x00, 0x8e, 0x1d, 0x3b, 0xc6, 0x98, 0x7e, 0xbf, 0xf1, 0xc5, 0x00, 0x5d, 0xda, 0x8d, 0x41, 0x96, 0xdd, 0x88, 0x35, 0x37, 0x37, 0xc3, 0xc9, 0x93, 0x27, 0x85, 0x95, 0xf4, 0x95, 0x36, 0xff, 0x52, 0xbb, 0xa3, 0xa3, 0x03, 0x2a, 0x2b, 0x2b, 0xc5, 0xfb, 0x83, 0x41, 0x83, 0x06, 0xb1, 0x1d, 0x93, 0x49, 0x98, 0x78, 0x55, 0x55, 0x55, 0x10, 0x0a, 0x85, 0x80, 0xd3, 0xb7, 0x6e, 0xdd, 0x0a, 0x7b, 0xf7, 0xee, 0x85, 0xd2, 0xd2, 0x52, 0x98, 0x3a, 0x75, 0x2a, 0xd0, 0xfe, 0xd6, 0xe5, 0xe5, 0xe5, 0x40, 0x3f, 0xab, 0x8a, 0x5f, 0xcb, 0xc2, 0xa4, 0x49, 0x93, 0xa0, 0x7f, 0x7f, 0xed, 0x8d, 0x37, 0x16, 0x2c, 0x58, 0x00, 0xcb, 0x96, 0x2d, 0x63, 0xf6, 0xe0, 0x17, 0xb3, 0xb0, 0x69, 0x53, 0xd7, 0x83, 0x3e, 0x61, 0xa4, 0x15, 0x80, 0x91, 0xcf, 0x13, 0xf5, 0x96, 0x79, 0xfa, 0xe9, 0xa7, 0xa3, 0x7e, 0x1a, 0xf9, 0xe4, 0x93, 0x4f, 0x4a, 0xe7, 0xcf, 0x9f, 0x8f, 0xca, 0x5b, 0xba, 0x74, 0xa9, 0x84, 0x2d, 0x34, 0x2a, 0x0f, 0xe3, 0x12, 0x93, 0x3e, 0x71, 0xe2, 0x44, 0x4d, 0x33, 0xf9, 0x9e, 0xdc, 0xa4, 0x2b, 0xd1, 0x4f, 0x96, 0x35, 0x2b, 0x8a, 0xc1, 0xb4, 0x64, 0x0e, 0x58, 0xb2, 0x64, 0x09, 0x6b, 0xdd, 0x5e, 0xaf, 0x17, 0x46, 0x8f, 0x1e, 0x2d, 0xda, 0xd5, 0x99, 0x33, 0x67, 0x00, 0x7f, 0x04, 0x54, 0xfc, 0xc0, 0x58, 0x59, 0x59, 0x19, 0x50, 0x4f, 0x41, 0x9b, 0x01, 0x7f, 0x04, 0x14, 0x86, 0x0c, 0x19, 0xc2, 0x60, 0xfc, 0x84, 0x5a, 0x94, 0x79, 0xe9, 0xa5, 0x97, 0x18, 0x8d, 0xce, 0xfc, 0xe8, 0xd3, 0xa7, 0x0f, 0xa3, 0x51, 0x2f, 0xe1, 0x1f, 0xb6, 0x6f, 0xd9, 0xb2, 0x05, 0x70, 0xf3, 0x74, 0x2e, 0xd2, 0x6d, 0xcf, 0x96, 0x24, 0x80, 0x7b, 0x4f, 0xc3, 0xc3, 0xfe, 0xfd, 0xfb, 0xe1, 0xd4, 0xa9, 0x53, 0x50, 0x5c, 0x5c, 0x0c, 0x6b, 0xd6, 0xac, 0x81, 0xe9, 0xd3, 0xa7, 0x03, 0x7d, 0xd2, 0x4d, 0xc9, 0xd8, 0xb1, 0x63, 0x07, 0x64, 0x67, 0x67, 0x73, 0xf1, 0xa8, 0xe7, 0xf1, 0xe3, 0xc7, 0x33, 0x7a, 0xdf, 0xbe, 0x7d, 0x05, 0x7f, 0xdc, 0xb8, 0x71, 0x0c, 0xa6, 0x49, 0x95, 0x92, 0xc1, 0x0f, 0xfc, 0x48, 0x99, 0x83, 0xdd, 0xf6, 0x6c, 0x69, 0x02, 0x28, 0x0a, 0x3b, 0x77, 0xee, 0x84, 0x91, 0x23, 0x47, 0xb2, 0xc9, 0xb0, 0x5f, 0xbf, 0x7e, 0x30, 0x60, 0xc0, 0x00, 0xf0, 0xf9, 0x7c, 0x6c, 0xeb, 0x86, 0x85, 0x0b, 0x17, 0x76, 0xdb, 0x40, 0x99, 0x66, 0x58, 0x8c, 0x21, 0x2a, 0x29, 0x6c, 0xfa, 0x1d, 0x85, 0xc9, 0x93, 0x27, 0x4b, 0xf8, 0xa3, 0x10, 0x62, 0xec, 0xc6, 0x96, 0x2c, 0xe1, 0x90, 0x24, 0xe1, 0x3e, 0x19, 0xd2, 0x94, 0x29, 0x53, 0x04, 0x1d, 0x1d, 0x95, 0x70, 0x72, 0x95, 0x56, 0xae, 0x5c, 0x29, 0xe1, 0x90, 0xc2, 0xb6, 0xa4, 0x08, 0x2f, 0x87, 0xbf, 0x5d, 0x2e, 0xe1, 0x8f, 0x44, 0x88, 0xdf, 0x5d, 0x20, 0xf9, 0xcc, 0xcc, 0x4c, 0x69, 0xf6, 0xec, 0xd9, 0xcc, 0x56, 0xfe, 0x1b, 0x0c, 0x44, 0x1f, 0x35, 0x6a, 0x94, 0x84, 0x7b, 0x12, 0x30, 0xfa, 0x73, 0xcf, 0x3d, 0x27, 0xe1, 0x5e, 0x1c, 0x12, 0xf6, 0x3c, 0xf6, 0x47, 0x3f, 0x87, 0x48, 0x32, 0xf4, 0x97, 0x91, 0x91, 0x21, 0xe8, 0x54, 0xf7, 0x23, 0x8f, 0x3c, 0x92, 0x14, 0xbf, 0xe3, 0x51, 0x62, 0xc9, 0x65, 0x28, 0x5d, 0xc9, 0xd0, 0xf8, 0x4f, 0x67, 0x7e, 0x34, 0x34, 0x34, 0x00, 0xee, 0xe1, 0xc1, 0x50, 0xe2, 0x85, 0x1f, 0xf5, 0xf5, 0xf5, 0x40, 0x7f, 0xe8, 0x00, 0x1b, 0xc7, 0xc3, 0xcb, 0x11, 0x9d, 0xe6, 0x09, 0x1a, 0x6a, 0xe8, 0x57, 0x3e, 0xe8, 0x12, 0x92, 0xfe, 0x70, 0x32, 0x67, 0x2a, 0x68, 0x13, 0x14, 0xba, 0x6a, 0xa2, 0xa3, 0xae, 0xae, 0x8e, 0xc9, 0x12, 0x8c, 0xdb, 0x72, 0xb0, 0x5e, 0xd7, 0xd4, 0xd4, 0xb9, 0x9a, 0x82, 0x68, 0xfc, 0x68, 0x6b, 0x6b, 0x63, 0x3c, 0xc2, 0x49, 0xe7, 0xb9, 0x73, 0x9d, 0xdb, 0xb7, 0x72, 0xbe, 0x99, 0x67, 0xcb, 0x2e, 0x43, 0xcd, 0x74, 0xa2, 0x27, 0xeb, 0xb6, 0x7c, 0x0e, 0xe8, 0xc9, 0xc1, 0x32, 0xc3, 0x76, 0x3b, 0x01, 0x66, 0x44, 0x55, 0x87, 0x4e, 0x3b, 0x01, 0x3a, 0x82, 0x65, 0x86, 0xa8, 0x9d, 0x00, 0x33, 0xa2, 0xaa, 0x43, 0xa7, 0x9d, 0x00, 0x1d, 0xc1, 0x32, 0x43, 0xd4, 0x4e, 0x80, 0x19, 0x51, 0xd5, 0xa1, 0xd3, 0x4e, 0x80, 0x8e, 0x60, 0x99, 0x21, 0x6a, 0x27, 0xc0, 0x8c, 0xa8, 0xea, 0xd0, 0xf9, 0x7f, 0xbe, 0xc9, 0xb9, 0x8c, 0x96, 0x81, 0xf7, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXBinaryIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x04, 0x24, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x36, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x42, 0x61, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x35, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x31, 0x3a, 0x30, 0x32, 0x3a, 0x38, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0xce, 0xc3, 0x0a, 0xd6, 0x00, 0x00, 0x05, 0xc2, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x59, 0x6f, 0x48, 0x64, 0x55, 0x14, 0x3f, 0x33, 0xe3, 0x8c, 0x63, 0xce, 0x44, 0x6e, 0x5b, 0xae, 0xd9, 0x22, 0xa8, 0x59, 0xae, 0x08, 0x91, 0x9a, 0x1b, 0xea, 0x87, 0x30, 0xd1, 0x45, 0x03, 0x29, 0x58, 0x61, 0x2b, 0xe9, 0x93, 0x89, 0x84, 0x44, 0x11, 0x51, 0x7d, 0x0b, 0x4a, 0x21, 0xb2, 0xed, 0x8b, 0xfa, 0x29, 0xa8, 0x2c, 0xf1, 0x83, 0x4a, 0x1b, 0xad, 0x6c, 0x61, 0x05, 0x9b, 0xc9, 0x36, 0xba, 0x9b, 0xe8, 0x1a, 0xa2, 0x28, 0xfe, 0xd7, 0x36, 0xf3, 0xcf, 0x6c, 0x3a, 0xce, 0x8c, 0x33, 0x9d, 0x3b, 0xf7, 0xcd, 0x9d, 0xf7, 0xe6, 0xcd, 0xcc, 0xfb, 0x3b, 0xc4, 0xc2, 0xbb, 0x1f, 0xc6, 0x73, 0xcf, 0xfd, 0x9d, 0xdf, 0xb9, 0xe7, 0x9c, 0xfb, 0xe7, 0x71, 0x35, 0x05, 0x83, 0x41, 0xb8, 0x97, 0x9b, 0xf9, 0x5e, 0x9e, 0x3c, 0x99, 0xbb, 0x11, 0xc0, 0xff, 0x5d, 0x41, 0xa3, 0x02, 0x46, 0x05, 0x34, 0x66, 0xc0, 0x58, 0x42, 0x1a, 0x13, 0xa8, 0xd9, 0x5c, 0x46, 0x05, 0x0e, 0xff, 0x82, 0xe0, 0x89, 0x66, 0x47, 0x3c, 0x02, 0x5d, 0x09, 0x53, 0x78, 0xc4, 0x42, 0x71, 0x67, 0x16, 0x7e, 0x7a, 0x13, 0xb6, 0x26, 0xe0, 0x68, 0x07, 0x52, 0xec, 0x70, 0xfa, 0x1c, 0x94, 0xbc, 0x01, 0x45, 0xaf, 0x08, 0x41, 0xbc, 0x1e, 0x06, 0x39, 0xf0, 0x1c, 0x78, 0xdd, 0x60, 0x3f, 0x05, 0x17, 0x7f, 0xe0, 0x0d, 0x84, 0x45, 0xa5, 0x84, 0xb3, 0x7d, 0x30, 0x71, 0x99, 0x18, 0x9f, 0x7f, 0x17, 0x0a, 0x5e, 0x0c, 0xb3, 0x44, 0xff, 0x8d, 0x13, 0x00, 0x1a, 0x5f, 0x7b, 0x0d, 0x7c, 0x87, 0x1c, 0xdc, 0xef, 0x81, 0xad, 0x9b, 0xf0, 0x7d, 0x33, 0x2c, 0xff, 0x08, 0x17, 0x3e, 0x07, 0x53, 0x2c, 0xab, 0xc5, 0xab, 0xb0, 0xf2, 0x0b, 0xc1, 0xa7, 0x67, 0x72, 0x56, 0xfc, 0x3f, 0x8a, 0x09, 0x83, 0x30, 0xf9, 0x19, 0x6c, 0x4d, 0x12, 0x8e, 0xc3, 0x3b, 0x7c, 0xa6, 0x28, 0x39, 0xd6, 0x12, 0x3a, 0xdc, 0x86, 0x6b, 0x2d, 0xdc, 0xec, 0x6d, 0xe9, 0x90, 0xf3, 0x2c, 0x38, 0x1f, 0xe1, 0xcc, 0x66, 0xbe, 0x82, 0x99, 0x2f, 0xa2, 0x28, 0x48, 0x77, 0x7d, 0x0c, 0xae, 0xbe, 0x1a, 0x43, 0x4f, 0x55, 0x4a, 0x09, 0x4f, 0x3c, 0x84, 0x6d, 0x73, 0x22, 0x2e, 0x21, 0x6f, 0x20, 0x56, 0x2e, 0x7f, 0xff, 0x18, 0x7c, 0x47, 0x04, 0xe3, 0xcc, 0x86, 0x97, 0xc7, 0xc1, 0x79, 0x96, 0xec, 0x81, 0x2b, 0x4d, 0x30, 0x37, 0x48, 0x94, 0xe3, 0x1f, 0x42, 0x51, 0x33, 0x98, 0xad, 0x44, 0x9e, 0xfd, 0x1a, 0xb6, 0x27, 0x61, 0xfd, 0x37, 0xd8, 0xb8, 0x41, 0xba, 0xf1, 0x9a, 0x4c, 0xc2, 0xbb, 0xeb, 0x70, 0xab, 0x1b, 0xdc, 0xab, 0xb0, 0xf0, 0x1d, 0x78, 0xf6, 0xe2, 0x91, 0x45, 0xe9, 0x45, 0x15, 0x08, 0x78, 0xe1, 0x56, 0x0f, 0x07, 0x2a, 0x7f, 0x87, 0xcc, 0x1e, 0x9b, 0xc9, 0x02, 0xd5, 0x97, 0xc1, 0x6c, 0x21, 0xf2, 0xde, 0x12, 0x2c, 0x5c, 0x21, 0x02, 0xb6, 0xdb, 0x5f, 0x82, 0xeb, 0x53, 0x89, 0xd9, 0xcb, 0x27, 0x3c, 0x58, 0x81, 0xf1, 0x8f, 0x00, 0x8b, 0x2c, 0x7b, 0xf6, 0x38, 0x05, 0x51, 0x00, 0xee, 0xb5, 0xc8, 0xd2, 0xcf, 0xbd, 0x10, 0x9a, 0x66, 0xe8, 0xc7, 0xf1, 0x28, 0x3c, 0x54, 0xcc, 0x75, 0xff, 0x99, 0x8b, 0xe8, 0x25, 0x25, 0xdd, 0x09, 0x85, 0x1e, 0x45, 0x4b, 0x08, 0xd3, 0x40, 0x1b, 0xe6, 0xfb, 0x81, 0x3c, 0x01, 0xf8, 0xd4, 0xe3, 0xb0, 0xfd, 0x07, 0xd1, 0x30, 0xcc, 0xf3, 0xdf, 0x80, 0xff, 0x98, 0xc3, 0xb8, 0x3e, 0x01, 0x57, 0x97, 0x00, 0x4f, 0x3b, 0x0c, 0x2c, 0x49, 0x78, 0xa6, 0x04, 0xda, 0xd6, 0x23, 0x0c, 0xdd, 0xd9, 0x11, 0x39, 0xbe, 0x24, 0x0e, 0x60, 0x99, 0x03, 0xdb, 0xee, 0xc7, 0xa5, 0x23, 0x30, 0xc4, 0xf3, 0x91, 0xb6, 0x83, 0x30, 0xc6, 0xfe, 0x60, 0x04, 0x60, 0x73, 0x46, 0x64, 0xbe, 0xc4, 0xc0, 0x92, 0x84, 0x66, 0x1b, 0x38, 0xc2, 0xa7, 0x05, 0x9f, 0x21, 0xa1, 0x2c, 0x5a, 0x42, 0x77, 0x37, 0x38, 0xbc, 0x59, 0x14, 0x1b, 0xd3, 0x30, 0x4c, 0x42, 0x6a, 0x6e, 0x90, 0x81, 0x99, 0x39, 0xb3, 0x62, 0x1a, 0x86, 0x61, 0x43, 0xb2, 0x05, 0x51, 0x00, 0x16, 0x3b, 0x67, 0x7b, 0xe2, 0x8d, 0x26, 0x61, 0x1a, 0x86, 0x89, 0x46, 0xc4, 0xea, 0x33, 0x30, 0x33, 0x67, 0x28, 0xa6, 0x61, 0x18, 0x36, 0x24, 0x5b, 0x10, 0x05, 0x90, 0x8a, 0x2b, 0x27, 0xd4, 0x7c, 0xff, 0x42, 0xd0, 0x2f, 0xe0, 0x39, 0x0e, 0x1f, 0x6d, 0x0c, 0x23, 0x18, 0x8e, 0xd3, 0x61, 0x60, 0xbd, 0x08, 0x85, 0x7e, 0x44, 0x01, 0x64, 0x3c, 0xc6, 0x01, 0x02, 0x7e, 0x72, 0x62, 0xf2, 0xdb, 0xee, 0x3c, 0xd7, 0x63, 0x18, 0xfe, 0x68, 0x3c, 0x99, 0x81, 0xf5, 0x22, 0x14, 0x3a, 0x12, 0x05, 0xf0, 0xf0, 0x93, 0xc0, 0x96, 0xe6, 0xca, 0xcf, 0x11, 0x30, 0xde, 0xa6, 0x7f, 0xdf, 0xe6, 0xba, 0x67, 0x4a, 0x23, 0x7a, 0x49, 0x49, 0x77, 0x42, 0xa1, 0x47, 0x51, 0x00, 0x78, 0x56, 0x9c, 0xbb, 0xc4, 0x61, 0x6e, 0x74, 0x82, 0x3f, 0x74, 0x25, 0x63, 0x1f, 0x2f, 0x60, 0x7a, 0x62, 0xde, 0x77, 0x1a, 0x9e, 0xb8, 0x28, 0x24, 0x49, 0xd8, 0xd3, 0x9d, 0x50, 0xe8, 0x4d, 0x74, 0xd4, 0xe0, 0xf0, 0x33, 0xef, 0x93, 0x6f, 0x84, 0xc0, 0x09, 0x59, 0x42, 0x3d, 0x67, 0xc9, 0x74, 0xd7, 0xae, 0xc3, 0x9d, 0x19, 0xce, 0xb0, 0xec, 0x2d, 0xb0, 0x3a, 0x84, 0x24, 0x52, 0x3d, 0xdd, 0x09, 0x79, 0x0e, 0x45, 0x15, 0xc0, 0xb1, 0x8c, 0x02, 0x38, 0xff, 0x1e, 0x87, 0xc1, 0x6f, 0x69, 0xfc, 0xb2, 0x60, 0xb3, 0xcf, 0x7a, 0x1a, 0x9e, 0x7a, 0x9d, 0x67, 0x2e, 0x4f, 0xd4, 0x9d, 0x90, 0xe7, 0x36, 0x56, 0x00, 0x38, 0x5c, 0xf9, 0x01, 0xbc, 0xf0, 0x6d, 0xe4, 0x23, 0x14, 0x35, 0xd6, 0x34, 0x28, 0x7f, 0x1b, 0x5e, 0xfa, 0x55, 0x71, 0xfa, 0xa9, 0x33, 0xdd, 0x09, 0xc3, 0x31, 0x98, 0x24, 0x9e, 0x16, 0xf1, 0x8a, 0xd9, 0xf9, 0x93, 0x7c, 0x96, 0xe2, 0x61, 0x82, 0x9f, 0x74, 0xda, 0x9b, 0xde, 0x84, 0x52, 0x01, 0x68, 0x9f, 0x71, 0x92, 0x19, 0xe2, 0x2c, 0xa1, 0x24, 0x7b, 0xd5, 0x91, 0xde, 0x08, 0x40, 0xc7, 0x64, 0xaa, 0xa2, 0x32, 0x2a, 0xa0, 0x2a, 0x6d, 0x3a, 0x1a, 0x19, 0x15, 0xd0, 0x31, 0x99, 0xaa, 0xa8, 0x8c, 0x0a, 0xa8, 0x4a, 0x9b, 0x8e, 0x46, 0x6a, 0x2a, 0x70, 0x12, 0x6a, 0x92, 0x93, 0xf0, 0xfb, 0xfd, 0x5d, 0x5d, 0x5d, 0x43, 0x43, 0x43, 0x92, 0x48, 0x4d, 0x00, 0xfc, 0x16, 0x52, 0xd4, 0xaa, 0xaa, 0xaa, 0xd0, 0x9f, 0xc9, 0x64, 0x1a, 0x1d, 0x1d, 0x4d, 0x6c, 0x38, 0x37, 0x47, 0x9e, 0x8f, 0x72, 0x73, 0x73, 0x13, 0xc3, 0x34, 0x8e, 0x2a, 0xae, 0x40, 0x41, 0x41, 0x41, 0x4a, 0x4a, 0x0a, 0x7a, 0x3d, 0x38, 0x38, 0x48, 0x9c, 0x39, 0x9b, 0xcd, 0x96, 0x9a, 0x9a, 0x9a, 0x9d, 0x2d, 0xeb, 0x79, 0x27, 0x31, 0x55, 0xa2, 0x51, 0x15, 0x09, 0xc0, 0xa4, 0x22, 0xe3, 0xf0, 0xf0, 0xb0, 0xa4, 0xad, 0xcf, 0xe7, 0x93, 0xc4, 0x68, 0x04, 0x28, 0xae, 0x00, 0x4b, 0xc6, 0xc6, 0xc6, 0x46, 0x7b, 0x7b, 0x7b, 0x75, 0x75, 0x75, 0x6b, 0x6b, 0xeb, 0xd8, 0xd8, 0x18, 0xd3, 0x53, 0xa1, 0xb9, 0xb9, 0xb9, 0xbc, 0xbc, 0xbc, 0xa2, 0xa2, 0xa2, 0xb2, 0xb2, 0x72, 0x6f, 0x2f, 0xfc, 0x9c, 0x01, 0xb0, 0xbb, 0xbb, 0xdb, 0xd8, 0xd8, 0x88, 0x43, 0x6d, 0x6d, 0x6d, 0x83, 0x83, 0x83, 0xc5, 0xc5, 0xc5, 0xe9, 0xe9, 0xe9, 0x75, 0x75, 0x75, 0x9b, 0x9b, 0x9b, 0x51, 0x0c, 0x72, 0xbb, 0x2a, 0x12, 0x40, 0x2b, 0x80, 0xdb, 0x80, 0xf9, 0xb0, 0x58, 0x2c, 0x3d, 0x3d, 0x3d, 0x8c, 0xca, 0xed, 0x76, 0xe3, 0x32, 0x63, 0xa3, 0x4b, 0x4b, 0x4b, 0x6c, 0x68, 0x62, 0x82, 0x7b, 0x34, 0x77, 0x3a, 0x9d, 0x7c, 0x4c, 0x53, 0x53, 0x13, 0xc3, 0x28, 0x12, 0x40, 0x11, 0x9a, 0x82, 0x69, 0x00, 0x38, 0x69, 0x4c, 0x73, 0x77, 0x77, 0x77, 0x5e, 0x1e, 0x79, 0x42, 0x4d, 0x4b, 0x4b, 0x5b, 0x5e, 0x5e, 0x66, 0x6c, 0xd3, 0xd3, 0xd3, 0x03, 0x03, 0x03, 0x34, 0x06, 0x7e, 0x00, 0x08, 0xe8, 0xeb, 0xeb, 0xa3, 0xfa, 0xda, 0xda, 0x5a, 0x8c, 0xa7, 0xa5, 0xa5, 0x05, 0xbb, 0x58, 0x07, 0x66, 0xab, 0x48, 0x50, 0x1f, 0x00, 0xcb, 0xd9, 0xfc, 0x3c, 0xf7, 0x5e, 0xd4, 0xd9, 0xd9, 0xc9, 0xf7, 0x7d, 0x74, 0xc4, 0xbd, 0x68, 0x44, 0x05, 0x30, 0x35, 0x35, 0x45, 0x03, 0x70, 0xb9, 0x5c, 0x88, 0x9f, 0x9c, 0x0c, 0xfd, 0x1b, 0x06, 0x60, 0x7f, 0x7f, 0x9f, 0x6f, 0x2e, 0x53, 0x56, 0xbf, 0x07, 0x1a, 0x1a, 0x1a, 0xe8, 0x3c, 0xf2, 0xf3, 0xf3, 0x0b, 0x0b, 0x0b, 0x51, 0xa6, 0xe7, 0x26, 0x55, 0xca, 0xf9, 0x2d, 0x29, 0x29, 0x41, 0x58, 0x4e, 0x4e, 0x0e, 0x05, 0x7b, 0x3c, 0x1e, 0x39, 0x56, 0x51, 0x18, 0xf5, 0x01, 0x60, 0x5e, 0x29, 0x97, 0xd7, 0xeb, 0x5d, 0x5b, 0x5b, 0x43, 0xd9, 0x6e, 0x0f, 0xbf, 0xab, 0x46, 0x39, 0x89, 0xd3, 0xa5, 0xbb, 0x88, 0xbf, 0x97, 0xe2, 0x00, 0x13, 0xa9, 0xd5, 0x07, 0xd0, 0xdf, 0xdf, 0xbf, 0xba, 0xba, 0x8a, 0x85, 0xee, 0xed, 0xed, 0xc5, 0x5d, 0x8b, 0x4e, 0x4a, 0x4b, 0x23, 0x2f, 0x76, 0x78, 0x59, 0xe3, 0x4d, 0x4c, 0x3d, 0xf3, 0x65, 0xd4, 0xa0, 0x09, 0xd5, 0x53, 0x21, 0x10, 0x08, 0xf0, 0xbb, 0x54, 0x56, 0xf0, 0x2b, 0x73, 0xa9, 0x31, 0x58, 0x4d, 0x4d, 0x0d, 0x63, 0xc7, 0xab, 0x8a, 0x6e, 0x68, 0xd4, 0x14, 0x15, 0x15, 0x61, 0x29, 0x28, 0xac, 0xac, 0xac, 0x8c, 0x61, 0x98, 0x80, 0x2b, 0xed, 0xf8, 0xf8, 0x18, 0x37, 0x7a, 0x46, 0x46, 0x06, 0x55, 0xa2, 0x06, 0x4f, 0xd8, 0xac, 0xac, 0x2c, 0xda, 0xcd, 0xcc, 0xcc, 0xc4, 0xcb, 0x91, 0x39, 0x92, 0x29, 0x28, 0xae, 0x80, 0xd9, 0x4c, 0x4c, 0x1c, 0x0e, 0x47, 0x47, 0x47, 0x07, 0x26, 0x6f, 0x71, 0x71, 0x11, 0xd7, 0x40, 0x7d, 0x7d, 0xfd, 0xc8, 0xc8, 0x88, 0xd5, 0x6a, 0xa5, 0x53, 0xa1, 0x18, 0x2a, 0xb3, 0x5f, 0x54, 0xe2, 0x9c, 0xb0, 0x8b, 0x61, 0x53, 0x25, 0xde, 0xd3, 0xc8, 0xc0, 0xef, 0x52, 0x00, 0x33, 0x91, 0x23, 0x68, 0x7a, 0x56, 0xc1, 0x6d, 0xb7, 0xb0, 0xb0, 0x80, 0xbb, 0x10, 0x0f, 0x75, 0x39, 0xce, 0x92, 0x81, 0xd1, 0x14, 0x40, 0x32, 0x26, 0xa4, 0x94, 0x53, 0xf1, 0x12, 0x52, 0xea, 0x20, 0xd9, 0x78, 0x23, 0x80, 0x64, 0x67, 0x58, 0x8a, 0xdf, 0xa8, 0x80, 0x54, 0x86, 0x92, 0x3d, 0x6e, 0x54, 0x20, 0xd9, 0x19, 0x96, 0xe2, 0x37, 0x2a, 0x20, 0x95, 0xa1, 0x64, 0x8f, 0x1b, 0x15, 0x48, 0x76, 0x86, 0xa5, 0xf8, 0xff, 0x03, 0xf5, 0x1a, 0x5a, 0xe0, 0xcf, 0xeb, 0xd5, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXBinaryIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x01, 0x95, 0x9f, 0x47, 0xae, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xfb, 0x7d, 0x74, 0x00, 0x00, 0x0e, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x5d, 0x09, 0x90, 0x15, 0xc5, 0x19, 0xfe, 0x39, 0xe4, 0x94, 0x1b, 0x24, 0xc8, 0x11, 0x8e, 0x04, 0x90, 0x80, 0x18, 0x29, 0x54, 0x02, 0x05, 0x2b, 0x09, 0x87, 0x2b, 0x44, 0x70, 0x03, 0x16, 0xa9, 0x84, 0xc2, 0x58, 0x20, 0x26, 0x9a, 0x22, 0xa1, 0xa2, 0x22, 0x11, 0x10, 0x22, 0x18, 0xc3, 0x12, 0x23, 0x72, 0x54, 0x59, 0x31, 0x72, 0x48, 0x09, 0xca, 0x21, 0x97, 0x82, 0xc8, 0x11, 0x90, 0x2b, 0x40, 0x10, 0x10, 0x50, 0x14, 0xd0, 0x05, 0x21, 0x28, 0xc7, 0x22, 0xb0, 0xdc, 0x93, 0xff, 0x9b, 0x7e, 0x3d, 0xd3, 0x73, 0xbd, 0x37, 0x33, 0xef, 0xbd, 0x1d, 0x57, 0xe7, 0xaf, 0x7a, 0xd3, 0x7f, 0xff, 0xfd, 0x1f, 0xdd, 0xfd, 0x77, 0xf7, 0xf4, 0xcc, 0x74, 0xf7, 0x2b, 0xa5, 0x31, 0x50, 0x00, 0x28, 0x1d, 0x80, 0x57, 0x67, 0x4d, 0x43, 0xa0, 0xc5, 0x28, 0xa2, 0xae, 0x93, 0x84, 0x41, 0xe0, 0x00, 0x84, 0x2a, 0xce, 0xa4, 0xb2, 0x7a, 0x82, 0xbc, 0xcc, 0x1b, 0x22, 0x31, 0x11, 0xde, 0x58, 0x81, 0xa8, 0x57, 0x1b, 0x0b, 0xcd, 0xcc, 0xd2, 0xad, 0x0d, 0x88, 0x3a, 0x3e, 0x6f, 0x49, 0xa4, 0xcb, 0x57, 0x89, 0x5e, 0xff, 0x8f, 0x85, 0x56, 0xea, 0x9b, 0x5c, 0x4b, 0x96, 0x9c, 0x12, 0x95, 0x2a, 0x55, 0xca, 0x46, 0x11, 0x51, 0xb3, 0xd0, 0xb6, 0x64, 0xaf, 0x06, 0xe0, 0x29, 0x60, 0x93, 0x37, 0xa2, 0xdf, 0xf8, 0x5a, 0xea, 0x3c, 0x51, 0xb4, 0x9d, 0x99, 0x9b, 0x89, 0x0a, 0x8b, 0x44, 0xbe, 0xdf, 0xd8, 0x6e, 0xd2, 0x98, 0x22, 0x0a, 0xbd, 0xef, 0xb8, 0x48, 0xfc, 0xdf, 0x59, 0xa2, 0x8f, 0xc6, 0x0a, 0x7c, 0xc4, 0x42, 0x11, 0xfe, 0xf9, 0x2d, 0x93, 0x06, 0x0a, 0x9a, 0x86, 0x0e, 0xcd, 0x9f, 0x96, 0x98, 0xa6, 0xcd, 0xd8, 0xe4, 0x49, 0x2b, 0xe6, 0x5a, 0xf2, 0x6a, 0x0e, 0xa2, 0x30, 0xe2, 0xea, 0xf0, 0xb4, 0x2a, 0x04, 0x5c, 0x8d, 0x43, 0x24, 0xfb, 0x65, 0x08, 0x6c, 0x41, 0x2d, 0x8f, 0x1f, 0xdc, 0x51, 0x66, 0x3f, 0x42, 0x41, 0x78, 0x9c, 0x06, 0x30, 0x94, 0xa9, 0xc3, 0x97, 0xd4, 0x76, 0x37, 0xd3, 0xd1, 0x24, 0x25, 0x48, 0x1e, 0x39, 0xcc, 0x49, 0xba, 0x8d, 0xcf, 0x6a, 0x60, 0xcf, 0x17, 0x44, 0x47, 0xcf, 0x88, 0xa6, 0xb9, 0xf9, 0x49, 0x73, 0x8c, 0x84, 0xf0, 0x17, 0x4c, 0x97, 0xf0, 0xf0, 0x6b, 0x44, 0x35, 0x2a, 0x09, 0xbe, 0x5f, 0xdd, 0xe9, 0xcd, 0xc7, 0xfc, 0x56, 0x03, 0xad, 0x6f, 0x96, 0x2a, 0xc4, 0xa0, 0xdc, 0xac, 0x8e, 0x19, 0x57, 0xb1, 0x17, 0xfa, 0x13, 0x9d, 0xbe, 0x20, 0x28, 0xb3, 0xb7, 0x10, 0xfd, 0x9d, 0xe3, 0x1e, 0xf0, 0x6d, 0x74, 0xb2, 0x52, 0xd4, 0x35, 0x6b, 0xd6, 0x28, 0x31, 0x81, 0x56, 0xac, 0x58, 0xd1, 0x41, 0x4b, 0x4a, 0x30, 0xc7, 0x14, 0x27, 0xc6, 0x82, 0x5a, 0xdf, 0xbe, 0x7d, 0x71, 0xff, 0xd6, 0x13, 0x47, 0x8e, 0x1c, 0x69, 0xe0, 0x20, 0x80, 0x3e, 0x7e, 0xfc, 0x78, 0x0b, 0xcd, 0xae, 0xc5, 0x1c, 0xbf, 0x6c, 0x29, 0x05, 0x05, 0x05, 0xba, 0x20, 0x94, 0x70, 0xff, 0xd7, 0x53, 0x81, 0xcb, 0x1f, 0x08, 0xc0, 0x77, 0xee, 0xdc, 0x99, 0xd4, 0x40, 0xc9, 0x77, 0x72, 0xc9, 0x2f, 0x81, 0xb5, 0x27, 0xb3, 0xd7, 0x32, 0x0d, 0xdf, 0x46, 0x03, 0x72, 0x18, 0xfe, 0xd7, 0x46, 0x6b, 0x6d, 0xa9, 0xc3, 0xf2, 0x95, 0x6b, 0xe6, 0x90, 0x5e, 0x70, 0xda, 0x9b, 0x8f, 0x53, 0xac, 0xb3, 0x54, 0x28, 0x79, 0x83, 0x67, 0xaa, 0x98, 0x80, 0x02, 0x7f, 0xf0, 0x27, 0xd6, 0xa1, 0x58, 0xaa, 0x6a, 0xfd, 0x8c, 0x39, 0xdb, 0x00, 0x1f, 0x66, 0x23, 0x6a, 0x06, 0x24, 0x1f, 0x87, 0x4e, 0x1f, 0x40, 0xb9, 0x0a, 0x10, 0x96, 0xd3, 0x19, 0x95, 0x6e, 0xc7, 0x3d, 0xf8, 0x9c, 0x06, 0xec, 0x82, 0x69, 0xc6, 0x9d, 0x06, 0x16, 0xee, 0xf4, 0xa7, 0xf2, 0xda, 0x75, 0x7f, 0x7c, 0xb6, 0x31, 0x4e, 0xd3, 0x30, 0x69, 0xc3, 0xef, 0xdd, 0xbd, 0xd6, 0x24, 0x75, 0x32, 0x87, 0x14, 0xc9, 0x57, 0x58, 0x94, 0x94, 0x2f, 0x1e, 0x2a, 0x52, 0xfa, 0xc1, 0xe9, 0xe4, 0x94, 0x22, 0xc1, 0x18, 0x3c, 0x0d, 0x6c, 0xdc, 0x68, 0xeb, 0xc9, 0xac, 0x77, 0xf9, 0xf2, 0xe5, 0xc1, 0xb4, 0x83, 0xdb, 0xda, 0x04, 0xcc, 0xd8, 0xf0, 0xe1, 0xc3, 0xcd, 0x48, 0x1a, 0x98, 0x67, 0x09, 0x60, 0x5c, 0xce, 0xc7, 0x0f, 0x1f, 0x3e, 0x8c, 0x28, 0xb5, 0x6f, 0xdf, 0x5e, 0x0f, 0x71, 0xe9, 0xda, 0xb5, 0xab, 0x91, 0x6e, 0x10, 0x5d, 0x90, 0xa4, 0x06, 0x38, 0xe3, 0x28, 0x21, 0x35, 0x69, 0xd2, 0x44, 0x17, 0x1d, 0x35, 0x8a, 0xc7, 0x9d, 0x04, 0x8c, 0x18, 0x31, 0x42, 0x4f, 0x7b, 0xe8, 0xa1, 0x87, 0x24, 0xc9, 0x3d, 0xf4, 0x2a, 0x3d, 0x73, 0x5b, 0x66, 0x10, 0xdd, 0xbb, 0x77, 0x37, 0xe2, 0xb3, 0x66, 0xcd, 0x32, 0x70, 0xc9, 0xe7, 0xa5, 0x27, 0xee, 0x68, 0xee, 0xf5, 0xae, 0x50, 0x93, 0x3a, 0x59, 0xe1, 0x0b, 0x8d, 0x66, 0xdd, 0x07, 0xa1, 0x73, 0xe6, 0x53, 0x30, 0xeb, 0x35, 0xe4, 0x33, 0x1f, 0xa1, 0xd9, 0xe2, 0x02, 0x84, 0xae, 0xba, 0x0c, 0x09, 0xfa, 0xf3, 0x00, 0x9e, 0x87, 0x5f, 0xdd, 0x44, 0xb4, 0x6a, 0x5f, 0x6a, 0xb3, 0x78, 0xfd, 0xf8, 0xe5, 0x39, 0xa2, 0xa2, 0x2b, 0xde, 0xbc, 0x9f, 0x7c, 0x49, 0xf4, 0x8f, 0xd5, 0x44, 0x3b, 0x0b, 0xbc, 0x79, 0x64, 0x4a, 0x0a, 0x7d, 0xc9, 0x3b, 0xf1, 0x83, 0x33, 0x88, 0x36, 0x7e, 0x2a, 0x54, 0x0d, 0xef, 0x46, 0xb4, 0xeb, 0x28, 0xd1, 0xbb, 0x7b, 0x45, 0x7c, 0xed, 0x70, 0xa2, 0x7a, 0xd5, 0xa4, 0x19, 0x11, 0x3e, 0xc9, 0x2f, 0xc9, 0x16, 0xfe, 0x57, 0xe0, 0x23, 0x73, 0x89, 0x06, 0xde, 0x65, 0x4d, 0x97, 0x53, 0xd7, 0x72, 0x3c, 0x1b, 0x1e, 0xf7, 0x73, 0xa2, 0x17, 0xde, 0x23, 0x3a, 0x56, 0x28, 0x78, 0xdc, 0x66, 0xa6, 0xa9, 0xf4, 0xb1, 0xa4, 0x75, 0x5e, 0xad, 0x9a, 0xbb, 0xca, 0x93, 0x42, 0x99, 0x79, 0x55, 0xf9, 0xea, 0x8f, 0x88, 0x1e, 0xe1, 0xd7, 0x20, 0x39, 0xf9, 0xe6, 0x74, 0x58, 0x66, 0x4c, 0x95, 0xb7, 0xe3, 0x53, 0xd7, 0x99, 0x94, 0xdd, 0x89, 0x7b, 0x62, 0x9f, 0xdb, 0x88, 0xfa, 0x4e, 0x23, 0xda, 0x7b, 0x8c, 0x28, 0x77, 0x32, 0xcf, 0x27, 0x1e, 0x13, 0x3c, 0x7e, 0xf4, 0x25, 0xb4, 0x79, 0x17, 0x40, 0x66, 0xde, 0x34, 0x2b, 0xb0, 0x8e, 0xcd, 0xec, 0x14, 0xb3, 0x20, 0x48, 0xf1, 0x32, 0xbe, 0x86, 0x0b, 0x0e, 0xb0, 0x7b, 0xad, 0xe3, 0x0f, 0x44, 0x01, 0x3e, 0xe5, 0x66, 0x25, 0x41, 0xad, 0x30, 0x2f, 0x7d, 0x09, 0x5e, 0xef, 0x3e, 0xd0, 0xf9, 0x87, 0x52, 0x9d, 0x35, 0x7c, 0xe7, 0x43, 0x6b, 0xdc, 0x6f, 0x2c, 0xb7, 0xb5, 0xe0, 0x94, 0x4d, 0x46, 0xca, 0x49, 0x7d, 0xea, 0x3b, 0x2a, 0x99, 0xe6, 0x23, 0xf4, 0x2e, 0x00, 0x84, 0xef, 0x4d, 0x7c, 0x3c, 0x40, 0x2d, 0xa0, 0xbd, 0xa2, 0x4f, 0x3c, 0x3e, 0x5f, 0xa8, 0xdd, 0x32, 0xc2, 0x87, 0x7a, 0x85, 0x05, 0x8f, 0x79, 0x65, 0xcb, 0x08, 0x42, 0xcb, 0xd1, 0x44, 0x78, 0xb6, 0xbc, 0x63, 0x02, 0x51, 0xc1, 0x29, 0x41, 0x9b, 0x3f, 0x54, 0x61, 0xf6, 0x8f, 0x26, 0xef, 0xc4, 0xaa, 0x9e, 0xf7, 0xf6, 0x13, 0x35, 0xa8, 0xc1, 0x4d, 0xa4, 0xae, 0x4a, 0x0d, 0x87, 0x63, 0x84, 0x7a, 0x7b, 0x0f, 0x51, 0x97, 0xe6, 0x44, 0xb5, 0x2a, 0x87, 0xd3, 0x91, 0x90, 0xf2, 0x5f, 0x80, 0xb4, 0xcc, 0x64, 0x4f, 0x38, 0x79, 0x13, 0xca, 0x9e, 0xdd, 0x8c, 0x69, 0x8e, 0x0b, 0x90, 0xb1, 0xaa, 0x0c, 0xa9, 0x28, 0xb0, 0x07, 0x8e, 0x1c, 0x39, 0xe2, 0xeb, 0x69, 0x0f, 0xf9, 0x19, 0x36, 0x6c, 0x98, 0xe3, 0x2b, 0x4e, 0xc8, 0x7c, 0x7a, 0x8b, 0x79, 0x3d, 0xac, 0x25, 0xa3, 0xe3, 0x99, 0x99, 0x35, 0x6a, 0xf9, 0xf9, 0xf9, 0x7a, 0x58, 0xa7, 0x4e, 0x1d, 0xed, 0xd0, 0xa1, 0x43, 0x16, 0x91, 0x0a, 0x15, 0x2a, 0x18, 0x0f, 0x86, 0x96, 0x04, 0x8e, 0x40, 0x16, 0xbf, 0x05, 0x0b, 0x16, 0x18, 0xf8, 0x8a, 0x15, 0x2b, 0xec, 0x6c, 0xbe, 0xe2, 0x9e, 0x0f, 0xf6, 0xc9, 0xa4, 0x65, 0x01, 0x0a, 0x0b, 0x0b, 0x0d, 0x36, 0x64, 0x08, 0x99, 0x56, 0x61, 0xf1, 0xe2, 0xc5, 0x7a, 0x06, 0x55, 0x1a, 0xf0, 0x95, 0x2b, 0x57, 0xea, 0x74, 0xfe, 0x5a, 0xa0, 0x27, 0xe5, 0xe5, 0xe5, 0xb9, 0xf2, 0xe9, 0x89, 0x29, 0x2e, 0x81, 0x9b, 0x10, 0x67, 0xd4, 0x80, 0x09, 0x13, 0xf8, 0x46, 0xc4, 0xb0, 0x6a, 0xd5, 0x2a, 0x3d, 0x6c, 0xd6, 0xcc, 0x65, 0x9a, 0xa1, 0xa7, 0xb8, 0x5f, 0x72, 0x72, 0x72, 0xf4, 0x84, 0xea, 0xd5, 0xab, 0xbb, 0x33, 0xf8, 0xa0, 0xa6, 0x7d, 0x1f, 0xd8, 0xb4, 0x69, 0x13, 0xb5, 0x68, 0xd1, 0x82, 0x6a, 0xd6, 0xac, 0xe9, 0xc3, 0x5c, 0xe6, 0x59, 0xd2, 0x2e, 0x40, 0xe6, 0xb3, 0x14, 0x4c, 0x63, 0x5a, 0x4d, 0x28, 0x98, 0xa9, 0xec, 0x70, 0xc7, 0x05, 0xc8, 0x4e, 0xbd, 0xfa, 0xd7, 0x1a, 0xf7, 0x01, 0xff, 0x75, 0x95, 0x1d, 0xce, 0xb8, 0x0f, 0x64, 0xa7, 0x5e, 0xfd, 0x6b, 0x8d, 0x3d, 0xe0, 0xbf, 0xae, 0xb2, 0xc3, 0x19, 0x7b, 0x20, 0x3b, 0xf5, 0xea, 0x5f, 0xeb, 0x77, 0xc4, 0x03, 0x9b, 0x0e, 0x12, 0x4d, 0x59, 0x4b, 0x74, 0xc4, 0xf6, 0xfd, 0xdf, 0xad, 0xa2, 0xf0, 0x62, 0x17, 0x3f, 0x2f, 0xc0, 0x77, 0xec, 0xa5, 0xbb, 0xc5, 0xfa, 0xd3, 0xf3, 0x97, 0xbd, 0xb8, 0x4c, 0x7a, 0x0a, 0x7d, 0xc9, 0xef, 0xc4, 0x78, 0x7b, 0xfc, 0xc0, 0xcb, 0xa6, 0x32, 0x60, 0x37, 0xf0, 0xcb, 0xa9, 0x3d, 0xa3, 0xad, 0x34, 0x19, 0xc3, 0xc3, 0x16, 0x5e, 0x5a, 0x01, 0xd4, 0xd7, 0x83, 0x82, 0xc2, 0x2f, 0x74, 0xf9, 0x53, 0xea, 0x6c, 0x65, 0x1d, 0x1b, 0xe8, 0x6d, 0xea, 0x13, 0xbd, 0xf9, 0xb0, 0xe4, 0xb0, 0x86, 0xa9, 0xf4, 0x31, 0x77, 0xf2, 0x26, 0x24, 0x33, 0x7f, 0x4b, 0x3d, 0xf1, 0x36, 0x19, 0xea, 0xb1, 0x22, 0xe3, 0xb6, 0xbf, 0x58, 0x0d, 0x21, 0xf6, 0x39, 0xbf, 0x61, 0x93, 0x99, 0x77, 0xa6, 0x12, 0x6d, 0xff, 0xcc, 0xcc, 0x7c, 0xff, 0x76, 0x44, 0x0f, 0x77, 0x16, 0x5c, 0xbb, 0xf9, 0x8d, 0xf7, 0xb8, 0x65, 0x4e, 0x89, 0x54, 0xfa, 0x12, 0x12, 0xde, 0x2f, 0x77, 0xff, 0xba, 0xc2, 0x54, 0xba, 0xe8, 0x11, 0x81, 0xf7, 0x63, 0xc3, 0xc8, 0x64, 0x91, 0xe2, 0xfa, 0xf3, 0x97, 0x88, 0x6e, 0x7f, 0xd6, 0xe4, 0xf5, 0xc2, 0x06, 0xf1, 0x6b, 0x49, 0x00, 0xde, 0xb9, 0x8e, 0xbb, 0x4f, 0xe0, 0xdf, 0xaf, 0x45, 0xf4, 0x14, 0xbf, 0x92, 0xc7, 0x3a, 0xbc, 0xa7, 0xef, 0x15, 0x34, 0xbf, 0xfa, 0x04, 0x77, 0x12, 0x0f, 0x40, 0x29, 0xe0, 0xbe, 0xb6, 0x22, 0xc4, 0x55, 0x5d, 0x7b, 0xfc, 0x76, 0xc0, 0x97, 0xbc, 0xf8, 0x50, 0x01, 0x18, 0xd5, 0x4b, 0x84, 0xb8, 0xe6, 0xfd, 0xd8, 0xc4, 0x43, 0x62, 0xde, 0x1e, 0x90, 0x06, 0xdb, 0xda, 0x96, 0xc9, 0x48, 0x43, 0xbb, 0x8e, 0x10, 0xdd, 0xf3, 0x23, 0xa2, 0xca, 0xe5, 0xcd, 0xf6, 0xbe, 0xf5, 0x30, 0xd1, 0xaf, 0x5f, 0x91, 0x1c, 0xee, 0x61, 0x43, 0x7e, 0xbf, 0xea, 0x06, 0xe8, 0xd0, 0x95, 0xcb, 0x05, 0xd6, 0x97, 0xbc, 0x0f, 0xc0, 0x50, 0x05, 0x56, 0xea, 0x06, 0x27, 0x93, 0x8c, 0x34, 0x6e, 0xfc, 0xa9, 0x68, 0xa7, 0xce, 0xa7, 0xe2, 0x70, 0x4d, 0x4f, 0x5d, 0x80, 0xaf, 0x2f, 0xba, 0x0a, 0x52, 0x9d, 0x2a, 0xee, 0xf4, 0xb0, 0xd4, 0x3a, 0x37, 0x86, 0x92, 0xf4, 0x2e, 0x00, 0x36, 0x17, 0x00, 0xbc, 0xbe, 0xd4, 0xb4, 0x6f, 0xac, 0x27, 0x07, 0xbe, 0x60, 0xd4, 0x71, 0x83, 0x0a, 0x37, 0xb8, 0x51, 0x53, 0xd2, 0xbc, 0x0b, 0xf0, 0x1b, 0xfe, 0x20, 0x01, 0x58, 0xf7, 0xb1, 0x08, 0x71, 0x95, 0xab, 0x77, 0x81, 0xe7, 0xf0, 0xbb, 0xfd, 0x20, 0x80, 0xbe, 0x02, 0xf8, 0xe3, 0x1b, 0x22, 0xc4, 0x75, 0xe2, 0xbb, 0x26, 0x1e, 0x12, 0xf3, 0x2e, 0xc0, 0xef, 0x72, 0x4c, 0x95, 0x3f, 0x7b, 0x81, 0xe8, 0xe5, 0x0d, 0x44, 0x77, 0x3d, 0x27, 0x68, 0x58, 0xe7, 0x1c, 0x14, 0x5e, 0x1f, 0x2c, 0x24, 0x30, 0xbe, 0x0f, 0x7f, 0x93, 0xe8, 0xb9, 0x77, 0x58, 0xe7, 0x7a, 0x41, 0x7b, 0xac, 0x6b, 0x50, 0x6d, 0x06, 0xbf, 0x77, 0x01, 0xc0, 0xf2, 0xde, 0x1f, 0x04, 0x23, 0x3e, 0x03, 0x4d, 0x5c, 0x29, 0xf0, 0x6a, 0x15, 0x89, 0xb0, 0x90, 0x3b, 0x28, 0x34, 0xbf, 0x89, 0x68, 0xc4, 0x3d, 0x42, 0x6a, 0xe9, 0x2e, 0xf1, 0x89, 0x09, 0xb1, 0x6e, 0xad, 0x88, 0x1e, 0xcd, 0x11, 0xf4, 0x10, 0xd7, 0xe4, 0x53, 0x09, 0xa9, 0xf0, 0xc8, 0x19, 0xa2, 0x1d, 0x9f, 0x8b, 0x61, 0x13, 0x53, 0x89, 0x74, 0xe1, 0x03, 0x1e, 0x82, 0xcf, 0xf1, 0x0d, 0xd0, 0xed, 0x8b, 0x67, 0x40, 0xdd, 0xfe, 0x0a, 0x10, 0x50, 0x69, 0x71, 0xb2, 0x27, 0x6f, 0x42, 0xc5, 0x99, 0x93, 0x90, 0xb6, 0xe2, 0x02, 0x84, 0xac, 0xb8, 0x8c, 0x89, 0xc5, 0x1e, 0xc8, 0x58, 0x55, 0x86, 0x54, 0x14, 0x7b, 0x20, 0x64, 0xc5, 0x65, 0x4c, 0x2c, 0xb0, 0x07, 0x8a, 0x8a, 0x8a, 0x08, 0x5b, 0x79, 0x4e, 0x9e, 0x3c, 0x99, 0x32, 0x13, 0xf8, 0xec, 0x34, 0x63, 0x46, 0xe2, 0x49, 0x2c, 0x25, 0x77, 0x48, 0x86, 0x14, 0x1f, 0x01, 0x1d, 0xc9, 0x6c, 0x86, 0x9f, 0xdc, 0x49, 0xeb, 0xd1, 0xa3, 0x87, 0x23, 0xcd, 0x4e, 0x00, 0xdf, 0xe0, 0xc1, 0x83, 0xed, 0xe4, 0x8c, 0xc6, 0xbd, 0x9f, 0xc8, 0x3c, 0x2a, 0x84, 0xad, 0xfb, 0xfe, 0x78, 0x0d, 0xde, 0x6c, 0x43, 0xe0, 0x26, 0x24, 0x33, 0x74, 0xf9, 0xf2, 0x65, 0xaa, 0x57, 0xaf, 0x9e, 0x5e, 0x98, 0xfb, 0xef, 0xbf, 0x5f, 0x92, 0xf5, 0x70, 0xd7, 0xae, 0x5d, 0x3a, 0x1d, 0xeb, 0xbb, 0xc1, 0xa3, 0xc2, 0xc0, 0x81, 0x03, 0xf5, 0xb4, 0x3e, 0x7d, 0xfa, 0x10, 0x7f, 0x57, 0xd6, 0x71, 0xfe, 0x50, 0xae, 0xb2, 0x04, 0xc3, 0xc3, 0xf8, 0x93, 0x2d, 0xe8, 0xcd, 0xa8, 0x61, 0xc3, 0x86, 0x5a, 0xdb, 0xb6, 0x6d, 0x75, 0xbc, 0x72, 0xe5, 0xca, 0x86, 0xaa, 0x73, 0xe7, 0xce, 0x69, 0xbd, 0x7a, 0xf5, 0xd2, 0xe9, 0xd5, 0xaa, 0x55, 0x33, 0xe8, 0x40, 0xf0, 0x45, 0x5e, 0xca, 0xb7, 0x6b, 0xd7, 0x4e, 0x6b, 0xd3, 0xa6, 0x8d, 0x1e, 0x9f, 0x3d, 0x7b, 0xb6, 0x85, 0xcf, 0x6f, 0x24, 0xd4, 0x97, 0x7a, 0x64, 0xa0, 0x46, 0x8d, 0x1a, 0x86, 0x8d, 0xdc, 0xdc, 0x5c, 0x3d, 0x13, 0x57, 0xaf, 0x5e, 0x35, 0x68, 0x40, 0xc0, 0x67, 0x2f, 0x80, 0xa4, 0x23, 0x4d, 0x02, 0xf0, 0xb0, 0x7d, 0x25, 0x74, 0x13, 0xea, 0xd0, 0xa1, 0x03, 0xdb, 0x15, 0xd0, 0xb3, 0x67, 0x4f, 0x1d, 0xd9, 0xba, 0x75, 0xab, 0x24, 0x05, 0x0e, 0x2f, 0x5d, 0xe2, 0xe9, 0x75, 0x18, 0x90, 0xb5, 0x10, 0x24, 0x64, 0x3b, 0x7a, 0xed, 0x4a, 0x99, 0xd2, 0xa5, 0x4b, 0x5b, 0xe2, 0x92, 0x0e, 0x3e, 0xbf, 0x1e, 0xe0, 0xbe, 0x21, 0xc5, 0x02, 0x85, 0xa6, 0x1f, 0x7d, 0x8a, 0xc9, 0xcc, 0x37, 0x68, 0xd0, 0x40, 0xcf, 0xb4, 0x8c, 0x8f, 0x1e, 0x3d, 0xda, 0xd0, 0x20, 0x69, 0xf6, 0x10, 0x0c, 0xad, 0x5a, 0xb5, 0x32, 0xe4, 0x78, 0x39, 0x8e, 0x81, 0x83, 0x77, 0xc7, 0x8e, 0x1d, 0x86, 0x0e, 0xbf, 0x48, 0x5a, 0x0f, 0x34, 0x17, 0x2e, 0x5c, 0xa0, 0x6d, 0xdb, 0xb6, 0x51, 0xe7, 0xce, 0x89, 0xf7, 0x9c, 0x9c, 0x8b, 0xe2, 0x86, 0xb4, 0x0a, 0x50, 0xdc, 0x99, 0x75, 0xb3, 0x17, 0xba, 0x13, 0xbb, 0x29, 0x8b, 0x82, 0x16, 0x17, 0x20, 0x8a, 0x5a, 0x57, 0x6d, 0xc6, 0x1e, 0x50, 0x6b, 0x23, 0x0a, 0x3c, 0xf6, 0x40, 0x14, 0xb5, 0xae, 0xda, 0x2c, 0xf1, 0xf7, 0x01, 0xb5, 0x30, 0x25, 0x11, 0x2f, 0xf1, 0x5d, 0xa0, 0x24, 0x56, 0xba, 0x9a, 0xe7, 0xd8, 0x01, 0x6a, 0x6d, 0x44, 0x80, 0xc7, 0x0e, 0x88, 0xa0, 0xd2, 0x55, 0x93, 0xb1, 0x03, 0xd4, 0xda, 0x88, 0x00, 0x8f, 0x1d, 0x10, 0x41, 0xa5, 0xab, 0x26, 0x63, 0x07, 0xa8, 0xb5, 0x11, 0x01, 0x1e, 0x3b, 0x20, 0x82, 0x4a, 0x57, 0x4d, 0xc6, 0x0e, 0x50, 0x6b, 0x23, 0x02, 0x3c, 0xf0, 0x87, 0x19, 0x47, 0x1e, 0xcf, 0xf2, 0x42, 0xae, 0x91, 0x8b, 0x88, 0x56, 0x26, 0xce, 0x08, 0xb0, 0x33, 0x60, 0x41, 0xe2, 0xa4, 0x7e, 0x44, 0x21, 0x37, 0x6a, 0xeb, 0xfb, 0xaf, 0xa7, 0xad, 0x33, 0xb5, 0xba, 0x9d, 0x31, 0x60, 0xa6, 0x3a, 0xb1, 0x55, 0xfb, 0x45, 0xfe, 0xce, 0x5c, 0x70, 0xa6, 0x61, 0xed, 0x22, 0xf6, 0x67, 0xe3, 0x9c, 0x83, 0xb2, 0x21, 0xdb, 0x22, 0xf6, 0x87, 0xa7, 0x91, 0xbf, 0xf0, 0x0e, 0xf8, 0x8c, 0x57, 0x0e, 0xf5, 0x9e, 0x42, 0x74, 0x89, 0xf7, 0x64, 0x27, 0x83, 0xcf, 0xf8, 0x1b, 0x66, 0xde, 0x74, 0xc1, 0xf1, 0x24, 0x7f, 0x3d, 0x40, 0x81, 0xfd, 0x02, 0xd6, 0x71, 0xc9, 0xa5, 0x50, 0x7e, 0x65, 0x24, 0x1f, 0x4e, 0x4d, 0xc5, 0x51, 0xab, 0xc9, 0x00, 0x5f, 0x5e, 0x5e, 0x79, 0x5f, 0xfc, 0xea, 0x56, 0xe5, 0xbd, 0xe5, 0x8f, 0x89, 0xb5, 0x92, 0xc9, 0x64, 0xd4, 0xb4, 0x74, 0xf2, 0x97, 0xd0, 0x13, 0xce, 0x01, 0x07, 0x4e, 0xf0, 0x11, 0xc3, 0x2f, 0xa9, 0x59, 0xe1, 0x35, 0xc8, 0x0d, 0xf9, 0xc8, 0x8f, 0x41, 0x44, 0x15, 0x95, 0x15, 0x7b, 0x2f, 0xad, 0x25, 0x9a, 0xbc, 0xda, 0xe4, 0xc3, 0xda, 0x32, 0x9c, 0x43, 0x3b, 0xec, 0xa7, 0x26, 0x0d, 0x18, 0xd6, 0x30, 0xe7, 0x73, 0x65, 0x63, 0x21, 0xde, 0x21, 0x76, 0xd8, 0x07, 0x05, 0xd6, 0xf4, 0xa0, 0xb1, 0xdf, 0xcf, 0x25, 0x5a, 0xf1, 0xa1, 0x29, 0x85, 0x96, 0xfe, 0xe2, 0x03, 0x44, 0xdd, 0x79, 0x49, 0x98, 0x04, 0x1c, 0xa9, 0xf0, 0xcb, 0x7f, 0x9a, 0x67, 0x85, 0xe2, 0xe8, 0xdb, 0x8e, 0x7f, 0x23, 0x7a, 0xff, 0x4f, 0x4e, 0x27, 0x64, 0x3a, 0x7f, 0x32, 0x0f, 0x1c, 0x86, 0xeb, 0x77, 0x4f, 0xf1, 0x90, 0xa3, 0x42, 0xd3, 0xda, 0x44, 0x73, 0x79, 0x21, 0x9f, 0x5a, 0xf9, 0x48, 0xc7, 0xfa, 0xb7, 0xdf, 0xf2, 0x4f, 0x05, 0x74, 0x57, 0xfb, 0xd1, 0x7d, 0x58, 0x19, 0x8c, 0xa3, 0x19, 0x16, 0xed, 0x4c, 0xbf, 0xf2, 0xff, 0x7d, 0xc0, 0x5a, 0xf9, 0xb0, 0x8d, 0x21, 0x50, 0xad, 0x7c, 0xd0, 0x70, 0x28, 0x07, 0x5a, 0x3c, 0x8e, 0x46, 0x91, 0x80, 0x75, 0xe0, 0xcf, 0x2c, 0x95, 0x31, 0x33, 0xcc, 0x64, 0xfe, 0x4c, 0xad, 0x3a, 0xa6, 0x58, 0xb7, 0xa5, 0x78, 0x45, 0x31, 0xf4, 0x60, 0xa9, 0xb4, 0x0a, 0xbd, 0x6e, 0x55, 0x63, 0x56, 0xbc, 0x37, 0xa7, 0x4d, 0x5d, 0x6b, 0xa5, 0x2d, 0xfe, 0x80, 0x48, 0x5d, 0xc9, 0x89, 0x65, 0xa4, 0x7b, 0xc7, 0x58, 0x79, 0x10, 0xdb, 0x7a, 0x98, 0x68, 0xd0, 0xab, 0xc0, 0xfc, 0x03, 0x74, 0xab, 0x50, 0x86, 0xdb, 0x98, 0x3c, 0x48, 0x44, 0xa5, 0x03, 0xc7, 0xfa, 0xda, 0x6e, 0xb7, 0x10, 0x2d, 0xdb, 0x6d, 0xa6, 0x2c, 0x61, 0xfc, 0xf9, 0x3c, 0x33, 0x0e, 0x2c, 0x93, 0xf9, 0xb3, 0x6a, 0x4e, 0x72, 0x9e, 0x8d, 0x8d, 0xd1, 0x88, 0xda, 0x4f, 0x43, 0x41, 0x02, 0xc6, 0x4f, 0x2f, 0xb8, 0x99, 0x5b, 0x9a, 0x1d, 0x8e, 0x73, 0xf7, 0xb7, 0x03, 0x2a, 0xca, 0x0e, 0x6e, 0x34, 0x3b, 0x8f, 0x3d, 0x7e, 0x8c, 0x87, 0x12, 0x15, 0x52, 0xad, 0xea, 0xb6, 0x1f, 0x4f, 0x73, 0xfd, 0x3a, 0xd1, 0x89, 0xaf, 0x89, 0x6e, 0xaa, 0xa2, 0x6a, 0x21, 0x72, 0xcb, 0x8b, 0x1b, 0xcd, 0x2a, 0x95, 0x32, 0xe6, 0x52, 0xea, 0x14, 0x32, 0x65, 0x78, 0x3c, 0xb5, 0x03, 0xc6, 0x48, 0x2f, 0x70, 0x4b, 0x2b, 0x1d, 0xdc, 0xac, 0x97, 0x7a, 0x07, 0xdd, 0x9e, 0x3f, 0x37, 0xfb, 0xaa, 0x90, 0x5b, 0x7a, 0x69, 0x97, 0x32, 0xaa, 0x32, 0x19, 0xc4, 0x83, 0xd7, 0x44, 0xfd, 0x1a, 0x4e, 0xf3, 0x87, 0xf9, 0xc6, 0xe9, 0x05, 0x6e, 0x69, 0xf5, 0xab, 0x7b, 0x71, 0xa7, 0x4f, 0xb7, 0xeb, 0xc6, 0xc6, 0x0c, 0x2c, 0x6b, 0xf6, 0x02, 0x7b, 0xfe, 0x70, 0x4f, 0xa8, 0x1d, 0x6e, 0xef, 0x82, 0x97, 0x89, 0x64, 0xf4, 0xe0, 0x0e, 0xc0, 0x90, 0xd2, 0xbe, 0xb1, 0x55, 0xe7, 0x72, 0x1e, 0x37, 0xbd, 0xce, 0x65, 0x7d, 0xcb, 0x36, 0x26, 0x43, 0x12, 0x47, 0x69, 0x65, 0x0b, 0xdc, 0x74, 0xdb, 0xef, 0x0b, 0xd2, 0x36, 0x66, 0x5d, 0xeb, 0x3f, 0x91, 0x31, 0x11, 0xba, 0xc9, 0x5b, 0x39, 0x32, 0x1a, 0x0b, 0xee, 0x00, 0x98, 0xc7, 0x4d, 0x4a, 0xdd, 0x80, 0x85, 0x31, 0xb3, 0xe7, 0x8b, 0x44, 0xd8, 0xf1, 0xa5, 0xc2, 0xd8, 0x65, 0x44, 0xb3, 0x36, 0xab, 0x14, 0x3e, 0xd0, 0xaa, 0x87, 0x73, 0x7c, 0xb5, 0x72, 0xa4, 0x17, 0xbb, 0xb3, 0x09, 0xd1, 0x2f, 0x6e, 0xb7, 0xea, 0xc0, 0xcc, 0xe6, 0x35, 0xdb, 0xca, 0x25, 0x6c, 0x20, 0xc0, 0x39, 0xf9, 0x18, 0xf3, 0x25, 0x54, 0xaf, 0xc4, 0x9b, 0xc2, 0x12, 0xbb, 0xd7, 0x24, 0x2d, 0xcb, 0x61, 0xf0, 0x59, 0x10, 0x32, 0x84, 0x5e, 0xf0, 0xfe, 0xe3, 0x7c, 0x62, 0xd8, 0x64, 0x73, 0x13, 0x0d, 0xf6, 0x91, 0xa4, 0xda, 0xbe, 0x97, 0xcf, 0xd3, 0x41, 0xdb, 0x5f, 0x94, 0x64, 0xa5, 0x7c, 0xcf, 0xf6, 0x21, 0x7d, 0x4b, 0x95, 0xfa, 0x84, 0x3a, 0x96, 0x9d, 0x80, 0x9f, 0x17, 0x34, 0xe7, 0x83, 0xc2, 0x16, 0x0c, 0x15, 0x1b, 0x43, 0xbd, 0x78, 0xb2, 0x40, 0x0f, 0xe7, 0x00, 0x64, 0x04, 0x27, 0x91, 0x61, 0xdf, 0x0c, 0x4e, 0xff, 0x9b, 0xb4, 0x8a, 0x9f, 0x03, 0xb6, 0xf1, 0x58, 0x7b, 0xd1, 0x9a, 0x45, 0x6c, 0x4d, 0xb9, 0x83, 0x5b, 0xe4, 0x78, 0xae, 0x90, 0xef, 0x25, 0x99, 0x29, 0x59, 0xa5, 0x32, 0x13, 0xc3, 0xc3, 0x1e, 0x7e, 0xf8, 0xc3, 0x06, 0xbc, 0x2a, 0xf9, 0x98, 0x1f, 0x1e, 0xd5, 0xd6, 0x0e, 0x2b, 0xb5, 0x78, 0xac, 0x7f, 0x34, 0x87, 0x1f, 0xc8, 0xee, 0xc8, 0x8c, 0xcd, 0x10, 0x5a, 0xe2, 0x8f, 0xf2, 0x21, 0x2a, 0x2d, 0x93, 0x22, 0xe1, 0xee, 0x01, 0x99, 0xcc, 0xc1, 0x77, 0x5c, 0x57, 0xec, 0x80, 0x88, 0x1b, 0x40, 0xec, 0x80, 0xd8, 0x01, 0x11, 0xd7, 0x40, 0xc4, 0xe6, 0xe3, 0x1e, 0x10, 0x3b, 0x20, 0xe2, 0x1a, 0x88, 0xd8, 0x7c, 0xdc, 0x03, 0x62, 0x07, 0x84, 0xaf, 0x81, 0x2b, 0x57, 0xae, 0x10, 0x76, 0xba, 0x94, 0x64, 0xc8, 0x7a, 0x0f, 0xb8, 0x78, 0xf1, 0x22, 0xf1, 0xb6, 0x38, 0x63, 0xf3, 0x21, 0x36, 0x20, 0xf6, 0xef, 0xdf, 0x3f, 0xed, 0x3a, 0xe3, 0x83, 0x71, 0xa9, 0x5c, 0xb9, 0x72, 0xc4, 0x7b, 0x04, 0x69, 0xc8, 0x90, 0x21, 0x69, 0xeb, 0x8b, 0x4a, 0x41, 0xd6, 0x1d, 0x80, 0x1d, 0x9d, 0x13, 0x27, 0x4e, 0xa4, 0x96, 0x2d, 0x5b, 0x1a, 0x65, 0x3c, 0x7b, 0xd6, 0xf6, 0xd1, 0xc4, 0x48, 0xf1, 0x8f, 0xf0, 0x7f, 0xeb, 0xe8, 0x7f, 0xe2, 0x83, 0x3f, 0xf2, 0x01, 0x5e, 0x52, 0x21, 0xfc, 0xbb, 0xa0, 0x00, 0x25, 0xc6, 0x3f, 0x1a, 0xed, 0xdb, 0xb7, 0x8f, 0xf6, 0xef, 0xdf, 0xef, 0x90, 0x3a, 0x71, 0xe2, 0x04, 0x5d, 0xbb, 0x76, 0xcd, 0xb1, 0x2d, 0xd7, 0xc1, 0x98, 0x20, 0xa0, 0x47, 0x1d, 0x38, 0x70, 0x40, 0xe7, 0x5f, 0xb4, 0x88, 0xdf, 0xf1, 0x30, 0xd4, 0xad, 0x9b, 0xfa, 0xc4, 0x7d, 0x1c, 0xe8, 0x7d, 0xea, 0x14, 0xbf, 0x30, 0x64, 0x68, 0xdc, 0xb8, 0x31, 0x55, 0xad, 0x5a, 0x55, 0x8f, 0x6f, 0xd9, 0xb2, 0x45, 0xcf, 0x5b, 0xd3, 0xa6, 0x4d, 0xa9, 0x53, 0xa7, 0x4e, 0x54, 0xbb, 0x76, 0x6d, 0x9d, 0xa7, 0xd8, 0x2e, 0x7e, 0xb7, 0x24, 0xa6, 0xcb, 0x27, 0xcf, 0xd2, 0xe6, 0x82, 0x59, 0xb6, 0x47, 0xda, 0xe3, 0x55, 0xaa, 0x54, 0xd1, 0xe6, 0xce, 0x9d, 0xeb, 0x6a, 0x6e, 0xde, 0xbc, 0x79, 0xae, 0xb2, 0x6e, 0xfb, 0x49, 0x55, 0x05, 0xdc, 0xfb, 0x2c, 0x72, 0x5c, 0xc9, 0x5a, 0x99, 0x32, 0x65, 0x2c, 0x34, 0x99, 0x0f, 0x76, 0xa6, 0xc6, 0xf7, 0x15, 0x55, 0x3c, 0xab, 0x78, 0xe0, 0xbd, 0xaa, 0x61, 0x73, 0x63, 0x77, 0xc0, 0x80, 0x01, 0x03, 0x34, 0x6e, 0xcd, 0x86, 0xba, 0x69, 0xd3, 0xa6, 0x59, 0x2a, 0xa4, 0x77, 0xef, 0xde, 0x46, 0x9a, 0x1b, 0x22, 0x0f, 0x3d, 0x47, 0xc5, 0xa5, 0x72, 0x00, 0xe4, 0xe5, 0x61, 0xe8, 0xb2, 0xa2, 0xd5, 0xed, 0xd5, 0xd7, 0xaf, 0x5f, 0xd7, 0xb8, 0x57, 0x18, 0xf6, 0xc7, 0x8c, 0x19, 0xe3, 0x66, 0x32, 0x2b, 0xb4, 0xac, 0xdf, 0x03, 0xb8, 0xc0, 0x0e, 0xe8, 0xd6, 0xad, 0x1b, 0xcd, 0x99, 0x33, 0x87, 0xca, 0x97, 0x4f, 0x1c, 0xaa, 0xc5, 0x1c, 0x43, 0x87, 0x0e, 0x25, 0x3e, 0x2a, 0xdf, 0xe0, 0x5d, 0xb2, 0x64, 0x89, 0x3e, 0xd4, 0x18, 0x84, 0x0c, 0x23, 0xec, 0x70, 0x43, 0x23, 0x26, 0x06, 0xf8, 0x87, 0x39, 0x09, 0x07, 0x0f, 0x1e, 0x94, 0x68, 0xd6, 0xc3, 0x48, 0x1c, 0xb0, 0x67, 0xcf, 0x1e, 0xc2, 0x91, 0x0d, 0x76, 0xd8, 0xbe, 0x7d, 0xbb, 0x41, 0x42, 0xa5, 0xf8, 0x19, 0xdb, 0x0d, 0x81, 0x80, 0x08, 0x0f, 0x41, 0x16, 0x09, 0xd8, 0x8b, 0x04, 0xb2, 0xd2, 0xaf, 0x6c, 0x4a, 0x79, 0x9a, 0xa8, 0x0f, 0x13, 0x5c, 0x40, 0xa3, 0x9b, 0x03, 0xe7, 0x9b, 0x9e, 0xf6, 0xc4, 0x13, 0x4f, 0x68, 0x3c, 0x2d, 0xb5, 0x8c, 0xc9, 0x5c, 0x19, 0xda, 0xea, 0xd5, 0xab, 0x2d, 0x5a, 0xe6, 0xcf, 0x9f, 0xaf, 0xf1, 0x7f, 0x22, 0x6a, 0xd8, 0xa6, 0x8f, 0x1f, 0xc6, 0x71, 0xa9, 0x0f, 0xfc, 0x92, 0xde, 0xa8, 0x51, 0x23, 0xfd, 0xb8, 0x8a, 0xe3, 0xc7, 0x8f, 0x1b, 0xf2, 0xd3, 0xa7, 0x4f, 0xd7, 0x70, 0xbc, 0x85, 0xe4, 0x47, 0x88, 0xe3, 0x2a, 0x36, 0x6f, 0xde, 0xac, 0xf1, 0x04, 0x40, 0xeb, 0xd7, 0xaf, 0x9f, 0x56, 0xa9, 0x52, 0x25, 0x23, 0x9d, 0xff, 0xcd, 0x59, 0xc3, 0xff, 0x35, 0xd8, 0x4f, 0x90, 0x30, 0x14, 0x66, 0x10, 0x29, 0x96, 0x59, 0xd0, 0xd1, 0xa3, 0x47, 0x09, 0xe7, 0xdd, 0x00, 0xf0, 0x57, 0xd5, 0xeb, 0xd7, 0xaf, 0xa7, 0x29, 0x53, 0xa6, 0xd0, 0xcc, 0x99, 0x33, 0x69, 0xc3, 0x86, 0x0d, 0x3a, 0x1d, 0x17, 0x3e, 0xcb, 0x80, 0xa6, 0x4e, 0x9d, 0x4a, 0x5d, 0xba, 0x74, 0x31, 0x68, 0x12, 0xc1, 0xd4, 0x15, 0x33, 0x99, 0x63, 0xc7, 0x8e, 0x49, 0x92, 0x11, 0x72, 0x7d, 0xe8, 0x69, 0x20, 0xa0, 0x25, 0xd7, 0xaa, 0x55, 0xcb, 0xd2, 0xc3, 0xce, 0x9c, 0x39, 0x43, 0xe7, 0xcf, 0x9f, 0x37, 0xf8, 0x81, 0x9c, 0x3e, 0x7d, 0x5a, 0x7f, 0x88, 0xe3, 0xf1, 0x9f, 0xbe, 0xfa, 0xea, 0x2b, 0xc2, 0x43, 0x9d, 0x04, 0xe0, 0xa0, 0x21, 0xcd, 0xde, 0x53, 0x24, 0x4f, 0xa6, 0xc2, 0xf8, 0x8b, 0x58, 0xa6, 0x6a, 0x32, 0xa4, 0x9e, 0x48, 0xee, 0x01, 0x21, 0xf3, 0xfa, 0xad, 0x14, 0x8b, 0x1d, 0x10, 0xb1, 0x5b, 0x63, 0x07, 0xc4, 0x0e, 0x88, 0xb8, 0x06, 0x22, 0x36, 0x1f, 0xf7, 0x80, 0xd8, 0x01, 0x11, 0xd7, 0x40, 0xc4, 0xe6, 0xe3, 0x1e, 0x10, 0x3b, 0x20, 0xe2, 0x1a, 0x88, 0xd8, 0xfc, 0xff, 0x01, 0xdc, 0xb2, 0xfe, 0x66, 0x57, 0x2f, 0x5a, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; #pragma mark - 3D Explorer Icons static const u_int8_t FLEXToggle2DIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x5d, 0x3f, 0x5b, 0x66, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xdf, 0xdc, 0x41, 0x00, 0x00, 0x02, 0x1f, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x98, 0xcf, 0x2b, 0x44, 0x51, 0x14, 0xc7, 0x3d, 0xbf, 0x86, 0x92, 0x8d, 0x3f, 0x60, 0xc4, 0xc2, 0xd6, 0x8f, 0xb2, 0x40, 0x2c, 0xc9, 0x46, 0xcd, 0x82, 0x94, 0x7f, 0x40, 0x52, 0xca, 0x4a, 0x12, 0x4a, 0x76, 0xca, 0x42, 0xc9, 0xc6, 0xc6, 0x02, 0x0b, 0x25, 0x8b, 0xd9, 0x48, 0xc6, 0xc6, 0x0e, 0x65, 0x37, 0x7e, 0x65, 0x83, 0x0d, 0x2b, 0xbf, 0xc2, 0x68, 0x7c, 0x8e, 0x86, 0x5e, 0xd7, 0x79, 0x79, 0xca, 0xdc, 0xa1, 0xee, 0xa9, 0x6f, 0xf7, 0x9e, 0x73, 0xcf, 0x7d, 0xe7, 0x7b, 0xce, 0xbb, 0xef, 0xbe, 0xfb, 0x5e, 0x5e, 0x1e, 0x92, 0x4e, 0xa7, 0x63, 0x60, 0x1d, 0xac, 0x82, 0x26, 0xb1, 0xfd, 0x57, 0xf1, 0x48, 0xa0, 0x07, 0xf2, 0x2b, 0xbe, 0x04, 0x52, 0xf4, 0x1b, 0x3d, 0xcf, 0x3b, 0xf0, 0xd9, 0x24, 0xe9, 0x32, 0xf4, 0x29, 0xd0, 0x01, 0xf2, 0xfd, 0x63, 0x59, 0xec, 0xa7, 0xb9, 0x76, 0x02, 0x8c, 0xc2, 0xe7, 0x3a, 0x54, 0x1c, 0x88, 0x6e, 0x02, 0x53, 0x66, 0xcd, 0xc9, 0x38, 0xf4, 0x9b, 0x4e, 0x16, 0xf5, 0x09, 0x93, 0x4f, 0x90, 0x2e, 0x95, 0x7e, 0x51, 0x06, 0x35, 0xdb, 0x83, 0xe2, 0x67, 0xcb, 0x14, 0x3a, 0xb6, 0x2c, 0xb9, 0x76, 0x58, 0xc5, 0xc1, 0xc7, 0x32, 0xba, 0xa7, 0x5f, 0xcf, 0x2d, 0x3e, 0xf2, 0xb3, 0xc5, 0x2f, 0x82, 0x3e, 0x04, 0x72, 0xb1, 0xe4, 0x66, 0xe0, 0x73, 0xeb, 0xe7, 0x13, 0xd4, 0xf7, 0x64, 0x00, 0xb2, 0xcd, 0x34, 0x7d, 0xe0, 0x09, 0xcc, 0x33, 0x39, 0x49, 0xeb, 0xc4, 0x55, 0xc0, 0x55, 0xc0, 0x55, 0xc0, 0x55, 0xe0, 0xcf, 0x55, 0xe0, 0x7d, 0xdb, 0xd6, 0x58, 0xb1, 0x95, 0x17, 0x61, 0x6f, 0x03, 0xc5, 0x20, 0xc1, 0x56, 0x1e, 0xfa, 0xe5, 0xa6, 0x5d, 0xcf, 0x96, 0x4d, 0x4d, 0x88, 0x64, 0xca, 0x21, 0xb0, 0x03, 0x6a, 0x33, 0x44, 0xce, 0x68, 0x5b, 0x49, 0xea, 0x22, 0xa3, 0x7f, 0x36, 0xf8, 0x46, 0x51, 0xba, 0x80, 0xbc, 0x78, 0x6d, 0xc9, 0x1e, 0x81, 0xb6, 0xe1, 0x23, 0x67, 0xbd, 0xef, 0x05, 0x92, 0xe3, 0xc0, 0x94, 0x05, 0x73, 0x26, 0x0e, 0x51, 0x90, 0x32, 0x1d, 0x2d, 0xe9, 0x23, 0x26, 0x1f, 0xd1, 0x3f, 0x8e, 0x3b, 0xe6, 0x58, 0x95, 0x69, 0x40, 0xaf, 0x56, 0x6c, 0xf2, 0xa9, 0x51, 0xa0, 0xd8, 0x6d, 0x98, 0x5a, 0xb4, 0x20, 0x41, 0x09, 0x6d, 0x29, 0xce, 0x9a, 0x6d, 0x03, 0xbf, 0x7d, 0xc5, 0x37, 0xdb, 0xa6, 0x1b, 0x02, 0xcc, 0x69, 0x41, 0x82, 0x9e, 0x21, 0xb1, 0x4f, 0x83, 0x41, 0x20, 0x77, 0x60, 0x09, 0x0c, 0xb0, 0x66, 0xb5, 0x53, 0xb8, 0x9c, 0x05, 0x2b, 0x19, 0xb7, 0xf9, 0x0c, 0x9d, 0xc3, 0x45, 0xce, 0x9d, 0x3f, 0x13, 0x88, 0xe6, 0x83, 0xc2, 0x9f, 0xcd, 0x72, 0xde, 0xae, 0x02, 0xae, 0x02, 0xae, 0x02, 0xae, 0x02, 0xd9, 0xab, 0x80, 0xfa, 0x1e, 0x0a, 0x0a, 0xc7, 0x16, 0xde, 0xcd, 0xd8, 0x24, 0xa8, 0x00, 0x6b, 0x60, 0x98, 0xf7, 0x81, 0x7a, 0x68, 0xc5, 0xb7, 0x94, 0x71, 0x39, 0xd8, 0xda, 0x92, 0x3b, 0xb8, 0xbc, 0x86, 0x0e, 0x06, 0xc1, 0x06, 0xf0, 0x0a, 0xfc, 0xf2, 0xe5, 0xff, 0x9d, 0x5c, 0x10, 0x87, 0x45, 0xf0, 0xec, 0x77, 0xb4, 0xd0, 0xbf, 0x24, 0x86, 0xfc, 0x34, 0x0d, 0x27, 0x38, 0x8f, 0x29, 0xa4, 0x4e, 0xcd, 0xd9, 0xf8, 0x48, 0xe2, 0xb9, 0x92, 0x64, 0xd0, 0x59, 0xce, 0xe4, 0x29, 0xfa, 0x95, 0x62, 0xd4, 0x6c, 0x27, 0xf8, 0xa9, 0xcb, 0x50, 0x99, 0xff, 0xdb, 0xa6, 0xc3, 0xd0, 0xcf, 0x10, 0x25, 0x2f, 0x21, 0xfa, 0x2e, 0xa8, 0xcb, 0xb0, 0x78, 0xa4, 0xed, 0x64, 0xdd, 0x26, 0x32, 0xfa, 0x67, 0x83, 0x6f, 0x0d, 0x4a, 0x2f, 0x90, 0xff, 0xe1, 0xb6, 0xe4, 0x98, 0x40, 0xcb, 0xa1, 0x13, 0x12, 0x56, 0x10, 0x8d, 0xd0, 0xc4, 0x80, 0x6c, 0x0a, 0x71, 0x92, 0x91, 0x0f, 0x3f, 0x27, 0xd9, 0xac, 0xc0, 0x1b, 0xa1, 0x20, 0xca, 0x34, 0x04, 0xe9, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXToggle2DIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0xbf, 0x82, 0x4c, 0xac, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xa7, 0x5a, 0xe9, 0x00, 0x00, 0x03, 0x33, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x9b, 0x4d, 0x88, 0x4e, 0x61, 0x14, 0xc7, 0xe7, 0x1a, 0x5f, 0x65, 0xe3, 0x23, 0x8a, 0xed, 0x64, 0x61, 0x22, 0x5f, 0xc9, 0x57, 0xc8, 0x42, 0x29, 0x0b, 0xe5, 0x63, 0x43, 0xd9, 0x20, 0x11, 0x29, 0x9b, 0xd9, 0xb3, 0xb0, 0xd5, 0xac, 0x24, 0x42, 0xd9, 0x48, 0xb3, 0xf3, 0x11, 0x0d, 0x09, 0x35, 0x44, 0x61, 0x43, 0xde, 0xb1, 0x20, 0x29, 0x43, 0xb2, 0x41, 0xa9, 0xe9, 0x7d, 0xfd, 0xce, 0x64, 0xa6, 0x3b, 0x6f, 0xf7, 0x39, 0xe7, 0xbe, 0xf3, 0xbc, 0xf1, 0xdc, 0x9c, 0x53, 0xff, 0xee, 0xbd, 0xcf, 0xff, 0x39, 0xe7, 0x39, 0xe7, 0x7f, 0xef, 0x7d, 0x6e, 0xf7, 0xb9, 0xef, 0x9b, 0x75, 0x60, 0x8d, 0x46, 0x63, 0x32, 0x9b, 0x3d, 0x60, 0x35, 0xf8, 0x09, 0xfa, 0xb3, 0x2c, 0xbb, 0xcd, 0xd6, 0x2d, 0xa0, 0x40, 0x86, 0x68, 0xf3, 0xe0, 0x6e, 0x81, 0x15, 0x4d, 0x7d, 0xae, 0x70, 0xbc, 0x0f, 0x01, 0x1b, 0x4d, 0xed, 0xe3, 0x0e, 0xf1, 0x9f, 0x46, 0xc3, 0x32, 0x30, 0x73, 0x1c, 0x91, 0xe6, 0xc1, 0x0f, 0xd2, 0x7a, 0x4e, 0x4d, 0xb2, 0x8d, 0x33, 0x0a, 0xef, 0x03, 0x21, 0x3b, 0xa6, 0x45, 0xc7, 0xa9, 0x0b, 0xbc, 0x0d, 0x39, 0x27, 0xda, 0x3e, 0x44, 0x5e, 0x6b, 0xb4, 0xba, 0xca, 0x70, 0x72, 0xc5, 0xd5, 0xe9, 0x98, 0x05, 0x3a, 0x3f, 0xe3, 0xec, 0xac, 0x0a, 0x70, 0x72, 0x8b, 0xf7, 0xc1, 0xed, 0x08, 0xf1, 0x09, 0xb7, 0xbf, 0xa4, 0x2e, 0xb9, 0x4b, 0x26, 0x6c, 0x93, 0xf0, 0x0c, 0x89, 0x26, 0x41, 0xbb, 0x8c, 0xc8, 0xdd, 0x06, 0x9f, 0x2a, 0xbd, 0x88, 0x93, 0xae, 0xd5, 0x6d, 0xe6, 0x2d, 0xc2, 0x0d, 0x2b, 0xbd, 0x5e, 0x29, 0x9c, 0x50, 0x03, 0x06, 0x9f, 0x2a, 0xfd, 0xd8, 0x9a, 0xbb, 0xad, 0xc4, 0x45, 0xb8, 0x73, 0x4a, 0xa7, 0x5e, 0x85, 0x13, 0xaa, 0x07, 0xdc, 0x33, 0xfa, 0xa4, 0x46, 0xbf, 0x20, 0xa1, 0x03, 0xb1, 0x49, 0xc9, 0x1c, 0x37, 0x83, 0x20, 0x97, 0xc1, 0xce, 0x5c, 0x30, 0xb9, 0x0a, 0x4f, 0x72, 0x56, 0x4e, 0xe5, 0xda, 0x82, 0xbb, 0xc4, 0x98, 0x0d, 0x59, 0x85, 0xa7, 0xea, 0x77, 0x6a, 0xfa, 0x1c, 0x2c, 0xa4, 0x05, 0x62, 0xec, 0x3e, 0xa7, 0x78, 0x79, 0xd2, 0x08, 0xe4, 0x51, 0x7d, 0x9f, 0x01, 0x06, 0x5b, 0x88, 0xe3, 0x5d, 0x5d, 0x01, 0x57, 0xc0, 0x15, 0x70, 0x05, 0x5c, 0x01, 0x57, 0xc0, 0x15, 0x70, 0x05, 0x5c, 0x01, 0x57, 0xc0, 0x15, 0xf8, 0xa3, 0x00, 0x6f, 0x17, 0x53, 0x5d, 0x8c, 0x92, 0x0a, 0x20, 0x96, 0xbc, 0xcf, 0x1e, 0x05, 0x35, 0x50, 0x07, 0x1f, 0xc0, 0x69, 0x20, 0x2b, 0xbf, 0xff, 0xad, 0x8d, 0xbd, 0xab, 0x86, 0x14, 0x40, 0x20, 0x59, 0x21, 0x29, 0x5a, 0x09, 0xbe, 0x43, 0xfb, 0x56, 0x6d, 0x79, 0x06, 0xdf, 0x29, 0xf4, 0x39, 0x02, 0xd6, 0x82, 0x2a, 0x08, 0xfd, 0x89, 0x3c, 0x2f, 0x51, 0xd3, 0x13, 0xb6, 0x13, 0x37, 0x0a, 0x5f, 0x0a, 0xe4, 0x2a, 0x0b, 0xd9, 0x6e, 0x2d, 0x3a, 0x4e, 0xd7, 0x42, 0x8e, 0x09, 0xb7, 0x0f, 0x93, 0xdb, 0x3a, 0xad, 0x2e, 0xe1, 0x64, 0x3d, 0x4e, 0xb3, 0x0d, 0x90, 0xda, 0x55, 0xb9, 0x29, 0xe4, 0xcc, 0xe0, 0xf2, 0x11, 0x68, 0x57, 0x88, 0x4f, 0xb8, 0xbd, 0x93, 0xdc, 0x0e, 0x59, 0xf9, 0x59, 0xc2, 0x49, 0x10, 0xcd, 0x34, 0x7f, 0x8d, 0xd3, 0x62, 0xa6, 0xc0, 0x69, 0x17, 0xcb, 0x48, 0x7e, 0x56, 0x71, 0x0f, 0x8d, 0x2a, 0x1e, 0x85, 0x78, 0xe6, 0x09, 0x99, 0x2f, 0xae, 0x87, 0xf8, 0x84, 0xdb, 0xe5, 0x73, 0xe8, 0x05, 0x2b, 0x3f, 0x53, 0x59, 0x6e, 0x39, 0x59, 0x5a, 0x3f, 0x58, 0x10, 0xe8, 0x01, 0x6d, 0x9b, 0x11, 0x48, 0xbe, 0x92, 0x15, 0x1a, 0xbe, 0xd3, 0x21, 0x4e, 0x80, 0x2a, 0x3d, 0x1c, 0xce, 0x53, 0x93, 0xd4, 0x16, 0x67, 0x14, 0xdf, 0x09, 0x7a, 0xc0, 0x47, 0x20, 0xf6, 0x15, 0xf4, 0x02, 0x59, 0x72, 0x77, 0x2b, 0xa3, 0x00, 0x62, 0x55, 0xe1, 0xbb, 0x42, 0x99, 0x52, 0xbc, 0x8f, 0x2b, 0xe0, 0x0a, 0xb8, 0x02, 0xae, 0x80, 0x2b, 0xe0, 0x0a, 0xb8, 0x02, 0xae, 0x80, 0x2b, 0xe0, 0x0a, 0xb4, 0xa2, 0x80, 0xf9, 0xae, 0x5a, 0x26, 0x18, 0x6f, 0x14, 0x73, 0xe8, 0xb7, 0x11, 0xcc, 0x07, 0xf2, 0x33, 0xaa, 0x01, 0xde, 0xf7, 0xd4, 0xdf, 0x0e, 0xd3, 0xa7, 0xd2, 0x16, 0x2d, 0x1c, 0xa2, 0xc9, 0x9a, 0x9b, 0x2c, 0x04, 0xcc, 0xca, 0x29, 0x71, 0x97, 0xfd, 0xbd, 0x88, 0x37, 0x94, 0x6b, 0x2b, 0xdc, 0xc5, 0x5f, 0x72, 0x98, 0x0b, 0xa2, 0x73, 0x29, 0x1c, 0xa0, 0x7d, 0x8d, 0x75, 0xea, 0xf9, 0xd2, 0x96, 0x70, 0x14, 0xbd, 0x04, 0xfc, 0x02, 0x45, 0x76, 0xc3, 0x1a, 0x04, 0xa7, 0xc3, 0xe0, 0x5b, 0x91, 0x73, 0xa2, 0x6d, 0xb2, 0xc0, 0xb1, 0x5f, 0xea, 0x8a, 0x3a, 0xcb, 0x04, 0x09, 0x2d, 0x39, 0x8d, 0x6a, 0xd6, 0xcd, 0x59, 0x7a, 0x3d, 0x7a, 0x90, 0xdf, 0xe2, 0xbb, 0x92, 0xe3, 0xa7, 0x20, 0x2a, 0x87, 0x7c, 0xcc, 0xbf, 0xb4, 0x2f, 0xcb, 0x68, 0xcb, 0xad, 0x85, 0x4c, 0x2b, 0x97, 0xc5, 0x46, 0x07, 0x8d, 0xb7, 0x96, 0xe5, 0x8d, 0xd0, 0xff, 0x8c, 0x16, 0xcd, 0xd6, 0xc7, 0x0a, 0xf7, 0xce, 0x48, 0xff, 0xbd, 0xc2, 0xd7, 0x14, 0x2e, 0x75, 0x6a, 0x30, 0x56, 0xb8, 0xab, 0x4a, 0x85, 0x6f, 0xe0, 0xe4, 0x09, 0x5b, 0x68, 0xdc, 0xc2, 0x37, 0x21, 0xe4, 0xdf, 0x3b, 0x55, 0xb3, 0x8b, 0xe4, 0xde, 0x1f, 0x3d, 0xbf, 0x30, 0x57, 0x9d, 0xa1, 0xf2, 0xe3, 0x4d, 0xd5, 0xcb, 0xd3, 0x67, 0x1b, 0x03, 0xc8, 0x1c, 0xa6, 0x1a, 0xfe, 0x72, 0x3b, 0x2f, 0x04, 0xd1, 0xb9, 0xa8, 0x03, 0xc5, 0x93, 0x32, 0xb7, 0xd5, 0xa8, 0x69, 0xe4, 0x2f, 0x0c, 0x6d, 0x49, 0x96, 0xe2, 0xb7, 0x10, 0x74, 0x3b, 0x58, 0x00, 0xe4, 0x2a, 0x3b, 0xcb, 0x00, 0x6d, 0xf9, 0x75, 0x37, 0xb1, 0xdc, 0x5c, 0x01, 0x57, 0xa0, 0xe3, 0x37, 0x24, 0xed, 0x51, 0x75, 0x62, 0x2b, 0x68, 0x57, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXToggle3DIcon2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x00, 0x67, 0x8e, 0xe8, 0xa6, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x4a, 0x2a, 0x28, 0x00, 0x00, 0x03, 0xc8, 0x49, 0x44, 0x41, 0x54, 0x58, 0x09, 0xed, 0x99, 0x4b, 0x48, 0x55, 0x41, 0x18, 0xc7, 0x3b, 0xa2, 0x62, 0x19, 0x16, 0xa6, 0x2d, 0xa4, 0x37, 0x62, 0x21, 0x51, 0x46, 0x90, 0x2d, 0x24, 0x6a, 0x21, 0x2e, 0x5c, 0x45, 0x6e, 0x22, 0xdb, 0x18, 0xd5, 0x22, 0x03, 0x17, 0xad, 0x2c, 0x7b, 0x50, 0x41, 0x04, 0xb6, 0xa9, 0x4d, 0x42, 0xf4, 0x2e, 0x89, 0x12, 0x8a, 0x5a, 0x64, 0x44, 0x2f, 0x7a, 0xd0, 0x93, 0xca, 0x5c, 0x18, 0x24, 0xf4, 0xc2, 0xa4, 0x82, 0xa2, 0x0c, 0x33, 0xbb, 0xfd, 0x3e, 0x61, 0x8e, 0xc7, 0x61, 0xc6, 0x73, 0xee, 0xbd, 0x73, 0x11, 0xc3, 0x0f, 0x7e, 0xdc, 0x99, 0x6f, 0xe6, 0xfb, 0xbe, 0xff, 0x99, 0x33, 0x67, 0xee, 0xf1, 0xea, 0x8d, 0x73, 0x6c, 0xb1, 0x58, 0x6c, 0x0a, 0x29, 0x2b, 0xa1, 0x10, 0x4e, 0x7a, 0x9e, 0xf7, 0xda, 0x71, 0x89, 0xe4, 0xd2, 0x21, 0x70, 0x02, 0xec, 0x82, 0x1e, 0x50, 0xd6, 0x47, 0x23, 0x3f, 0xb9, 0xcc, 0x8e, 0xa2, 0x11, 0xe2, 0xc1, 0x1a, 0x78, 0x07, 0x26, 0xab, 0x76, 0x54, 0x2a, 0xf1, 0x34, 0xa8, 0x5a, 0x02, 0xf7, 0x4c, 0xea, 0xf0, 0xbd, 0x85, 0xd5, 0x89, 0x67, 0x77, 0x10, 0x89, 0x80, 0x02, 0x38, 0x0e, 0x7f, 0x41, 0x37, 0xb9, 0xf5, 0x3b, 0x61, 0xbc, 0x83, 0x52, 0x89, 0xa5, 0xa0, 0x78, 0x16, 0x6c, 0x85, 0x1f, 0x60, 0xb2, 0xb3, 0x38, 0xa7, 0x0f, 0x97, 0x9d, 0xf1, 0x0a, 0x38, 0x02, 0x4f, 0xa1, 0x01, 0xdc, 0x5e, 0x10, 0x09, 0xab, 0xa0, 0x13, 0x4c, 0xf6, 0x04, 0x67, 0x59, 0x88, 0xc0, 0x62, 0xe6, 0xb4, 0x1a, 0x82, 0xdd, 0x6c, 0x0f, 0x12, 0x97, 0xc0, 0x4d, 0x43, 0x01, 0x71, 0x75, 0xc1, 0x3a, 0x48, 0xb3, 0x89, 0x64, 0x2c, 0x17, 0x0e, 0xc2, 0x1f, 0xd0, 0xad, 0x1f, 0x47, 0x9d, 0x2d, 0x36, 0x92, 0x9f, 0x04, 0x53, 0xa1, 0x09, 0x24, 0x99, 0x6e, 0xbd, 0x38, 0xf6, 0x43, 0x8e, 0x2d, 0x19, 0x63, 0xe9, 0x50, 0x0b, 0x5f, 0xc0, 0x64, 0xb7, 0x70, 0x96, 0xd8, 0xe2, 0x43, 0xfd, 0x04, 0x67, 0xc2, 0x16, 0xf8, 0x06, 0x26, 0xbb, 0x84, 0x53, 0x0e, 0x73, 0xab, 0x31, 0x5e, 0x0e, 0x6d, 0xa6, 0x60, 0x7c, 0x9d, 0x50, 0x65, 0x0d, 0x8e, 0x32, 0x40, 0x82, 0x4a, 0xe8, 0x00, 0x93, 0xbd, 0xc2, 0x59, 0x3e, 0x5c, 0x1e, 0xc6, 0x0b, 0xe1, 0xa2, 0x29, 0x18, 0x9f, 0x3c, 0x80, 0xf2, 0x20, 0x66, 0x85, 0xe4, 0x98, 0xc6, 0x9c, 0x46, 0x90, 0x7a, 0x72, 0xb1, 0x83, 0xe7, 0x30, 0x9d, 0x0c, 0x38, 0x0d, 0x26, 0xfb, 0x8a, 0x73, 0x33, 0xa4, 0xdb, 0x0a, 0x30, 0x96, 0x03, 0xb2, 0x15, 0x64, 0x4b, 0xe8, 0x26, 0x47, 0xd8, 0x09, 0x28, 0xb0, 0xc5, 0x8b, 0x9f, 0xf1, 0x6c, 0xd8, 0x0b, 0xbf, 0x20, 0x68, 0x12, 0x3f, 0x77, 0x20, 0x96, 0x86, 0x1c, 0x13, 0xba, 0xc9, 0xe6, 0x3f, 0x04, 0xb9, 0xb6, 0x02, 0x8c, 0xa5, 0x81, 0x3c, 0x4c, 0xf2, 0x50, 0x99, 0xec, 0x01, 0xce, 0x52, 0x5b, 0xbc, 0xf8, 0x19, 0x97, 0x6f, 0xb6, 0xb5, 0xf0, 0x01, 0x6c, 0xd6, 0x30, 0x90, 0x83, 0x51, 0xfd, 0xa9, 0x7e, 0x88, 0x6f, 0x7e, 0x48, 0x81, 0x32, 0xe6, 0xc8, 0xb1, 0x64, 0xb2, 0xf7, 0x38, 0xab, 0xc1, 0x0b, 0xc9, 0x51, 0xca, 0x1c, 0xb9, 0x98, 0x30, 0x6b, 0x52, 0x42, 0x9f, 0x05, 0x66, 0xfe, 0xa4, 0x6d, 0x3d, 0x84, 0x19, 0x9b, 0x01, 0xcd, 0x81, 0xf9, 0xc1, 0xa6, 0xdc, 0xb6, 0xdd, 0x90, 0x1d, 0x22, 0x50, 0xbe, 0xd9, 0x64, 0x3b, 0xc8, 0x6d, 0x8d, 0x62, 0xcd, 0x4a, 0xe8, 0x8b, 0xc0, 0xec, 0x76, 0x53, 0x11, 0xc6, 0x65, 0x1f, 0x6f, 0x83, 0xe0, 0xdb, 0x51, 0x20, 0x2c, 0x76, 0x8e, 0xce, 0x4c, 0x53, 0xac, 0xf2, 0x31, 0x9e, 0x05, 0xf5, 0x20, 0x0f, 0x56, 0x3c, 0x66, 0x14, 0xda, 0xa6, 0x12, 0xab, 0x4f, 0x32, 0x4e, 0x84, 0xfb, 0x96, 0xcc, 0x72, 0x37, 0x96, 0xa9, 0xb9, 0xb6, 0x4f, 0xe6, 0xac, 0x82, 0x4e, 0x48, 0xc4, 0x9a, 0xad, 0xdf, 0x28, 0x5a, 0xc1, 0x1a, 0xfa, 0x4b, 0x35, 0x5f, 0x37, 0xfd, 0x0d, 0xb0, 0x98, 0x97, 0xe3, 0xdb, 0xda, 0x98, 0xdf, 0x45, 0xd5, 0x42, 0xb8, 0x81, 0xe3, 0x3c, 0xcc, 0xf2, 0x07, 0xe2, 0x6c, 0x58, 0x8f, 0x1c, 0x2d, 0xcf, 0x02, 0xad, 0x7f, 0x81, 0x7e, 0x0d, 0x02, 0xbf, 0x6b, 0x7e, 0xbf, 0x8b, 0xb8, 0x3c, 0x3a, 0x7b, 0x60, 0x3d, 0x44, 0x5d, 0x10, 0x3f, 0x5e, 0x6b, 0x78, 0x51, 0x85, 0xea, 0x0f, 0x47, 0xad, 0x4d, 0x24, 0x02, 0x33, 0x28, 0xb2, 0x09, 0x76, 0xc0, 0x64, 0xad, 0x60, 0xc2, 0xdd, 0xa8, 0x42, 0xf5, 0x63, 0xe6, 0xb3, 0xa9, 0xa2, 0xdc, 0x66, 0xfc, 0xa7, 0x60, 0xd8, 0xa3, 0xcd, 0x14, 0x1b, 0xe6, 0x8b, 0x2a, 0x34, 0x2c, 0x8f, 0x1c, 0xdc, 0x8b, 0x98, 0x74, 0x07, 0xf4, 0xd5, 0x0f, 0x8d, 0x8d, 0x32, 0x21, 0xd9, 0xbd, 0x13, 0xac, 0xd1, 0x98, 0x2a, 0x91, 0xe4, 0xf5, 0x9c, 0x08, 0x65, 0x35, 0x33, 0x49, 0xb6, 0x1c, 0x52, 0x66, 0x4e, 0x84, 0xa2, 0x4e, 0xfe, 0x24, 0xd6, 0xf7, 0xb1, 0x53, 0xd1, 0xae, 0x84, 0xca, 0x8a, 0xa6, 0xd4, 0x5c, 0x09, 0x4d, 0xa9, 0x48, 0x92, 0xbb, 0xd9, 0xa3, 0xa9, 0x56, 0x29, 0xf9, 0x47, 0xcb, 0x8a, 0x8e, 0x09, 0x75, 0xbd, 0x1b, 0xc6, 0xf6, 0xa8, 0xeb, 0x15, 0xed, 0x1b, 0x2d, 0x0f, 0x53, 0xb7, 0x12, 0xfa, 0xc6, 0xf5, 0x12, 0x38, 0xce, 0x77, 0x5d, 0x09, 0xbd, 0xea, 0x38, 0xb1, 0xcb, 0x74, 0xd7, 0x48, 0x76, 0x45, 0x09, 0x6d, 0xa1, 0xa3, 0x56, 0xb5, 0xc7, 0x65, 0x95, 0x24, 0x73, 0x1d, 0x26, 0x7e, 0x25, 0x2f, 0xe9, 0xfd, 0x03, 0xef, 0xa3, 0x34, 0x3e, 0xf1, 0x06, 0x54, 0x8c, 0xb3, 0x08, 0x8c, 0x7f, 0x85, 0x26, 0x59, 0x30, 0xde, 0xf0, 0xc7, 0x04, 0xd4, 0xa1, 0xeb, 0xae, 0x0a, 0xf4, 0x5f, 0x9c, 0x71, 0xf6, 0xe2, 0x7c, 0xa9, 0x06, 0xb4, 0xcf, 0x7e, 0xad, 0x9f, 0xaa, 0x6e, 0x17, 0x89, 0xeb, 0xe1, 0x18, 0x7a, 0x62, 0xc1, 0x22, 0xea, 0xd6, 0x07, 0x7d, 0xa6, 0xf6, 0x23, 0x93, 0xd3, 0xa1, 0x4f, 0x16, 0x69, 0x1f, 0x14, 0x21, 0xf0, 0xa8, 0x2e, 0x32, 0x72, 0x1d, 0xb6, 0xc5, 0x3c, 0x08, 0x9a, 0x7f, 0x27, 0x24, 0x09, 0x03, 0xb3, 0x83, 0x83, 0x71, 0xb6, 0x5b, 0x98, 0x3f, 0x27, 0xb2, 0x98, 0xb0, 0x89, 0x24, 0xdb, 0x0e, 0x1f, 0xa1, 0x15, 0x86, 0xdc, 0x09, 0xfa, 0x89, 0x08, 0x7d, 0x4e, 0xdc, 0x8a, 0xb0, 0xba, 0x4e, 0xc7, 0xe3, 0x14, 0xda, 0xcd, 0xfc, 0x8d, 0x30, 0xe4, 0x62, 0x9d, 0x0a, 0xb2, 0x25, 0xa3, 0x68, 0x3e, 0x84, 0xd9, 0x6f, 0x26, 0x1c, 0x80, 0x49, 0xb6, 0x3c, 0x29, 0xf7, 0x53, 0x5c, 0x7e, 0xe3, 0xd4, 0x7f, 0x80, 0xc5, 0xe5, 0xdb, 0x65, 0x5a, 0x72, 0xf4, 0x8d, 0xbc, 0x21, 0xe4, 0x8c, 0x2f, 0x6b, 0xb0, 0xd1, 0x4e, 0xb3, 0x62, 0xe4, 0xd5, 0x05, 0x14, 0x20, 0x28, 0x0f, 0x64, 0xe5, 0xba, 0x40, 0x7e, 0x18, 0x96, 0xff, 0x8f, 0x0e, 0x39, 0x1d, 0x02, 0xd3, 0xff, 0xdf, 0xe6, 0x3f, 0x87, 0x98, 0x3c, 0xc9, 0xe6, 0x6a, 0x0b, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXToggle3DIcon3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x48, 0x08, 0x06, 0x00, 0x00, 0x00, 0x41, 0x78, 0x2d, 0xa6, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3f, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x17, 0xf4, 0xd7, 0x00, 0x00, 0x05, 0xdb, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9b, 0x6b, 0x88, 0x55, 0x55, 0x14, 0xc7, 0xe7, 0xaa, 0x4d, 0x0f, 0xad, 0xc8, 0xc0, 0x40, 0x8b, 0xc8, 0x9a, 0x84, 0x94, 0x6a, 0xb4, 0x82, 0x0c, 0x7a, 0x9a, 0x45, 0x61, 0xfa, 0x41, 0xac, 0xc0, 0x40, 0x89, 0x04, 0x7b, 0x88, 0x69, 0x85, 0x86, 0x0a, 0x12, 0x98, 0x1f, 0xb4, 0xec, 0x21, 0x41, 0xd6, 0x87, 0x1e, 0x16, 0x15, 0x22, 0x96, 0x54, 0x16, 0x95, 0xd4, 0x60, 0x94, 0x20, 0x41, 0x25, 0x43, 0x06, 0x62, 0x08, 0x9a, 0x4c, 0x96, 0xe2, 0xa3, 0xb2, 0x71, 0xfa, 0xfd, 0xc7, 0xee, 0x78, 0xef, 0x3d, 0x8f, 0xbd, 0xf7, 0x79, 0xec, 0x73, 0x9a, 0x99, 0x05, 0x3f, 0xe6, 0x9c, 0xbd, 0xd7, 0x5e, 0x6b, 0xfd, 0xef, 0xde, 0xe7, 0x71, 0xef, 0x39, 0x53, 0x69, 0x2a, 0xd0, 0xba, 0xba, 0xba, 0x4e, 0x23, 0xfd, 0x0d, 0x30, 0x02, 0xda, 0x2b, 0x95, 0xca, 0x96, 0x02, 0xcb, 0xf1, 0x93, 0x5a, 0xa2, 0x61, 0x01, 0xec, 0x83, 0x5a, 0x7b, 0x9f, 0x9d, 0x81, 0x7e, 0xaa, 0x28, 0x20, 0x0b, 0xe2, 0xa6, 0xc2, 0x4e, 0x88, 0xb2, 0xd9, 0x05, 0x94, 0x95, 0x6f, 0x4a, 0x94, 0x5e, 0x09, 0x9b, 0xa3, 0x14, 0xd7, 0xb4, 0xbf, 0x93, 0x6f, 0x25, 0x1e, 0xa3, 0x23, 0x6a, 0x18, 0xbc, 0x0c, 0x9d, 0x35, 0x02, 0xa3, 0x36, 0x7f, 0xa2, 0xe3, 0x36, 0x8f, 0xe5, 0xe5, 0x93, 0x0a, 0x11, 0xcd, 0x30, 0x1f, 0x0e, 0x80, 0xc9, 0xe4, 0xf3, 0x18, 0x9c, 0x92, 0x4f, 0x35, 0x1e, 0xa3, 0x22, 0x62, 0x12, 0x68, 0x16, 0x4d, 0xa6, 0xd5, 0xb0, 0x06, 0x86, 0x79, 0x2c, 0x2f, 0x9f, 0x54, 0x88, 0x18, 0x0d, 0x9f, 0x80, 0x8d, 0x7d, 0x89, 0x53, 0xab, 0x6b, 0x25, 0x8c, 0x69, 0x81, 0x3b, 0x61, 0x2c, 0x14, 0x7f, 0x65, 0xa0, 0x88, 0xa1, 0xf0, 0x02, 0xfc, 0x03, 0x26, 0xdb, 0x85, 0xc3, 0xb4, 0x04, 0xa2, 0x27, 0x33, 0xee, 0x9b, 0x86, 0xe0, 0x5b, 0xd9, 0x1f, 0xee, 0x1a, 0x2b, 0x13, 0x7f, 0x12, 0x0f, 0x82, 0x47, 0x60, 0x3f, 0x98, 0xec, 0x30, 0x0e, 0x4b, 0xe0, 0x74, 0x97, 0xe4, 0xf8, 0x8f, 0x81, 0x4f, 0x21, 0xca, 0xde, 0x76, 0x89, 0x97, 0x89, 0x2f, 0x95, 0x4c, 0x84, 0x1f, 0xa3, 0x2a, 0x6a, 0x68, 0x5f, 0xcb, 0xfe, 0xf9, 0x2e, 0x89, 0xf1, 0x3f, 0x17, 0x56, 0x83, 0x69, 0x35, 0xed, 0x77, 0x89, 0x9b, 0xca, 0x97, 0x62, 0x2e, 0x85, 0x0f, 0xc0, 0xc6, 0xb4, 0x2c, 0xc7, 0xbb, 0x24, 0xc4, 0x5f, 0xab, 0x69, 0x0e, 0xd8, 0xac, 0xa6, 0xbf, 0xf1, 0x5b, 0x01, 0x15, 0x97, 0x1c, 0xce, 0xbe, 0x24, 0x38, 0x1b, 0x56, 0x82, 0x12, 0x9a, 0x6c, 0x0f, 0x0e, 0x33, 0xc1, 0xa9, 0x28, 0xfc, 0x5d, 0x56, 0x93, 0x26, 0xa0, 0xc5, 0x59, 0x88, 0xcb, 0x00, 0x12, 0x0c, 0x80, 0x59, 0xd0, 0x78, 0x1f, 0x4e, 0x53, 0xc0, 0xfe, 0xa4, 0x65, 0x39, 0x9c, 0xe9, 0x98, 0x43, 0x67, 0x70, 0xdb, 0xd5, 0xb4, 0x1d, 0xdf, 0xfc, 0x6f, 0x84, 0x48, 0x72, 0x23, 0x7c, 0x07, 0x36, 0xb6, 0x1e, 0xa7, 0x8b, 0x1d, 0x45, 0x6b, 0x35, 0x69, 0xd9, 0xda, 0xac, 0x26, 0x1d, 0x06, 0x3a, 0xb9, 0x0e, 0x72, 0xc9, 0xe1, 0xec, 0x4b, 0x82, 0x8b, 0x60, 0x1d, 0xd8, 0xd8, 0xf7, 0x38, 0xdd, 0xe2, 0x92, 0x04, 0x7f, 0xad, 0xa6, 0x07, 0xe0, 0x57, 0x30, 0x99, 0x4e, 0x78, 0x2f, 0xc2, 0x50, 0x97, 0x1c, 0xf2, 0x65, 0xcc, 0x10, 0x98, 0x0b, 0x1b, 0xa1, 0x1d, 0x34, 0x41, 0x13, 0x43, 0xe3, 0xd0, 0xa1, 0x93, 0xcd, 0x42, 0x38, 0x0a, 0x26, 0xeb, 0xc0, 0xe1, 0x21, 0x70, 0xba, 0xd1, 0xc0, 0xff, 0x7a, 0xd8, 0x06, 0x36, 0xa6, 0x4b, 0xdc, 0x98, 0xd0, 0x62, 0x63, 0x1a, 0x19, 0x73, 0x06, 0xe8, 0xb2, 0xfa, 0x3b, 0x34, 0xda, 0x31, 0x1a, 0xa6, 0xd4, 0x0d, 0xa7, 0x41, 0x9f, 0xd2, 0x96, 0x46, 0xcf, 0x90, 0x7d, 0x0d, 0x7e, 0x1e, 0xce, 0xa9, 0x0b, 0x60, 0xd8, 0xc1, 0xff, 0x42, 0x78, 0x17, 0x6c, 0x6c, 0x07, 0x4e, 0x93, 0x0d, 0x21, 0x43, 0xbb, 0x19, 0x77, 0x0f, 0xfc, 0x62, 0x48, 0xf2, 0x43, 0xdd, 0x60, 0x9c, 0xdf, 0x34, 0x0c, 0x50, 0xf7, 0x26, 0xb8, 0xac, 0x6e, 0xa0, 0x61, 0x07, 0xff, 0xc1, 0xf0, 0x14, 0xd8, 0xac, 0x26, 0x7d, 0xc1, 0x79, 0x1c, 0x9a, 0x0d, 0x61, 0x03, 0xdd, 0x8c, 0x19, 0x07, 0x6d, 0x60, 0x6b, 0x63, 0xbb, 0x83, 0xe0, 0xad, 0x65, 0x72, 0x24, 0x66, 0x94, 0x66, 0x62, 0x52, 0x20, 0x63, 0x4c, 0x03, 0xfe, 0x15, 0x98, 0x0e, 0xbb, 0xc1, 0x64, 0xfa, 0x82, 0xf3, 0x0a, 0x9c, 0x17, 0x13, 0x32, 0xb4, 0x4b, 0x63, 0xe0, 0x55, 0x38, 0x0e, 0x2e, 0x36, 0xb5, 0x3b, 0x20, 0x23, 0x46, 0xc5, 0x8c, 0x7a, 0x9a, 0x3e, 0xa7, 0x99, 0xc0, 0xff, 0x1a, 0xf8, 0x3a, 0x26, 0x66, 0x6d, 0xd7, 0x57, 0xec, 0x9c, 0x98, 0x85, 0x50, 0x79, 0xe1, 0x8d, 0x8c, 0x69, 0x86, 0x27, 0xe0, 0x20, 0x24, 0xb1, 0x39, 0x55, 0xf1, 0x57, 0x45, 0x8c, 0x7e, 0x26, 0x3c, 0x75, 0x78, 0x2b, 0x31, 0x86, 0xc3, 0x6b, 0x60, 0x33, 0x0b, 0xbb, 0xf0, 0xbb, 0x3b, 0x3c, 0x52, 0x7c, 0x2b, 0xe3, 0xee, 0x02, 0xad, 0xc6, 0x34, 0xb6, 0xa8, 0x7a, 0xcd, 0x3c, 0x35, 0x22, 0x5d, 0x5b, 0x44, 0x7b, 0x5d, 0x33, 0x15, 0xe8, 0x57, 0xd8, 0x79, 0xf0, 0x24, 0x0c, 0xae, 0xeb, 0x0c, 0xee, 0x1c, 0xa1, 0x69, 0x39, 0xac, 0xe0, 0xd7, 0xda, 0xa3, 0xc1, 0xee, 0xe8, 0x16, 0xf2, 0x8c, 0xa6, 0xf7, 0x59, 0xb8, 0x35, 0xda, 0xcb, 0xbe, 0xa7, 0x2a, 0x3e, 0x6a, 0x44, 0x47, 0x54, 0x47, 0xb5, 0x9d, 0x82, 0x6e, 0x62, 0x7b, 0x0d, 0xd8, 0xdc, 0xe0, 0xac, 0xc5, 0x6f, 0x01, 0xa2, 0x77, 0x57, 0xc7, 0xdb, 0xfc, 0x25, 0x87, 0xae, 0xf1, 0x4b, 0x41, 0x3f, 0x6e, 0x3a, 0x5d, 0x5a, 0xe3, 0xe2, 0x0f, 0x88, 0xeb, 0x34, 0xf5, 0x51, 0x94, 0x0a, 0xfa, 0x1c, 0x4c, 0xc2, 0xb7, 0xe2, 0x33, 0x1e, 0xd1, 0xd3, 0x5d, 0x84, 0x13, 0x5f, 0xf7, 0x1d, 0x0f, 0x33, 0x76, 0x07, 0xe8, 0x6f, 0x66, 0xc2, 0x89, 0xd5, 0x64, 0x9a, 0x79, 0xf9, 0x84, 0x1a, 0x45, 0xdd, 0x4b, 0xc7, 0x92, 0xd0, 0xce, 0x93, 0x8d, 0x7b, 0xd8, 0x5c, 0x08, 0xaf, 0x23, 0xba, 0xeb, 0x64, 0xb3, 0x79, 0x8b, 0xf8, 0x13, 0xf0, 0x5a, 0x05, 0x5a, 0xea, 0x79, 0x58, 0x25, 0xb1, 0x78, 0xaa, 0x99, 0x19, 0x53, 0xd1, 0x5f, 0xf4, 0xe9, 0x64, 0xb9, 0x0c, 0xd1, 0x87, 0x62, 0xfc, 0x02, 0x5d, 0x88, 0xd6, 0x2a, 0x5a, 0x09, 0x89, 0x6e, 0x72, 0x02, 0x01, 0x63, 0x1a, 0xd2, 0x88, 0x8f, 0xba, 0xed, 0xdc, 0x44, 0xbe, 0xd9, 0x88, 0xde, 0x19, 0x93, 0x37, 0xd0, 0x85, 0x68, 0x7d, 0x13, 0x5c, 0x04, 0x73, 0xc1, 0xe9, 0xd2, 0x1a, 0x08, 0x66, 0xd9, 0x90, 0x46, 0x7c, 0xd8, 0xd7, 0xd6, 0x6f, 0xc9, 0x7b, 0x07, 0xc2, 0x8f, 0x5b, 0xe6, 0xd7, 0x97, 0x0f, 0x9d, 0x77, 0x66, 0xc0, 0x32, 0x70, 0xbe, 0xc9, 0x61, 0x4c, 0x62, 0x4b, 0x23, 0x3e, 0xec, 0xb7, 0xb8, 0x36, 0x47, 0xe1, 0xd7, 0x51, 0xf9, 0x73, 0x30, 0x2e, 0xb1, 0x82, 0xe4, 0x03, 0x53, 0x1d, 0xf3, 0x61, 0x69, 0x75, 0xac, 0x1b, 0x8d, 0xd9, 0x1e, 0x82, 0x93, 0x4e, 0x66, 0xf7, 0x1b, 0x9d, 0x73, 0x74, 0x48, 0x33, 0xf3, 0x89, 0xca, 0x42, 0xf8, 0x28, 0x06, 0x7e, 0x08, 0x23, 0x13, 0x05, 0xc8, 0x70, 0x50, 0xaa, 0xeb, 0xbc, 0x6b, 0x1d, 0x08, 0xd7, 0xcd, 0xca, 0x46, 0x28, 0x5c, 0xb8, 0x6a, 0xf7, 0x2a, 0x9e, 0x7c, 0xf3, 0xe1, 0x12, 0x25, 0x2e, 0x81, 0x55, 0x7c, 0x8b, 0xbf, 0xaf, 0x04, 0xa2, 0x7b, 0x4a, 0xf0, 0x26, 0x9e, 0x25, 0x7f, 0x16, 0x59, 0x2f, 0xe8, 0xc9, 0x5c, 0x82, 0x0d, 0x6f, 0xe2, 0xd1, 0x5a, 0xba, 0x27, 0xb1, 0x3e, 0xc5, 0x87, 0xdd, 0x14, 0x15, 0x39, 0xff, 0xde, 0x8f, 0xf9, 0x22, 0xc5, 0x06, 0x72, 0xfb, 0x9c, 0xf9, 0x40, 0xf2, 0xa2, 0x1b, 0xfa, 0xc5, 0x17, 0x3d, 0x03, 0x05, 0xe5, 0xef, 0x3f, 0xe6, 0x0b, 0xfa, 0xe0, 0x8b, 0x4f, 0xdb, 0x7f, 0xcc, 0x17, 0x3f, 0x07, 0x85, 0x54, 0xd0, 0x7f, 0xcc, 0x17, 0xf2, 0xb1, 0x97, 0x21, 0x69, 0x5f, 0x3e, 0xe6, 0x3b, 0xfb, 0xb2, 0xf8, 0xdf, 0xfa, 0xb2, 0xf8, 0xbd, 0x55, 0xf1, 0x07, 0xca, 0x70, 0x0c, 0x7a, 0xac, 0xe1, 0x18, 0xb9, 0xbe, 0xa8, 0x8a, 0x77, 0x7a, 0xc0, 0xe0, 0xb1, 0xc8, 0xbc, 0x52, 0xad, 0xe3, 0x27, 0xf6, 0x13, 0xcb, 0x9e, 0x8d, 0xc3, 0x64, 0xd9, 0x9c, 0x57, 0xa6, 0x92, 0xc5, 0x6d, 0xa7, 0x1e, 0x3d, 0xed, 0xad, 0xfb, 0x01, 0x73, 0x31, 0xfb, 0x56, 0xbf, 0xbb, 0x6b, 0xe0, 0xff, 0xd4, 0xf4, 0x28, 0xed, 0x66, 0x26, 0xfb, 0x0f, 0xd5, 0x5f, 0x5d, 0xf6, 0x4d, 0x34, 0xb4, 0xb1, 0x3f, 0x01, 0xd6, 0x83, 0xde, 0x56, 0xd2, 0x33, 0xf7, 0xed, 0xd0, 0x1b, 0xac, 0x03, 0x11, 0xb3, 0xd0, 0x78, 0x3b, 0xe8, 0xc9, 0x71, 0xb7, 0xd5, 0x3d, 0xb4, 0xf8, 0xef, 0x03, 0xd0, 0x87, 0xd0, 0x5b, 0x4c, 0x27, 0xb6, 0xd5, 0xb0, 0x14, 0x6d, 0xdd, 0xb3, 0x5d, 0x2b, 0xac, 0x4e, 0x7c, 0x6d, 0x47, 0x2f, 0xd8, 0xfe, 0x08, 0x0d, 0xf3, 0x10, 0xad, 0x63, 0x3c, 0xd4, 0x7a, 0x96, 0x7d, 0x68, 0x6f, 0x7c, 0xe3, 0xc1, 0xf8, 0xee, 0xc2, 0x7a, 0x25, 0x56, 0x4f, 0x8a, 0x45, 0xa4, 0x70, 0x55, 0x97, 0x46, 0x7c, 0xd9, 0x2e, 0x8f, 0x5a, 0xd6, 0x8f, 0xc2, 0xe5, 0x88, 0xd6, 0xac, 0x1b, 0x2d, 0x8d, 0xf8, 0x8f, 0x8d, 0xd1, 0xfd, 0x38, 0x74, 0x92, 0xe6, 0x25, 0x68, 0x41, 0xf4, 0x2a, 0xd0, 0x71, 0x6e, 0x65, 0x69, 0xc4, 0xeb, 0x11, 0xf3, 0x3e, 0xab, 0x2c, 0xf9, 0x39, 0x7d, 0x46, 0xe8, 0x56, 0x04, 0x3f, 0x08, 0x3a, 0xa3, 0xfb, 0x33, 0x1e, 0x41, 0x8d, 0x84, 0x0d, 0xa0, 0x57, 0x4c, 0xdf, 0x83, 0x6b, 0xa3, 0xb2, 0xd3, 0xd7, 0x0a, 0x59, 0xd9, 0xcf, 0x04, 0x9a, 0x12, 0x95, 0xab, 0x74, 0xed, 0x14, 0x9b, 0x85, 0x78, 0xbd, 0x6a, 0xaa, 0x57, 0x4e, 0xbd, 0xbc, 0xb3, 0x93, 0xd9, 0x87, 0x48, 0xc1, 0x57, 0x40, 0x52, 0xd3, 0x8b, 0xc9, 0x7a, 0xb9, 0xd8, 0xeb, 0x3b, 0x3b, 0x59, 0x8a, 0xd7, 0x7b, 0xb9, 0x49, 0x2c, 0xd1, 0x8b, 0xc9, 0x99, 0x15, 0x9e, 0x45, 0x20, 0x54, 0x0f, 0x04, 0xcd, 0xa0, 0xad, 0xe9, 0xc5, 0xe4, 0x69, 0x59, 0xe4, 0x2e, 0x45, 0x0c, 0xc4, 0xd8, 0xbc, 0x86, 0x7e, 0x08, 0xbf, 0xc5, 0x10, 0xf6, 0xb6, 0x57, 0x29, 0x74, 0x24, 0x2a, 0x02, 0x41, 0x33, 0x20, 0xca, 0xf4, 0x9a, 0xfa, 0x1b, 0x30, 0x22, 0x51, 0xf0, 0xb2, 0x0f, 0x42, 0x98, 0xfe, 0xfb, 0x42, 0xff, 0x22, 0x5a, 0x6b, 0xfa, 0x9f, 0x9d, 0xb7, 0xa0, 0xd5, 0x77, 0xfd, 0x15, 0xdf, 0x09, 0x95, 0x0f, 0xa1, 0x7a, 0x99, 0xf8, 0x6a, 0xd8, 0x0b, 0xdb, 0xb8, 0x41, 0x29, 0xfa, 0x66, 0x89, 0x32, 0xfa, 0x98, 0xfd, 0x0b, 0x55, 0xca, 0x1e, 0xf8, 0x6a, 0x59, 0xc9, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderLeftHandle2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x50, 0x08, 0x06, 0x00, 0x00, 0x00, 0x56, 0x1d, 0x56, 0xa7, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x7d, 0xa9, 0x20, 0x00, 0x00, 0x01, 0x59, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x4c, 0xc2, 0x27, 0x59, 0x00, 0x00, 0x05, 0xdc, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9b, 0xbf, 0x4b, 0x64, 0x57, 0x14, 0xc7, 0x57, 0x67, 0xc6, 0xf9, 0xa1, 0x93, 0x15, 0xc6, 0x8c, 0x1b, 0x62, 0x58, 0x90, 0x14, 0x59, 0x48, 0x0a, 0x2d, 0x22, 0x08, 0x01, 0x21, 0x76, 0x0a, 0x81, 0x84, 0x80, 0xff, 0x40, 0xfe, 0x01, 0x6d, 0xad, 0x6c, 0x83, 0x18, 0xb0, 0x4c, 0xb0, 0xd9, 0x40, 0x8a, 0x14, 0x82, 0xa9, 0x84, 0x04, 0x41, 0x48, 0x2c, 0xc4, 0xc2, 0x22, 0x5b, 0x98, 0x42, 0x2c, 0x8c, 0x18, 0x2d, 0x74, 0x57, 0x9c, 0xd1, 0x71, 0xc6, 0xdc, 0xef, 0xb8, 0x5f, 0x39, 0x39, 0xb9, 0xef, 0xd7, 0x38, 0xf3, 0xbc, 0xee, 0xce, 0x85, 0xeb, 0x39, 0xf7, 0xdc, 0x73, 0xcf, 0x39, 0x9f, 0x77, 0x9e, 0x6f, 0x5c, 0xde, 0xec, 0xa3, 0x47, 0x6f, 0xd8, 0xe8, 0x68, 0x01, 0x4f, 0x2b, 0x62, 0x86, 0x2e, 0xb3, 0x19, 0xc9, 0x1b, 0x8d, 0xd1, 0xe8, 0x39, 0xc2, 0x5d, 0x53, 0x91, 0xb2, 0xd1, 0xa0, 0x61, 0xce, 0x85, 0xf1, 0x91, 0xb5, 0x34, 0x45, 0x8f, 0x9a, 0x34, 0xc8, 0xdf, 0xb6, 0x6f, 0xb3, 0xe9, 0xe2, 0xc3, 0xf8, 0xf0, 0x8c, 0xb5, 0x33, 0xaf, 0x37, 0xaf, 0xa3, 0x04, 0xf2, 0xf2, 0xd5, 0x76, 0xb9, 0x4e, 0x9a, 0x44, 0x98, 0xb0, 0x61, 0x26, 0xcc, 0x6c, 0xf6, 0x90, 0x80, 0x52, 0xf7, 0xcd, 0xc3, 0x82, 0xb4, 0xec, 0x34, 0xa7, 0x38, 0x51, 0x2c, 0x66, 0xca, 0xcc, 0xee, 0xd1, 0xd1, 0xd1, 0xe2, 0xf6, 0xf6, 0xf6, 0x57, 0x27, 0x27, 0x27, 0xdf, 0x97, 0x4a, 0xa5, 0xf5, 0xcb, 0xcb, 0xcb, 0xbf, 0x6a, 0xb5, 0xda, 0xab, 0xeb, 0x16, 0x0f, 0x93, 0x3b, 0x70, 0x68, 0x08, 0xac, 0x09, 0x01, 0x29, 0x41, 0xf2, 0x8b, 0x8b, 0x8b, 0x1f, 0x19, 0x88, 0x1f, 0xaa, 0xd5, 0xea, 0xcb, 0x16, 0xd7, 0x6e, 0x0d, 0x1f, 0x44, 0xe3, 0x07, 0x43, 0x10, 0xdc, 0x52, 0x99, 0xb1, 0xb1, 0xb1, 0x27, 0x87, 0x87, 0x87, 0xdf, 0x9a, 0x2e, 0x9c, 0x59, 0x33, 0xc5, 0x64, 0xf4, 0x03, 0x0a, 0x82, 0xe1, 0xef, 0x47, 0xf7, 0xc2, 0xc2, 0xc2, 0xb3, 0xf3, 0xf3, 0xf3, 0xcd, 0x98, 0x6a, 0xf6, 0x4d, 0xe3, 0x05, 0x14, 0x06, 0x06, 0xbf, 0x2b, 0xf9, 0xe5, 0xe5, 0xe5, 0xb1, 0x4a, 0xa5, 0xb2, 0xef, 0x9b, 0x25, 0xc6, 0x4d, 0x1b, 0x50, 0x58, 0x98, 0xdc, 0xdc, 0xdc, 0xdc, 0x27, 0x17, 0x17, 0x17, 0x7f, 0xc7, 0x58, 0x6f, 0x60, 0xaa, 0x30, 0x40, 0x7c, 0x00, 0xe0, 0x77, 0x06, 0xb7, 0x19, 0x3a, 0x93, 0x1d, 0x1a, 0x1a, 0x7a, 0x7a, 0x76, 0x76, 0xb6, 0x15, 0x98, 0x21, 0x66, 0x07, 0x0d, 0xa4, 0xbb, 0x63, 0x83, 0x49, 0x9b, 0x43, 0x7d, 0x7b, 0x7b, 0x7b, 0xdf, 0xc5, 0x5c, 0x6b, 0xa8, 0x74, 0x28, 0x38, 0x68, 0x68, 0xc8, 0xae, 0x99, 0x99, 0x99, 0xa7, 0x03, 0x03, 0x03, 0xdf, 0x04, 0x1d, 0x74, 0x61, 0x5f, 0x16, 0xaf, 0xbb, 0xd3, 0x65, 0x0a, 0xcc, 0x9a, 0xf9, 0x64, 0x7f, 0x7f, 0xff, 0xc7, 0x50, 0x97, 0xeb, 0x1e, 0x9c, 0x64, 0x87, 0x00, 0xa3, 0x87, 0x04, 0x84, 0x9e, 0x1c, 0x1c, 0x1c, 0x7c, 0xa7, 0xbf, 0xbf, 0xff, 0x0b, 0xed, 0xe8, 0xca, 0x5a, 0x02, 0xc9, 0x9a, 0x24, 0x1c, 0xa1, 0xe0, 0x9b, 0x99, 0x9f, 0x9f, 0xff, 0x2c, 0x91, 0x48, 0xf4, 0x48, 0x67, 0x97, 0x74, 0x2f, 0x20, 0xd4, 0x48, 0x10, 0xea, 0x78, 0xca, 0xe1, 0xe9, 0xf6, 0x39, 0x0c, 0xae, 0x0e, 0x1b, 0x90, 0xad, 0x3b, 0xb0, 0x01, 0x28, 0x9d, 0xcf, 0xe7, 0x3f, 0x74, 0x15, 0x06, 0x75, 0x11, 0x48, 0x42, 0xc0, 0xae, 0xbb, 0x03, 0xbf, 0xfa, 0xe7, 0x50, 0x26, 0x93, 0x29, 0xc2, 0xc1, 0xd5, 0x41, 0x20, 0xaf, 0xfa, 0x08, 0xc6, 0x0e, 0xa5, 0xd2, 0xe9, 0xf4, 0xbb, 0x5e, 0xce, 0x2e, 0xd8, 0x35, 0x90, 0xec, 0x94, 0xd6, 0xeb, 0x5d, 0x32, 0x0f, 0x84, 0x9c, 0x0b, 0x85, 0x7b, 0xd5, 0xa0, 0x81, 0xe8, 0x47, 0x18, 0x48, 0x4c, 0xf8, 0x71, 0xd2, 0xc7, 0x49, 0x69, 0x03, 0x22, 0x0c, 0x0b, 0xe6, 0x1a, 0xbe, 0xd4, 0xb9, 0xe7, 0x9c, 0xb4, 0x01, 0xb1, 0x48, 0x14, 0x4f, 0x00, 0xea, 0x5c, 0xd3, 0xc7, 0x39, 0xe9, 0x07, 0xc4, 0x62, 0x9d, 0x87, 0x60, 0xa1, 0x90, 0x5e, 0x40, 0x1a, 0xe2, 0x8d, 0xe8, 0x10, 0x21, 0xe4, 0x05, 0x70, 0x5e, 0xf7, 0xea, 0x90, 0x2c, 0x5c, 0x77, 0x4b, 0xee, 0x39, 0xa7, 0x87, 0x01, 0x42, 0xd1, 0x84, 0xa2, 0x74, 0x0e, 0x84, 0x05, 0x49, 0xa0, 0xa0, 0x62, 0x83, 0xf6, 0x19, 0xf3, 0x5e, 0xa5, 0x04, 0x62, 0x21, 0x0f, 0xa2, 0x70, 0x16, 0xab, 0xa5, 0x0d, 0x48, 0xfb, 0x3c, 0xa8, 0x75, 0x1b, 0xc8, 0xf5, 0x76, 0xb5, 0x3b, 0xd4, 0xee, 0x50, 0xcc, 0x57, 0xa0, 0x7d, 0xcb, 0xc5, 0x7c, 0xc1, 0x23, 0xa7, 0x6b, 0x77, 0x28, 0xf2, 0x25, 0x8b, 0xf9, 0x40, 0xbb, 0x43, 0x31, 0x5f, 0xf0, 0xc8, 0xe9, 0xda, 0x1d, 0x32, 0xaf, 0xeb, 0xcf, 0x23, 0x5f, 0xb6, 0x18, 0x0f, 0x44, 0xed, 0xd0, 0x75, 0xb9, 0x5c, 0x3e, 0x8e, 0xb1, 0xbe, 0xc8, 0xa9, 0x6c, 0x40, 0x5e, 0x5f, 0x2f, 0x81, 0xbd, 0x66, 0x5e, 0xdf, 0x3f, 0x18, 0x20, 0x2f, 0x10, 0x5c, 0xa5, 0x3a, 0x0c, 0x80, 0x8e, 0x8f, 0x8f, 0xf7, 0x60, 0x70, 0x75, 0xd8, 0x3a, 0x64, 0xab, 0x15, 0x40, 0x98, 0x57, 0x6b, 0x6b, 0x6b, 0x7f, 0xd8, 0x1c, 0x5c, 0xb1, 0xf1, 0x9f, 0xdb, 0x52, 0x42, 0x07, 0xa8, 0x9c, 0x78, 0x9d, 0x8f, 0xf7, 0xab, 0xbd, 0x7d, 0x7d, 0x7d, 0xef, 0x1d, 0x1c, 0x1c, 0xfc, 0x92, 0x4c, 0x26, 0xbb, 0xcd, 0xda, 0xb9, 0xe1, 0xd7, 0x21, 0x76, 0x05, 0x45, 0xf3, 0x96, 0xbb, 0x32, 0xb7, 0x5c, 0x79, 0x67, 0x67, 0xe7, 0x57, 0xe7, 0x48, 0x5e, 0x17, 0xe4, 0x05, 0x04, 0x00, 0x39, 0xb0, 0xae, 0x9a, 0x59, 0x31, 0xb3, 0x34, 0x3d, 0x3d, 0xfd, 0xdc, 0x3c, 0xbe, 0x4b, 0xd2, 0xc1, 0x15, 0xdd, 0x0b, 0x48, 0xd6, 0x47, 0xb8, 0x9a, 0x31, 0x5e, 0x99, 0x59, 0x59, 0x5d, 0x5d, 0xfd, 0x67, 0x6b, 0x6b, 0xeb, 0x67, 0xe9, 0xe4, 0x8a, 0xee, 0x07, 0x04, 0x10, 0xc2, 0x40, 0x02, 0x08, 0x1d, 0x2a, 0x9b, 0x59, 0x9a, 0x9c, 0x9c, 0xfc, 0xc9, 0xdc, 0x7e, 0x2f, 0x8c, 0xee, 0xd4, 0xb0, 0x01, 0x11, 0x82, 0x85, 0x72, 0x0d, 0x89, 0xdb, 0xee, 0xd2, 0xcc, 0xd2, 0xd1, 0xd1, 0xd1, 0xcb, 0xa9, 0xa9, 0xa9, 0x39, 0xf3, 0xb9, 0x74, 0x44, 0x47, 0x17, 0x24, 0x5e, 0x04, 0x63, 0xc8, 0xa7, 0xdc, 0x8d, 0xe5, 0xbf, 0x36, 0xec, 0x4b, 0x1f, 0xe8, 0x89, 0xdd, 0xdd, 0xdd, 0xca, 0xe9, 0xe9, 0xe9, 0x9f, 0xe6, 0xcb, 0x7f, 0x9f, 0xa6, 0x52, 0x29, 0x27, 0x9e, 0x7a, 0x1a, 0x08, 0x30, 0xb6, 0xe2, 0x69, 0xd3, 0x50, 0x1d, 0x9b, 0x9b, 0x9b, 0xaf, 0x36, 0x36, 0x36, 0x7e, 0x9f, 0x98, 0x98, 0x78, 0x96, 0xcb, 0xe5, 0xee, 0xfd, 0x85, 0xb2, 0x2c, 0x10, 0x30, 0x18, 0xb2, 0x78, 0xe8, 0xb8, 0x2d, 0x39, 0x71, 0x01, 0xea, 0xaf, 0xf7, 0x8d, 0xc4, 0xe7, 0x52, 0xde, 0x4c, 0x74, 0x26, 0x57, 0x28, 0x14, 0x1e, 0xaf, 0xac, 0xac, 0x7c, 0x3d, 0x32, 0x32, 0xf2, 0xa5, 0x79, 0xb1, 0x8c, 0xbd, 0x7b, 0x19, 0xec, 0x10, 0x92, 0x7b, 0xc1, 0x71, 0x8f, 0xa0, 0x2c, 0x14, 0x0f, 0x09, 0x8e, 0x0e, 0xf3, 0xcd, 0xdf, 0xea, 0xd2, 0xd2, 0xd2, 0x8b, 0xf5, 0xf5, 0xf5, 0xdf, 0x86, 0x87, 0x87, 0xbb, 0x0c, 0xe0, 0xfb, 0x9d, 0x9d, 0x9d, 0xf8, 0xc2, 0x53, 0xac, 0x83, 0x10, 0x48, 0xaa, 0x75, 0x02, 0x40, 0xb2, 0x43, 0x90, 0xb8, 0x08, 0x94, 0xf8, 0x32, 0x60, 0xc6, 0x4c, 0xbc, 0xea, 0xc7, 0x84, 0x0e, 0x5b, 0xba, 0xb7, 0xb7, 0x37, 0x33, 0x3b, 0x3b, 0xfb, 0xf1, 0xf8, 0xf8, 0xf8, 0x48, 0xb1, 0x58, 0xfc, 0xa0, 0xa7, 0xa7, 0xa7, 0x90, 0xcd, 0x66, 0x0b, 0xe6, 0x2f, 0x8c, 0x96, 0x76, 0x4f, 0x43, 0x98, 0x5a, 0xea, 0x83, 0x76, 0xc2, 0x50, 0xda, 0xc0, 0x00, 0x80, 0x4e, 0x10, 0x0c, 0x5f, 0x10, 0xc4, 0x9a, 0x5f, 0xb2, 0x85, 0xc4, 0x79, 0xc6, 0x30, 0xea, 0x9d, 0x86, 0x7c, 0xea, 0x42, 0xc7, 0x9d, 0xc2, 0x79, 0x85, 0x64, 0xb6, 0x01, 0x47, 0x14, 0x80, 0x61, 0x0b, 0x80, 0x3d, 0xde, 0x72, 0xf8, 0x6c, 0x62, 0x60, 0x7c, 0xf0, 0x02, 0x88, 0x9d, 0x02, 0x2c, 0xbb, 0x09, 0x29, 0x07, 0xe3, 0x4b, 0x5b, 0x90, 0xce, 0x5a, 0xe0, 0x07, 0x9d, 0x79, 0x29, 0x2b, 0x12, 0x08, 0x46, 0x9d, 0x04, 0x36, 0x0e, 0x1e, 0x42, 0x61, 0x84, 0xe1, 0x1e, 0x40, 0x60, 0xe3, 0x9f, 0x47, 0x17, 0x46, 0x07, 0x0c, 0x26, 0x3b, 0x85, 0x73, 0x88, 0xcf, 0x1c, 0x94, 0xc6, 0x14, 0x69, 0xa0, 0x0e, 0xdb, 0x44, 0xee, 0xaa, 0x04, 0xd2, 0x51, 0x71, 0x48, 0x26, 0xc5, 0x1a, 0x03, 0x52, 0x03, 0xd5, 0x37, 0xcc, 0x0f, 0x74, 0x8b, 0x50, 0x88, 0xcd, 0xa7, 0x22, 0xe2, 0x00, 0x88, 0x50, 0x46, 0xbd, 0x1d, 0x32, 0xc7, 0xad, 0xd1, 0x43, 0x91, 0x35, 0x40, 0xe7, 0x44, 0x3d, 0xf5, 0xe9, 0x07, 0xc4, 0x98, 0x0c, 0xc2, 0x35, 0x0e, 0xea, 0x2e, 0x31, 0x30, 0xec, 0xd4, 0xe1, 0x47, 0x10, 0x16, 0x8d, 0x7d, 0x0e, 0xda, 0xb8, 0x0e, 0x2b, 0x59, 0x8f, 0xcc, 0x03, 0xbd, 0x0e, 0x64, 0x0b, 0xaa, 0x6d, 0x5c, 0x43, 0xca, 0xc9, 0xab, 0x0d, 0x1b, 0x74, 0xae, 0x29, 0xa5, 0x2f, 0x74, 0x0c, 0x2d, 0x6f, 0xac, 0xd1, 0x7e, 0x6a, 0x20, 0x09, 0x56, 0x63, 0x02, 0x19, 0xd2, 0xcf, 0x86, 0x3d, 0x3d, 0x25, 0x80, 0xd4, 0xb5, 0x1f, 0x72, 0xc8, 0xd8, 0x52, 0x97, 0xf9, 0x83, 0x74, 0x09, 0x04, 0x5f, 0x74, 0xe6, 0x16, 0xca, 0x2b, 0xa8, 0xcd, 0x4e, 0x1b, 0x64, 0x23, 0x13, 0xc9, 0x19, 0x03, 0x3a, 0x86, 0x5e, 0xdf, 0x58, 0xfd, 0x7f, 0x6a, 0x20, 0xc2, 0x40, 0x5a, 0x3b, 0xc4, 0x70, 0xb6, 0x64, 0xb4, 0x41, 0x6a, 0x9d, 0x36, 0x2d, 0x11, 0x4f, 0xfa, 0xfa, 0xc5, 0xe7, 0x5e, 0x90, 0x94, 0x50, 0xd4, 0xeb, 0x9d, 0x62, 0x22, 0xaf, 0x00, 0xb6, 0x7d, 0xda, 0xa4, 0xd4, 0xba, 0x5e, 0x23, 0x3e, 0x6d, 0xcc, 0xa5, 0xd7, 0xb4, 0x87, 0x91, 0x84, 0xf8, 0x9f, 0x0c, 0x13, 0xd4, 0xe6, 0x23, 0x6d, 0xd4, 0x21, 0xa5, 0x8e, 0xc2, 0xf4, 0x5a, 0xda, 0xa0, 0xdf, 0x75, 0x34, 0x04, 0x84, 0xa4, 0x2c, 0x4c, 0x17, 0x20, 0xed, 0xd4, 0xb5, 0xb4, 0x9d, 0xa7, 0x8f, 0x8e, 0x17, 0x65, 0x4d, 0x18, 0x9e, 0xa9, 0xaf, 0xa3, 0x04, 0xf6, 0xf2, 0xd5, 0x76, 0xb9, 0x96, 0xba, 0x0d, 0x8c, 0xc5, 0xdc, 0x45, 0x4a, 0xb0, 0x48, 0xff, 0x4b, 0x32, 0x4c, 0x41, 0x1a, 0x20, 0xcc, 0x99, 0xb0, 0x3e, 0x84, 0x96, 0x00, 0xb4, 0x51, 0x46, 0x06, 0xe2, 0x41, 0x5b, 0xe1, 0xdc, 0xa3, 0x0c, 0xe3, 0x43, 0xdf, 0xa6, 0xc9, 0x66, 0x24, 0x6d, 0x34, 0x46, 0xa3, 0xe7, 0x08, 0x6f, 0xed, 0xd4, 0x5d, 0x83, 0x32, 0xb8, 0x94, 0xad, 0x88, 0x29, 0xe3, 0xbf, 0x5d, 0xfa, 0xbf, 0x74, 0x7e, 0xdc, 0x6e, 0x66, 0x84, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderLeftHandle3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x78, 0x08, 0x06, 0x00, 0x00, 0x00, 0x5c, 0x89, 0xc4, 0xad, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x54, 0xdc, 0xcf, 0x00, 0x00, 0x01, 0x59, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x4c, 0xc2, 0x27, 0x59, 0x00, 0x00, 0x0a, 0x58, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9d, 0xcb, 0x8b, 0x14, 0xdb, 0x1d, 0xc7, 0x9d, 0x9e, 0xd6, 0x79, 0x39, 0xd7, 0xc7, 0x98, 0x51, 0xc9, 0x20, 0x64, 0x20, 0x3e, 0x19, 0x2e, 0x04, 0x24, 0x8a, 0x49, 0x5c, 0x24, 0x17, 0x5c, 0x19, 0xd0, 0x24, 0x0b, 0x73, 0x71, 0x97, 0x2c, 0xee, 0xee, 0x6e, 0x44, 0x70, 0x95, 0x85, 0x22, 0x98, 0x6c, 0x62, 0xfe, 0x00, 0x11, 0x17, 0x6e, 0xcc, 0xc2, 0x08, 0xd9, 0xb8, 0x30, 0x0a, 0x77, 0x61, 0xc4, 0xbb, 0x31, 0x66, 0x21, 0xa8, 0x38, 0x68, 0xcc, 0x42, 0x88, 0x13, 0x93, 0x8c, 0xce, 0x2b, 0xe7, 0x53, 0xe3, 0x77, 0xee, 0xcf, 0xe3, 0x39, 0xd5, 0x55, 0xd5, 0xd3, 0xa3, 0x55, 0x5d, 0x07, 0xce, 0xfc, 0x9e, 0xe7, 0xf5, 0xe9, 0x5f, 0x55, 0xf7, 0xb4, 0x4c, 0xb9, 0x6a, 0x55, 0xdd, 0x6a, 0x02, 0x2b, 0x49, 0xa0, 0x67, 0x25, 0x17, 0x33, 0x6b, 0x7d, 0xa8, 0x75, 0xcd, 0x16, 0xda, 0x53, 0x57, 0xea, 0x00, 0x2b, 0xb5, 0x4e, 0x7b, 0x34, 0x72, 0x8c, 0xee, 0xe4, 0x81, 0xda, 0x9d, 0xbb, 0xdd, 0xf1, 0x39, 0x30, 0xe4, 0x4a, 0x5d, 0x20, 0xbb, 0x13, 0x9b, 0x2b, 0x32, 0x67, 0x91, 0x31, 0x3a, 0x6d, 0x3b, 0x63, 0x35, 0x07, 0x32, 0x01, 0x62, 0x1d, 0x69, 0xfa, 0x72, 0x2d, 0xca, 0x1a, 0x79, 0xe6, 0x6a, 0x95, 0xdb, 0x2a, 0x9e, 0x76, 0xa6, 0x4e, 0xc7, 0x96, 0xad, 0xe2, 0xf2, 0x1c, 0x32, 0x96, 0x1b, 0xf2, 0x37, 0x1c, 0x01, 0xba, 0x62, 0x92, 0x9d, 0x06, 0x13, 0x9a, 0xff, 0xbd, 0x6a, 0x6c, 0x77, 0x33, 0x59, 0xc6, 0xc7, 0x72, 0x7c, 0x7f, 0xd3, 0xed, 0xb8, 0xf7, 0x6d, 0x27, 0x66, 0x7b, 0xe8, 0x30, 0x2b, 0xe9, 0xf3, 0xc1, 0x2d, 0xf8, 0x9b, 0xcf, 0xb3, 0x99, 0x2c, 0x63, 0xfd, 0x1c, 0xdf, 0xa6, 0xa2, 0xd6, 0xb8, 0x0e, 0xb4, 0xc6, 0xe1, 0xc3, 0x87, 0x07, 0xce, 0x9d, 0x3b, 0xf7, 0xa3, 0xcd, 0x9b, 0x37, 0xff, 0xb0, 0xaf, 0xaf, 0xef, 0xbb, 0xab, 0x57, 0xaf, 0x1e, 0xef, 0xe9, 0xe9, 0xd9, 0xe0, 0xfa, 0x5a, 0xd7, 0xc9, 0x2b, 0x7d, 0x03, 0x40, 0x5a, 0xd7, 0x65, 0x26, 0xa9, 0x4a, 0x42, 0x02, 0x69, 0xb5, 0xeb, 0x43, 0xae, 0xaf, 0x3f, 0x79, 0xf2, 0xe4, 0xf8, 0xe3, 0xc7, 0x8f, 0xbf, 0x78, 0xf5, 0xea, 0xd5, 0xb5, 0xf9, 0xf9, 0xf9, 0xff, 0x2c, 0x94, 0xa4, 0xb9, 0xbd, 0xe7, 0x6a, 0x69, 0xb0, 0x88, 0x09, 0x94, 0xa4, 0x0f, 0x0c, 0x68, 0x00, 0xdb, 0x70, 0xe4, 0xc8, 0x91, 0xb1, 0xe7, 0xcf, 0x9f, 0x9f, 0x76, 0xb0, 0xa6, 0x4a, 0xc2, 0xea, 0x9d, 0x6d, 0xe6, 0xa1, 0x56, 0x14, 0x1a, 0xb0, 0xe8, 0x7d, 0xae, 0xaf, 0xdb, 0xb2, 0x65, 0xcb, 0xb7, 0x1e, 0x3d, 0x7a, 0xf4, 0xe5, 0xdc, 0xdc, 0xdc, 0x3f, 0xde, 0xd9, 0x49, 0xc9, 0x8c, 0xe5, 0x02, 0xa7, 0x0a, 0x43, 0x86, 0xaa, 0x6c, 0xc0, 0xf9, 0x37, 0x9c, 0x3f, 0x7f, 0x7e, 0x62, 0x7a, 0x7a, 0xfa, 0x6e, 0xc9, 0x18, 0x05, 0xb7, 0x9b, 0x15, 0x5c, 0x5a, 0xb5, 0x85, 0xa0, 0xa9, 0xca, 0x74, 0x2f, 0x1b, 0xb9, 0x7e, 0xfd, 0xfa, 0xa1, 0xd9, 0xd9, 0xd9, 0xe7, 0xc1, 0x5d, 0x94, 0xd0, 0x09, 0x90, 0x56, 0x2d, 0x2d, 0xc7, 0xc6, 0xa4, 0x5b, 0xc9, 0xe5, 0xd9, 0x7f, 0xff, 0xfe, 0xfd, 0x5f, 0xec, 0xdc, 0xb9, 0xf3, 0x77, 0xee, 0x9d, 0xb1, 0xbf, 0xd5, 0x62, 0x65, 0x89, 0x53, 0x19, 0x69, 0x4d, 0x10, 0x42, 0x39, 0x36, 0x26, 0x1d, 0x29, 0x9d, 0x8f, 0x0f, 0x83, 0xf7, 0xee, 0xdd, 0xfb, 0xf9, 0xae, 0x5d, 0xbb, 0xfe, 0x10, 0x9a, 0xa0, 0xcc, 0x3e, 0x2e, 0xb3, 0x22, 0x4d, 0x70, 0x18, 0x2b, 0xdd, 0x42, 0xe3, 0x05, 0x19, 0xba, 0x76, 0xed, 0xda, 0xde, 0xdd, 0xbb, 0x77, 0xff, 0xb6, 0xc8, 0x02, 0x65, 0x1e, 0x23, 0x10, 0xbe, 0x8c, 0xdd, 0xd3, 0xb8, 0x9f, 0xd1, 0xb9, 0x1c, 0x37, 0x9d, 0x3e, 0x7d, 0xfa, 0x7b, 0x55, 0xba, 0xa7, 0xf9, 0xb7, 0x61, 0x55, 0x4b, 0xe8, 0x05, 0x0e, 0xc5, 0xac, 0x4f, 0x3a, 0x52, 0x3a, 0x50, 0x07, 0x36, 0x6e, 0xdc, 0x38, 0x3c, 0x39, 0x39, 0xf9, 0xa7, 0xc1, 0xc1, 0xc1, 0x4f, 0x43, 0x13, 0x57, 0xc1, 0x17, 0xbb, 0x54, 0x05, 0x22, 0x76, 0x46, 0xc5, 0x7d, 0x68, 0x5c, 0xa2, 0x83, 0x37, 0x6e, 0xdc, 0xf8, 0x65, 0x95, 0xa1, 0x01, 0x25, 0x06, 0x2e, 0x04, 0x4c, 0xb0, 0x14, 0xb3, 0x36, 0x3a, 0x73, 0x0d, 0xee, 0xdf, 0xbf, 0xff, 0x13, 0xf7, 0x66, 0xf0, 0xa5, 0x92, 0xaa, 0x2a, 0x43, 0xe0, 0x2c, 0x90, 0xd0, 0xb9, 0x6d, 0x1c, 0x5d, 0xd0, 0xa8, 0xb6, 0xfe, 0x8b, 0x17, 0x2f, 0xfe, 0xaa, 0xd9, 0x6c, 0x8e, 0x86, 0x06, 0x56, 0xc9, 0x17, 0x02, 0x17, 0x3a, 0x9f, 0x60, 0x59, 0x29, 0x68, 0x48, 0x7e, 0x5b, 0x18, 0x3c, 0x7a, 0xf4, 0xe8, 0xa6, 0xf1, 0xf1, 0xf1, 0x5f, 0x87, 0x26, 0xa8, 0x9a, 0x2f, 0x0b, 0x38, 0xc1, 0xd2, 0xd9, 0x43, 0x36, 0xe0, 0xfa, 0x4f, 0x9c, 0x38, 0xf1, 0x59, 0xa3, 0xd1, 0x58, 0xab, 0xc4, 0x2a, 0x4b, 0x1f, 0x9c, 0x0f, 0xc5, 0x9e, 0xdd, 0xc6, 0xd0, 0xd5, 0x99, 0x83, 0xdf, 0x10, 0x9a, 0xae, 0xda, 0x3e, 0xb3, 0x03, 0xaa, 0xac, 0xfb, 0xe0, 0x5a, 0x9d, 0x55, 0xb0, 0xc8, 0x43, 0x67, 0x3c, 0xd5, 0xb6, 0x66, 0xcf, 0x9e, 0x3d, 0x6b, 0xdd, 0xc7, 0x90, 0x03, 0x4e, 0xef, 0x8a, 0xd6, 0x0a, 0x1c, 0x70, 0x68, 0x92, 0x8b, 0xd6, 0x37, 0xb6, 0xc0, 0xf5, 0x9d, 0x3d, 0x7b, 0xf6, 0x07, 0xee, 0x32, 0xe5, 0x5b, 0x90, 0xae, 0x68, 0xad, 0xc0, 0x59, 0x08, 0xaa, 0x36, 0x5f, 0xf2, 0x6e, 0xda, 0x74, 0x1f, 0x41, 0xbe, 0x6f, 0x93, 0xab, 0xae, 0x5b, 0x70, 0xb1, 0xaa, 0xf2, 0xfd, 0x62, 0x82, 0x9f, 0xf1, 0xfc, 0x9a, 0xd5, 0xbb, 0x6e, 0xdd, 0xba, 0xef, 0x28, 0xd0, 0x0d, 0xd2, 0x82, 0xcb, 0x73, 0x5e, 0xa0, 0x09, 0x1c, 0xf7, 0xb8, 0xe6, 0xd0, 0xd0, 0xd0, 0xb6, 0x3c, 0x13, 0x94, 0x3d, 0x37, 0x2b, 0x38, 0x81, 0x92, 0xe4, 0xdc, 0x02, 0xc7, 0xa5, 0xda, 0xeb, 0xfe, 0x45, 0xea, 0x93, 0xb2, 0xc3, 0xc8, 0xb3, 0xff, 0xac, 0xe0, 0xec, 0x9c, 0x82, 0x27, 0x49, 0xc5, 0xb9, 0xf7, 0x85, 0x06, 0xff, 0x08, 0xd3, 0x35, 0x2d, 0x06, 0x0e, 0x28, 0x34, 0xc9, 0x45, 0xeb, 0x5d, 0x9b, 0x18, 0xe3, 0xe9, 0x3d, 0x0e, 0x1c, 0xf7, 0xba, 0xae, 0x69, 0x31, 0x70, 0x16, 0x80, 0x2a, 0xcb, 0x42, 0x14, 0x34, 0xc5, 0xb2, 0xcc, 0x63, 0xe7, 0x2c, 0xbd, 0x5e, 0xe4, 0xc0, 0x16, 0x60, 0x52, 0x6d, 0x8e, 0x82, 0xf5, 0x95, 0x1e, 0x4a, 0x96, 0x03, 0xe4, 0x05, 0x27, 0x40, 0xaa, 0x34, 0xc9, 0x2c, 0x6b, 0x55, 0x2a, 0x27, 0x0d, 0x9c, 0x20, 0xe9, 0xc0, 0xb2, 0x25, 0xad, 0xdf, 0xf7, 0x29, 0x56, 0x59, 0x99, 0x06, 0xce, 0x1e, 0xda, 0x07, 0xe3, 0xdb, 0x36, 0xb7, 0x2b, 0xf4, 0x10, 0x38, 0x0b, 0x05, 0xdd, 0xb7, 0x05, 0x46, 0x31, 0x49, 0xf9, 0xbb, 0x42, 0x86, 0xc0, 0x75, 0xc5, 0xc1, 0xdb, 0x3d, 0x64, 0x11, 0x70, 0xb6, 0x02, 0xdb, 0x5d, 0xbf, 0xb4, 0xe3, 0xf3, 0x80, 0xb3, 0xc0, 0xec, 0xe5, 0x69, 0xf5, 0xd2, 0x82, 0xc8, 0xbb, 0xf1, 0x3c, 0xe0, 0x42, 0x73, 0x5b, 0x98, 0xa1, 0x78, 0x65, 0x7d, 0xed, 0x82, 0xab, 0x2c, 0x98, 0x56, 0x07, 0xab, 0xc1, 0xb5, 0x22, 0x14, 0x89, 0xe7, 0x05, 0xd7, 0xb5, 0x97, 0xa6, 0xcf, 0x2f, 0x06, 0xae, 0x06, 0xe4, 0x93, 0xf2, 0xec, 0x18, 0x38, 0x2f, 0xad, 0x36, 0x7d, 0x02, 0x59, 0xc1, 0xd5, 0x15, 0xe8, 0x91, 0xcb, 0x0a, 0xce, 0x0e, 0x0b, 0x41, 0x0c, 0xf9, 0xec, 0x98, 0xca, 0xe9, 0x45, 0xc0, 0x55, 0x0e, 0x42, 0x91, 0x03, 0xd5, 0xe0, 0x8a, 0x50, 0x73, 0x63, 0x6a, 0x70, 0x35, 0xb8, 0x82, 0x04, 0x0a, 0x0e, 0xab, 0x2b, 0xae, 0x06, 0x57, 0x90, 0x40, 0xc1, 0x61, 0x75, 0xc5, 0xd5, 0xe0, 0x0a, 0x12, 0x28, 0x38, 0xac, 0xae, 0xb8, 0x1a, 0x5c, 0x41, 0x02, 0x05, 0x87, 0xd5, 0x15, 0x57, 0x83, 0x2b, 0x48, 0xa0, 0xe0, 0xb0, 0xba, 0xe2, 0x6a, 0x70, 0x05, 0x09, 0x14, 0x1c, 0x56, 0x57, 0x5c, 0x0d, 0xae, 0x20, 0x81, 0x82, 0xc3, 0xea, 0x8a, 0xab, 0xc1, 0x15, 0x24, 0x50, 0x70, 0xd8, 0x72, 0x54, 0x5c, 0xf2, 0xc0, 0x26, 0xf7, 0xc4, 0x9a, 0x99, 0x82, 0x7b, 0x28, 0xe5, 0xb0, 0x22, 0xe0, 0xfc, 0x27, 0x5b, 0x71, 0xf0, 0x05, 0xf7, 0xe4, 0x9a, 0xff, 0x96, 0x92, 0x40, 0xc1, 0x4d, 0x67, 0x05, 0x17, 0x82, 0xa5, 0x25, 0x93, 0xd8, 0x9b, 0x37, 0x6f, 0xfe, 0x2d, 0x47, 0x37, 0xc8, 0xac, 0xe0, 0xd2, 0x58, 0xcc, 0x13, 0x74, 0x4f, 0xe3, 0x9a, 0x4c, 0x4b, 0xaa, 0x5a, 0x2c, 0x06, 0x2e, 0xad, 0xc2, 0x2c, 0x03, 0xf2, 0x00, 0xb7, 0xf0, 0xe2, 0xc5, 0x8b, 0x27, 0x36, 0x50, 0x75, 0x3d, 0x06, 0x2e, 0x76, 0x6e, 0x0b, 0x14, 0x5d, 0x7d, 0xfe, 0xce, 0x9d, 0x3b, 0x5f, 0xc7, 0x06, 0x55, 0xd1, 0x6f, 0xff, 0x21, 0x59, 0xba, 0x95, 0xe8, 0xea, 0x40, 0x46, 0x47, 0xd2, 0xf9, 0x1b, 0x2e, 0xfe, 0x32, 0x3a, 0x79, 0x80, 0xde, 0xb6, 0x6d, 0xdb, 0x36, 0x3e, 0x7c, 0xf8, 0xf0, 0x6a, 0x6f, 0x6f, 0x6f, 0x65, 0x9e, 0x9f, 0xe4, 0xce, 0x16, 0x6d, 0x79, 0x2b, 0xce, 0x4e, 0xb4, 0x54, 0x6d, 0xce, 0x39, 0xf7, 0xe4, 0xc9, 0x93, 0xe9, 0x67, 0xcf, 0x9e, 0xfd, 0xd5, 0x26, 0x54, 0x59, 0x6f, 0x07, 0x1c, 0x5c, 0xb8, 0xbf, 0xd1, 0xf9, 0x0c, 0xb7, 0xe0, 0x2e, 0xd7, 0xaf, 0x9c, 0xec, 0x8a, 0x96, 0x07, 0x9c, 0x7f, 0x7f, 0x4b, 0xde, 0x14, 0x1c, 0x25, 0xe4, 0x1c, 0xfd, 0xcc, 0x99, 0x33, 0x5f, 0xb9, 0xe7, 0x29, 0x75, 0xc5, 0xe7, 0xb9, 0x3c, 0xe0, 0x54, 0x49, 0x21, 0x80, 0x80, 0x9b, 0x76, 0x15, 0x37, 0x75, 0xfb, 0xf6, 0xed, 0xcb, 0x4a, 0xac, 0xb2, 0x2c, 0x02, 0xce, 0xf2, 0x00, 0x62, 0x52, 0x6d, 0x4e, 0xce, 0xba, 0x3e, 0x73, 0xec, 0xd8, 0xb1, 0x3f, 0xba, 0xc7, 0x39, 0xbe, 0xb0, 0x49, 0x55, 0xd4, 0xf9, 0x23, 0x5d, 0x35, 0xbd, 0x9b, 0x62, 0xeb, 0x9d, 0xd4, 0xc6, 0x14, 0xb7, 0x31, 0xe9, 0xcc, 0xc3, 0xdf, 0xab, 0x36, 0x5e, 0xbe, 0x7c, 0xd9, 0xd8, 0xb7, 0x6f, 0xdf, 0xff, 0xb6, 0x6f, 0xdf, 0xbe, 0x5f, 0x83, 0xab, 0x28, 0x05, 0x83, 0xb3, 0xf9, 0xba, 0x6c, 0xc1, 0xa1, 0x3a, 0xd1, 0xad, 0x44, 0x4f, 0x9e, 0xa9, 0xe4, 0x24, 0x8f, 0xce, 0xe0, 0xa3, 0xc9, 0xb0, 0x7b, 0xf8, 0xf1, 0xe0, 0xd3, 0xa7, 0x4f, 0x7f, 0x3f, 0x32, 0x32, 0xb2, 0xc3, 0xd9, 0x95, 0x6c, 0x69, 0x97, 0xaa, 0xbd, 0x97, 0x71, 0x78, 0xd9, 0x92, 0x02, 0xc2, 0x9b, 0x43, 0x72, 0x99, 0x3a, 0xc9, 0xbb, 0xeb, 0xf4, 0xeb, 0xd7, 0xaf, 0x67, 0x8e, 0x1f, 0x3f, 0xfe, 0x9b, 0x2a, 0x5f, 0xb2, 0x69, 0x97, 0x2a, 0x60, 0x54, 0x6d, 0x92, 0xf2, 0x49, 0xca, 0xcf, 0x0b, 0xa0, 0x4e, 0x05, 0xf6, 0x3c, 0x78, 0xf0, 0x60, 0xc6, 0x3d, 0x4c, 0xf4, 0x6f, 0x07, 0x0f, 0x1e, 0xfc, 0xb1, 0xfb, 0xb3, 0x73, 0xbb, 0x0e, 0x63, 0x4b, 0xdf, 0xec, 0x81, 0x80, 0xa0, 0x26, 0x5d, 0x60, 0xac, 0x24, 0x47, 0xb6, 0x74, 0x8d, 0x13, 0x3c, 0xe6, 0x6d, 0xdc, 0xbc, 0x79, 0x73, 0xca, 0x3d, 0x8d, 0xf5, 0x9f, 0x13, 0x13, 0x13, 0x95, 0x7b, 0x74, 0x50, 0x16, 0x70, 0x82, 0x22, 0x48, 0x21, 0xa8, 0xc4, 0x80, 0x26, 0xa0, 0x4b, 0xfa, 0x95, 0x2b, 0x57, 0x26, 0x77, 0xec, 0xd8, 0xf1, 0xcc, 0x3d, 0x64, 0x74, 0x6f, 0x95, 0x2a, 0x2f, 0x06, 0x4e, 0x90, 0x24, 0x05, 0xc4, 0xda, 0x56, 0xb7, 0x71, 0x41, 0x5b, 0x92, 0xc0, 0x73, 0x1f, 0x8c, 0xbf, 0x3e, 0x70, 0xe0, 0xc0, 0x5e, 0xf7, 0x50, 0xbe, 0x41, 0x06, 0x96, 0xbd, 0xe5, 0x05, 0xa7, 0x6a, 0xe3, 0xdc, 0x16, 0x96, 0xf5, 0x8b, 0x09, 0x3e, 0xc1, 0x5b, 0x75, 0xeb, 0xd6, 0xad, 0x7f, 0xdd, 0xbd, 0x7b, 0xf7, 0x2f, 0x87, 0x0e, 0x1d, 0xda, 0xe3, 0x9e, 0x9f, 0xb9, 0x49, 0x49, 0x65, 0x95, 0xf6, 0xc0, 0x56, 0xe7, 0x3c, 0xb2, 0xad, 0x44, 0xa7, 0x0b, 0x88, 0x95, 0xe8, 0x74, 0xde, 0x1c, 0xf4, 0xcd, 0x09, 0xd5, 0xc5, 0xc7, 0x14, 0x1e, 0xa6, 0xcc, 0xe7, 0xbc, 0x01, 0xf7, 0x51, 0xa5, 0x79, 0xf9, 0xf2, 0xe5, 0x9f, 0x38, 0x80, 0x9f, 0xf7, 0xf7, 0xf7, 0x8f, 0x38, 0x5f, 0x29, 0x9b, 0xa0, 0xb0, 0x79, 0xab, 0x5b, 0x5b, 0x7e, 0xa4, 0xed, 0x16, 0x1a, 0x7e, 0x81, 0x43, 0x52, 0xc9, 0xc0, 0x4a, 0x1e, 0xd9, 0xed, 0x24, 0x5f, 0x35, 0x09, 0x1e, 0x7a, 0x73, 0x6c, 0x6c, 0xac, 0xff, 0xd2, 0xa5, 0x4b, 0x3f, 0x75, 0x97, 0xef, 0xcf, 0xca, 0x78, 0xf9, 0xda, 0x4b, 0xd5, 0x9d, 0x27, 0x15, 0x9e, 0x85, 0x86, 0xae, 0x26, 0x5d, 0x71, 0xf9, 0x25, 0xf9, 0x9c, 0x67, 0x73, 0x92, 0x2f, 0x07, 0xa6, 0xa6, 0xa6, 0xe6, 0x2e, 0x5c, 0xb8, 0xf0, 0xf7, 0xab, 0x57, 0xaf, 0xfe, 0x79, 0xfd, 0xfa, 0xf5, 0x93, 0xc3, 0xc3, 0xc3, 0xf3, 0xae, 0x8f, 0xba, 0x37, 0x10, 0xaa, 0xf5, 0xa3, 0x6f, 0x3a, 0x90, 0x36, 0x1a, 0xb3, 0xe5, 0x47, 0xda, 0xae, 0xaa, 0xc3, 0xa7, 0x8a, 0xb3, 0x3a, 0x2f, 0x0c, 0x97, 0x28, 0x9d, 0x4a, 0xa3, 0x02, 0xe9, 0xd8, 0xc4, 0x80, 0x44, 0x25, 0x26, 0xf3, 0x6c, 0xdd, 0xba, 0x75, 0xcd, 0xa9, 0x53, 0xa7, 0x3e, 0x75, 0x8f, 0xc4, 0x9d, 0x18, 0x1d, 0x1d, 0x1d, 0x73, 0x8f, 0x56, 0xfb, 0xb6, 0xbb, 0xb4, 0xd7, 0xba, 0x8a, 0x1c, 0xf8, 0xd8, 0x1e, 0x41, 0xc4, 0x21, 0x6d, 0xf3, 0x6d, 0x62, 0xf2, 0x21, 0xd5, 0xf1, 0x5b, 0x68, 0xf8, 0x65, 0xfb, 0x00, 0x81, 0xa3, 0x4b, 0x17, 0x48, 0xba, 0x84, 0xf1, 0x2b, 0x46, 0x5c, 0x20, 0x35, 0x8f, 0x73, 0x2d, 0xad, 0x8d, 0xee, 0x37, 0xed, 0xcb, 0xf7, 0x17, 0xb1, 0xf5, 0xdb, 0x90, 0x95, 0xbe, 0xce, 0x95, 0x42, 0xa7, 0xcd, 0xb0, 0xf1, 0x3c, 0x4d, 0x93, 0xb1, 0x69, 0xe9, 0x8c, 0xb7, 0x36, 0x7e, 0x16, 0x00, 0x00, 0x3a, 0xbf, 0x8e, 0x21, 0xe5, 0xd7, 0xb7, 0x29, 0xaa, 0x44, 0x55, 0x1f, 0xe0, 0xc8, 0x65, 0x2e, 0xba, 0x7d, 0x01, 0x9c, 0x19, 0x84, 0x48, 0x5e, 0xbb, 0x8d, 0x7d, 0xa9, 0x49, 0xb7, 0x12, 0x9d, 0x9e, 0xdc, 0x62, 0xde, 0xca, 0xf7, 0xc0, 0x91, 0x10, 0xdb, 0x8c, 0x8d, 0xa1, 0xdb, 0x66, 0x6d, 0xe9, 0x48, 0x01, 0x04, 0x16, 0x0d, 0x9f, 0xc0, 0x01, 0x49, 0x5d, 0x95, 0x87, 0x14, 0x34, 0xa4, 0xba, 0x53, 0xa3, 0xfb, 0x22, 0xb6, 0x5c, 0xcd, 0xee, 0x1d, 0xdd, 0x76, 0xd6, 0xe0, 0x3c, 0xc9, 0xfe, 0xb3, 0x54, 0x1c, 0x83, 0x2d, 0x4c, 0xdf, 0x66, 0x42, 0x7c, 0x6a, 0xd2, 0x05, 0x4d, 0x92, 0x05, 0xd1, 0x89, 0xd3, 0xb1, 0x01, 0xf7, 0xc6, 0x75, 0x5b, 0x75, 0x31, 0x78, 0x76, 0x0f, 0x6e, 0xc8, 0xb2, 0x37, 0xed, 0x9b, 0x89, 0xb5, 0xc7, 0x90, 0xe4, 0x0c, 0xb3, 0xa1, 0xcd, 0x84, 0x7c, 0x4c, 0x26, 0xbf, 0x95, 0xe8, 0xa1, 0xae, 0xfb, 0x14, 0xb1, 0x34, 0x9d, 0x98, 0xee, 0x6f, 0x48, 0x6b, 0x6b, 0x5e, 0x7c, 0x34, 0x6c, 0x35, 0xab, 0xcb, 0xd7, 0x8e, 0xf4, 0xa1, 0x31, 0x57, 0x08, 0x9a, 0x7c, 0xb3, 0xa1, 0x8a, 0x23, 0x98, 0xb6, 0x31, 0x1b, 0x47, 0x0f, 0x35, 0x55, 0x19, 0x31, 0x5f, 0x17, 0x10, 0xc6, 0x12, 0xa3, 0x53, 0x7d, 0x00, 0xa2, 0x13, 0xb7, 0xd2, 0x99, 0x4b, 0x2f, 0x0e, 0x7a, 0x27, 0x9b, 0xce, 0x83, 0x94, 0xce, 0xfe, 0x68, 0xf2, 0x21, 0xe7, 0x42, 0xe0, 0x92, 0xac, 0xc0, 0x0f, 0x06, 0x58, 0xa0, 0xd6, 0x46, 0xf7, 0x5b, 0x0c, 0x98, 0xc0, 0x28, 0x5f, 0xf0, 0x04, 0x34, 0x24, 0xc9, 0xb5, 0x6b, 0x6b, 0xec, 0x72, 0x4b, 0x7b, 0x0e, 0xf4, 0x50, 0x67, 0xbf, 0x0b, 0x31, 0x70, 0x0c, 0x48, 0xdb, 0xa8, 0xe2, 0x48, 0x1a, 0xb9, 0xd2, 0x13, 0xc7, 0xdb, 0x1f, 0x2c, 0x62, 0x41, 0xe8, 0xb2, 0xf3, 0xfd, 0xa4, 0x2b, 0x4f, 0xba, 0x95, 0xbe, 0x8e, 0xad, 0x96, 0xb6, 0x4f, 0xe5, 0xb4, 0x92, 0x76, 0xef, 0xd2, 0x91, 0xbe, 0x9e, 0x40, 0xc3, 0x1f, 0x03, 0x17, 0x5b, 0x88, 0x89, 0xb4, 0x51, 0xe9, 0x9a, 0x1c, 0xbf, 0xf4, 0xd8, 0x78, 0x1f, 0x98, 0x60, 0x69, 0xac, 0xe6, 0x96, 0x5f, 0xf3, 0x29, 0x1e, 0x9b, 0x57, 0xe3, 0x62, 0xf1, 0x56, 0x7e, 0xad, 0x43, 0x9e, 0x74, 0x2b, 0xd1, 0xdf, 0xe9, 0xad, 0x16, 0x8c, 0xc5, 0xad, 0x5f, 0x3a, 0xd2, 0xd7, 0xe5, 0x4b, 0xf3, 0x2b, 0x87, 0x4d, 0xdb, 0x3c, 0x6b, 0xfb, 0x3a, 0xb6, 0x9a, 0xc6, 0xc8, 0x2e, 0x22, 0x05, 0x89, 0xb1, 0xd2, 0xad, 0xb4, 0xd0, 0xc8, 0x99, 0xcf, 0x5b, 0x71, 0x0c, 0xa2, 0x31, 0x91, 0x36, 0x2c, 0x5d, 0x0b, 0xe1, 0x97, 0x6e, 0x73, 0x95, 0x8f, 0x8f, 0xa6, 0x71, 0xf8, 0x15, 0x8b, 0x49, 0xf2, 0x15, 0x43, 0xef, 0x64, 0xd3, 0xde, 0x43, 0x12, 0x5f, 0xd2, 0xb3, 0x6c, 0x26, 0x2d, 0xc7, 0xc6, 0xa4, 0x5b, 0xe9, 0xeb, 0x69, 0x36, 0x30, 0x88, 0x2b, 0x47, 0x36, 0x92, 0x66, 0xfd, 0x8b, 0x9e, 0xce, 0xfc, 0x14, 0x30, 0x66, 0x97, 0xbe, 0x04, 0xec, 0xed, 0x92, 0x99, 0xff, 0x7f, 0xd5, 0xb4, 0x4d, 0xdb, 0x98, 0xf4, 0x90, 0xb4, 0x3e, 0xab, 0xb3, 0x17, 0x6c, 0xdf, 0x27, 0x3f, 0x92, 0xa6, 0xf8, 0xa2, 0xd5, 0xd9, 0x9f, 0x16, 0x18, 0x2b, 0x59, 0x3b, 0xd1, 0xf3, 0x6c, 0x26, 0x2d, 0xd7, 0xc6, 0x42, 0xba, 0x7c, 0x48, 0xab, 0xb3, 0xa9, 0x56, 0x36, 0x39, 0x34, 0xe5, 0x2d, 0x5a, 0x9d, 0xfd, 0x29, 0x50, 0xac, 0x22, 0xdd, 0xca, 0xcc, 0x15, 0xc7, 0x04, 0xad, 0x36, 0xee, 0xc7, 0x65, 0xc7, 0xa4, 0xe6, 0x54, 0x5c, 0xb6, 0x95, 0xe8, 0x34, 0x9b, 0xb3, 0xe8, 0x89, 0xff, 0xcc, 0x93, 0xcb, 0x2c, 0x02, 0xe2, 0xcf, 0x68, 0xfd, 0xd2, 0x97, 0x64, 0xde, 0x45, 0x5a, 0xe5, 0xfb, 0x71, 0x6b, 0x4b, 0x97, 0x64, 0xa3, 0xd2, 0x25, 0xad, 0xcf, 0xd7, 0xb1, 0xfd, 0x66, 0xc7, 0xf9, 0xb1, 0xbc, 0xb6, 0xa0, 0xf8, 0xe3, 0xac, 0x5f, 0x7a, 0xae, 0x8a, 0xb3, 0x13, 0xb6, 0xda, 0xb0, 0x1f, 0xb7, 0x76, 0x5e, 0x5d, 0xeb, 0xda, 0x71, 0xf2, 0x75, 0x52, 0x0a, 0x92, 0xd6, 0xb0, 0x76, 0x61, 0x70, 0x4c, 0x96, 0xe5, 0x20, 0x7e, 0x4e, 0x9a, 0x9d, 0x16, 0xd3, 0xe6, 0x7d, 0xe9, 0x8f, 0xf1, 0xe3, 0x79, 0x6c, 0x0b, 0xc6, 0x1f, 0xe7, 0xc7, 0xda, 0x02, 0xc7, 0xe4, 0x59, 0x36, 0x1e, 0xcb, 0xf1, 0xfd, 0xbe, 0x9d, 0x75, 0x7e, 0xff, 0x90, 0x9d, 0xb0, 0xdf, 0x03, 0xc7, 0x57, 0x39, 0xed, 0xb6, 0xd0, 0x81, 0x63, 0x73, 0xc6, 0x72, 0x63, 0xfe, 0xd8, 0x3c, 0x2b, 0xed, 0xf7, 0xc1, 0x65, 0xaa, 0x98, 0xac, 0x9b, 0xcc, 0x73, 0xf8, 0x56, 0xb9, 0xad, 0xe2, 0x59, 0xf7, 0xd4, 0x89, 0xbc, 0x04, 0x62, 0x27, 0x36, 0x58, 0x64, 0xce, 0x22, 0x63, 0x04, 0xa5, 0x9d, 0xb1, 0x9a, 0x03, 0xf9, 0x5e, 0x55, 0xd9, 0xa0, 0xaf, 0x2f, 0xd7, 0xa2, 0xfe, 0xbc, 0xd8, 0xed, 0xce, 0xdd, 0xee, 0xf8, 0xd0, 0x9e, 0x96, 0xc3, 0xd7, 0xb1, 0x8a, 0x0b, 0x6d, 0xee, 0x63, 0x85, 0x10, 0xda, 0x6b, 0x26, 0xdf, 0x87, 0x3a, 0xd0, 0x87, 0x5a, 0x37, 0x13, 0x94, 0x3a, 0xa9, 0x26, 0xf0, 0xf1, 0x11, 0xf8, 0x3f, 0x7a, 0xc4, 0x58, 0xcc, 0x89, 0xbc, 0xc8, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderRightHandle2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x50, 0x08, 0x06, 0x00, 0x00, 0x00, 0x56, 0x1d, 0x56, 0xa7, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x7d, 0xa9, 0x20, 0x00, 0x00, 0x01, 0x59, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x4c, 0xc2, 0x27, 0x59, 0x00, 0x00, 0x06, 0x18, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9a, 0xcf, 0x4b, 0x5c, 0x57, 0x14, 0xc7, 0x33, 0x31, 0x89, 0xb6, 0x89, 0x59, 0xb5, 0xb1, 0x82, 0x50, 0x90, 0x2c, 0x92, 0x45, 0x16, 0x6e, 0x85, 0x10, 0x4b, 0xb6, 0xc6, 0x2e, 0x0a, 0xe2, 0x3f, 0xd0, 0xb5, 0xa0, 0xae, 0xbb, 0x10, 0xb3, 0x2a, 0x62, 0xc0, 0x65, 0xb2, 0x4b, 0x20, 0x81, 0x80, 0x7b, 0x41, 0x51, 0xba, 0x08, 0x48, 0x71, 0x21, 0x14, 0x17, 0x76, 0x25, 0xc5, 0xb6, 0xfe, 0x18, 0xb0, 0xa9, 0x71, 0xe2, 0xef, 0x9e, 0xef, 0xf3, 0x7d, 0xaf, 0x67, 0xee, 0xbb, 0xef, 0xcd, 0x7b, 0x33, 0x3a, 0x73, 0x5f, 0x99, 0x0b, 0xc7, 0x73, 0x7f, 0x9c, 0x77, 0xef, 0xf7, 0xf3, 0xce, 0x7d, 0x77, 0x1e, 0xce, 0x5c, 0xbb, 0xf6, 0x3f, 0x2b, 0x05, 0xe1, 0x81, 0x35, 0xb2, 0x9c, 0x5d, 0xe6, 0xe2, 0x49, 0x40, 0xb5, 0x82, 0x56, 0x2b, 0xb4, 0xda, 0xeb, 0x82, 0xfb, 0x02, 0xd1, 0xd7, 0x2f, 0xf3, 0x0e, 0x65, 0x98, 0x2b, 0x8d, 0xf0, 0x34, 0x31, 0x65, 0x4b, 0x12, 0x28, 0x29, 0x1b, 0x49, 0x63, 0x65, 0x93, 0x49, 0x23, 0x8d, 0x00, 0x57, 0x8c, 0xab, 0x4f, 0xcf, 0x5d, 0x69, 0xdc, 0xc4, 0x42, 0xec, 0x5d, 0x31, 0x2d, 0x5a, 0xd7, 0x4d, 0x60, 0x8d, 0x95, 0x13, 0xb9, 0x1e, 0xa2, 0x60, 0xc7, 0xa1, 0x89, 0x0b, 0x8a, 0x2d, 0xd6, 0x6e, 0xc7, 0xc5, 0xb1, 0xbf, 0xdc, 0x9f, 0x5d, 0x71, 0x39, 0x3d, 0x3d, 0xfd, 0xf7, 0xf0, 0xf0, 0xf0, 0xf7, 0x52, 0xa9, 0xf4, 0xcb, 0xee, 0xee, 0xee, 0xcb, 0x95, 0x95, 0x95, 0x1f, 0x7a, 0x7b, 0x7b, 0xef, 0x89, 0x8a, 0xdb, 0x62, 0x37, 0xc5, 0x5a, 0x42, 0xc3, 0xd6, 0xa7, 0xe1, 0xa6, 0xba, 0x4c, 0xba, 0x2b, 0x94, 0x2b, 0xe6, 0x71, 0x4e, 0x7f, 0x72, 0x72, 0xf2, 0x51, 0xe0, 0x5e, 0x4d, 0x4f, 0x4f, 0x3f, 0x10, 0x79, 0xed, 0x62, 0x59, 0xc0, 0x92, 0x89, 0x9c, 0x2b, 0xd6, 0xa9, 0x53, 0xb2, 0xb7, 0xb7, 0xb9, 0xb9, 0xf9, 0x73, 0x5f, 0x5f, 0xdf, 0x37, 0xa2, 0xb2, 0x4d, 0xec, 0x86, 0x98, 0x9d, 0xb1, 0x6c, 0x99, 0xaa, 0x93, 0xf6, 0xc4, 0x65, 0xf6, 0xf7, 0xf7, 0x7f, 0x9d, 0x9a, 0x9a, 0x7a, 0x28, 0x30, 0xd8, 0x86, 0x80, 0x22, 0x58, 0xd2, 0x16, 0x94, 0x30, 0x47, 0x49, 0x5c, 0xa9, 0x8e, 0x83, 0x47, 0x47, 0x47, 0x1b, 0x33, 0x33, 0x33, 0x4f, 0x44, 0x22, 0xb7, 0x60, 0x75, 0x50, 0x75, 0xd4, 0x5c, 0x71, 0xa9, 0x83, 0x83, 0x83, 0x3f, 0xc7, 0xc7, 0xc7, 0x1f, 0x09, 0xd4, 0x97, 0x62, 0x78, 0xae, 0xb2, 0x43, 0x55, 0x5c, 0xa5, 0xce, 0x01, 0x7b, 0x7b, 0x7b, 0xcb, 0x3d, 0x3d, 0x3d, 0xdf, 0x0a, 0xcc, 0x17, 0x29, 0xa1, 0x24, 0x4c, 0x95, 0x3a, 0xeb, 0x4d, 0xb5, 0xdc, 0xfa, 0xfa, 0xfa, 0x0b, 0x91, 0xf8, 0x95, 0x58, 0xab, 0x58, 0x9a, 0x4c, 0x19, 0xa2, 0x46, 0xbd, 0xf6, 0x18, 0x01, 0xae, 0x4a, 0x57, 0x57, 0xd7, 0x8f, 0xa3, 0xa3, 0xa3, 0xc8, 0xd2, 0x2d, 0x31, 0xfb, 0x94, 0x73, 0x5d, 0x72, 0xd1, 0x97, 0xea, 0x96, 0x35, 0x20, 0x68, 0x63, 0x63, 0xe3, 0x8d, 0xa8, 0xc4, 0x71, 0x8e, 0xad, 0x07, 0xb0, 0xa4, 0xe7, 0x49, 0x86, 0xcf, 0x8b, 0x97, 0x19, 0x82, 0xb4, 0x8e, 0x8e, 0x8e, 0xef, 0xbb, 0xbb, 0xbb, 0xf1, 0x5a, 0x06, 0x90, 0x4a, 0x59, 0xc2, 0x78, 0x50, 0xbc, 0x05, 0x6a, 0x69, 0x69, 0xb9, 0x33, 0x39, 0x39, 0xf9, 0x58, 0x54, 0xe2, 0x03, 0x17, 0x3a, 0x09, 0x15, 0x08, 0x0f, 0xdb, 0xac, 0x1b, 0xef, 0x2d, 0x10, 0x14, 0xca, 0x69, 0xf7, 0x54, 0x1c, 0xb6, 0x1c, 0xde, 0x1e, 0x98, 0x05, 0x1b, 0x4c, 0x86, 0x2e, 0x8a, 0xd7, 0x40, 0xed, 0xed, 0xed, 0xf7, 0x45, 0x2a, 0x4e, 0x3a, 0x02, 0xd9, 0x30, 0x84, 0x34, 0x44, 0x5e, 0x03, 0xb5, 0xb5, 0xb5, 0xe1, 0xad, 0x5c, 0x1f, 0x06, 0x04, 0xb0, 0xc1, 0x00, 0x14, 0x8c, 0x79, 0x0d, 0xd4, 0xda, 0xda, 0xfa, 0xb5, 0x08, 0xe5, 0x9b, 0x38, 0x21, 0x08, 0x05, 0x88, 0x48, 0xf1, 0x1a, 0x48, 0x0e, 0x06, 0xbc, 0x02, 0x61, 0xbb, 0x41, 0xa7, 0x06, 0x89, 0xab, 0x37, 0xec, 0xff, 0x09, 0x91, 0x3b, 0x9b, 0xd0, 0x01, 0x18, 0x1a, 0xb3, 0x84, 0x70, 0x0d, 0x65, 0x2e, 0xf7, 0x3a, 0x43, 0xa1, 0x4a, 0x08, 0xa7, 0x4e, 0x1b, 0xc2, 0x6e, 0x9b, 0x40, 0x43, 0xe8, 0x61, 0x05, 0xa2, 0x69, 0x90, 0xa7, 0xeb, 0x11, 0xb9, 0x24, 0x8f, 0x0c, 0x78, 0xda, 0x11, 0xc9, 0x88, 0xad, 0x33, 0x0f, 0x40, 0xcc, 0x88, 0x0d, 0x63, 0xb7, 0x03, 0xb6, 0x3c, 0x00, 0xd9, 0x49, 0x20, 0xa0, 0xdd, 0x9f, 0x4b, 0x20, 0x67, 0x56, 0x34, 0x59, 0x1e, 0x32, 0x44, 0x08, 0x7a, 0xad, 0x3f, 0x52, 0xcf, 0x03, 0x10, 0x44, 0x57, 0x82, 0x31, 0xe3, 0x79, 0x01, 0x8a, 0x64, 0x22, 0xec, 0x30, 0x20, 0x0c, 0xc8, 0x3b, 0x10, 0x39, 0x8c, 0x6f, 0x02, 0x99, 0x5b, 0xe1, 0x69, 0xa5, 0x99, 0x21, 0x4f, 0x13, 0x63, 0x64, 0x35, 0x33, 0x64, 0x6e, 0x85, 0xa7, 0x95, 0x66, 0x86, 0x3c, 0x4d, 0x8c, 0x91, 0xd5, 0xcc, 0x90, 0xb9, 0x15, 0x9e, 0x56, 0x9a, 0x19, 0xf2, 0x34, 0x31, 0x46, 0x96, 0xd7, 0x19, 0x3a, 0x3e, 0x3e, 0xde, 0x37, 0x4a, 0x53, 0x56, 0xbc, 0x06, 0x92, 0xef, 0x5c, 0x77, 0x84, 0x23, 0xee, 0x97, 0x25, 0x4e, 0x44, 0xaf, 0x81, 0xe4, 0xeb, 0x7e, 0x00, 0x9d, 0x26, 0x40, 0x45, 0x60, 0xbd, 0x06, 0xda, 0xd9, 0xd9, 0x59, 0x0f, 0x81, 0x92, 0xa0, 0x90, 0x29, 0x03, 0xe6, 0x35, 0xd0, 0xc2, 0xc2, 0xc2, 0x07, 0x11, 0x8b, 0x1f, 0x3b, 0x41, 0xb0, 0x11, 0x2d, 0xf5, 0xd8, 0x52, 0xc0, 0xd7, 0xa7, 0xb1, 0xa3, 0x0d, 0x1c, 0x90, 0x03, 0xe1, 0x53, 0x67, 0x67, 0xe7, 0x33, 0xc9, 0xd2, 0x5f, 0x22, 0x63, 0x57, 0xac, 0x24, 0x06, 0x38, 0x64, 0x4b, 0x9b, 0x86, 0x3d, 0xf3, 0x36, 0x43, 0x6b, 0x6b, 0x6b, 0x73, 0x02, 0xf3, 0x59, 0x41, 0xf0, 0xc6, 0x6b, 0x00, 0x19, 0x2e, 0x2f, 0x5e, 0x02, 0xc9, 0xaf, 0xb5, 0x4a, 0x23, 0x23, 0x23, 0xaf, 0x45, 0x2a, 0xb2, 0x72, 0x24, 0xc6, 0xdf, 0xdb, 0x69, 0xf5, 0x04, 0xd4, 0x7d, 0x7e, 0xfe, 0xb3, 0x7e, 0x79, 0x79, 0xf9, 0xfd, 0xec, 0xec, 0xec, 0x56, 0x08, 0xc3, 0x6d, 0x06, 0xe1, 0x4e, 0x08, 0x4d, 0xe4, 0x5d, 0x86, 0x64, 0x9b, 0xad, 0xf6, 0xf7, 0xf7, 0xbf, 0x15, 0x91, 0xc8, 0x0e, 0xb6, 0x1c, 0x32, 0xa4, 0x4f, 0xb9, 0xfc, 0x6c, 0x39, 0xf9, 0xdc, 0xd9, 0x1e, 0x1a, 0x1a, 0x1a, 0xdf, 0xde, 0xde, 0xfe, 0x18, 0x02, 0x1d, 0x8a, 0xd7, 0xdb, 0xcd, 0xce, 0x90, 0xdd, 0xf6, 0x67, 0xcb, 0x01, 0x66, 0x6c, 0x6c, 0xec, 0xa7, 0xf9, 0xf9, 0xf9, 0xbf, 0x43, 0x18, 0x64, 0x48, 0x03, 0x31, 0x33, 0x84, 0xa0, 0x97, 0xb0, 0x8b, 0x82, 0x6f, 0x98, 0x1b, 0x5e, 0xb0, 0xcd, 0x06, 0x07, 0x07, 0x9f, 0xcb, 0xe7, 0x0e, 0x8e, 0xe8, 0x4f, 0x62, 0x78, 0x87, 0x3b, 0x10, 0xd3, 0x9f, 0x41, 0x1a, 0x20, 0xae, 0x1e, 0x7c, 0x65, 0x2e, 0xd7, 0x35, 0xa6, 0xe0, 0x34, 0x5b, 0x5a, 0x5a, 0x9a, 0x19, 0x18, 0x18, 0x78, 0x5f, 0x2c, 0x16, 0xff, 0x11, 0x15, 0x00, 0x41, 0x66, 0x98, 0x1d, 0x3e, 0x3b, 0x00, 0xd0, 0x26, 0x4d, 0x77, 0x69, 0x48, 0x86, 0xf0, 0xa1, 0xb9, 0xba, 0xba, 0xba, 0x30, 0x3c, 0x3c, 0xfc, 0x6e, 0x71, 0x71, 0x11, 0xef, 0x6b, 0x78, 0xf8, 0x01, 0x81, 0xec, 0xc0, 0xd0, 0xc6, 0xb3, 0xe3, 0x02, 0x92, 0x6e, 0x03, 0x87, 0x3a, 0x4b, 0x90, 0xb5, 0x2b, 0x07, 0x12, 0xf1, 0xf2, 0x0b, 0xe7, 0x52, 0x51, 0x7e, 0xd8, 0x57, 0xdc, 0xda, 0xda, 0xfa, 0x63, 0x6e, 0x6e, 0x6e, 0x69, 0x62, 0x62, 0xe2, 0x37, 0xf9, 0x55, 0x30, 0x44, 0x63, 0x5b, 0xe1, 0x14, 0x43, 0x1d, 0xd9, 0x81, 0xd9, 0x30, 0x71, 0x50, 0x12, 0x1a, 0x3d, 0xc6, 0x6f, 0x14, 0x0a, 0x85, 0xef, 0x64, 0x00, 0x60, 0x38, 0xc2, 0x69, 0xf8, 0xaf, 0x3e, 0xff, 0xb3, 0x4f, 0x2f, 0x5d, 0x55, 0x17, 0x2d, 0x0a, 0xcf, 0x05, 0x0d, 0x0f, 0x3d, 0xa0, 0x08, 0x82, 0x36, 0x8f, 0x69, 0x5c, 0xa3, 0xaf, 0xe3, 0x73, 0xc3, 0xad, 0x27, 0xc3, 0xd1, 0x02, 0x10, 0x4c, 0x86, 0x5f, 0x6b, 0x00, 0x06, 0xe2, 0xe9, 0x35, 0x88, 0xae, 0x4b, 0x48, 0xaa, 0x42, 0x01, 0x0c, 0x86, 0x38, 0x6e, 0x23, 0x88, 0x66, 0x66, 0x00, 0x44, 0x10, 0x7e, 0x88, 0x12, 0x84, 0x9e, 0x10, 0x7a, 0x4e, 0x5d, 0xe7, 0x1a, 0x41, 0x66, 0xf8, 0x00, 0xda, 0x3f, 0x10, 0x02, 0x04, 0xcd, 0x5c, 0x90, 0xa1, 0xc2, 0x05, 0x29, 0x06, 0xe2, 0x98, 0x19, 0x02, 0xc1, 0xb3, 0x0f, 0xe3, 0xb6, 0xe1, 0xda, 0x24, 0x28, 0x19, 0x0e, 0x0a, 0xd7, 0x0a, 0x80, 0x70, 0xba, 0x70, 0xab, 0xe9, 0xec, 0x68, 0x98, 0x2c, 0x19, 0x32, 0x93, 0xab, 0xc5, 0x28, 0x14, 0x63, 0xc8, 0x12, 0xcc, 0x95, 0x0d, 0xc6, 0xd1, 0x23, 0x9e, 0x26, 0x55, 0x53, 0xec, 0x35, 0xcc, 0x00, 0xb6, 0x1c, 0x4e, 0x15, 0x02, 0x41, 0xb8, 0x86, 0x42, 0x60, 0x16, 0x18, 0xc4, 0xb3, 0xe8, 0x45, 0x21, 0x10, 0x05, 0x7d, 0xa8, 0x53, 0x24, 0xeb, 0x1a, 0xc0, 0xae, 0x33, 0x96, 0x1e, 0xf3, 0xc4, 0x16, 0x00, 0xe1, 0x4e, 0x69, 0x20, 0x66, 0x86, 0x20, 0xf4, 0xb1, 0x93, 0x24, 0x0c, 0x40, 0x04, 0x8a, 0xf6, 0x14, 0x46, 0xef, 0x82, 0xc2, 0x18, 0xfb, 0x19, 0x07, 0x8f, 0x42, 0x7f, 0xde, 0xb2, 0xda, 0x00, 0xc2, 0x85, 0x28, 0x08, 0x84, 0x78, 0xc0, 0xa1, 0x10, 0x84, 0xfe, 0xbc, 0x37, 0xfd, 0x5f, 0xbd, 0x30, 0xeb, 0xf0, 0x2e, 0xd3, 0xe2, 0x75, 0x5d, 0xc7, 0x62, 0x65, 0xce, 0x13, 0xab, 0x42, 0x03, 0x41, 0x38, 0x8c, 0x17, 0x11, 0x84, 0x3e, 0x76, 0x12, 0xc7, 0x00, 0xe7, 0xe0, 0x10, 0xdb, 0xf0, 0xd5, 0x18, 0xe6, 0xe1, 0x1c, 0x9c, 0xd3, 0xd9, 0x47, 0x20, 0x04, 0xeb, 0xcc, 0x10, 0x82, 0x5e, 0x4f, 0x92, 0xb6, 0xae, 0x05, 0xb0, 0xae, 0x3d, 0xea, 0x71, 0x86, 0x35, 0x38, 0xc6, 0x3a, 0xbc, 0x2e, 0x9c, 0x4b, 0xf7, 0x05, 0xa7, 0x1c, 0x07, 0x70, 0xf2, 0x10, 0xc0, 0xf6, 0x65, 0x17, 0xa5, 0x6c, 0x70, 0x5e, 0x86, 0xb3, 0x0d, 0x6f, 0xd7, 0xed, 0x36, 0xae, 0xd1, 0x7d, 0x9c, 0x83, 0x9e, 0x63, 0x6c, 0x1b, 0xcf, 0x0c, 0xd9, 0x00, 0x6c, 0x9b, 0xc0, 0x1a, 0x2a, 0x7a, 0x71, 0xd6, 0x5d, 0xde, 0xee, 0xc3, 0x92, 0xec, 0xd3, 0xcb, 0xbb, 0xfa, 0xcc, 0x38, 0x80, 0x10, 0x00, 0x03, 0x84, 0x0e, 0xbe, 0x0c, 0x28, 0x3d, 0x9f, 0x4c, 0x5f, 0x36, 0x3f, 0xc7, 0x6c, 0x6f, 0xc7, 0xa1, 0xcd, 0xc2, 0x58, 0xb6, 0x23, 0x1e, 0xa2, 0xf9, 0x86, 0xc0, 0xc1, 0xcb, 0x00, 0xe1, 0x5c, 0xf4, 0xb6, 0x10, 0xdd, 0xd6, 0x75, 0xc4, 0xdb, 0xed, 0xb8, 0x39, 0xd8, 0x5f, 0xe6, 0x99, 0x21, 0xdd, 0x69, 0x4f, 0x98, 0x05, 0xd0, 0xbe, 0x56, 0xcf, 0xcb, 0xba, 0x2b, 0xc6, 0xd5, 0xc7, 0x78, 0xf8, 0x4a, 0xe3, 0x26, 0x16, 0x62, 0x79, 0xba, 0x99, 0xce, 0x3a, 0x55, 0xd2, 0x88, 0x4c, 0x13, 0x53, 0x26, 0x17, 0xdb, 0x2d, 0xae, 0x64, 0xc9, 0x8c, 0x6b, 0x8e, 0xcc, 0x62, 0xc2, 0x49, 0xaa, 0xbd, 0x2e, 0xb8, 0x1c, 0xa2, 0x6b, 0x15, 0xee, 0x82, 0xc9, 0xd2, 0x57, 0x13, 0x40, 0x96, 0x85, 0x72, 0x19, 0xfb, 0x1f, 0xc1, 0xeb, 0xcc, 0x70, 0xd6, 0x0c, 0xd1, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderRightHandle3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x78, 0x08, 0x06, 0x00, 0x00, 0x00, 0x5c, 0x89, 0xc4, 0xad, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x44, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4e, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x54, 0xdc, 0xcf, 0x00, 0x00, 0x01, 0x59, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x4c, 0xc2, 0x27, 0x59, 0x00, 0x00, 0x0a, 0x85, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xed, 0x9c, 0xdf, 0xab, 0x55, 0x5b, 0x15, 0xc7, 0x3d, 0x76, 0xac, 0xae, 0x5e, 0xf2, 0x66, 0x3f, 0x10, 0xcc, 0x20, 0xdf, 0xfc, 0x11, 0x41, 0x10, 0x17, 0xa9, 0x87, 0x5e, 0x82, 0x5e, 0xea, 0x41, 0x5f, 0xc4, 0xd4, 0xb7, 0x7a, 0x88, 0x08, 0x2e, 0xa1, 0xf8, 0x24, 0x5c, 0x2e, 0x8a, 0x44, 0x0f, 0x81, 0x7f, 0x41, 0x12, 0x78, 0xb1, 0x08, 0x12, 0x51, 0x1f, 0xa4, 0xa4, 0x30, 0xc9, 0xac, 0x38, 0x84, 0x60, 0x04, 0xf9, 0x23, 0xf3, 0x57, 0x1c, 0x6f, 0xde, 0xce, 0x3d, 0xf7, 0x78, 0xfc, 0x71, 0x1b, 0x9f, 0xb5, 0xf7, 0x77, 0x39, 0xf6, 0xdc, 0x73, 0xae, 0xbd, 0xf6, 0xda, 0xfb, 0x9c, 0xeb, 0x5e, 0x6b, 0x4d, 0x98, 0x7b, 0xfc, 0x98, 0x63, 0xcc, 0x35, 0xe7, 0xe7, 0x8c, 0xb9, 0xd7, 0xde, 0x47, 0xcf, 0x5a, 0xb1, 0xa2, 0x6d, 0x2d, 0x81, 0xe5, 0x24, 0x30, 0x65, 0x17, 0xa3, 0x4f, 0x7a, 0xfb, 0x60, 0xb9, 0x37, 0x50, 0x17, 0x70, 0x21, 0xb7, 0x25, 0x07, 0xe9, 0xc1, 0xbd, 0xac, 0x95, 0x37, 0x2a, 0x84, 0x51, 0xf3, 0xc3, 0x1f, 0x4a, 0x66, 0x03, 0x6b, 0x65, 0x74, 0x24, 0xed, 0x1c, 0x17, 0xe0, 0x51, 0x36, 0x54, 0x25, 0xb7, 0x4a, 0x4e, 0x92, 0x82, 0x07, 0x37, 0x2e, 0x20, 0xc9, 0x8b, 0x8d, 0x30, 0x30, 0x68, 0xd3, 0x83, 0xc6, 0xfd, 0xa5, 0x87, 0x89, 0xf5, 0x79, 0x3d, 0x3a, 0xb0, 0x3e, 0xde, 0xe3, 0xe9, 0x18, 0x1f, 0x26, 0x44, 0x6d, 0x0c, 0xf9, 0xbc, 0xdb, 0xc3, 0x25, 0x2a, 0xa6, 0xac, 0x3f, 0x8c, 0xc3, 0x4e, 0xcd, 0x11, 0x8b, 0xed, 0xf3, 0x4d, 0x9b, 0xe7, 0x63, 0xd6, 0x43, 0x50, 0xa1, 0xdd, 0x97, 0xb8, 0x0c, 0x0e, 0x36, 0xe6, 0xfb, 0x33, 0xb3, 0xe9, 0x4f, 0xad, 0xfb, 0xf5, 0x79, 0x00, 0x29, 0xbf, 0xa5, 0xf4, 0x35, 0x62, 0x7d, 0x6e, 0x5f, 0x40, 0x91, 0x63, 0xea, 0x03, 0x6b, 0x45, 0x01, 0xcb, 0x3d, 0x66, 0xcb, 0x59, 0xb4, 0x3e, 0x67, 0xfd, 0x9d, 0x27, 0x4f, 0x9e, 0xfc, 0xf3, 0xf1, 0xe3, 0xc7, 0xff, 0xb8, 0x7f, 0xff, 0xfe, 0xef, 0xf7, 0xef, 0xdf, 0xff, 0xbb, 0x53, 0xa7, 0x4e, 0xbd, 0x6f, 0xeb, 0xa1, 0x0a, 0x81, 0xb7, 0xd8, 0xd5, 0x4d, 0xe4, 0x2d, 0xdc, 0x4b, 0x68, 0xe7, 0x81, 0x4e, 0x29, 0x13, 0xe3, 0xc2, 0xbb, 0xaa, 0x2d, 0x70, 0x22, 0xda, 0xf3, 0xe7, 0xcf, 0xdf, 0x9b, 0x9b, 0x9b, 0x3b, 0x7d, 0xe3, 0xc6, 0x8d, 0xef, 0x1f, 0x3c, 0x78, 0x70, 0x93, 0x2d, 0xff, 0x35, 0xeb, 0x6b, 0xac, 0xaf, 0xb2, 0xce, 0xc9, 0xf9, 0x88, 0xeb, 0xdc, 0xf0, 0x7c, 0xa7, 0xba, 0x8a, 0xba, 0x0d, 0x0f, 0xd9, 0x26, 0x82, 0x5a, 0xb0, 0x48, 0x83, 0xf8, 0xee, 0xbd, 0x7b, 0xf7, 0x0e, 0xef, 0xd8, 0xb1, 0xe3, 0x73, 0xb6, 0xdd, 0x4f, 0x5a, 0x07, 0x20, 0xf0, 0x96, 0x0f, 0x60, 0xb0, 0xa6, 0x89, 0x32, 0x9f, 0x3d, 0x7b, 0x76, 0xf7, 0xfa, 0xf5, 0xeb, 0x6f, 0xac, 0x5f, 0xbf, 0xfe, 0x33, 0x06, 0x6d, 0xad, 0x75, 0xde, 0xaf, 0x43, 0x80, 0xbe, 0xf2, 0xd0, 0x8b, 0x2a, 0x8f, 0xb1, 0x72, 0x6d, 0xa2, 0x48, 0x25, 0x16, 0xbb, 0xb0, 0xb0, 0xf0, 0x97, 0x63, 0xc7, 0x8e, 0x7d, 0xd1, 0x76, 0x4c, 0xf5, 0xbd, 0x62, 0x3d, 0x84, 0xc7, 0x31, 0xf6, 0x00, 0x8b, 0xe0, 0x59, 0x68, 0x89, 0x96, 0x58, 0xcb, 0xc4, 0xb9, 0x9f, 0x3e, 0x7d, 0x7a, 0xef, 0xfc, 0xf9, 0xf3, 0xdf, 0xb4, 0x2d, 0x7f, 0xca, 0xba, 0x7f, 0xef, 0xf3, 0xc7, 0x77, 0x6c, 0xf0, 0x5e, 0xba, 0xbb, 0x6a, 0x89, 0x9f, 0x75, 0x32, 0xc4, 0x7e, 0xda, 0x0b, 0xd7, 0xae, 0x5d, 0xfb, 0xd1, 0x96, 0x2d, 0x5b, 0x4e, 0x5a, 0xd0, 0x82, 0xf5, 0xc7, 0xd6, 0x75, 0xd7, 0x0c, 0x25, 0xf3, 0xc8, 0x87, 0x1e, 0xb6, 0xa2, 0xb1, 0x15, 0xb5, 0x02, 0xa7, 0x9d, 0x5f, 0xbd, 0x7a, 0xf5, 0x07, 0xdb, 0xb6, 0x6d, 0xfb, 0x85, 0xd9, 0xef, 0x59, 0xe7, 0x63, 0x0b, 0x0d, 0x10, 0x82, 0x21, 0x29, 0x3f, 0x32, 0xd6, 0x7c, 0x5c, 0xcf, 0x38, 0xa5, 0x5b, 0xbb, 0x66, 0x15, 0xf7, 0x93, 0xd3, 0xa7, 0x4f, 0x7f, 0xc5, 0x36, 0xa6, 0xbb, 0x2d, 0x7b, 0xd4, 0xfb, 0x9a, 0x74, 0x24, 0xad, 0xfc, 0x0d, 0xa1, 0x13, 0xdf, 0x79, 0x9d, 0xb8, 0x37, 0xb3, 0x92, 0x0b, 0xe6, 0x3d, 0xef, 0xf0, 0xe1, 0xc3, 0x5f, 0xb6, 0x5d, 0x7e, 0xda, 0x3a, 0x5f, 0x2b, 0xf9, 0xbc, 0x17, 0x7e, 0xe6, 0x2b, 0xf3, 0x9e, 0x67, 0x69, 0xfd, 0xad, 0x96, 0x47, 0x55, 0xdb, 0x9c, 0x9f, 0x9f, 0x9f, 0xd9, 0xb8, 0x71, 0xe3, 0xb7, 0x1e, 0x3e, 0x7c, 0xf8, 0x3f, 0xf3, 0xe9, 0x5b, 0x07, 0xc3, 0xc3, 0x1c, 0xdb, 0xe8, 0x71, 0xad, 0xe5, 0x51, 0x15, 0xb8, 0xd5, 0xab, 0x57, 0x7f, 0xe9, 0xc2, 0x85, 0x0b, 0xdf, 0x31, 0x7b, 0xb5, 0x75, 0xee, 0xae, 0xda, 0x6f, 0xea, 0xd8, 0x2a, 0xd5, 0xcb, 0xe8, 0x51, 0xd6, 0x44, 0x3e, 0xb0, 0x56, 0xfa, 0xe6, 0xcd, 0x9b, 0xdf, 0xd8, 0xbe, 0x7d, 0xfb, 0x27, 0x6c, 0x53, 0xc0, 0x63, 0xbf, 0x1e, 0x84, 0xd7, 0xd9, 0x77, 0x68, 0xe3, 0x8b, 0xb6, 0xda, 0x83, 0x9b, 0x9e, 0x9e, 0xfe, 0xec, 0xf1, 0xe3, 0xc7, 0xbf, 0x6b, 0xbb, 0xe7, 0x7d, 0x4e, 0x55, 0x07, 0x20, 0x0f, 0xc9, 0xeb, 0x31, 0x50, 0x7d, 0xe3, 0xb5, 0x07, 0x07, 0x85, 0x4d, 0x9b, 0x36, 0x7d, 0x6f, 0xe7, 0xce, 0x9d, 0xdc, 0x24, 0xa8, 0x3a, 0xbe, 0x45, 0x08, 0x9c, 0xa4, 0xb9, 0x72, 0x90, 0x7d, 0x90, 0x18, 0x0c, 0x5b, 0x23, 0xc0, 0xad, 0x5c, 0xb9, 0xf2, 0xd5, 0x03, 0x07, 0x0e, 0x7c, 0xc3, 0x36, 0x4f, 0xd5, 0x09, 0x9c, 0x67, 0x11, 0xc2, 0x0a, 0x6d, 0x1f, 0x9b, 0xe9, 0x8d, 0x00, 0xc7, 0x4e, 0xad, 0xea, 0x00, 0xc7, 0x51, 0xe5, 0x17, 0x01, 0xec, 0x5b, 0xd5, 0xe6, 0x21, 0x79, 0xdd, 0x42, 0x7a, 0x5a, 0xcf, 0x58, 0x63, 0xc0, 0xad, 0x5b, 0xb7, 0xee, 0xab, 0x5b, 0xb7, 0x6e, 0x7d, 0xd5, 0x50, 0x7c, 0xd4, 0xba, 0xbe, 0xf4, 0x0b, 0x06, 0x52, 0x7a, 0x0f, 0xad, 0x94, 0xd1, 0x18, 0x70, 0x76, 0x5c, 0x5f, 0x39, 0x7a, 0xf4, 0xe8, 0xd7, 0x0c, 0x04, 0x15, 0x27, 0x70, 0x70, 0x09, 0x81, 0xc9, 0x96, 0x24, 0xa6, 0xaf, 0x35, 0x06, 0x1c, 0x3b, 0xb7, 0x8f, 0x26, 0xaf, 0x9b, 0xe0, 0xb8, 0xd2, 0x01, 0x13, 0xeb, 0xe6, 0x1e, 0xdc, 0x1a, 0x05, 0x6e, 0xed, 0xda, 0xb5, 0x5f, 0x30, 0x24, 0x54, 0x1b, 0x5f, 0xbd, 0xd8, 0x7b, 0xaa, 0xaa, 0xe4, 0x97, 0xb4, 0xd0, 0xac, 0xe5, 0x76, 0xa3, 0xc0, 0xad, 0x59, 0xb3, 0xe6, 0xf3, 0xb6, 0x7d, 0xaa, 0x4d, 0x47, 0x15, 0x10, 0x39, 0x8c, 0x0c, 0x4d, 0xc9, 0x17, 0x26, 0x69, 0x4c, 0x5b, 0xb5, 0x6a, 0x15, 0xdf, 0x20, 0x80, 0xc6, 0xbe, 0x7d, 0xc5, 0x85, 0xf0, 0xa2, 0xdf, 0x4f, 0x3d, 0xa8, 0x46, 0x55, 0x9c, 0xdd, 0x20, 0xf8, 0x35, 0x13, 0x7b, 0x06, 0x9e, 0xaa, 0x4d, 0xd2, 0x73, 0x19, 0xa8, 0x37, 0x0d, 0x1c, 0xef, 0x6d, 0x80, 0x62, 0xdf, 0x74, 0x5f, 0x69, 0x5e, 0xb7, 0xa1, 0x7c, 0x2c, 0xf4, 0x33, 0x96, 0x25, 0x67, 0x4a, 0x83, 0x5e, 0x04, 0x4c, 0x00, 0x3d, 0x18, 0x74, 0xf5, 0x42, 0x24, 0x8d, 0xaa, 0xb8, 0x2e, 0x09, 0x81, 0xf1, 0x7b, 0xf7, 0xf0, 0x0a, 0x81, 0x69, 0xd0, 0x27, 0xcb, 0xd7, 0x04, 0x29, 0x78, 0x92, 0xec, 0x79, 0x28, 0x78, 0x4d, 0x04, 0xe7, 0x61, 0xa9, 0x48, 0x04, 0x4d, 0x32, 0xf4, 0xcb, 0xce, 0x65, 0x13, 0xc1, 0xe5, 0x9b, 0xef, 0x2a, 0xa5, 0x61, 0xf9, 0xc4, 0x26, 0x82, 0x53, 0xc5, 0x49, 0x8a, 0x87, 0x07, 0x58, 0x34, 0x96, 0xc5, 0x37, 0x11, 0x9c, 0x40, 0x8d, 0x24, 0x5b, 0x70, 0x1d, 0x7c, 0xbe, 0xda, 0x4a, 0x01, 0x6d, 0x22, 0x38, 0x7f, 0x0c, 0xbd, 0x0e, 0xb0, 0xd2, 0x00, 0x9b, 0x08, 0x4e, 0x15, 0x55, 0x1a, 0x92, 0x12, 0xbc, 0x6c, 0x32, 0x38, 0xcf, 0x61, 0x68, 0xbd, 0x05, 0x37, 0x34, 0xb2, 0x4e, 0x42, 0x0b, 0xee, 0x05, 0xb8, 0xa1, 0x8e, 0x6e, 0x0b, 0xee, 0x05, 0xb8, 0x94, 0x16, 0x05, 0xda, 0x82, 0x4b, 0xe1, 0x1a, 0xe0, 0x6f, 0xc1, 0xf5, 0x03, 0x8a, 0x56, 0x58, 0x18, 0xd6, 0x44, 0x70, 0x31, 0x30, 0x31, 0x5f, 0xc8, 0xaa, 0xc7, 0x6e, 0x22, 0xb8, 0x1e, 0x00, 0x55, 0x8d, 0x16, 0x5c, 0x45, 0x72, 0x2d, 0xb8, 0x16, 0x5c, 0x45, 0x02, 0x15, 0xd3, 0xda, 0x8a, 0x6b, 0xc1, 0x55, 0x24, 0x50, 0x31, 0xad, 0xad, 0xb8, 0x16, 0x5c, 0x45, 0x02, 0x15, 0xd3, 0xda, 0x8a, 0x6b, 0xc1, 0x55, 0x24, 0x50, 0x31, 0xad, 0xad, 0xb8, 0x16, 0x5c, 0x45, 0x02, 0x15, 0xd3, 0xda, 0x8a, 0x6b, 0xc1, 0x55, 0x24, 0x50, 0x31, 0xad, 0xad, 0xb8, 0x16, 0x5c, 0x45, 0x02, 0x15, 0xd3, 0xda, 0x8a, 0x6b, 0xc1, 0x55, 0x24, 0x50, 0x31, 0xad, 0x51, 0x15, 0x67, 0x4f, 0xc8, 0x79, 0xd2, 0xe5, 0x34, 0xf0, 0x7f, 0x95, 0x0f, 0xe2, 0xd9, 0x28, 0x70, 0xf6, 0x64, 0x9c, 0x79, 0x03, 0x12, 0x83, 0x16, 0xf3, 0x15, 0xb2, 0x6b, 0x14, 0xb8, 0xc5, 0xc5, 0x45, 0xfe, 0x36, 0x9f, 0x56, 0x04, 0xaa, 0x68, 0xac, 0x93, 0x6d, 0xaf, 0x8d, 0x02, 0x67, 0x4f, 0x03, 0xfb, 0x57, 0x77, 0xe7, 0x3c, 0x4a, 0x6d, 0xa4, 0xd6, 0x28, 0x70, 0xb3, 0xb3, 0xb3, 0xb7, 0x8c, 0x16, 0x15, 0x05, 0xb8, 0x52, 0x95, 0x95, 0x8a, 0x6b, 0x14, 0xb8, 0x2b, 0x57, 0xae, 0xfc, 0xd5, 0x41, 0x03, 0x9c, 0x87, 0xe7, 0x75, 0x1b, 0x2a, 0x6e, 0xb5, 0x7e, 0xee, 0x88, 0xdf, 0xba, 0xdd, 0x18, 0x16, 0xec, 0xaf, 0xa4, 0xbf, 0x7d, 0xeb, 0xd6, 0xad, 0x87, 0xe6, 0xff, 0xaf, 0x75, 0x1e, 0x1f, 0xc4, 0xb3, 0x97, 0x78, 0xfa, 0x21, 0x15, 0xa8, 0x2a, 0x94, 0x14, 0x58, 0x01, 0xed, 0x91, 0x8d, 0xa9, 0xb8, 0x3b, 0x77, 0xee, 0xfc, 0xc9, 0xa0, 0xf1, 0xa0, 0x2a, 0x9e, 0xb3, 0xe9, 0xe1, 0x98, 0x39, 0x7c, 0x6b, 0x0c, 0x38, 0x3b, 0xa6, 0x7f, 0x30, 0x3c, 0x54, 0x0d, 0x9f, 0xe5, 0x54, 0x61, 0xc3, 0x13, 0xeb, 0x66, 0x34, 0x02, 0x9c, 0x3d, 0x67, 0x69, 0xfe, 0xc8, 0x91, 0x23, 0x80, 0xd3, 0x93, 0x5d, 0x55, 0x71, 0x92, 0x02, 0xa8, 0xe3, 0x28, 0x3b, 0x29, 0x1b, 0x01, 0xee, 0xf2, 0xe5, 0xcb, 0x6f, 0x5b, 0xc5, 0xbd, 0x6b, 0x14, 0xc2, 0xa3, 0x2a, 0x30, 0xa5, 0x81, 0x29, 0xa1, 0xf6, 0xe0, 0xec, 0x31, 0x90, 0xb3, 0xbb, 0x77, 0xef, 0xfe, 0x95, 0x6d, 0x98, 0x23, 0xca, 0x8d, 0x40, 0x55, 0x37, 0x34, 0x2c, 0x41, 0x43, 0xd6, 0x1e, 0x9c, 0x3d, 0xfe, 0xf1, 0x67, 0x37, 0x6f, 0xde, 0xe4, 0xab, 0x16, 0x77, 0x50, 0x3d, 0x80, 0xd9, 0xdf, 0x20, 0xe0, 0x40, 0xf3, 0x20, 0xd1, 0x43, 0x3b, 0x0b, 0xd2, 0x4b, 0xad, 0x3f, 0x8e, 0xd8, 0x07, 0xde, 0xbf, 0x6f, 0xd8, 0xb0, 0xe1, 0x87, 0xf6, 0x10, 0x66, 0xc0, 0xf1, 0x75, 0x8b, 0x8f, 0x20, 0x3c, 0x0e, 0x8d, 0x23, 0x9b, 0xfa, 0x18, 0xa2, 0xf7, 0x3d, 0x81, 0x8b, 0x42, 0xac, 0x6d, 0xc5, 0x71, 0x44, 0xf7, 0xed, 0xdb, 0xf7, 0xa6, 0x41, 0xe3, 0x88, 0x0a, 0x94, 0x8e, 0x2b, 0x70, 0x7c, 0xf3, 0x90, 0x62, 0x7e, 0xef, 0xcb, 0xf4, 0x5a, 0x82, 0xb3, 0x0f, 0xbb, 0x8b, 0xf6, 0xb4, 0xc2, 0x37, 0xcf, 0x9c, 0x39, 0xf3, 0x1f, 0xdb, 0xa5, 0xa0, 0xf1, 0xac, 0x4c, 0x7f, 0x44, 0x55, 0x49, 0x29, 0x68, 0x7d, 0xb0, 0xbc, 0xa3, 0x96, 0x47, 0xf5, 0xc4, 0x89, 0x13, 0x3f, 0xb6, 0x1b, 0xc2, 0x6f, 0x6c, 0xa3, 0x1c, 0x4b, 0x8e, 0xa9, 0x3a, 0x36, 0x55, 0xe7, 0x01, 0x52, 0x7d, 0xfe, 0x78, 0x7a, 0xa0, 0x31, 0xa8, 0x99, 0xaf, 0x56, 0x8f, 0xcf, 0xa0, 0xd2, 0x4e, 0x9e, 0x3c, 0xf9, 0xd3, 0x2e, 0x34, 0x2a, 0x8d, 0xce, 0x4d, 0x81, 0x0e, 0x30, 0x41, 0x32, 0x35, 0x6b, 0x82, 0x14, 0xda, 0x02, 0x26, 0x7f, 0x9f, 0xac, 0xcd, 0x51, 0xe5, 0x3d, 0xed, 0xd0, 0xa1, 0x43, 0x07, 0x0c, 0xda, 0x6f, 0x6d, 0x97, 0x1c, 0x4b, 0x60, 0x21, 0xe9, 0xdc, 0x08, 0x74, 0x33, 0x30, 0x35, 0xbb, 0x63, 0xc6, 0xa0, 0x31, 0xa6, 0x26, 0x78, 0x92, 0xf2, 0x67, 0xb2, 0x16, 0x15, 0xc7, 0xdd, 0x73, 0xef, 0xde, 0xbd, 0x6f, 0x9d, 0x3d, 0x7b, 0x76, 0xd6, 0x76, 0xa5, 0x0a, 0x13, 0x3c, 0x01, 0xf4, 0xc7, 0xd3, 0xc3, 0x08, 0x01, 0xf6, 0x00, 0x4a, 0x19, 0x13, 0x0d, 0x8e, 0x2a, 0x3b, 0x77, 0xee, 0xdc, 0xcf, 0x77, 0xed, 0xda, 0x75, 0xde, 0xee, 0x9e, 0x54, 0x94, 0xde, 0xc3, 0x04, 0x8d, 0xa3, 0x8a, 0x1e, 0x42, 0x13, 0x2c, 0x01, 0xf4, 0xb6, 0x7c, 0x29, 0x66, 0x99, 0x7f, 0x22, 0xc1, 0xf1, 0xdd, 0xf3, 0xe2, 0xc5, 0x8b, 0xbf, 0xdc, 0xb3, 0x67, 0xcf, 0xaf, 0x6f, 0xdf, 0xbe, 0xad, 0xbb, 0x26, 0x92, 0xf7, 0x31, 0x40, 0xa1, 0xcb, 0x06, 0x28, 0xe0, 0x3c, 0x1c, 0x33, 0xf3, 0x0f, 0xb8, 0xa5, 0x40, 0x91, 0xe0, 0xdb, 0xc4, 0x80, 0xe3, 0xf7, 0x69, 0x06, 0xe9, 0xcf, 0x97, 0x2e, 0x5d, 0xba, 0x6c, 0xcf, 0x81, 0xfb, 0xe3, 0xcc, 0xcc, 0xcc, 0x9c, 0x6d, 0x04, 0x28, 0xc0, 0xa2, 0xa3, 0xeb, 0x98, 0xaa, 0xf2, 0xf0, 0x17, 0x55, 0x5b, 0x0a, 0x9a, 0xfc, 0x92, 0x36, 0x4d, 0xd6, 0x72, 0xfb, 0xa5, 0x03, 0xc7, 0x3f, 0xe1, 0x59, 0x45, 0xbd, 0x6f, 0x47, 0x6f, 0xee, 0xd1, 0xa3, 0x47, 0xff, 0x7e, 0xf0, 0xe0, 0xc1, 0x6d, 0x83, 0xf5, 0x37, 0xfb, 0x5c, 0x36, 0x73, 0xf7, 0xee, 0x5d, 0xaa, 0x89, 0xc5, 0x73, 0x77, 0x44, 0x57, 0x35, 0x01, 0x48, 0xd0, 0x54, 0x69, 0x82, 0x46, 0x7c, 0xac, 0x9b, 0x3b, 0xe9, 0x67, 0xac, 0xb0, 0x4d, 0x4f, 0x4d, 0x4d, 0x7d, 0xdd, 0x22, 0x78, 0xe6, 0x10, 0x8d, 0xbb, 0x2c, 0x9d, 0x3f, 0xd1, 0xd1, 0x9f, 0xe9, 0x84, 0xba, 0x0d, 0xe5, 0x63, 0xe8, 0xa3, 0xb6, 0xfc, 0xa7, 0x18, 0x99, 0x48, 0x63, 0x48, 0x60, 0x09, 0x14, 0x55, 0x44, 0xc7, 0x56, 0xa5, 0x01, 0x52, 0x5d, 0xe3, 0xfe, 0xf3, 0x99, 0xd7, 0x05, 0xd2, 0x52, 0xf2, 0x23, 0x8b, 0x4e, 0xf3, 0xd7, 0xec, 0x78, 0x22, 0xaf, 0x54, 0x1c, 0x3f, 0x19, 0x1a, 0xc0, 0x98, 0xdc, 0x83, 0xf3, 0xf0, 0x88, 0x91, 0x1d, 0xea, 0xd8, 0x55, 0x9a, 0x16, 0xe9, 0x73, 0xe5, 0x43, 0xb2, 0x1e, 0xbf, 0x61, 0x74, 0x41, 0x61, 0xdd, 0xea, 0x00, 0x93, 0xae, 0x71, 0xf2, 0x35, 0x87, 0x74, 0x24, 0x2d, 0x65, 0x77, 0x46, 0x4b, 0xbc, 0x02, 0x8e, 0x8b, 0x72, 0x31, 0x1e, 0x0d, 0x06, 0x34, 0x9a, 0xaa, 0xcc, 0x4b, 0xf9, 0xb3, 0x80, 0x25, 0x7c, 0x89, 0x6d, 0x4e, 0xf0, 0x54, 0x71, 0x48, 0x81, 0x92, 0x64, 0x0f, 0x02, 0xcb, 0x1c, 0xca, 0xf1, 0x90, 0x52, 0xba, 0xb6, 0xa3, 0x6b, 0xcb, 0xf6, 0xb2, 0x67, 0x0c, 0x70, 0xbc, 0x37, 0x20, 0xc3, 0x4a, 0xf3, 0xd0, 0xc6, 0x5d, 0x69, 0x7e, 0x41, 0xa1, 0x1e, 0x6e, 0x4e, 0x00, 0x04, 0x0d, 0x40, 0xc0, 0x42, 0x7a, 0x1f, 0xb6, 0x72, 0x95, 0x23, 0x29, 0xbf, 0xa4, 0x85, 0xf6, 0x34, 0xfc, 0xbe, 0x85, 0xb6, 0x1f, 0xcb, 0x74, 0x80, 0x71, 0x07, 0x42, 0x86, 0xa0, 0xbc, 0x4d, 0xf0, 0x52, 0xc2, 0xf3, 0x0b, 0x95, 0xee, 0x37, 0x0d, 0x14, 0x3a, 0x3e, 0xe9, 0xb2, 0x7d, 0x5c, 0x19, 0x9d, 0xf9, 0xc3, 0x6e, 0xae, 0xcc, 0xe7, 0x25, 0x7a, 0xb2, 0x09, 0x5c, 0xf8, 0x04, 0x3f, 0x12, 0xc2, 0x63, 0x8b, 0xcf, 0xc3, 0xc3, 0x5e, 0x8a, 0xa6, 0x4d, 0x31, 0xb7, 0x40, 0x78, 0x89, 0xae, 0xae, 0xd8, 0x50, 0x2a, 0x1e, 0xbf, 0xd7, 0xc3, 0x38, 0xae, 0x41, 0xc3, 0x5f, 0xd4, 0xfa, 0xc6, 0x01, 0xc7, 0x51, 0x05, 0x48, 0xd1, 0x51, 0xd5, 0xa4, 0xcb, 0x05, 0x8e, 0xeb, 0x85, 0x9b, 0xf4, 0x76, 0x38, 0x9e, 0x82, 0x93, 0xf2, 0x6b, 0x3f, 0x21, 0x90, 0xd0, 0x56, 0x5c, 0x9f, 0x04, 0x1c, 0xef, 0x13, 0x00, 0x09, 0xe1, 0x11, 0x2c, 0xbf, 0x74, 0x24, 0x6d, 0x1c, 0x00, 0x53, 0x8b, 0xf4, 0x7e, 0xe9, 0x5e, 0x86, 0x3a, 0xb6, 0x7a, 0x0a, 0x94, 0xc6, 0xc3, 0x5c, 0xf6, 0x22, 0x1f, 0x7a, 0xac, 0x45, 0xc7, 0x01, 0xe7, 0x07, 0xb8, 0xb0, 0x60, 0x21, 0x19, 0x13, 0x24, 0x49, 0x73, 0xe5, 0x3e, 0xf4, 0x2a, 0xcd, 0x5f, 0x33, 0x96, 0xef, 0xc7, 0xd1, 0x65, 0x7b, 0x29, 0xff, 0x30, 0x92, 0x6b, 0x29, 0x5e, 0x3a, 0x92, 0xa6, 0xb9, 0x3b, 0xd6, 0x80, 0x57, 0xc0, 0x01, 0x8b, 0x63, 0x4a, 0xa2, 0x87, 0x26, 0x50, 0xa1, 0xb4, 0xb0, 0x91, 0xc1, 0x31, 0x47, 0x6a, 0xa1, 0xde, 0x2f, 0x3d, 0x26, 0xf1, 0xc5, 0xba, 0xe6, 0x0e, 0xc7, 0xbc, 0x5f, 0x3a, 0x92, 0xa6, 0xf9, 0x3b, 0xd6, 0x8b, 0xd7, 0x94, 0x3f, 0xbb, 0x9b, 0x32, 0xa8, 0x4a, 0x53, 0x0a, 0xbe, 0x10, 0x98, 0x6c, 0xc5, 0x2c, 0x95, 0xf4, 0x8b, 0x95, 0x1e, 0x93, 0xf8, 0xbc, 0x5f, 0xb6, 0x24, 0xeb, 0x2b, 0xd2, 0xb5, 0x7e, 0xcd, 0x21, 0xbb, 0x94, 0xf4, 0x47, 0x95, 0x09, 0x80, 0xa3, 0x2e, 0x5b, 0x13, 0x7f, 0x98, 0xe0, 0xd8, 0x0c, 0xeb, 0xd0, 0x5a, 0xbc, 0x0c, 0xfd, 0x31, 0x5b, 0xf9, 0x5e, 0x86, 0x3a, 0xb6, 0x6f, 0xba, 0x86, 0xf7, 0xe5, 0xba, 0xc0, 0x11, 0x24, 0x30, 0x5e, 0x97, 0x0f, 0x59, 0x38, 0x51, 0x3e, 0xe3, 0xe8, 0x8a, 0xbf, 0x4e, 0x4c, 0xc7, 0x27, 0xbf, 0x97, 0x29, 0x9d, 0x15, 0xf9, 0x31, 0xad, 0x50, 0x3e, 0xd9, 0x5e, 0x16, 0x8d, 0x65, 0x71, 0x7a, 0x8f, 0x13, 0x20, 0x12, 0x04, 0x49, 0x92, 0x40, 0x8d, 0x67, 0x49, 0x4b, 0xfc, 0x12, 0x2e, 0x5a, 0x76, 0x19, 0x99, 0x8a, 0x61, 0xc9, 0x1a, 0x0b, 0xf5, 0x70, 0x3b, 0x3e, 0x2e, 0x1c, 0xcb, 0x6d, 0x55, 0x9c, 0x26, 0x13, 0x2c, 0x49, 0xfc, 0x5e, 0xcf, 0x13, 0xbb, 0x7e, 0x6f, 0x0f, 0xd2, 0x4b, 0x2d, 0xa8, 0x3b, 0x49, 0x18, 0x2b, 0x5b, 0x92, 0x30, 0xf4, 0xd0, 0x96, 0x3f, 0x26, 0xf1, 0xd1, 0x7c, 0x4e, 0xc7, 0x53, 0xe1, 0xd5, 0x83, 0xf3, 0xe9, 0x4c, 0xae, 0x2a, 0x0b, 0x2f, 0x94, 0xf2, 0xfb, 0xfc, 0xb2, 0x7a, 0x38, 0x77, 0x98, 0xe7, 0xc7, 0x63, 0x7a, 0x59, 0x9f, 0xe6, 0xf5, 0xf1, 0xf2, 0x79, 0x39, 0x68, 0x3c, 0x8f, 0x0d, 0xc1, 0x91, 0x18, 0x82, 0x91, 0xad, 0xa4, 0xd2, 0x93, 0x2b, 0x61, 0x48, 0x19, 0x9b, 0xdf, 0xfb, 0x86, 0xd5, 0xb9, 0xbc, 0xcf, 0x89, 0x2d, 0x67, 0xd0, 0x78, 0x5f, 0x4e, 0x08, 0x4e, 0x17, 0xf1, 0xb0, 0xc2, 0x49, 0xfd, 0x58, 0xdf, 0x84, 0x43, 0x3a, 0xc2, 0xb9, 0x63, 0xe9, 0x61, 0x8c, 0xb7, 0xbd, 0xae, 0xb5, 0xfb, 0x39, 0xc2, 0x71, 0x3f, 0x26, 0xbd, 0x4c, 0x8c, 0x62, 0x73, 0x19, 0x03, 0x97, 0x0f, 0x26, 0x94, 0x4a, 0x17, 0x4a, 0xcc, 0x35, 0xc8, 0x1d, 0xbb, 0x56, 0xe8, 0x0b, 0x6d, 0xcd, 0x99, 0xf2, 0x6b, 0x1c, 0x59, 0x26, 0xc6, 0xc7, 0xe7, 0x3a, 0xe0, 0x68, 0x95, 0x27, 0xe8, 0xa4, 0x2f, 0xd9, 0x6b, 0x6a, 0x5d, 0xc3, 0xfa, 0x63, 0x0b, 0x4c, 0xcd, 0x11, 0x8b, 0xed, 0xf3, 0xe9, 0xd7, 0x49, 0x0c, 0x8c, 0xf3, 0x08, 0xf6, 0x5d, 0x68, 0x44, 0xc7, 0xa0, 0x4d, 0x0e, 0x1a, 0xf7, 0x97, 0x1f, 0x26, 0xd6, 0xe7, 0xf5, 0xe8, 0x1e, 0x5c, 0xcf, 0x40, 0x81, 0x31, 0x2e, 0xc0, 0xa3, 0x6c, 0xa0, 0x4a, 0x6e, 0x95, 0x9c, 0x24, 0x06, 0xc0, 0xa9, 0x8d, 0x0b, 0x88, 0xe6, 0x1b, 0x97, 0x1c, 0x75, 0xc3, 0xa3, 0xe6, 0x47, 0xf7, 0x01, 0xac, 0x97, 0x15, 0x58, 0x74, 0xc1, 0x25, 0x9d, 0x4b, 0x02, 0xcb, 0x5f, 0x5b, 0x37, 0x07, 0xef, 0x9b, 0x44, 0x7d, 0xc9, 0x41, 0x4d, 0x22, 0x94, 0x76, 0xcd, 0x75, 0x22, 0xf0, 0x7f, 0x3a, 0xd5, 0x8c, 0xbe, 0x72, 0x32, 0xcf, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderTrack2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0x8e, 0x5f, 0x1f, 0x96, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x47, 0x49, 0x44, 0x41, 0x54, 0x18, 0x19, 0x63, 0x3c, 0x75, 0xea, 0xac, 0xc7, 0xbf, 0x7f, 0xff, 0x67, 0xff, 0xff, 0xcf, 0x20, 0xc3, 0x40, 0x01, 0x60, 0x64, 0x64, 0x78, 0xc2, 0xc0, 0xc0, 0x9c, 0xce, 0x78, 0xe2, 0xc4, 0x99, 0x47, 0x40, 0xc3, 0x64, 0x29, 0x30, 0x0b, 0xae, 0x15, 0x64, 0x28, 0x13, 0x90, 0xc7, 0x08, 0x17, 0xa1, 0x9c, 0xf1, 0x1f, 0x68, 0x20, 0xd0, 0x99, 0x60, 0xe7, 0x52, 0x66, 0x1a, 0xd8, 0x75, 0x4c, 0x8c, 0x69, 0x00, 0xf4, 0x9d, 0x13, 0x90, 0xe0, 0x80, 0xf3, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderTrack3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x08, 0x06, 0x00, 0x00, 0x00, 0xd4, 0xb5, 0x2e, 0x54, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x77, 0x49, 0x44, 0x41, 0x54, 0x28, 0x15, 0x63, 0x3c, 0x73, 0xe6, 0x0c, 0xeb, 0x9f, 0x3f, 0x0c, 0x4d, 0x0c, 0x0c, 0x0c, 0xf1, 0xff, 0xff, 0x33, 0x48, 0x02, 0x69, 0x9a, 0x01, 0x46, 0x46, 0x86, 0xe7, 0x0c, 0x0c, 0x8c, 0x4b, 0x78, 0x79, 0x39, 0x6a, 0x58, 0x40, 0x96, 0x02, 0x2d, 0xac, 0xa0, 0x99, 0x6d, 0x48, 0x06, 0x43, 0x3c, 0xf6, 0xbf, 0xf4, 0xd3, 0xa7, 0x1f, 0xff, 0x99, 0xfe, 0xff, 0x67, 0x8c, 0x43, 0x92, 0xa3, 0x0b, 0x93, 0x91, 0xf1, 0x7f, 0x2c, 0x13, 0x90, 0x60, 0xa4, 0x8b, 0x6d, 0x48, 0x96, 0x00, 0x3d, 0x0b, 0xf6, 0xf1, 0x62, 0x24, 0x31, 0xba, 0x30, 0x81, 0x9e, 0x5d, 0xc4, 0xc2, 0xc7, 0xc7, 0x51, 0xfb, 0xf9, 0xf3, 0x0f, 0xa0, 0xaf, 0xff, 0xc7, 0xd0, 0x27, 0x71, 0x31, 0x2c, 0x64, 0x61, 0x61, 0xa8, 0x03, 0x00, 0xad, 0xe3, 0x2a, 0xed, 0xfc, 0x71, 0x9f, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderFill2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0x8e, 0x5f, 0x1f, 0x96, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x47, 0x49, 0x44, 0x41, 0x54, 0x18, 0x19, 0x63, 0x64, 0xa8, 0xf9, 0xef, 0xc1, 0xf0, 0x8f, 0x61, 0x36, 0x03, 0x03, 0x83, 0x0c, 0x10, 0x53, 0x02, 0x9e, 0x30, 0x30, 0x31, 0xa4, 0x33, 0x32, 0x54, 0xff, 0x7f, 0xc4, 0xf0, 0x9f, 0x41, 0x96, 0x12, 0x93, 0x90, 0xf4, 0x3e, 0x61, 0x02, 0x1a, 0xc6, 0x88, 0x24, 0x40, 0x19, 0x93, 0x91, 0xe1, 0x3f, 0x13, 0xc8, 0x99, 0x40, 0x53, 0x9e, 0x50, 0x66, 0x12, 0x58, 0xf7, 0x13, 0xa0, 0xd3, 0xd2, 0x00, 0xbe, 0xcf, 0x0d, 0xc0, 0x10, 0xc9, 0x8a, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXRangeSliderFill3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x08, 0x06, 0x00, 0x00, 0x00, 0xd4, 0xb5, 0x2e, 0x54, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x6e, 0x49, 0x44, 0x41, 0x54, 0x28, 0x15, 0xbd, 0x92, 0xbb, 0x0d, 0x80, 0x30, 0x0c, 0x05, 0xcf, 0x51, 0x32, 0x04, 0x0d, 0x93, 0xf1, 0x99, 0x02, 0x89, 0x02, 0x28, 0xd8, 0x82, 0x92, 0xcf, 0x64, 0x34, 0x0c, 0x01, 0x92, 0x49, 0x28, 0xa2, 0x2c, 0x40, 0xdc, 0xf8, 0xc9, 0x96, 0x7c, 0x3a, 0xc9, 0xc2, 0xa2, 0x8e, 0x93, 0x19, 0x68, 0x51, 0x0a, 0xdf, 0xff, 0x2b, 0xe1, 0x42, 0x38, 0xb0, 0x0c, 0xf6, 0x83, 0x2a, 0xfd, 0x7f, 0xb4, 0xe4, 0x72, 0x10, 0x53, 0x3a, 0x6e, 0xd4, 0xf8, 0xd0, 0x24, 0xab, 0x5c, 0xb1, 0x36, 0x5e, 0x5d, 0x72, 0xd1, 0x22, 0x47, 0x83, 0x31, 0xec, 0x71, 0x90, 0x2b, 0x08, 0x9b, 0xc5, 0x31, 0xf2, 0x78, 0x6b, 0xa5, 0xca, 0xf2, 0x5c, 0xb0, 0x52, 0x32, 0xbd, 0x8a, 0x37, 0x19, 0x0d, 0x36, 0x20, 0x19, 0x81, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; #pragma mark - Misc Icons static const u_int8_t FLEXCheckerPattern[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x6c, 0xbb, 0xce, 0xa4, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x1a, 0x18, 0x18, 0xff, 0xff, 0xfd, 0x2c, 0xa7, 0x7c, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4e, 0x53, 0x80, 0x80, 0xa0, 0xa8, 0xd6, 0x53, 0x00, 0x00, 0x00, 0x11, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0xfe, 0xc0, 0x80, 0x86, 0xfe, 0x30, 0xa0, 0x21, 0x00, 0xcf, 0x3f, 0x0b, 0x9b, 0xd8, 0xd3, 0xac, 0x83, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXCheckerPattern2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x01, 0x03, 0x00, 0x00, 0x00, 0xda, 0xb9, 0xaf, 0xbb, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x1a, 0x18, 0x18, 0xff, 0xff, 0xfd, 0x2c, 0xa7, 0x7c, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4e, 0x53, 0x80, 0x80, 0xa0, 0xa8, 0xd6, 0x53, 0x00, 0x00, 0x00, 0x14, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0xe0, 0xff, 0xcf, 0x40, 0x0a, 0xfe, 0xff, 0x81, 0x81, 0x14, 0x0c, 0x00, 0xbc, 0xcb, 0x23, 0xdd, 0xc8, 0xaf, 0xa2, 0x74, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXCheckerPattern3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x00, 0x00, 0x01, 0x20, 0x05, 0xc9, 0x11, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x38, 0x65, 0x58, 0x49, 0x66, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x4e, 0x36, 0x00, 0x00, 0x01, 0x16, 0x49, 0x44, 0x41, 0x54, 0x68, 0x05, 0xed, 0x98, 0x51, 0x0e, 0xc2, 0x30, 0x0c, 0x43, 0x57, 0xae, 0x0c, 0x5c, 0x82, 0x8d, 0x33, 0x8f, 0x7d, 0x6c, 0x51, 0x40, 0xb2, 0x88, 0x51, 0x2d, 0x56, 0xc9, 0xfb, 0xb2, 0x42, 0x9a, 0x34, 0xcf, 0x65, 0x63, 0xb4, 0xe5, 0x31, 0xaf, 0xd3, 0x7e, 0x5d, 0xef, 0xb7, 0x43, 0x4e, 0x97, 0x50, 0x1f, 0x02, 0x7e, 0xd0, 0xd6, 0xed, 0x3a, 0x92, 0x9f, 0xf3, 0x72, 0xc8, 0x5f, 0x4a, 0x9d, 0x7f, 0x57, 0x31, 0xdd, 0x26, 0x32, 0xb7, 0x1c, 0x87, 0xa8, 0x72, 0x52, 0xd6, 0xf4, 0x82, 0x37, 0xe6, 0xb9, 0x52, 0xe6, 0x9f, 0xe3, 0x74, 0x07, 0x7a, 0x41, 0xcb, 0xde, 0xe5, 0xd6, 0xff, 0xa3, 0x44, 0xcf, 0xa0, 0x5f, 0x70, 0x42, 0xe3, 0xf4, 0x43, 0xd3, 0x1d, 0xe0, 0x59, 0xca, 0xe7, 0x2a, 0x6b, 0x74, 0xc6, 0x72, 0x4e, 0xd6, 0xf4, 0x8e, 0xf2, 0xe2, 0x8a, 0x76, 0x83, 0xaf, 0x94, 0xe4, 0x88, 0xe0, 0x77, 0x0d, 0x6d, 0x0d, 0xdd, 0x3c, 0x51, 0xbe, 0x7c, 0x02, 0x37, 0x40, 0xe8, 0x23, 0x2e, 0x47, 0xe4, 0x7b, 0x51, 0xb0, 0x46, 0x42, 0xee, 0x81, 0x1b, 0x20, 0xf4, 0x11, 0x37, 0xa2, 0x40, 0x81, 0xc4, 0xf8, 0x88, 0xfc, 0x44, 0x43, 0xde, 0x46, 0x7c, 0x7c, 0x93, 0x3d, 0x41, 0x98, 0x89, 0x84, 0x11, 0x21, 0x32, 0x11, 0x97, 0x23, 0xa2, 0x7f, 0x17, 0xc5, 0xd6, 0x8a, 0x82, 0x7d, 0xa7, 0x2b, 0x96, 0x8d, 0x34, 0x39, 0xa1, 0xe8, 0x24, 0x12, 0x1e, 0x40, 0x04, 0xb6, 0x5c, 0xd6, 0x0e, 0x94, 0x51, 0x89, 0x12, 0xed, 0x80, 0x08, 0x6c, 0xb9, 0xac, 0x1d, 0x28, 0xa3, 0x12, 0x25, 0xda, 0x01, 0x11, 0xd8, 0x72, 0x59, 0xfa, 0xbd, 0xac, 0x5c, 0x79, 0x4f, 0x64, 0xff, 0x99, 0x64, 0xeb, 0xfb, 0x08, 0xb1, 0xc4, 0x7a, 0xe7, 0xdb, 0x81, 0xde, 0x44, 0xd9, 0x7a, 0x76, 0x80, 0x25, 0xd6, 0x3b, 0xdf, 0x0e, 0xf4, 0x26, 0xca, 0xd6, 0xb3, 0x03, 0x2c, 0xb1, 0xde, 0xf9, 0xc3, 0x3b, 0xf0, 0x02, 0x0c, 0xd8, 0x43, 0xeb, 0xbc, 0x94, 0xc3, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHierarchyIndentPattern[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0xf9, 0x3c, 0x0f, 0xcd, 0x00, 0x00, 0x0a, 0x41, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x48, 0x0d, 0x9d, 0x96, 0x77, 0x54, 0x53, 0xd9, 0x16, 0x87, 0xcf, 0xbd, 0x37, 0xbd, 0xd0, 0x12, 0x22, 0x20, 0x25, 0xf4, 0x1a, 0x7a, 0x09, 0x20, 0xd2, 0x3b, 0x48, 0x15, 0x04, 0x51, 0x89, 0x49, 0x80, 0x50, 0x02, 0x86, 0x84, 0x26, 0x76, 0x44, 0x05, 0x46, 0x14, 0x11, 0x29, 0x56, 0x64, 0x54, 0xc0, 0x01, 0x47, 0x87, 0x22, 0x63, 0x45, 0x14, 0x0b, 0x83, 0x82, 0x62, 0xd7, 0x09, 0xf2, 0x10, 0x50, 0xc6, 0xc1, 0x51, 0x44, 0x45, 0xe5, 0xdd, 0x8c, 0x6b, 0x09, 0xef, 0xad, 0x35, 0xf3, 0xde, 0x9a, 0xfd, 0xc7, 0x59, 0xdf, 0xd9, 0xe7, 0xb7, 0xd7, 0xd9, 0x67, 0xef, 0x7d, 0xd7, 0xba, 0x00, 0x50, 0xfc, 0x82, 0x04, 0xc2, 0x74, 0x58, 0x01, 0x80, 0x34, 0xa1, 0x58, 0x14, 0xee, 0xeb, 0xc1, 0x5c, 0x12, 0x13, 0xcb, 0xc4, 0xf7, 0x02, 0x18, 0x10, 0x01, 0x0e, 0x58, 0x01, 0xc0, 0xe1, 0x66, 0x66, 0x04, 0x47, 0xf8, 0x44, 0x02, 0xd4, 0xfc, 0xbd, 0x3d, 0x99, 0x99, 0xa8, 0x48, 0xc6, 0xb3, 0xf6, 0xee, 0x2e, 0x80, 0x64, 0xbb, 0xdb, 0x2c, 0xbf, 0x50, 0x26, 0x73, 0xd6, 0xff, 0x7f, 0x91, 0x22, 0x37, 0x43, 0x24, 0x06, 0x00, 0x0a, 0x45, 0xd5, 0x36, 0x3c, 0x7e, 0x26, 0x17, 0xe5, 0x02, 0x94, 0x53, 0xb3, 0xc5, 0x19, 0x32, 0xff, 0x04, 0xca, 0xf4, 0x95, 0x29, 0x32, 0x86, 0x31, 0x32, 0x16, 0xa1, 0x09, 0xa2, 0xac, 0x22, 0xe3, 0xc4, 0xaf, 0x6c, 0xf6, 0xa7, 0xe6, 0x2b, 0xbb, 0xc9, 0x98, 0x97, 0x26, 0xe4, 0xa1, 0x1a, 0x59, 0xce, 0x19, 0xbc, 0x34, 0x9e, 0x8c, 0xbb, 0x50, 0xde, 0x9a, 0x25, 0xe1, 0xa3, 0x8c, 0x04, 0xa1, 0x5c, 0x98, 0x25, 0xe0, 0x67, 0xa3, 0x7c, 0x07, 0x65, 0xbd, 0x54, 0x49, 0x9a, 0x00, 0xe5, 0xf7, 0x28, 0xd3, 0xd3, 0xf8, 0x9c, 0x4c, 0x00, 0x30, 0x14, 0x99, 0x5f, 0xcc, 0xe7, 0x26, 0xa1, 0x6c, 0x89, 0x32, 0x45, 0x14, 0x19, 0xee, 0x89, 0xf2, 0x02, 0x00, 0x08, 0x94, 0xc4, 0x39, 0xbc, 0x72, 0x0e, 0x8b, 0xf9, 0x39, 0x68, 0x9e, 0x00, 0x78, 0xa6, 0x67, 0xe4, 0x8a, 0x04, 0x89, 0x49, 0x62, 0xa6, 0x11, 0xd7, 0x98, 0x69, 0xe5, 0xe8, 0xc8, 0x66, 0xfa, 0xf1, 0xb3, 0x53, 0xf9, 0x62, 0x31, 0x2b, 0x94, 0xc3, 0x4d, 0xe1, 0x88, 0x78, 0x4c, 0xcf, 0xf4, 0xb4, 0x0c, 0x8e, 0x30, 0x17, 0x80, 0xaf, 0x6f, 0x96, 0x45, 0x01, 0x25, 0x59, 0x6d, 0x99, 0x68, 0x91, 0xed, 0xad, 0x1c, 0xed, 0xed, 0x59, 0xd6, 0xe6, 0x68, 0xf9, 0xbf, 0xd9, 0xdf, 0x1e, 0x7e, 0x53, 0xfd, 0x3d, 0xc8, 0x7a, 0xfb, 0x55, 0xf1, 0x26, 0xec, 0xcf, 0x9e, 0x41, 0x8c, 0x9e, 0x59, 0xdf, 0x6c, 0xec, 0xac, 0x2f, 0xbd, 0x16, 0x00, 0xf6, 0x24, 0x5a, 0x9b, 0x1d, 0xb3, 0xbe, 0x95, 0x55, 0x00, 0xb4, 0x6d, 0x06, 0x40, 0xe5, 0xe1, 0xac, 0x4f, 0xef, 0x20, 0x00, 0xf2, 0x05, 0x00, 0xb4, 0xde, 0x9c, 0xf3, 0x1e, 0x86, 0x6c, 0x5e, 0x92, 0xc4, 0xe2, 0x0c, 0x27, 0x0b, 0x8b, 0xec, 0xec, 0x6c, 0x73, 0x01, 0x9f, 0x6b, 0x2e, 0x2b, 0xe8, 0x37, 0xfb, 0x9f, 0x82, 0x6f, 0xca, 0xbf, 0x86, 0x39, 0xf7, 0x99, 0xcb, 0xee, 0xfb, 0x56, 0x3b, 0xa6, 0x17, 0x3f, 0x81, 0x23, 0x49, 0x15, 0x33, 0x65, 0x45, 0xe5, 0xa6, 0xa7, 0xa6, 0x4b, 0x44, 0xcc, 0xcc, 0x0c, 0x0e, 0x97, 0xcf, 0x64, 0xfd, 0xf7, 0x10, 0xff, 0xe3, 0xc0, 0x39, 0x69, 0xcd, 0xc9, 0xc3, 0x2c, 0x9c, 0x9f, 0xc0, 0x17, 0xf1, 0x85, 0xe8, 0x55, 0x51, 0xe8, 0x94, 0x09, 0x84, 0x89, 0x68, 0xbb, 0x85, 0x3c, 0x81, 0x58, 0x90, 0x2e, 0x64, 0x0a, 0x84, 0x7f, 0xd5, 0xe1, 0x7f, 0x18, 0x36, 0x27, 0x07, 0x19, 0x7e, 0x9d, 0x6b, 0x14, 0x68, 0x75, 0x5f, 0x00, 0x7d, 0x85, 0x39, 0x50, 0xb8, 0x49, 0x07, 0xc8, 0x6f, 0x3d, 0x00, 0x43, 0x23, 0x03, 0x24, 0x6e, 0x3f, 0x7a, 0x02, 0x7d, 0xeb, 0x5b, 0x10, 0x31, 0x0a, 0xc8, 0xbe, 0xbc, 0x68, 0xad, 0x91, 0xaf, 0x73, 0x8f, 0x32, 0x7a, 0xfe, 0xe7, 0xfa, 0x1f, 0x0b, 0x5c, 0x8a, 0x6e, 0xe1, 0x4c, 0x41, 0x22, 0x53, 0xe6, 0xf6, 0x0c, 0x8f, 0x64, 0x72, 0x25, 0xa2, 0x2c, 0x19, 0xa3, 0xdf, 0x84, 0x6c, 0xc1, 0x02, 0x12, 0x90, 0x07, 0x74, 0xa0, 0x0a, 0x34, 0x81, 0x2e, 0x30, 0x02, 0x2c, 0x60, 0x0d, 0x1c, 0x80, 0x33, 0x70, 0x03, 0xde, 0x20, 0x00, 0x84, 0x80, 0x48, 0x10, 0x03, 0x96, 0x03, 0x2e, 0x48, 0x02, 0x69, 0x40, 0x04, 0xb2, 0x41, 0x3e, 0xd8, 0x00, 0x0a, 0x41, 0x31, 0xd8, 0x01, 0x76, 0x83, 0x6a, 0x70, 0x00, 0xd4, 0x81, 0x7a, 0xd0, 0x04, 0x4e, 0x82, 0x36, 0x70, 0x06, 0x5c, 0x04, 0x57, 0xc0, 0x0d, 0x70, 0x0b, 0x0c, 0x80, 0x47, 0x40, 0x0a, 0x86, 0xc1, 0x4b, 0x30, 0x01, 0xde, 0x81, 0x69, 0x08, 0x82, 0xf0, 0x10, 0x15, 0xa2, 0x41, 0xaa, 0x90, 0x16, 0xa4, 0x0f, 0x99, 0x42, 0xd6, 0x10, 0x1b, 0x5a, 0x08, 0x79, 0x43, 0x41, 0x50, 0x38, 0x14, 0x03, 0xc5, 0x43, 0x89, 0x90, 0x10, 0x92, 0x40, 0xf9, 0xd0, 0x26, 0xa8, 0x18, 0x2a, 0x83, 0xaa, 0xa1, 0x43, 0x50, 0x3d, 0xf4, 0x23, 0x74, 0x1a, 0xba, 0x08, 0x5d, 0x83, 0xfa, 0xa0, 0x07, 0xd0, 0x20, 0x34, 0x06, 0xfd, 0x01, 0x7d, 0x84, 0x11, 0x98, 0x02, 0xd3, 0x61, 0x0d, 0xd8, 0x00, 0xb6, 0x80, 0xd9, 0xb0, 0x3b, 0x1c, 0x08, 0x47, 0xc2, 0xcb, 0xe0, 0x44, 0x78, 0x15, 0x9c, 0x07, 0x17, 0xc0, 0xdb, 0xe1, 0x4a, 0xb8, 0x16, 0x3e, 0x0e, 0xb7, 0xc2, 0x17, 0xe1, 0x1b, 0xf0, 0x00, 0x2c, 0x85, 0x5f, 0xc2, 0x93, 0x08, 0x40, 0xc8, 0x08, 0x03, 0xd1, 0x46, 0x58, 0x08, 0x1b, 0xf1, 0x44, 0x42, 0x90, 0x58, 0x24, 0x01, 0x11, 0x21, 0x6b, 0x91, 0x22, 0xa4, 0x02, 0xa9, 0x45, 0x9a, 0x90, 0x0e, 0xa4, 0x1b, 0xb9, 0x8d, 0x48, 0x91, 0x71, 0xe4, 0x03, 0x06, 0x87, 0xa1, 0x61, 0x98, 0x18, 0x16, 0xc6, 0x19, 0xe3, 0x87, 0x59, 0x8c, 0xe1, 0x62, 0x56, 0x61, 0xd6, 0x62, 0x4a, 0x30, 0xd5, 0x98, 0x63, 0x98, 0x56, 0x4c, 0x17, 0xe6, 0x36, 0x66, 0x10, 0x33, 0x81, 0xf9, 0x82, 0xa5, 0x62, 0xd5, 0xb1, 0xa6, 0x58, 0x27, 0xac, 0x3f, 0x76, 0x09, 0x36, 0x11, 0x9b, 0x8d, 0x2d, 0xc4, 0x56, 0x60, 0x8f, 0x60, 0x5b, 0xb0, 0x97, 0xb1, 0x03, 0xd8, 0x61, 0xec, 0x3b, 0x1c, 0x0e, 0xc7, 0xc0, 0x19, 0xe2, 0x1c, 0x70, 0x7e, 0xb8, 0x18, 0x5c, 0x32, 0x6e, 0x35, 0xae, 0x04, 0xb7, 0x0f, 0xd7, 0x8c, 0xbb, 0x80, 0xeb, 0xc3, 0x0d, 0xe1, 0x26, 0xf1, 0x78, 0xbc, 0x2a, 0xde, 0x14, 0xef, 0x82, 0x0f, 0xc1, 0x73, 0xf0, 0x62, 0x7c, 0x21, 0xbe, 0x0a, 0x7f, 0x1c, 0x7f, 0x1e, 0xdf, 0x8f, 0x1f, 0xc6, 0xbf, 0x27, 0x90, 0x09, 0x5a, 0x04, 0x6b, 0x82, 0x0f, 0x21, 0x96, 0x20, 0x24, 0x6c, 0x24, 0x54, 0x10, 0x1a, 0x08, 0xe7, 0x08, 0xfd, 0x84, 0x11, 0xc2, 0x34, 0x51, 0x81, 0xa8, 0x4f, 0x74, 0x22, 0x86, 0x10, 0x79, 0xc4, 0x5c, 0x62, 0x29, 0xb1, 0x8e, 0xd8, 0x41, 0xbc, 0x49, 0x1c, 0x26, 0x4e, 0x93, 0x14, 0x49, 0x86, 0x24, 0x17, 0x52, 0x24, 0x29, 0x99, 0xb4, 0x81, 0x54, 0x49, 0x6a, 0x22, 0x5d, 0x26, 0x3d, 0x26, 0xbd, 0x21, 0x93, 0xc9, 0x3a, 0x64, 0x47, 0x72, 0x18, 0x59, 0x40, 0x5e, 0x4f, 0xae, 0x24, 0x9f, 0x20, 0x5f, 0x25, 0x0f, 0x92, 0x3f, 0x50, 0x94, 0x28, 0x26, 0x14, 0x4f, 0x4a, 0x1c, 0x45, 0x42, 0xd9, 0x4e, 0x39, 0x4a, 0xb9, 0x40, 0x79, 0x40, 0x79, 0x43, 0xa5, 0x52, 0x0d, 0xa8, 0x6e, 0xd4, 0x58, 0xaa, 0x98, 0xba, 0x9d, 0x5a, 0x4f, 0xbd, 0x44, 0x7d, 0x4a, 0x7d, 0x2f, 0x47, 0x93, 0x33, 0x97, 0xf3, 0x97, 0xe3, 0xc9, 0xad, 0x93, 0xab, 0x91, 0x6b, 0x95, 0xeb, 0x97, 0x7b, 0x25, 0x4f, 0x94, 0xd7, 0x97, 0x77, 0x97, 0x5f, 0x2e, 0x9f, 0x27, 0x5f, 0x21, 0x7f, 0x4a, 0xfe, 0xa6, 0xfc, 0xb8, 0x02, 0x51, 0xc1, 0x40, 0xc1, 0x53, 0x81, 0xa3, 0xb0, 0x56, 0xa1, 0x46, 0xe1, 0xb4, 0xc2, 0x3d, 0x85, 0x49, 0x45, 0x9a, 0xa2, 0x95, 0x62, 0x88, 0x62, 0x9a, 0x62, 0x89, 0x62, 0x83, 0xe2, 0x35, 0xc5, 0x51, 0x25, 0xbc, 0x92, 0x81, 0x92, 0xb7, 0x12, 0x4f, 0xa9, 0x40, 0xe9, 0xb0, 0xd2, 0x25, 0xa5, 0x21, 0x1a, 0x42, 0xd3, 0xa5, 0x79, 0xd2, 0xb8, 0xb4, 0x4d, 0xb4, 0x3a, 0xda, 0x65, 0xda, 0x30, 0x1d, 0x47, 0x37, 0xa4, 0xfb, 0xd3, 0x93, 0xe9, 0xc5, 0xf4, 0x1f, 0xe8, 0xbd, 0xf4, 0x09, 0x65, 0x25, 0x65, 0x5b, 0xe5, 0x28, 0xe5, 0x1c, 0xe5, 0x1a, 0xe5, 0xb3, 0xca, 0x52, 0x06, 0xc2, 0x30, 0x60, 0xf8, 0x33, 0x52, 0x19, 0xa5, 0x8c, 0x93, 0x8c, 0xbb, 0x8c, 0x8f, 0xf3, 0x34, 0xe6, 0xb9, 0xcf, 0xe3, 0xcf, 0xdb, 0x36, 0xaf, 0x69, 0x5e, 0xff, 0xbc, 0x29, 0x95, 0xf9, 0x2a, 0x6e, 0x2a, 0x7c, 0x95, 0x22, 0x95, 0x66, 0x95, 0x01, 0x95, 0x8f, 0xaa, 0x4c, 0x55, 0x6f, 0xd5, 0x14, 0xd5, 0x9d, 0xaa, 0x6d, 0xaa, 0x4f, 0xd4, 0x30, 0x6a, 0x26, 0x6a, 0x61, 0x6a, 0xd9, 0x6a, 0xfb, 0xd5, 0x2e, 0xab, 0x8d, 0xcf, 0xa7, 0xcf, 0x77, 0x9e, 0xcf, 0x9d, 0x5f, 0x34, 0xff, 0xe4, 0xfc, 0x87, 0xea, 0xb0, 0xba, 0x89, 0x7a, 0xb8, 0xfa, 0x6a, 0xf5, 0xc3, 0xea, 0x3d, 0xea, 0x93, 0x1a, 0x9a, 0x1a, 0xbe, 0x1a, 0x19, 0x1a, 0x55, 0x1a, 0x97, 0x34, 0xc6, 0x35, 0x19, 0x9a, 0x6e, 0x9a, 0xc9, 0x9a, 0xe5, 0x9a, 0xe7, 0x34, 0xc7, 0xb4, 0x68, 0x5a, 0x0b, 0xb5, 0x04, 0x5a, 0xe5, 0x5a, 0xe7, 0xb5, 0x5e, 0x30, 0x95, 0x99, 0xee, 0xcc, 0x54, 0x66, 0x25, 0xb3, 0x8b, 0x39, 0xa1, 0xad, 0xae, 0xed, 0xa7, 0x2d, 0xd1, 0x3e, 0xa4, 0xdd, 0xab, 0x3d, 0xad, 0x63, 0xa8, 0xb3, 0x58, 0x67, 0xa3, 0x4e, 0xb3, 0xce, 0x13, 0x5d, 0x92, 0x2e, 0x5b, 0x37, 0x41, 0xb7, 0x5c, 0xb7, 0x53, 0x77, 0x42, 0x4f, 0x4b, 0x2f, 0x58, 0x2f, 0x5f, 0xaf, 0x51, 0xef, 0xa1, 0x3e, 0x51, 0x9f, 0xad, 0x9f, 0xa4, 0xbf, 0x47, 0xbf, 0x5b, 0x7f, 0xca, 0xc0, 0xd0, 0x20, 0xda, 0x60, 0x8b, 0x41, 0x9b, 0xc1, 0xa8, 0xa1, 0x8a, 0xa1, 0xbf, 0x61, 0x9e, 0x61, 0xa3, 0xe1, 0x63, 0x23, 0xaa, 0x91, 0xab, 0xd1, 0x2a, 0xa3, 0x5a, 0xa3, 0x3b, 0xc6, 0x38, 0x63, 0xb6, 0x71, 0x8a, 0xf1, 0x3e, 0xe3, 0x5b, 0x26, 0xb0, 0x89, 0x9d, 0x49, 0x92, 0x49, 0x8d, 0xc9, 0x4d, 0x53, 0xd8, 0xd4, 0xde, 0x54, 0x60, 0xba, 0xcf, 0xb4, 0xcf, 0x0c, 0x6b, 0xe6, 0x68, 0x26, 0x34, 0xab, 0x35, 0xbb, 0xc7, 0xa2, 0xb0, 0xdc, 0x59, 0x59, 0xac, 0x46, 0xd6, 0xa0, 0x39, 0xc3, 0x3c, 0xc8, 0x7c, 0xa3, 0x79, 0x9b, 0xf9, 0x2b, 0x0b, 0x3d, 0x8b, 0x58, 0x8b, 0x9d, 0x16, 0xdd, 0x16, 0x5f, 0x2c, 0xed, 0x2c, 0x53, 0x2d, 0xeb, 0x2c, 0x1f, 0x59, 0x29, 0x59, 0x05, 0x58, 0x6d, 0xb4, 0xea, 0xb0, 0xfa, 0xc3, 0xda, 0xc4, 0x9a, 0x6b, 0x5d, 0x63, 0x7d, 0xc7, 0x86, 0x6a, 0xe3, 0x63, 0xb3, 0xce, 0xa6, 0xdd, 0xe6, 0xb5, 0xad, 0xa9, 0x2d, 0xdf, 0x76, 0xbf, 0xed, 0x7d, 0x3b, 0x9a, 0x5d, 0xb0, 0xdd, 0x16, 0xbb, 0x4e, 0xbb, 0xcf, 0xf6, 0x0e, 0xf6, 0x22, 0xfb, 0x26, 0xfb, 0x31, 0x07, 0x3d, 0x87, 0x78, 0x87, 0xbd, 0x0e, 0xf7, 0xd8, 0x74, 0x76, 0x28, 0xbb, 0x84, 0x7d, 0xd5, 0x11, 0xeb, 0xe8, 0xe1, 0xb8, 0xce, 0xf1, 0x8c, 0xe3, 0x07, 0x27, 0x7b, 0x27, 0xb1, 0xd3, 0x49, 0xa7, 0xdf, 0x9d, 0x59, 0xce, 0x29, 0xce, 0x0d, 0xce, 0xa3, 0x0b, 0x0c, 0x17, 0xf0, 0x17, 0xd4, 0x2d, 0x18, 0x72, 0xd1, 0x71, 0xe1, 0xb8, 0x1c, 0x72, 0x91, 0x2e, 0x64, 0x2e, 0x8c, 0x5f, 0x78, 0x70, 0xa1, 0xd4, 0x55, 0xdb, 0x95, 0xe3, 0x5a, 0xeb, 0xfa, 0xcc, 0x4d, 0xd7, 0x8d, 0xe7, 0x76, 0xc4, 0x6d, 0xc4, 0xdd, 0xd8, 0x3d, 0xd9, 0xfd, 0xb8, 0xfb, 0x2b, 0x0f, 0x4b, 0x0f, 0x91, 0x47, 0x8b, 0xc7, 0x94, 0xa7, 0x93, 0xe7, 0x1a, 0xcf, 0x0b, 0x5e, 0x88, 0x97, 0xaf, 0x57, 0x91, 0x57, 0xaf, 0xb7, 0x92, 0xf7, 0x62, 0xef, 0x6a, 0xef, 0xa7, 0x3e, 0x3a, 0x3e, 0x89, 0x3e, 0x8d, 0x3e, 0x13, 0xbe, 0x76, 0xbe, 0xab, 0x7d, 0x2f, 0xf8, 0x61, 0xfd, 0x02, 0xfd, 0x76, 0xfa, 0xdd, 0xf3, 0xd7, 0xf0, 0xe7, 0xfa, 0xd7, 0xfb, 0x4f, 0x04, 0x38, 0x04, 0xac, 0x09, 0xe8, 0x0a, 0xa4, 0x04, 0x46, 0x04, 0x56, 0x07, 0x3e, 0x0b, 0x32, 0x09, 0x12, 0x05, 0x75, 0x04, 0xc3, 0xc1, 0x01, 0xc1, 0xbb, 0x82, 0x1f, 0x2f, 0xd2, 0x5f, 0x24, 0x5c, 0xd4, 0x16, 0x02, 0x42, 0xfc, 0x43, 0x76, 0x85, 0x3c, 0x09, 0x35, 0x0c, 0x5d, 0x15, 0xfa, 0x73, 0x18, 0x2e, 0x2c, 0x34, 0xac, 0x26, 0xec, 0x79, 0xb8, 0x55, 0x78, 0x7e, 0x78, 0x77, 0x04, 0x2d, 0x62, 0x45, 0x44, 0x43, 0xc4, 0xbb, 0x48, 0x8f, 0xc8, 0xd2, 0xc8, 0x47, 0x8b, 0x8d, 0x16, 0x4b, 0x16, 0x77, 0x46, 0xc9, 0x47, 0xc5, 0x45, 0xd5, 0x47, 0x4d, 0x45, 0x7b, 0x45, 0x97, 0x45, 0x4b, 0x97, 0x58, 0x2c, 0x59, 0xb3, 0xe4, 0x46, 0x8c, 0x5a, 0x8c, 0x20, 0xa6, 0x3d, 0x16, 0x1f, 0x1b, 0x15, 0x7b, 0x24, 0x76, 0x72, 0xa9, 0xf7, 0xd2, 0xdd, 0x4b, 0x87, 0xe3, 0xec, 0xe2, 0x0a, 0xe3, 0xee, 0x2e, 0x33, 0x5c, 0x96, 0xb3, 0xec, 0xda, 0x72, 0xb5, 0xe5, 0xa9, 0xcb, 0xcf, 0xae, 0x90, 0x5f, 0xc1, 0x59, 0x71, 0x2a, 0x1e, 0x1b, 0x1f, 0x1d, 0xdf, 0x10, 0xff, 0x89, 0x13, 0xc2, 0xa9, 0xe5, 0x4c, 0xae, 0xf4, 0x5f, 0xb9, 0x77, 0xe5, 0x04, 0xd7, 0x93, 0xbb, 0x87, 0xfb, 0x92, 0xe7, 0xc6, 0x2b, 0xe7, 0x8d, 0xf1, 0x5d, 0xf8, 0x65, 0xfc, 0x91, 0x04, 0x97, 0x84, 0xb2, 0x84, 0xd1, 0x44, 0x97, 0xc4, 0x5d, 0x89, 0x63, 0x49, 0xae, 0x49, 0x15, 0x49, 0xe3, 0x02, 0x4f, 0x41, 0xb5, 0xe0, 0x75, 0xb2, 0x5f, 0xf2, 0x81, 0xe4, 0xa9, 0x94, 0x90, 0x94, 0xa3, 0x29, 0x33, 0xa9, 0xd1, 0xa9, 0xcd, 0x69, 0x84, 0xb4, 0xf8, 0xb4, 0xd3, 0x42, 0x25, 0x61, 0x8a, 0xb0, 0x2b, 0x5d, 0x33, 0x3d, 0x27, 0xbd, 0x2f, 0xc3, 0x34, 0xa3, 0x30, 0x43, 0xba, 0xca, 0x69, 0xd5, 0xee, 0x55, 0x13, 0xa2, 0x40, 0xd1, 0x91, 0x4c, 0x28, 0x73, 0x59, 0x66, 0xbb, 0x98, 0x8e, 0xfe, 0x4c, 0xf5, 0x48, 0x8c, 0x24, 0x9b, 0x25, 0x83, 0x59, 0x0b, 0xb3, 0x6a, 0xb2, 0xde, 0x67, 0x47, 0x65, 0x9f, 0xca, 0x51, 0xcc, 0x11, 0xe6, 0xf4, 0xe4, 0x9a, 0xe4, 0x6e, 0xcb, 0x1d, 0xc9, 0xf3, 0xc9, 0xfb, 0x7e, 0x35, 0x66, 0x35, 0x77, 0x75, 0x67, 0xbe, 0x76, 0xfe, 0x86, 0xfc, 0xc1, 0x35, 0xee, 0x6b, 0x0e, 0xad, 0x85, 0xd6, 0xae, 0x5c, 0xdb, 0xb9, 0x4e, 0x77, 0x5d, 0xc1, 0xba, 0xe1, 0xf5, 0xbe, 0xeb, 0x8f, 0x6d, 0x20, 0x6d, 0x48, 0xd9, 0xf0, 0xcb, 0x46, 0xcb, 0x8d, 0x65, 0x1b, 0xdf, 0x6e, 0x8a, 0xde, 0xd4, 0x51, 0xa0, 0x51, 0xb0, 0xbe, 0x60, 0x68, 0xb3, 0xef, 0xe6, 0xc6, 0x42, 0xb9, 0x42, 0x51, 0xe1, 0xbd, 0x2d, 0xce, 0x5b, 0x0e, 0x6c, 0xc5, 0x6c, 0x15, 0x6c, 0xed, 0xdd, 0x66, 0xb3, 0xad, 0x6a, 0xdb, 0x97, 0x22, 0x5e, 0xd1, 0xf5, 0x62, 0xcb, 0xe2, 0x8a, 0xe2, 0x4f, 0x25, 0xdc, 0x92, 0xeb, 0xdf, 0x59, 0x7d, 0x57, 0xf9, 0xdd, 0xcc, 0xf6, 0x84, 0xed, 0xbd, 0xa5, 0xf6, 0xa5, 0xfb, 0x77, 0xe0, 0x76, 0x08, 0x77, 0xdc, 0xdd, 0xe9, 0xba, 0xf3, 0x58, 0x99, 0x62, 0x59, 0x5e, 0xd9, 0xd0, 0xae, 0xe0, 0x5d, 0xad, 0xe5, 0xcc, 0xf2, 0xa2, 0xf2, 0xb7, 0xbb, 0x57, 0xec, 0xbe, 0x56, 0x61, 0x5b, 0x71, 0x60, 0x0f, 0x69, 0x8f, 0x64, 0x8f, 0xb4, 0x32, 0xa8, 0xb2, 0xbd, 0x4a, 0xaf, 0x6a, 0x47, 0xd5, 0xa7, 0xea, 0xa4, 0xea, 0x81, 0x1a, 0x8f, 0x9a, 0xe6, 0xbd, 0xea, 0x7b, 0xb7, 0xed, 0x9d, 0xda, 0xc7, 0xdb, 0xd7, 0xbf, 0xdf, 0x6d, 0x7f, 0xd3, 0x01, 0x8d, 0x03, 0xc5, 0x07, 0x3e, 0x1e, 0x14, 0x1c, 0xbc, 0x7f, 0xc8, 0xf7, 0x50, 0x6b, 0xad, 0x41, 0x6d, 0xc5, 0x61, 0xdc, 0xe1, 0xac, 0xc3, 0xcf, 0xeb, 0xa2, 0xea, 0xba, 0xbf, 0x67, 0x7f, 0x5f, 0x7f, 0x44, 0xed, 0x48, 0xf1, 0x91, 0xcf, 0x47, 0x85, 0x47, 0xa5, 0xc7, 0xc2, 0x8f, 0x75, 0xd5, 0x3b, 0xd4, 0xd7, 0x37, 0xa8, 0x37, 0x94, 0x36, 0xc2, 0x8d, 0x92, 0xc6, 0xb1, 0xe3, 0x71, 0xc7, 0x6f, 0xfd, 0xe0, 0xf5, 0x43, 0x7b, 0x13, 0xab, 0xe9, 0x50, 0x33, 0xa3, 0xb9, 0xf8, 0x04, 0x38, 0x21, 0x39, 0xf1, 0xe2, 0xc7, 0xf8, 0x1f, 0xef, 0x9e, 0x0c, 0x3c, 0xd9, 0x79, 0x8a, 0x7d, 0xaa, 0xe9, 0x27, 0xfd, 0x9f, 0xf6, 0xb6, 0xd0, 0x5a, 0x8a, 0x5a, 0xa1, 0xd6, 0xdc, 0xd6, 0x89, 0xb6, 0xa4, 0x36, 0x69, 0x7b, 0x4c, 0x7b, 0xdf, 0xe9, 0x80, 0xd3, 0x9d, 0x1d, 0xce, 0x1d, 0x2d, 0x3f, 0x9b, 0xff, 0x7c, 0xf4, 0x8c, 0xf6, 0x99, 0x9a, 0xb3, 0xca, 0x67, 0x4b, 0xcf, 0x91, 0xce, 0x15, 0x9c, 0x9b, 0x39, 0x9f, 0x77, 0x7e, 0xf2, 0x42, 0xc6, 0x85, 0xf1, 0x8b, 0x89, 0x17, 0x87, 0x3a, 0x57, 0x74, 0x3e, 0xba, 0xb4, 0xe4, 0xd2, 0x9d, 0xae, 0xb0, 0xae, 0xde, 0xcb, 0x81, 0x97, 0xaf, 0x5e, 0xf1, 0xb9, 0x72, 0xa9, 0xdb, 0xbd, 0xfb, 0xfc, 0x55, 0x97, 0xab, 0x67, 0xae, 0x39, 0x5d, 0x3b, 0x7d, 0x9d, 0x7d, 0xbd, 0xed, 0x86, 0xfd, 0x8d, 0xd6, 0x1e, 0xbb, 0x9e, 0x96, 0x5f, 0xec, 0x7e, 0x69, 0xe9, 0xb5, 0xef, 0x6d, 0xbd, 0xe9, 0x70, 0xb3, 0xfd, 0x96, 0xe3, 0xad, 0x8e, 0xbe, 0x05, 0x7d, 0xe7, 0xfa, 0x5d, 0xfb, 0x2f, 0xde, 0xf6, 0xba, 0x7d, 0xe5, 0x8e, 0xff, 0x9d, 0x1b, 0x03, 0x8b, 0x06, 0xfa, 0xee, 0x2e, 0xbe, 0x7b, 0xff, 0x5e, 0xdc, 0x3d, 0xe9, 0x7d, 0xde, 0xfd, 0xd1, 0x07, 0xa9, 0x0f, 0x5e, 0x3f, 0xcc, 0x7a, 0x38, 0xfd, 0x68, 0xfd, 0x63, 0xec, 0xe3, 0xa2, 0x27, 0x0a, 0x4f, 0x2a, 0x9e, 0xaa, 0x3f, 0xad, 0xfd, 0xd5, 0xf8, 0xd7, 0x66, 0xa9, 0xbd, 0xf4, 0xec, 0xa0, 0xd7, 0x60, 0xcf, 0xb3, 0x88, 0x67, 0x8f, 0x86, 0xb8, 0x43, 0x2f, 0xff, 0x95, 0xf9, 0xaf, 0x4f, 0xc3, 0x05, 0xcf, 0xa9, 0xcf, 0x2b, 0x46, 0xb4, 0x46, 0xea, 0x47, 0xad, 0x47, 0xcf, 0x8c, 0xf9, 0x8c, 0xdd, 0x7a, 0xb1, 0xf4, 0xc5, 0xf0, 0xcb, 0x8c, 0x97, 0xd3, 0xe3, 0x85, 0xbf, 0x29, 0xfe, 0xb6, 0xf7, 0x95, 0xd1, 0xab, 0x9f, 0x7e, 0x77, 0xfb, 0xbd, 0x67, 0x62, 0xc9, 0xc4, 0xf0, 0x6b, 0xd1, 0xeb, 0x99, 0x3f, 0x4a, 0xde, 0xa8, 0xbe, 0x39, 0xfa, 0xd6, 0xf6, 0x6d, 0xe7, 0x64, 0xe8, 0xe4, 0xd3, 0x77, 0x69, 0xef, 0xa6, 0xa7, 0x8a, 0xde, 0xab, 0xbe, 0x3f, 0xf6, 0x81, 0xfd, 0xa1, 0xfb, 0x63, 0xf4, 0xc7, 0x91, 0xe9, 0xec, 0x4f, 0xf8, 0x4f, 0x95, 0x9f, 0x8d, 0x3f, 0x77, 0x7c, 0x09, 0xfc, 0xf2, 0x78, 0x26, 0x6d, 0x66, 0xe6, 0xdf, 0xf7, 0x84, 0xf3, 0xfb, 0x32, 0x3a, 0x59, 0x7e, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x03, 0xa4, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x34, 0x2d, 0x30, 0x35, 0x2d, 0x30, 0x32, 0x54, 0x31, 0x31, 0x3a, 0x30, 0x35, 0x3a, 0x35, 0x35, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x34, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0xc0, 0x10, 0xf8, 0x70, 0x00, 0x00, 0x00, 0x10, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1d, 0x63, 0x60, 0x60, 0x60, 0xf8, 0x0f, 0xc4, 0x70, 0x00, 0x00, 0x0d, 0x04, 0x01, 0x00, 0x65, 0x59, 0x09, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHierarchyIndentPattern2x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0xe3, 0x00, 0xef, 0x43, 0x00, 0x00, 0x0a, 0x41, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x48, 0x0d, 0x9d, 0x96, 0x77, 0x54, 0x53, 0xd9, 0x16, 0x87, 0xcf, 0xbd, 0x37, 0xbd, 0xd0, 0x12, 0x22, 0x20, 0x25, 0xf4, 0x1a, 0x7a, 0x09, 0x20, 0xd2, 0x3b, 0x48, 0x15, 0x04, 0x51, 0x89, 0x49, 0x80, 0x50, 0x02, 0x86, 0x84, 0x26, 0x76, 0x44, 0x05, 0x46, 0x14, 0x11, 0x29, 0x56, 0x64, 0x54, 0xc0, 0x01, 0x47, 0x87, 0x22, 0x63, 0x45, 0x14, 0x0b, 0x83, 0x82, 0x62, 0xd7, 0x09, 0xf2, 0x10, 0x50, 0xc6, 0xc1, 0x51, 0x44, 0x45, 0xe5, 0xdd, 0x8c, 0x6b, 0x09, 0xef, 0xad, 0x35, 0xf3, 0xde, 0x9a, 0xfd, 0xc7, 0x59, 0xdf, 0xd9, 0xe7, 0xb7, 0xd7, 0xd9, 0x67, 0xef, 0x7d, 0xd7, 0xba, 0x00, 0x50, 0xfc, 0x82, 0x04, 0xc2, 0x74, 0x58, 0x01, 0x80, 0x34, 0xa1, 0x58, 0x14, 0xee, 0xeb, 0xc1, 0x5c, 0x12, 0x13, 0xcb, 0xc4, 0xf7, 0x02, 0x18, 0x10, 0x01, 0x0e, 0x58, 0x01, 0xc0, 0xe1, 0x66, 0x66, 0x04, 0x47, 0xf8, 0x44, 0x02, 0xd4, 0xfc, 0xbd, 0x3d, 0x99, 0x99, 0xa8, 0x48, 0xc6, 0xb3, 0xf6, 0xee, 0x2e, 0x80, 0x64, 0xbb, 0xdb, 0x2c, 0xbf, 0x50, 0x26, 0x73, 0xd6, 0xff, 0x7f, 0x91, 0x22, 0x37, 0x43, 0x24, 0x06, 0x00, 0x0a, 0x45, 0xd5, 0x36, 0x3c, 0x7e, 0x26, 0x17, 0xe5, 0x02, 0x94, 0x53, 0xb3, 0xc5, 0x19, 0x32, 0xff, 0x04, 0xca, 0xf4, 0x95, 0x29, 0x32, 0x86, 0x31, 0x32, 0x16, 0xa1, 0x09, 0xa2, 0xac, 0x22, 0xe3, 0xc4, 0xaf, 0x6c, 0xf6, 0xa7, 0xe6, 0x2b, 0xbb, 0xc9, 0x98, 0x97, 0x26, 0xe4, 0xa1, 0x1a, 0x59, 0xce, 0x19, 0xbc, 0x34, 0x9e, 0x8c, 0xbb, 0x50, 0xde, 0x9a, 0x25, 0xe1, 0xa3, 0x8c, 0x04, 0xa1, 0x5c, 0x98, 0x25, 0xe0, 0x67, 0xa3, 0x7c, 0x07, 0x65, 0xbd, 0x54, 0x49, 0x9a, 0x00, 0xe5, 0xf7, 0x28, 0xd3, 0xd3, 0xf8, 0x9c, 0x4c, 0x00, 0x30, 0x14, 0x99, 0x5f, 0xcc, 0xe7, 0x26, 0xa1, 0x6c, 0x89, 0x32, 0x45, 0x14, 0x19, 0xee, 0x89, 0xf2, 0x02, 0x00, 0x08, 0x94, 0xc4, 0x39, 0xbc, 0x72, 0x0e, 0x8b, 0xf9, 0x39, 0x68, 0x9e, 0x00, 0x78, 0xa6, 0x67, 0xe4, 0x8a, 0x04, 0x89, 0x49, 0x62, 0xa6, 0x11, 0xd7, 0x98, 0x69, 0xe5, 0xe8, 0xc8, 0x66, 0xfa, 0xf1, 0xb3, 0x53, 0xf9, 0x62, 0x31, 0x2b, 0x94, 0xc3, 0x4d, 0xe1, 0x88, 0x78, 0x4c, 0xcf, 0xf4, 0xb4, 0x0c, 0x8e, 0x30, 0x17, 0x80, 0xaf, 0x6f, 0x96, 0x45, 0x01, 0x25, 0x59, 0x6d, 0x99, 0x68, 0x91, 0xed, 0xad, 0x1c, 0xed, 0xed, 0x59, 0xd6, 0xe6, 0x68, 0xf9, 0xbf, 0xd9, 0xdf, 0x1e, 0x7e, 0x53, 0xfd, 0x3d, 0xc8, 0x7a, 0xfb, 0x55, 0xf1, 0x26, 0xec, 0xcf, 0x9e, 0x41, 0x8c, 0x9e, 0x59, 0xdf, 0x6c, 0xec, 0xac, 0x2f, 0xbd, 0x16, 0x00, 0xf6, 0x24, 0x5a, 0x9b, 0x1d, 0xb3, 0xbe, 0x95, 0x55, 0x00, 0xb4, 0x6d, 0x06, 0x40, 0xe5, 0xe1, 0xac, 0x4f, 0xef, 0x20, 0x00, 0xf2, 0x05, 0x00, 0xb4, 0xde, 0x9c, 0xf3, 0x1e, 0x86, 0x6c, 0x5e, 0x92, 0xc4, 0xe2, 0x0c, 0x27, 0x0b, 0x8b, 0xec, 0xec, 0x6c, 0x73, 0x01, 0x9f, 0x6b, 0x2e, 0x2b, 0xe8, 0x37, 0xfb, 0x9f, 0x82, 0x6f, 0xca, 0xbf, 0x86, 0x39, 0xf7, 0x99, 0xcb, 0xee, 0xfb, 0x56, 0x3b, 0xa6, 0x17, 0x3f, 0x81, 0x23, 0x49, 0x15, 0x33, 0x65, 0x45, 0xe5, 0xa6, 0xa7, 0xa6, 0x4b, 0x44, 0xcc, 0xcc, 0x0c, 0x0e, 0x97, 0xcf, 0x64, 0xfd, 0xf7, 0x10, 0xff, 0xe3, 0xc0, 0x39, 0x69, 0xcd, 0xc9, 0xc3, 0x2c, 0x9c, 0x9f, 0xc0, 0x17, 0xf1, 0x85, 0xe8, 0x55, 0x51, 0xe8, 0x94, 0x09, 0x84, 0x89, 0x68, 0xbb, 0x85, 0x3c, 0x81, 0x58, 0x90, 0x2e, 0x64, 0x0a, 0x84, 0x7f, 0xd5, 0xe1, 0x7f, 0x18, 0x36, 0x27, 0x07, 0x19, 0x7e, 0x9d, 0x6b, 0x14, 0x68, 0x75, 0x5f, 0x00, 0x7d, 0x85, 0x39, 0x50, 0xb8, 0x49, 0x07, 0xc8, 0x6f, 0x3d, 0x00, 0x43, 0x23, 0x03, 0x24, 0x6e, 0x3f, 0x7a, 0x02, 0x7d, 0xeb, 0x5b, 0x10, 0x31, 0x0a, 0xc8, 0xbe, 0xbc, 0x68, 0xad, 0x91, 0xaf, 0x73, 0x8f, 0x32, 0x7a, 0xfe, 0xe7, 0xfa, 0x1f, 0x0b, 0x5c, 0x8a, 0x6e, 0xe1, 0x4c, 0x41, 0x22, 0x53, 0xe6, 0xf6, 0x0c, 0x8f, 0x64, 0x72, 0x25, 0xa2, 0x2c, 0x19, 0xa3, 0xdf, 0x84, 0x6c, 0xc1, 0x02, 0x12, 0x90, 0x07, 0x74, 0xa0, 0x0a, 0x34, 0x81, 0x2e, 0x30, 0x02, 0x2c, 0x60, 0x0d, 0x1c, 0x80, 0x33, 0x70, 0x03, 0xde, 0x20, 0x00, 0x84, 0x80, 0x48, 0x10, 0x03, 0x96, 0x03, 0x2e, 0x48, 0x02, 0x69, 0x40, 0x04, 0xb2, 0x41, 0x3e, 0xd8, 0x00, 0x0a, 0x41, 0x31, 0xd8, 0x01, 0x76, 0x83, 0x6a, 0x70, 0x00, 0xd4, 0x81, 0x7a, 0xd0, 0x04, 0x4e, 0x82, 0x36, 0x70, 0x06, 0x5c, 0x04, 0x57, 0xc0, 0x0d, 0x70, 0x0b, 0x0c, 0x80, 0x47, 0x40, 0x0a, 0x86, 0xc1, 0x4b, 0x30, 0x01, 0xde, 0x81, 0x69, 0x08, 0x82, 0xf0, 0x10, 0x15, 0xa2, 0x41, 0xaa, 0x90, 0x16, 0xa4, 0x0f, 0x99, 0x42, 0xd6, 0x10, 0x1b, 0x5a, 0x08, 0x79, 0x43, 0x41, 0x50, 0x38, 0x14, 0x03, 0xc5, 0x43, 0x89, 0x90, 0x10, 0x92, 0x40, 0xf9, 0xd0, 0x26, 0xa8, 0x18, 0x2a, 0x83, 0xaa, 0xa1, 0x43, 0x50, 0x3d, 0xf4, 0x23, 0x74, 0x1a, 0xba, 0x08, 0x5d, 0x83, 0xfa, 0xa0, 0x07, 0xd0, 0x20, 0x34, 0x06, 0xfd, 0x01, 0x7d, 0x84, 0x11, 0x98, 0x02, 0xd3, 0x61, 0x0d, 0xd8, 0x00, 0xb6, 0x80, 0xd9, 0xb0, 0x3b, 0x1c, 0x08, 0x47, 0xc2, 0xcb, 0xe0, 0x44, 0x78, 0x15, 0x9c, 0x07, 0x17, 0xc0, 0xdb, 0xe1, 0x4a, 0xb8, 0x16, 0x3e, 0x0e, 0xb7, 0xc2, 0x17, 0xe1, 0x1b, 0xf0, 0x00, 0x2c, 0x85, 0x5f, 0xc2, 0x93, 0x08, 0x40, 0xc8, 0x08, 0x03, 0xd1, 0x46, 0x58, 0x08, 0x1b, 0xf1, 0x44, 0x42, 0x90, 0x58, 0x24, 0x01, 0x11, 0x21, 0x6b, 0x91, 0x22, 0xa4, 0x02, 0xa9, 0x45, 0x9a, 0x90, 0x0e, 0xa4, 0x1b, 0xb9, 0x8d, 0x48, 0x91, 0x71, 0xe4, 0x03, 0x06, 0x87, 0xa1, 0x61, 0x98, 0x18, 0x16, 0xc6, 0x19, 0xe3, 0x87, 0x59, 0x8c, 0xe1, 0x62, 0x56, 0x61, 0xd6, 0x62, 0x4a, 0x30, 0xd5, 0x98, 0x63, 0x98, 0x56, 0x4c, 0x17, 0xe6, 0x36, 0x66, 0x10, 0x33, 0x81, 0xf9, 0x82, 0xa5, 0x62, 0xd5, 0xb1, 0xa6, 0x58, 0x27, 0xac, 0x3f, 0x76, 0x09, 0x36, 0x11, 0x9b, 0x8d, 0x2d, 0xc4, 0x56, 0x60, 0x8f, 0x60, 0x5b, 0xb0, 0x97, 0xb1, 0x03, 0xd8, 0x61, 0xec, 0x3b, 0x1c, 0x0e, 0xc7, 0xc0, 0x19, 0xe2, 0x1c, 0x70, 0x7e, 0xb8, 0x18, 0x5c, 0x32, 0x6e, 0x35, 0xae, 0x04, 0xb7, 0x0f, 0xd7, 0x8c, 0xbb, 0x80, 0xeb, 0xc3, 0x0d, 0xe1, 0x26, 0xf1, 0x78, 0xbc, 0x2a, 0xde, 0x14, 0xef, 0x82, 0x0f, 0xc1, 0x73, 0xf0, 0x62, 0x7c, 0x21, 0xbe, 0x0a, 0x7f, 0x1c, 0x7f, 0x1e, 0xdf, 0x8f, 0x1f, 0xc6, 0xbf, 0x27, 0x90, 0x09, 0x5a, 0x04, 0x6b, 0x82, 0x0f, 0x21, 0x96, 0x20, 0x24, 0x6c, 0x24, 0x54, 0x10, 0x1a, 0x08, 0xe7, 0x08, 0xfd, 0x84, 0x11, 0xc2, 0x34, 0x51, 0x81, 0xa8, 0x4f, 0x74, 0x22, 0x86, 0x10, 0x79, 0xc4, 0x5c, 0x62, 0x29, 0xb1, 0x8e, 0xd8, 0x41, 0xbc, 0x49, 0x1c, 0x26, 0x4e, 0x93, 0x14, 0x49, 0x86, 0x24, 0x17, 0x52, 0x24, 0x29, 0x99, 0xb4, 0x81, 0x54, 0x49, 0x6a, 0x22, 0x5d, 0x26, 0x3d, 0x26, 0xbd, 0x21, 0x93, 0xc9, 0x3a, 0x64, 0x47, 0x72, 0x18, 0x59, 0x40, 0x5e, 0x4f, 0xae, 0x24, 0x9f, 0x20, 0x5f, 0x25, 0x0f, 0x92, 0x3f, 0x50, 0x94, 0x28, 0x26, 0x14, 0x4f, 0x4a, 0x1c, 0x45, 0x42, 0xd9, 0x4e, 0x39, 0x4a, 0xb9, 0x40, 0x79, 0x40, 0x79, 0x43, 0xa5, 0x52, 0x0d, 0xa8, 0x6e, 0xd4, 0x58, 0xaa, 0x98, 0xba, 0x9d, 0x5a, 0x4f, 0xbd, 0x44, 0x7d, 0x4a, 0x7d, 0x2f, 0x47, 0x93, 0x33, 0x97, 0xf3, 0x97, 0xe3, 0xc9, 0xad, 0x93, 0xab, 0x91, 0x6b, 0x95, 0xeb, 0x97, 0x7b, 0x25, 0x4f, 0x94, 0xd7, 0x97, 0x77, 0x97, 0x5f, 0x2e, 0x9f, 0x27, 0x5f, 0x21, 0x7f, 0x4a, 0xfe, 0xa6, 0xfc, 0xb8, 0x02, 0x51, 0xc1, 0x40, 0xc1, 0x53, 0x81, 0xa3, 0xb0, 0x56, 0xa1, 0x46, 0xe1, 0xb4, 0xc2, 0x3d, 0x85, 0x49, 0x45, 0x9a, 0xa2, 0x95, 0x62, 0x88, 0x62, 0x9a, 0x62, 0x89, 0x62, 0x83, 0xe2, 0x35, 0xc5, 0x51, 0x25, 0xbc, 0x92, 0x81, 0x92, 0xb7, 0x12, 0x4f, 0xa9, 0x40, 0xe9, 0xb0, 0xd2, 0x25, 0xa5, 0x21, 0x1a, 0x42, 0xd3, 0xa5, 0x79, 0xd2, 0xb8, 0xb4, 0x4d, 0xb4, 0x3a, 0xda, 0x65, 0xda, 0x30, 0x1d, 0x47, 0x37, 0xa4, 0xfb, 0xd3, 0x93, 0xe9, 0xc5, 0xf4, 0x1f, 0xe8, 0xbd, 0xf4, 0x09, 0x65, 0x25, 0x65, 0x5b, 0xe5, 0x28, 0xe5, 0x1c, 0xe5, 0x1a, 0xe5, 0xb3, 0xca, 0x52, 0x06, 0xc2, 0x30, 0x60, 0xf8, 0x33, 0x52, 0x19, 0xa5, 0x8c, 0x93, 0x8c, 0xbb, 0x8c, 0x8f, 0xf3, 0x34, 0xe6, 0xb9, 0xcf, 0xe3, 0xcf, 0xdb, 0x36, 0xaf, 0x69, 0x5e, 0xff, 0xbc, 0x29, 0x95, 0xf9, 0x2a, 0x6e, 0x2a, 0x7c, 0x95, 0x22, 0x95, 0x66, 0x95, 0x01, 0x95, 0x8f, 0xaa, 0x4c, 0x55, 0x6f, 0xd5, 0x14, 0xd5, 0x9d, 0xaa, 0x6d, 0xaa, 0x4f, 0xd4, 0x30, 0x6a, 0x26, 0x6a, 0x61, 0x6a, 0xd9, 0x6a, 0xfb, 0xd5, 0x2e, 0xab, 0x8d, 0xcf, 0xa7, 0xcf, 0x77, 0x9e, 0xcf, 0x9d, 0x5f, 0x34, 0xff, 0xe4, 0xfc, 0x87, 0xea, 0xb0, 0xba, 0x89, 0x7a, 0xb8, 0xfa, 0x6a, 0xf5, 0xc3, 0xea, 0x3d, 0xea, 0x93, 0x1a, 0x9a, 0x1a, 0xbe, 0x1a, 0x19, 0x1a, 0x55, 0x1a, 0x97, 0x34, 0xc6, 0x35, 0x19, 0x9a, 0x6e, 0x9a, 0xc9, 0x9a, 0xe5, 0x9a, 0xe7, 0x34, 0xc7, 0xb4, 0x68, 0x5a, 0x0b, 0xb5, 0x04, 0x5a, 0xe5, 0x5a, 0xe7, 0xb5, 0x5e, 0x30, 0x95, 0x99, 0xee, 0xcc, 0x54, 0x66, 0x25, 0xb3, 0x8b, 0x39, 0xa1, 0xad, 0xae, 0xed, 0xa7, 0x2d, 0xd1, 0x3e, 0xa4, 0xdd, 0xab, 0x3d, 0xad, 0x63, 0xa8, 0xb3, 0x58, 0x67, 0xa3, 0x4e, 0xb3, 0xce, 0x13, 0x5d, 0x92, 0x2e, 0x5b, 0x37, 0x41, 0xb7, 0x5c, 0xb7, 0x53, 0x77, 0x42, 0x4f, 0x4b, 0x2f, 0x58, 0x2f, 0x5f, 0xaf, 0x51, 0xef, 0xa1, 0x3e, 0x51, 0x9f, 0xad, 0x9f, 0xa4, 0xbf, 0x47, 0xbf, 0x5b, 0x7f, 0xca, 0xc0, 0xd0, 0x20, 0xda, 0x60, 0x8b, 0x41, 0x9b, 0xc1, 0xa8, 0xa1, 0x8a, 0xa1, 0xbf, 0x61, 0x9e, 0x61, 0xa3, 0xe1, 0x63, 0x23, 0xaa, 0x91, 0xab, 0xd1, 0x2a, 0xa3, 0x5a, 0xa3, 0x3b, 0xc6, 0x38, 0x63, 0xb6, 0x71, 0x8a, 0xf1, 0x3e, 0xe3, 0x5b, 0x26, 0xb0, 0x89, 0x9d, 0x49, 0x92, 0x49, 0x8d, 0xc9, 0x4d, 0x53, 0xd8, 0xd4, 0xde, 0x54, 0x60, 0xba, 0xcf, 0xb4, 0xcf, 0x0c, 0x6b, 0xe6, 0x68, 0x26, 0x34, 0xab, 0x35, 0xbb, 0xc7, 0xa2, 0xb0, 0xdc, 0x59, 0x59, 0xac, 0x46, 0xd6, 0xa0, 0x39, 0xc3, 0x3c, 0xc8, 0x7c, 0xa3, 0x79, 0x9b, 0xf9, 0x2b, 0x0b, 0x3d, 0x8b, 0x58, 0x8b, 0x9d, 0x16, 0xdd, 0x16, 0x5f, 0x2c, 0xed, 0x2c, 0x53, 0x2d, 0xeb, 0x2c, 0x1f, 0x59, 0x29, 0x59, 0x05, 0x58, 0x6d, 0xb4, 0xea, 0xb0, 0xfa, 0xc3, 0xda, 0xc4, 0x9a, 0x6b, 0x5d, 0x63, 0x7d, 0xc7, 0x86, 0x6a, 0xe3, 0x63, 0xb3, 0xce, 0xa6, 0xdd, 0xe6, 0xb5, 0xad, 0xa9, 0x2d, 0xdf, 0x76, 0xbf, 0xed, 0x7d, 0x3b, 0x9a, 0x5d, 0xb0, 0xdd, 0x16, 0xbb, 0x4e, 0xbb, 0xcf, 0xf6, 0x0e, 0xf6, 0x22, 0xfb, 0x26, 0xfb, 0x31, 0x07, 0x3d, 0x87, 0x78, 0x87, 0xbd, 0x0e, 0xf7, 0xd8, 0x74, 0x76, 0x28, 0xbb, 0x84, 0x7d, 0xd5, 0x11, 0xeb, 0xe8, 0xe1, 0xb8, 0xce, 0xf1, 0x8c, 0xe3, 0x07, 0x27, 0x7b, 0x27, 0xb1, 0xd3, 0x49, 0xa7, 0xdf, 0x9d, 0x59, 0xce, 0x29, 0xce, 0x0d, 0xce, 0xa3, 0x0b, 0x0c, 0x17, 0xf0, 0x17, 0xd4, 0x2d, 0x18, 0x72, 0xd1, 0x71, 0xe1, 0xb8, 0x1c, 0x72, 0x91, 0x2e, 0x64, 0x2e, 0x8c, 0x5f, 0x78, 0x70, 0xa1, 0xd4, 0x55, 0xdb, 0x95, 0xe3, 0x5a, 0xeb, 0xfa, 0xcc, 0x4d, 0xd7, 0x8d, 0xe7, 0x76, 0xc4, 0x6d, 0xc4, 0xdd, 0xd8, 0x3d, 0xd9, 0xfd, 0xb8, 0xfb, 0x2b, 0x0f, 0x4b, 0x0f, 0x91, 0x47, 0x8b, 0xc7, 0x94, 0xa7, 0x93, 0xe7, 0x1a, 0xcf, 0x0b, 0x5e, 0x88, 0x97, 0xaf, 0x57, 0x91, 0x57, 0xaf, 0xb7, 0x92, 0xf7, 0x62, 0xef, 0x6a, 0xef, 0xa7, 0x3e, 0x3a, 0x3e, 0x89, 0x3e, 0x8d, 0x3e, 0x13, 0xbe, 0x76, 0xbe, 0xab, 0x7d, 0x2f, 0xf8, 0x61, 0xfd, 0x02, 0xfd, 0x76, 0xfa, 0xdd, 0xf3, 0xd7, 0xf0, 0xe7, 0xfa, 0xd7, 0xfb, 0x4f, 0x04, 0x38, 0x04, 0xac, 0x09, 0xe8, 0x0a, 0xa4, 0x04, 0x46, 0x04, 0x56, 0x07, 0x3e, 0x0b, 0x32, 0x09, 0x12, 0x05, 0x75, 0x04, 0xc3, 0xc1, 0x01, 0xc1, 0xbb, 0x82, 0x1f, 0x2f, 0xd2, 0x5f, 0x24, 0x5c, 0xd4, 0x16, 0x02, 0x42, 0xfc, 0x43, 0x76, 0x85, 0x3c, 0x09, 0x35, 0x0c, 0x5d, 0x15, 0xfa, 0x73, 0x18, 0x2e, 0x2c, 0x34, 0xac, 0x26, 0xec, 0x79, 0xb8, 0x55, 0x78, 0x7e, 0x78, 0x77, 0x04, 0x2d, 0x62, 0x45, 0x44, 0x43, 0xc4, 0xbb, 0x48, 0x8f, 0xc8, 0xd2, 0xc8, 0x47, 0x8b, 0x8d, 0x16, 0x4b, 0x16, 0x77, 0x46, 0xc9, 0x47, 0xc5, 0x45, 0xd5, 0x47, 0x4d, 0x45, 0x7b, 0x45, 0x97, 0x45, 0x4b, 0x97, 0x58, 0x2c, 0x59, 0xb3, 0xe4, 0x46, 0x8c, 0x5a, 0x8c, 0x20, 0xa6, 0x3d, 0x16, 0x1f, 0x1b, 0x15, 0x7b, 0x24, 0x76, 0x72, 0xa9, 0xf7, 0xd2, 0xdd, 0x4b, 0x87, 0xe3, 0xec, 0xe2, 0x0a, 0xe3, 0xee, 0x2e, 0x33, 0x5c, 0x96, 0xb3, 0xec, 0xda, 0x72, 0xb5, 0xe5, 0xa9, 0xcb, 0xcf, 0xae, 0x90, 0x5f, 0xc1, 0x59, 0x71, 0x2a, 0x1e, 0x1b, 0x1f, 0x1d, 0xdf, 0x10, 0xff, 0x89, 0x13, 0xc2, 0xa9, 0xe5, 0x4c, 0xae, 0xf4, 0x5f, 0xb9, 0x77, 0xe5, 0x04, 0xd7, 0x93, 0xbb, 0x87, 0xfb, 0x92, 0xe7, 0xc6, 0x2b, 0xe7, 0x8d, 0xf1, 0x5d, 0xf8, 0x65, 0xfc, 0x91, 0x04, 0x97, 0x84, 0xb2, 0x84, 0xd1, 0x44, 0x97, 0xc4, 0x5d, 0x89, 0x63, 0x49, 0xae, 0x49, 0x15, 0x49, 0xe3, 0x02, 0x4f, 0x41, 0xb5, 0xe0, 0x75, 0xb2, 0x5f, 0xf2, 0x81, 0xe4, 0xa9, 0x94, 0x90, 0x94, 0xa3, 0x29, 0x33, 0xa9, 0xd1, 0xa9, 0xcd, 0x69, 0x84, 0xb4, 0xf8, 0xb4, 0xd3, 0x42, 0x25, 0x61, 0x8a, 0xb0, 0x2b, 0x5d, 0x33, 0x3d, 0x27, 0xbd, 0x2f, 0xc3, 0x34, 0xa3, 0x30, 0x43, 0xba, 0xca, 0x69, 0xd5, 0xee, 0x55, 0x13, 0xa2, 0x40, 0xd1, 0x91, 0x4c, 0x28, 0x73, 0x59, 0x66, 0xbb, 0x98, 0x8e, 0xfe, 0x4c, 0xf5, 0x48, 0x8c, 0x24, 0x9b, 0x25, 0x83, 0x59, 0x0b, 0xb3, 0x6a, 0xb2, 0xde, 0x67, 0x47, 0x65, 0x9f, 0xca, 0x51, 0xcc, 0x11, 0xe6, 0xf4, 0xe4, 0x9a, 0xe4, 0x6e, 0xcb, 0x1d, 0xc9, 0xf3, 0xc9, 0xfb, 0x7e, 0x35, 0x66, 0x35, 0x77, 0x75, 0x67, 0xbe, 0x76, 0xfe, 0x86, 0xfc, 0xc1, 0x35, 0xee, 0x6b, 0x0e, 0xad, 0x85, 0xd6, 0xae, 0x5c, 0xdb, 0xb9, 0x4e, 0x77, 0x5d, 0xc1, 0xba, 0xe1, 0xf5, 0xbe, 0xeb, 0x8f, 0x6d, 0x20, 0x6d, 0x48, 0xd9, 0xf0, 0xcb, 0x46, 0xcb, 0x8d, 0x65, 0x1b, 0xdf, 0x6e, 0x8a, 0xde, 0xd4, 0x51, 0xa0, 0x51, 0xb0, 0xbe, 0x60, 0x68, 0xb3, 0xef, 0xe6, 0xc6, 0x42, 0xb9, 0x42, 0x51, 0xe1, 0xbd, 0x2d, 0xce, 0x5b, 0x0e, 0x6c, 0xc5, 0x6c, 0x15, 0x6c, 0xed, 0xdd, 0x66, 0xb3, 0xad, 0x6a, 0xdb, 0x97, 0x22, 0x5e, 0xd1, 0xf5, 0x62, 0xcb, 0xe2, 0x8a, 0xe2, 0x4f, 0x25, 0xdc, 0x92, 0xeb, 0xdf, 0x59, 0x7d, 0x57, 0xf9, 0xdd, 0xcc, 0xf6, 0x84, 0xed, 0xbd, 0xa5, 0xf6, 0xa5, 0xfb, 0x77, 0xe0, 0x76, 0x08, 0x77, 0xdc, 0xdd, 0xe9, 0xba, 0xf3, 0x58, 0x99, 0x62, 0x59, 0x5e, 0xd9, 0xd0, 0xae, 0xe0, 0x5d, 0xad, 0xe5, 0xcc, 0xf2, 0xa2, 0xf2, 0xb7, 0xbb, 0x57, 0xec, 0xbe, 0x56, 0x61, 0x5b, 0x71, 0x60, 0x0f, 0x69, 0x8f, 0x64, 0x8f, 0xb4, 0x32, 0xa8, 0xb2, 0xbd, 0x4a, 0xaf, 0x6a, 0x47, 0xd5, 0xa7, 0xea, 0xa4, 0xea, 0x81, 0x1a, 0x8f, 0x9a, 0xe6, 0xbd, 0xea, 0x7b, 0xb7, 0xed, 0x9d, 0xda, 0xc7, 0xdb, 0xd7, 0xbf, 0xdf, 0x6d, 0x7f, 0xd3, 0x01, 0x8d, 0x03, 0xc5, 0x07, 0x3e, 0x1e, 0x14, 0x1c, 0xbc, 0x7f, 0xc8, 0xf7, 0x50, 0x6b, 0xad, 0x41, 0x6d, 0xc5, 0x61, 0xdc, 0xe1, 0xac, 0xc3, 0xcf, 0xeb, 0xa2, 0xea, 0xba, 0xbf, 0x67, 0x7f, 0x5f, 0x7f, 0x44, 0xed, 0x48, 0xf1, 0x91, 0xcf, 0x47, 0x85, 0x47, 0xa5, 0xc7, 0xc2, 0x8f, 0x75, 0xd5, 0x3b, 0xd4, 0xd7, 0x37, 0xa8, 0x37, 0x94, 0x36, 0xc2, 0x8d, 0x92, 0xc6, 0xb1, 0xe3, 0x71, 0xc7, 0x6f, 0xfd, 0xe0, 0xf5, 0x43, 0x7b, 0x13, 0xab, 0xe9, 0x50, 0x33, 0xa3, 0xb9, 0xf8, 0x04, 0x38, 0x21, 0x39, 0xf1, 0xe2, 0xc7, 0xf8, 0x1f, 0xef, 0x9e, 0x0c, 0x3c, 0xd9, 0x79, 0x8a, 0x7d, 0xaa, 0xe9, 0x27, 0xfd, 0x9f, 0xf6, 0xb6, 0xd0, 0x5a, 0x8a, 0x5a, 0xa1, 0xd6, 0xdc, 0xd6, 0x89, 0xb6, 0xa4, 0x36, 0x69, 0x7b, 0x4c, 0x7b, 0xdf, 0xe9, 0x80, 0xd3, 0x9d, 0x1d, 0xce, 0x1d, 0x2d, 0x3f, 0x9b, 0xff, 0x7c, 0xf4, 0x8c, 0xf6, 0x99, 0x9a, 0xb3, 0xca, 0x67, 0x4b, 0xcf, 0x91, 0xce, 0x15, 0x9c, 0x9b, 0x39, 0x9f, 0x77, 0x7e, 0xf2, 0x42, 0xc6, 0x85, 0xf1, 0x8b, 0x89, 0x17, 0x87, 0x3a, 0x57, 0x74, 0x3e, 0xba, 0xb4, 0xe4, 0xd2, 0x9d, 0xae, 0xb0, 0xae, 0xde, 0xcb, 0x81, 0x97, 0xaf, 0x5e, 0xf1, 0xb9, 0x72, 0xa9, 0xdb, 0xbd, 0xfb, 0xfc, 0x55, 0x97, 0xab, 0x67, 0xae, 0x39, 0x5d, 0x3b, 0x7d, 0x9d, 0x7d, 0xbd, 0xed, 0x86, 0xfd, 0x8d, 0xd6, 0x1e, 0xbb, 0x9e, 0x96, 0x5f, 0xec, 0x7e, 0x69, 0xe9, 0xb5, 0xef, 0x6d, 0xbd, 0xe9, 0x70, 0xb3, 0xfd, 0x96, 0xe3, 0xad, 0x8e, 0xbe, 0x05, 0x7d, 0xe7, 0xfa, 0x5d, 0xfb, 0x2f, 0xde, 0xf6, 0xba, 0x7d, 0xe5, 0x8e, 0xff, 0x9d, 0x1b, 0x03, 0x8b, 0x06, 0xfa, 0xee, 0x2e, 0xbe, 0x7b, 0xff, 0x5e, 0xdc, 0x3d, 0xe9, 0x7d, 0xde, 0xfd, 0xd1, 0x07, 0xa9, 0x0f, 0x5e, 0x3f, 0xcc, 0x7a, 0x38, 0xfd, 0x68, 0xfd, 0x63, 0xec, 0xe3, 0xa2, 0x27, 0x0a, 0x4f, 0x2a, 0x9e, 0xaa, 0x3f, 0xad, 0xfd, 0xd5, 0xf8, 0xd7, 0x66, 0xa9, 0xbd, 0xf4, 0xec, 0xa0, 0xd7, 0x60, 0xcf, 0xb3, 0x88, 0x67, 0x8f, 0x86, 0xb8, 0x43, 0x2f, 0xff, 0x95, 0xf9, 0xaf, 0x4f, 0xc3, 0x05, 0xcf, 0xa9, 0xcf, 0x2b, 0x46, 0xb4, 0x46, 0xea, 0x47, 0xad, 0x47, 0xcf, 0x8c, 0xf9, 0x8c, 0xdd, 0x7a, 0xb1, 0xf4, 0xc5, 0xf0, 0xcb, 0x8c, 0x97, 0xd3, 0xe3, 0x85, 0xbf, 0x29, 0xfe, 0xb6, 0xf7, 0x95, 0xd1, 0xab, 0x9f, 0x7e, 0x77, 0xfb, 0xbd, 0x67, 0x62, 0xc9, 0xc4, 0xf0, 0x6b, 0xd1, 0xeb, 0x99, 0x3f, 0x4a, 0xde, 0xa8, 0xbe, 0x39, 0xfa, 0xd6, 0xf6, 0x6d, 0xe7, 0x64, 0xe8, 0xe4, 0xd3, 0x77, 0x69, 0xef, 0xa6, 0xa7, 0x8a, 0xde, 0xab, 0xbe, 0x3f, 0xf6, 0x81, 0xfd, 0xa1, 0xfb, 0x63, 0xf4, 0xc7, 0x91, 0xe9, 0xec, 0x4f, 0xf8, 0x4f, 0x95, 0x9f, 0x8d, 0x3f, 0x77, 0x7c, 0x09, 0xfc, 0xf2, 0x78, 0x26, 0x6d, 0x66, 0xe6, 0xdf, 0xf7, 0x84, 0xf3, 0xfb, 0x32, 0x3a, 0x59, 0x7e, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x03, 0xa4, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x34, 0x2d, 0x30, 0x35, 0x2d, 0x30, 0x32, 0x54, 0x31, 0x31, 0x3a, 0x30, 0x35, 0x3a, 0x30, 0x36, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x38, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x90, 0x7a, 0xe1, 0x8d, 0x00, 0x00, 0x00, 0x12, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1d, 0x63, 0x60, 0x60, 0x60, 0xf8, 0x0f, 0xc5, 0x40, 0x0a, 0x13, 0x00, 0x00, 0x35, 0xeb, 0x01, 0xff, 0x0f, 0x5e, 0xbc, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const u_int8_t FLEXHierarchyIndentPattern3x[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0xe3, 0x00, 0xef, 0x43, 0x00, 0x00, 0x0a, 0x41, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x48, 0x0d, 0x9d, 0x96, 0x77, 0x54, 0x53, 0xd9, 0x16, 0x87, 0xcf, 0xbd, 0x37, 0xbd, 0xd0, 0x12, 0x22, 0x20, 0x25, 0xf4, 0x1a, 0x7a, 0x09, 0x20, 0xd2, 0x3b, 0x48, 0x15, 0x04, 0x51, 0x89, 0x49, 0x80, 0x50, 0x02, 0x86, 0x84, 0x26, 0x76, 0x44, 0x05, 0x46, 0x14, 0x11, 0x29, 0x56, 0x64, 0x54, 0xc0, 0x01, 0x47, 0x87, 0x22, 0x63, 0x45, 0x14, 0x0b, 0x83, 0x82, 0x62, 0xd7, 0x09, 0xf2, 0x10, 0x50, 0xc6, 0xc1, 0x51, 0x44, 0x45, 0xe5, 0xdd, 0x8c, 0x6b, 0x09, 0xef, 0xad, 0x35, 0xf3, 0xde, 0x9a, 0xfd, 0xc7, 0x59, 0xdf, 0xd9, 0xe7, 0xb7, 0xd7, 0xd9, 0x67, 0xef, 0x7d, 0xd7, 0xba, 0x00, 0x50, 0xfc, 0x82, 0x04, 0xc2, 0x74, 0x58, 0x01, 0x80, 0x34, 0xa1, 0x58, 0x14, 0xee, 0xeb, 0xc1, 0x5c, 0x12, 0x13, 0xcb, 0xc4, 0xf7, 0x02, 0x18, 0x10, 0x01, 0x0e, 0x58, 0x01, 0xc0, 0xe1, 0x66, 0x66, 0x04, 0x47, 0xf8, 0x44, 0x02, 0xd4, 0xfc, 0xbd, 0x3d, 0x99, 0x99, 0xa8, 0x48, 0xc6, 0xb3, 0xf6, 0xee, 0x2e, 0x80, 0x64, 0xbb, 0xdb, 0x2c, 0xbf, 0x50, 0x26, 0x73, 0xd6, 0xff, 0x7f, 0x91, 0x22, 0x37, 0x43, 0x24, 0x06, 0x00, 0x0a, 0x45, 0xd5, 0x36, 0x3c, 0x7e, 0x26, 0x17, 0xe5, 0x02, 0x94, 0x53, 0xb3, 0xc5, 0x19, 0x32, 0xff, 0x04, 0xca, 0xf4, 0x95, 0x29, 0x32, 0x86, 0x31, 0x32, 0x16, 0xa1, 0x09, 0xa2, 0xac, 0x22, 0xe3, 0xc4, 0xaf, 0x6c, 0xf6, 0xa7, 0xe6, 0x2b, 0xbb, 0xc9, 0x98, 0x97, 0x26, 0xe4, 0xa1, 0x1a, 0x59, 0xce, 0x19, 0xbc, 0x34, 0x9e, 0x8c, 0xbb, 0x50, 0xde, 0x9a, 0x25, 0xe1, 0xa3, 0x8c, 0x04, 0xa1, 0x5c, 0x98, 0x25, 0xe0, 0x67, 0xa3, 0x7c, 0x07, 0x65, 0xbd, 0x54, 0x49, 0x9a, 0x00, 0xe5, 0xf7, 0x28, 0xd3, 0xd3, 0xf8, 0x9c, 0x4c, 0x00, 0x30, 0x14, 0x99, 0x5f, 0xcc, 0xe7, 0x26, 0xa1, 0x6c, 0x89, 0x32, 0x45, 0x14, 0x19, 0xee, 0x89, 0xf2, 0x02, 0x00, 0x08, 0x94, 0xc4, 0x39, 0xbc, 0x72, 0x0e, 0x8b, 0xf9, 0x39, 0x68, 0x9e, 0x00, 0x78, 0xa6, 0x67, 0xe4, 0x8a, 0x04, 0x89, 0x49, 0x62, 0xa6, 0x11, 0xd7, 0x98, 0x69, 0xe5, 0xe8, 0xc8, 0x66, 0xfa, 0xf1, 0xb3, 0x53, 0xf9, 0x62, 0x31, 0x2b, 0x94, 0xc3, 0x4d, 0xe1, 0x88, 0x78, 0x4c, 0xcf, 0xf4, 0xb4, 0x0c, 0x8e, 0x30, 0x17, 0x80, 0xaf, 0x6f, 0x96, 0x45, 0x01, 0x25, 0x59, 0x6d, 0x99, 0x68, 0x91, 0xed, 0xad, 0x1c, 0xed, 0xed, 0x59, 0xd6, 0xe6, 0x68, 0xf9, 0xbf, 0xd9, 0xdf, 0x1e, 0x7e, 0x53, 0xfd, 0x3d, 0xc8, 0x7a, 0xfb, 0x55, 0xf1, 0x26, 0xec, 0xcf, 0x9e, 0x41, 0x8c, 0x9e, 0x59, 0xdf, 0x6c, 0xec, 0xac, 0x2f, 0xbd, 0x16, 0x00, 0xf6, 0x24, 0x5a, 0x9b, 0x1d, 0xb3, 0xbe, 0x95, 0x55, 0x00, 0xb4, 0x6d, 0x06, 0x40, 0xe5, 0xe1, 0xac, 0x4f, 0xef, 0x20, 0x00, 0xf2, 0x05, 0x00, 0xb4, 0xde, 0x9c, 0xf3, 0x1e, 0x86, 0x6c, 0x5e, 0x92, 0xc4, 0xe2, 0x0c, 0x27, 0x0b, 0x8b, 0xec, 0xec, 0x6c, 0x73, 0x01, 0x9f, 0x6b, 0x2e, 0x2b, 0xe8, 0x37, 0xfb, 0x9f, 0x82, 0x6f, 0xca, 0xbf, 0x86, 0x39, 0xf7, 0x99, 0xcb, 0xee, 0xfb, 0x56, 0x3b, 0xa6, 0x17, 0x3f, 0x81, 0x23, 0x49, 0x15, 0x33, 0x65, 0x45, 0xe5, 0xa6, 0xa7, 0xa6, 0x4b, 0x44, 0xcc, 0xcc, 0x0c, 0x0e, 0x97, 0xcf, 0x64, 0xfd, 0xf7, 0x10, 0xff, 0xe3, 0xc0, 0x39, 0x69, 0xcd, 0xc9, 0xc3, 0x2c, 0x9c, 0x9f, 0xc0, 0x17, 0xf1, 0x85, 0xe8, 0x55, 0x51, 0xe8, 0x94, 0x09, 0x84, 0x89, 0x68, 0xbb, 0x85, 0x3c, 0x81, 0x58, 0x90, 0x2e, 0x64, 0x0a, 0x84, 0x7f, 0xd5, 0xe1, 0x7f, 0x18, 0x36, 0x27, 0x07, 0x19, 0x7e, 0x9d, 0x6b, 0x14, 0x68, 0x75, 0x5f, 0x00, 0x7d, 0x85, 0x39, 0x50, 0xb8, 0x49, 0x07, 0xc8, 0x6f, 0x3d, 0x00, 0x43, 0x23, 0x03, 0x24, 0x6e, 0x3f, 0x7a, 0x02, 0x7d, 0xeb, 0x5b, 0x10, 0x31, 0x0a, 0xc8, 0xbe, 0xbc, 0x68, 0xad, 0x91, 0xaf, 0x73, 0x8f, 0x32, 0x7a, 0xfe, 0xe7, 0xfa, 0x1f, 0x0b, 0x5c, 0x8a, 0x6e, 0xe1, 0x4c, 0x41, 0x22, 0x53, 0xe6, 0xf6, 0x0c, 0x8f, 0x64, 0x72, 0x25, 0xa2, 0x2c, 0x19, 0xa3, 0xdf, 0x84, 0x6c, 0xc1, 0x02, 0x12, 0x90, 0x07, 0x74, 0xa0, 0x0a, 0x34, 0x81, 0x2e, 0x30, 0x02, 0x2c, 0x60, 0x0d, 0x1c, 0x80, 0x33, 0x70, 0x03, 0xde, 0x20, 0x00, 0x84, 0x80, 0x48, 0x10, 0x03, 0x96, 0x03, 0x2e, 0x48, 0x02, 0x69, 0x40, 0x04, 0xb2, 0x41, 0x3e, 0xd8, 0x00, 0x0a, 0x41, 0x31, 0xd8, 0x01, 0x76, 0x83, 0x6a, 0x70, 0x00, 0xd4, 0x81, 0x7a, 0xd0, 0x04, 0x4e, 0x82, 0x36, 0x70, 0x06, 0x5c, 0x04, 0x57, 0xc0, 0x0d, 0x70, 0x0b, 0x0c, 0x80, 0x47, 0x40, 0x0a, 0x86, 0xc1, 0x4b, 0x30, 0x01, 0xde, 0x81, 0x69, 0x08, 0x82, 0xf0, 0x10, 0x15, 0xa2, 0x41, 0xaa, 0x90, 0x16, 0xa4, 0x0f, 0x99, 0x42, 0xd6, 0x10, 0x1b, 0x5a, 0x08, 0x79, 0x43, 0x41, 0x50, 0x38, 0x14, 0x03, 0xc5, 0x43, 0x89, 0x90, 0x10, 0x92, 0x40, 0xf9, 0xd0, 0x26, 0xa8, 0x18, 0x2a, 0x83, 0xaa, 0xa1, 0x43, 0x50, 0x3d, 0xf4, 0x23, 0x74, 0x1a, 0xba, 0x08, 0x5d, 0x83, 0xfa, 0xa0, 0x07, 0xd0, 0x20, 0x34, 0x06, 0xfd, 0x01, 0x7d, 0x84, 0x11, 0x98, 0x02, 0xd3, 0x61, 0x0d, 0xd8, 0x00, 0xb6, 0x80, 0xd9, 0xb0, 0x3b, 0x1c, 0x08, 0x47, 0xc2, 0xcb, 0xe0, 0x44, 0x78, 0x15, 0x9c, 0x07, 0x17, 0xc0, 0xdb, 0xe1, 0x4a, 0xb8, 0x16, 0x3e, 0x0e, 0xb7, 0xc2, 0x17, 0xe1, 0x1b, 0xf0, 0x00, 0x2c, 0x85, 0x5f, 0xc2, 0x93, 0x08, 0x40, 0xc8, 0x08, 0x03, 0xd1, 0x46, 0x58, 0x08, 0x1b, 0xf1, 0x44, 0x42, 0x90, 0x58, 0x24, 0x01, 0x11, 0x21, 0x6b, 0x91, 0x22, 0xa4, 0x02, 0xa9, 0x45, 0x9a, 0x90, 0x0e, 0xa4, 0x1b, 0xb9, 0x8d, 0x48, 0x91, 0x71, 0xe4, 0x03, 0x06, 0x87, 0xa1, 0x61, 0x98, 0x18, 0x16, 0xc6, 0x19, 0xe3, 0x87, 0x59, 0x8c, 0xe1, 0x62, 0x56, 0x61, 0xd6, 0x62, 0x4a, 0x30, 0xd5, 0x98, 0x63, 0x98, 0x56, 0x4c, 0x17, 0xe6, 0x36, 0x66, 0x10, 0x33, 0x81, 0xf9, 0x82, 0xa5, 0x62, 0xd5, 0xb1, 0xa6, 0x58, 0x27, 0xac, 0x3f, 0x76, 0x09, 0x36, 0x11, 0x9b, 0x8d, 0x2d, 0xc4, 0x56, 0x60, 0x8f, 0x60, 0x5b, 0xb0, 0x97, 0xb1, 0x03, 0xd8, 0x61, 0xec, 0x3b, 0x1c, 0x0e, 0xc7, 0xc0, 0x19, 0xe2, 0x1c, 0x70, 0x7e, 0xb8, 0x18, 0x5c, 0x32, 0x6e, 0x35, 0xae, 0x04, 0xb7, 0x0f, 0xd7, 0x8c, 0xbb, 0x80, 0xeb, 0xc3, 0x0d, 0xe1, 0x26, 0xf1, 0x78, 0xbc, 0x2a, 0xde, 0x14, 0xef, 0x82, 0x0f, 0xc1, 0x73, 0xf0, 0x62, 0x7c, 0x21, 0xbe, 0x0a, 0x7f, 0x1c, 0x7f, 0x1e, 0xdf, 0x8f, 0x1f, 0xc6, 0xbf, 0x27, 0x90, 0x09, 0x5a, 0x04, 0x6b, 0x82, 0x0f, 0x21, 0x96, 0x20, 0x24, 0x6c, 0x24, 0x54, 0x10, 0x1a, 0x08, 0xe7, 0x08, 0xfd, 0x84, 0x11, 0xc2, 0x34, 0x51, 0x81, 0xa8, 0x4f, 0x74, 0x22, 0x86, 0x10, 0x79, 0xc4, 0x5c, 0x62, 0x29, 0xb1, 0x8e, 0xd8, 0x41, 0xbc, 0x49, 0x1c, 0x26, 0x4e, 0x93, 0x14, 0x49, 0x86, 0x24, 0x17, 0x52, 0x24, 0x29, 0x99, 0xb4, 0x81, 0x54, 0x49, 0x6a, 0x22, 0x5d, 0x26, 0x3d, 0x26, 0xbd, 0x21, 0x93, 0xc9, 0x3a, 0x64, 0x47, 0x72, 0x18, 0x59, 0x40, 0x5e, 0x4f, 0xae, 0x24, 0x9f, 0x20, 0x5f, 0x25, 0x0f, 0x92, 0x3f, 0x50, 0x94, 0x28, 0x26, 0x14, 0x4f, 0x4a, 0x1c, 0x45, 0x42, 0xd9, 0x4e, 0x39, 0x4a, 0xb9, 0x40, 0x79, 0x40, 0x79, 0x43, 0xa5, 0x52, 0x0d, 0xa8, 0x6e, 0xd4, 0x58, 0xaa, 0x98, 0xba, 0x9d, 0x5a, 0x4f, 0xbd, 0x44, 0x7d, 0x4a, 0x7d, 0x2f, 0x47, 0x93, 0x33, 0x97, 0xf3, 0x97, 0xe3, 0xc9, 0xad, 0x93, 0xab, 0x91, 0x6b, 0x95, 0xeb, 0x97, 0x7b, 0x25, 0x4f, 0x94, 0xd7, 0x97, 0x77, 0x97, 0x5f, 0x2e, 0x9f, 0x27, 0x5f, 0x21, 0x7f, 0x4a, 0xfe, 0xa6, 0xfc, 0xb8, 0x02, 0x51, 0xc1, 0x40, 0xc1, 0x53, 0x81, 0xa3, 0xb0, 0x56, 0xa1, 0x46, 0xe1, 0xb4, 0xc2, 0x3d, 0x85, 0x49, 0x45, 0x9a, 0xa2, 0x95, 0x62, 0x88, 0x62, 0x9a, 0x62, 0x89, 0x62, 0x83, 0xe2, 0x35, 0xc5, 0x51, 0x25, 0xbc, 0x92, 0x81, 0x92, 0xb7, 0x12, 0x4f, 0xa9, 0x40, 0xe9, 0xb0, 0xd2, 0x25, 0xa5, 0x21, 0x1a, 0x42, 0xd3, 0xa5, 0x79, 0xd2, 0xb8, 0xb4, 0x4d, 0xb4, 0x3a, 0xda, 0x65, 0xda, 0x30, 0x1d, 0x47, 0x37, 0xa4, 0xfb, 0xd3, 0x93, 0xe9, 0xc5, 0xf4, 0x1f, 0xe8, 0xbd, 0xf4, 0x09, 0x65, 0x25, 0x65, 0x5b, 0xe5, 0x28, 0xe5, 0x1c, 0xe5, 0x1a, 0xe5, 0xb3, 0xca, 0x52, 0x06, 0xc2, 0x30, 0x60, 0xf8, 0x33, 0x52, 0x19, 0xa5, 0x8c, 0x93, 0x8c, 0xbb, 0x8c, 0x8f, 0xf3, 0x34, 0xe6, 0xb9, 0xcf, 0xe3, 0xcf, 0xdb, 0x36, 0xaf, 0x69, 0x5e, 0xff, 0xbc, 0x29, 0x95, 0xf9, 0x2a, 0x6e, 0x2a, 0x7c, 0x95, 0x22, 0x95, 0x66, 0x95, 0x01, 0x95, 0x8f, 0xaa, 0x4c, 0x55, 0x6f, 0xd5, 0x14, 0xd5, 0x9d, 0xaa, 0x6d, 0xaa, 0x4f, 0xd4, 0x30, 0x6a, 0x26, 0x6a, 0x61, 0x6a, 0xd9, 0x6a, 0xfb, 0xd5, 0x2e, 0xab, 0x8d, 0xcf, 0xa7, 0xcf, 0x77, 0x9e, 0xcf, 0x9d, 0x5f, 0x34, 0xff, 0xe4, 0xfc, 0x87, 0xea, 0xb0, 0xba, 0x89, 0x7a, 0xb8, 0xfa, 0x6a, 0xf5, 0xc3, 0xea, 0x3d, 0xea, 0x93, 0x1a, 0x9a, 0x1a, 0xbe, 0x1a, 0x19, 0x1a, 0x55, 0x1a, 0x97, 0x34, 0xc6, 0x35, 0x19, 0x9a, 0x6e, 0x9a, 0xc9, 0x9a, 0xe5, 0x9a, 0xe7, 0x34, 0xc7, 0xb4, 0x68, 0x5a, 0x0b, 0xb5, 0x04, 0x5a, 0xe5, 0x5a, 0xe7, 0xb5, 0x5e, 0x30, 0x95, 0x99, 0xee, 0xcc, 0x54, 0x66, 0x25, 0xb3, 0x8b, 0x39, 0xa1, 0xad, 0xae, 0xed, 0xa7, 0x2d, 0xd1, 0x3e, 0xa4, 0xdd, 0xab, 0x3d, 0xad, 0x63, 0xa8, 0xb3, 0x58, 0x67, 0xa3, 0x4e, 0xb3, 0xce, 0x13, 0x5d, 0x92, 0x2e, 0x5b, 0x37, 0x41, 0xb7, 0x5c, 0xb7, 0x53, 0x77, 0x42, 0x4f, 0x4b, 0x2f, 0x58, 0x2f, 0x5f, 0xaf, 0x51, 0xef, 0xa1, 0x3e, 0x51, 0x9f, 0xad, 0x9f, 0xa4, 0xbf, 0x47, 0xbf, 0x5b, 0x7f, 0xca, 0xc0, 0xd0, 0x20, 0xda, 0x60, 0x8b, 0x41, 0x9b, 0xc1, 0xa8, 0xa1, 0x8a, 0xa1, 0xbf, 0x61, 0x9e, 0x61, 0xa3, 0xe1, 0x63, 0x23, 0xaa, 0x91, 0xab, 0xd1, 0x2a, 0xa3, 0x5a, 0xa3, 0x3b, 0xc6, 0x38, 0x63, 0xb6, 0x71, 0x8a, 0xf1, 0x3e, 0xe3, 0x5b, 0x26, 0xb0, 0x89, 0x9d, 0x49, 0x92, 0x49, 0x8d, 0xc9, 0x4d, 0x53, 0xd8, 0xd4, 0xde, 0x54, 0x60, 0xba, 0xcf, 0xb4, 0xcf, 0x0c, 0x6b, 0xe6, 0x68, 0x26, 0x34, 0xab, 0x35, 0xbb, 0xc7, 0xa2, 0xb0, 0xdc, 0x59, 0x59, 0xac, 0x46, 0xd6, 0xa0, 0x39, 0xc3, 0x3c, 0xc8, 0x7c, 0xa3, 0x79, 0x9b, 0xf9, 0x2b, 0x0b, 0x3d, 0x8b, 0x58, 0x8b, 0x9d, 0x16, 0xdd, 0x16, 0x5f, 0x2c, 0xed, 0x2c, 0x53, 0x2d, 0xeb, 0x2c, 0x1f, 0x59, 0x29, 0x59, 0x05, 0x58, 0x6d, 0xb4, 0xea, 0xb0, 0xfa, 0xc3, 0xda, 0xc4, 0x9a, 0x6b, 0x5d, 0x63, 0x7d, 0xc7, 0x86, 0x6a, 0xe3, 0x63, 0xb3, 0xce, 0xa6, 0xdd, 0xe6, 0xb5, 0xad, 0xa9, 0x2d, 0xdf, 0x76, 0xbf, 0xed, 0x7d, 0x3b, 0x9a, 0x5d, 0xb0, 0xdd, 0x16, 0xbb, 0x4e, 0xbb, 0xcf, 0xf6, 0x0e, 0xf6, 0x22, 0xfb, 0x26, 0xfb, 0x31, 0x07, 0x3d, 0x87, 0x78, 0x87, 0xbd, 0x0e, 0xf7, 0xd8, 0x74, 0x76, 0x28, 0xbb, 0x84, 0x7d, 0xd5, 0x11, 0xeb, 0xe8, 0xe1, 0xb8, 0xce, 0xf1, 0x8c, 0xe3, 0x07, 0x27, 0x7b, 0x27, 0xb1, 0xd3, 0x49, 0xa7, 0xdf, 0x9d, 0x59, 0xce, 0x29, 0xce, 0x0d, 0xce, 0xa3, 0x0b, 0x0c, 0x17, 0xf0, 0x17, 0xd4, 0x2d, 0x18, 0x72, 0xd1, 0x71, 0xe1, 0xb8, 0x1c, 0x72, 0x91, 0x2e, 0x64, 0x2e, 0x8c, 0x5f, 0x78, 0x70, 0xa1, 0xd4, 0x55, 0xdb, 0x95, 0xe3, 0x5a, 0xeb, 0xfa, 0xcc, 0x4d, 0xd7, 0x8d, 0xe7, 0x76, 0xc4, 0x6d, 0xc4, 0xdd, 0xd8, 0x3d, 0xd9, 0xfd, 0xb8, 0xfb, 0x2b, 0x0f, 0x4b, 0x0f, 0x91, 0x47, 0x8b, 0xc7, 0x94, 0xa7, 0x93, 0xe7, 0x1a, 0xcf, 0x0b, 0x5e, 0x88, 0x97, 0xaf, 0x57, 0x91, 0x57, 0xaf, 0xb7, 0x92, 0xf7, 0x62, 0xef, 0x6a, 0xef, 0xa7, 0x3e, 0x3a, 0x3e, 0x89, 0x3e, 0x8d, 0x3e, 0x13, 0xbe, 0x76, 0xbe, 0xab, 0x7d, 0x2f, 0xf8, 0x61, 0xfd, 0x02, 0xfd, 0x76, 0xfa, 0xdd, 0xf3, 0xd7, 0xf0, 0xe7, 0xfa, 0xd7, 0xfb, 0x4f, 0x04, 0x38, 0x04, 0xac, 0x09, 0xe8, 0x0a, 0xa4, 0x04, 0x46, 0x04, 0x56, 0x07, 0x3e, 0x0b, 0x32, 0x09, 0x12, 0x05, 0x75, 0x04, 0xc3, 0xc1, 0x01, 0xc1, 0xbb, 0x82, 0x1f, 0x2f, 0xd2, 0x5f, 0x24, 0x5c, 0xd4, 0x16, 0x02, 0x42, 0xfc, 0x43, 0x76, 0x85, 0x3c, 0x09, 0x35, 0x0c, 0x5d, 0x15, 0xfa, 0x73, 0x18, 0x2e, 0x2c, 0x34, 0xac, 0x26, 0xec, 0x79, 0xb8, 0x55, 0x78, 0x7e, 0x78, 0x77, 0x04, 0x2d, 0x62, 0x45, 0x44, 0x43, 0xc4, 0xbb, 0x48, 0x8f, 0xc8, 0xd2, 0xc8, 0x47, 0x8b, 0x8d, 0x16, 0x4b, 0x16, 0x77, 0x46, 0xc9, 0x47, 0xc5, 0x45, 0xd5, 0x47, 0x4d, 0x45, 0x7b, 0x45, 0x97, 0x45, 0x4b, 0x97, 0x58, 0x2c, 0x59, 0xb3, 0xe4, 0x46, 0x8c, 0x5a, 0x8c, 0x20, 0xa6, 0x3d, 0x16, 0x1f, 0x1b, 0x15, 0x7b, 0x24, 0x76, 0x72, 0xa9, 0xf7, 0xd2, 0xdd, 0x4b, 0x87, 0xe3, 0xec, 0xe2, 0x0a, 0xe3, 0xee, 0x2e, 0x33, 0x5c, 0x96, 0xb3, 0xec, 0xda, 0x72, 0xb5, 0xe5, 0xa9, 0xcb, 0xcf, 0xae, 0x90, 0x5f, 0xc1, 0x59, 0x71, 0x2a, 0x1e, 0x1b, 0x1f, 0x1d, 0xdf, 0x10, 0xff, 0x89, 0x13, 0xc2, 0xa9, 0xe5, 0x4c, 0xae, 0xf4, 0x5f, 0xb9, 0x77, 0xe5, 0x04, 0xd7, 0x93, 0xbb, 0x87, 0xfb, 0x92, 0xe7, 0xc6, 0x2b, 0xe7, 0x8d, 0xf1, 0x5d, 0xf8, 0x65, 0xfc, 0x91, 0x04, 0x97, 0x84, 0xb2, 0x84, 0xd1, 0x44, 0x97, 0xc4, 0x5d, 0x89, 0x63, 0x49, 0xae, 0x49, 0x15, 0x49, 0xe3, 0x02, 0x4f, 0x41, 0xb5, 0xe0, 0x75, 0xb2, 0x5f, 0xf2, 0x81, 0xe4, 0xa9, 0x94, 0x90, 0x94, 0xa3, 0x29, 0x33, 0xa9, 0xd1, 0xa9, 0xcd, 0x69, 0x84, 0xb4, 0xf8, 0xb4, 0xd3, 0x42, 0x25, 0x61, 0x8a, 0xb0, 0x2b, 0x5d, 0x33, 0x3d, 0x27, 0xbd, 0x2f, 0xc3, 0x34, 0xa3, 0x30, 0x43, 0xba, 0xca, 0x69, 0xd5, 0xee, 0x55, 0x13, 0xa2, 0x40, 0xd1, 0x91, 0x4c, 0x28, 0x73, 0x59, 0x66, 0xbb, 0x98, 0x8e, 0xfe, 0x4c, 0xf5, 0x48, 0x8c, 0x24, 0x9b, 0x25, 0x83, 0x59, 0x0b, 0xb3, 0x6a, 0xb2, 0xde, 0x67, 0x47, 0x65, 0x9f, 0xca, 0x51, 0xcc, 0x11, 0xe6, 0xf4, 0xe4, 0x9a, 0xe4, 0x6e, 0xcb, 0x1d, 0xc9, 0xf3, 0xc9, 0xfb, 0x7e, 0x35, 0x66, 0x35, 0x77, 0x75, 0x67, 0xbe, 0x76, 0xfe, 0x86, 0xfc, 0xc1, 0x35, 0xee, 0x6b, 0x0e, 0xad, 0x85, 0xd6, 0xae, 0x5c, 0xdb, 0xb9, 0x4e, 0x77, 0x5d, 0xc1, 0xba, 0xe1, 0xf5, 0xbe, 0xeb, 0x8f, 0x6d, 0x20, 0x6d, 0x48, 0xd9, 0xf0, 0xcb, 0x46, 0xcb, 0x8d, 0x65, 0x1b, 0xdf, 0x6e, 0x8a, 0xde, 0xd4, 0x51, 0xa0, 0x51, 0xb0, 0xbe, 0x60, 0x68, 0xb3, 0xef, 0xe6, 0xc6, 0x42, 0xb9, 0x42, 0x51, 0xe1, 0xbd, 0x2d, 0xce, 0x5b, 0x0e, 0x6c, 0xc5, 0x6c, 0x15, 0x6c, 0xed, 0xdd, 0x66, 0xb3, 0xad, 0x6a, 0xdb, 0x97, 0x22, 0x5e, 0xd1, 0xf5, 0x62, 0xcb, 0xe2, 0x8a, 0xe2, 0x4f, 0x25, 0xdc, 0x92, 0xeb, 0xdf, 0x59, 0x7d, 0x57, 0xf9, 0xdd, 0xcc, 0xf6, 0x84, 0xed, 0xbd, 0xa5, 0xf6, 0xa5, 0xfb, 0x77, 0xe0, 0x76, 0x08, 0x77, 0xdc, 0xdd, 0xe9, 0xba, 0xf3, 0x58, 0x99, 0x62, 0x59, 0x5e, 0xd9, 0xd0, 0xae, 0xe0, 0x5d, 0xad, 0xe5, 0xcc, 0xf2, 0xa2, 0xf2, 0xb7, 0xbb, 0x57, 0xec, 0xbe, 0x56, 0x61, 0x5b, 0x71, 0x60, 0x0f, 0x69, 0x8f, 0x64, 0x8f, 0xb4, 0x32, 0xa8, 0xb2, 0xbd, 0x4a, 0xaf, 0x6a, 0x47, 0xd5, 0xa7, 0xea, 0xa4, 0xea, 0x81, 0x1a, 0x8f, 0x9a, 0xe6, 0xbd, 0xea, 0x7b, 0xb7, 0xed, 0x9d, 0xda, 0xc7, 0xdb, 0xd7, 0xbf, 0xdf, 0x6d, 0x7f, 0xd3, 0x01, 0x8d, 0x03, 0xc5, 0x07, 0x3e, 0x1e, 0x14, 0x1c, 0xbc, 0x7f, 0xc8, 0xf7, 0x50, 0x6b, 0xad, 0x41, 0x6d, 0xc5, 0x61, 0xdc, 0xe1, 0xac, 0xc3, 0xcf, 0xeb, 0xa2, 0xea, 0xba, 0xbf, 0x67, 0x7f, 0x5f, 0x7f, 0x44, 0xed, 0x48, 0xf1, 0x91, 0xcf, 0x47, 0x85, 0x47, 0xa5, 0xc7, 0xc2, 0x8f, 0x75, 0xd5, 0x3b, 0xd4, 0xd7, 0x37, 0xa8, 0x37, 0x94, 0x36, 0xc2, 0x8d, 0x92, 0xc6, 0xb1, 0xe3, 0x71, 0xc7, 0x6f, 0xfd, 0xe0, 0xf5, 0x43, 0x7b, 0x13, 0xab, 0xe9, 0x50, 0x33, 0xa3, 0xb9, 0xf8, 0x04, 0x38, 0x21, 0x39, 0xf1, 0xe2, 0xc7, 0xf8, 0x1f, 0xef, 0x9e, 0x0c, 0x3c, 0xd9, 0x79, 0x8a, 0x7d, 0xaa, 0xe9, 0x27, 0xfd, 0x9f, 0xf6, 0xb6, 0xd0, 0x5a, 0x8a, 0x5a, 0xa1, 0xd6, 0xdc, 0xd6, 0x89, 0xb6, 0xa4, 0x36, 0x69, 0x7b, 0x4c, 0x7b, 0xdf, 0xe9, 0x80, 0xd3, 0x9d, 0x1d, 0xce, 0x1d, 0x2d, 0x3f, 0x9b, 0xff, 0x7c, 0xf4, 0x8c, 0xf6, 0x99, 0x9a, 0xb3, 0xca, 0x67, 0x4b, 0xcf, 0x91, 0xce, 0x15, 0x9c, 0x9b, 0x39, 0x9f, 0x77, 0x7e, 0xf2, 0x42, 0xc6, 0x85, 0xf1, 0x8b, 0x89, 0x17, 0x87, 0x3a, 0x57, 0x74, 0x3e, 0xba, 0xb4, 0xe4, 0xd2, 0x9d, 0xae, 0xb0, 0xae, 0xde, 0xcb, 0x81, 0x97, 0xaf, 0x5e, 0xf1, 0xb9, 0x72, 0xa9, 0xdb, 0xbd, 0xfb, 0xfc, 0x55, 0x97, 0xab, 0x67, 0xae, 0x39, 0x5d, 0x3b, 0x7d, 0x9d, 0x7d, 0xbd, 0xed, 0x86, 0xfd, 0x8d, 0xd6, 0x1e, 0xbb, 0x9e, 0x96, 0x5f, 0xec, 0x7e, 0x69, 0xe9, 0xb5, 0xef, 0x6d, 0xbd, 0xe9, 0x70, 0xb3, 0xfd, 0x96, 0xe3, 0xad, 0x8e, 0xbe, 0x05, 0x7d, 0xe7, 0xfa, 0x5d, 0xfb, 0x2f, 0xde, 0xf6, 0xba, 0x7d, 0xe5, 0x8e, 0xff, 0x9d, 0x1b, 0x03, 0x8b, 0x06, 0xfa, 0xee, 0x2e, 0xbe, 0x7b, 0xff, 0x5e, 0xdc, 0x3d, 0xe9, 0x7d, 0xde, 0xfd, 0xd1, 0x07, 0xa9, 0x0f, 0x5e, 0x3f, 0xcc, 0x7a, 0x38, 0xfd, 0x68, 0xfd, 0x63, 0xec, 0xe3, 0xa2, 0x27, 0x0a, 0x4f, 0x2a, 0x9e, 0xaa, 0x3f, 0xad, 0xfd, 0xd5, 0xf8, 0xd7, 0x66, 0xa9, 0xbd, 0xf4, 0xec, 0xa0, 0xd7, 0x60, 0xcf, 0xb3, 0x88, 0x67, 0x8f, 0x86, 0xb8, 0x43, 0x2f, 0xff, 0x95, 0xf9, 0xaf, 0x4f, 0xc3, 0x05, 0xcf, 0xa9, 0xcf, 0x2b, 0x46, 0xb4, 0x46, 0xea, 0x47, 0xad, 0x47, 0xcf, 0x8c, 0xf9, 0x8c, 0xdd, 0x7a, 0xb1, 0xf4, 0xc5, 0xf0, 0xcb, 0x8c, 0x97, 0xd3, 0xe3, 0x85, 0xbf, 0x29, 0xfe, 0xb6, 0xf7, 0x95, 0xd1, 0xab, 0x9f, 0x7e, 0x77, 0xfb, 0xbd, 0x67, 0x62, 0xc9, 0xc4, 0xf0, 0x6b, 0xd1, 0xeb, 0x99, 0x3f, 0x4a, 0xde, 0xa8, 0xbe, 0x39, 0xfa, 0xd6, 0xf6, 0x6d, 0xe7, 0x64, 0xe8, 0xe4, 0xd3, 0x77, 0x69, 0xef, 0xa6, 0xa7, 0x8a, 0xde, 0xab, 0xbe, 0x3f, 0xf6, 0x81, 0xfd, 0xa1, 0xfb, 0x63, 0xf4, 0xc7, 0x91, 0xe9, 0xec, 0x4f, 0xf8, 0x4f, 0x95, 0x9f, 0x8d, 0x3f, 0x77, 0x7c, 0x09, 0xfc, 0xf2, 0x78, 0x26, 0x6d, 0x66, 0xe6, 0xdf, 0xf7, 0x84, 0xf3, 0xfb, 0x32, 0x3a, 0x59, 0x7e, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x03, 0xa4, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x34, 0x2d, 0x30, 0x35, 0x2d, 0x30, 0x32, 0x54, 0x31, 0x31, 0x3a, 0x30, 0x35, 0x3a, 0x30, 0x36, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x33, 0x2e, 0x31, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x35, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x38, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x90, 0x7a, 0xe1, 0x8d, 0x00, 0x00, 0x00, 0x12, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1d, 0x63, 0x60, 0x60, 0x60, 0xf8, 0x0f, 0xc5, 0x40, 0x0a, 0x13, 0x00, 0x00, 0x35, 0xeb, 0x01, 0xff, 0x0f, 0x5e, 0xbc, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; #pragma clang diagnostic pop @implementation FLEXResources #define FLEXImage(base) ( \ (UIScreen.mainScreen.scale > 1.5) ? \ ( (UIScreen.mainScreen.scale > 2.5) ? \ [self imageWithBytesNoCopy:(void *)base##3x length:sizeof(base##3x) scale:3.0] : \ [self imageWithBytesNoCopy:(void *)base##2x length:sizeof(base##2x) scale:2.0] \ ) : \ nil \ ) #define FLEXImageTemplate(base) ([FLEXImage(base) imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]) #define FLEXRetinaOnlyImage(base) ([self imageWithBytesNoCopy:(void *)(base) length:sizeof(base) scale:2.0]) #pragma mark - FLEX Toolbar Icons + (UIImage *)closeIcon { return FLEXImageTemplate(FLEXCloseIcon); } + (UIImage *)dragHandle { return FLEXImageTemplate(FLEXDragHandle); } + (UIImage *)globalsIcon { return FLEXImageTemplate(FLEXGlobalsIcon); } + (UIImage *)hierarchyIcon { return FLEXImageTemplate(FLEXHierarchyIcon); } + (UIImage *)recentIcon { return FLEXImageTemplate(FLEXRecentTabIcon); } + (UIImage *)moveIcon { return FLEXImageTemplate(FLEXMoveIcon); } + (UIImage *)selectIcon { return FLEXImageTemplate(FLEXSelectIcon); } #pragma mark - Toolbar Icons + (UIImage *)bookmarksIcon { return FLEXImage(FLEXBookmarksIcon); } + (UIImage *)openTabsIcon { return FLEXImage(FLEXOpenTabsIcon); } + (UIImage *)moreIcon { return FLEXImage(FLEXMoreIcon); } + (UIImage *)gearIcon { return FLEXImage(FLEXGearIcon); } + (UIImage *)scrollToBottomIcon { return FLEXImage(FLEXCircleDownArrowIcon); } #pragma mark - Content Type Icons + (UIImage *)jsonIcon { return FLEXImage(FLEXJSONIcon); } + (UIImage *)textPlainIcon { return FLEXImage(FLEXTextPlainIcon); } + (UIImage *)htmlIcon { return FLEXImage(FLEXHTMLIcon); } + (UIImage *)audioIcon { return FLEXImage(FLEXAudioIcon); } + (UIImage *)jsIcon { return FLEXImage(FLEXJSIcon); } + (UIImage *)plistIcon { return FLEXImage(FLEXPlistIcon); } + (UIImage *)textIcon { return FLEXImage(FLEXTextIcon); } + (UIImage *)videoIcon { return FLEXImage(FLEXVideoIcon); } + (UIImage *)xmlIcon { return FLEXImage(FLEXXMLIcon); } + (UIImage *)binaryIcon { return FLEXImage(FLEXBinaryIcon); } #pragma mark - 3D Explorer Icons + (UIImage *)toggle2DIcon { return FLEXImage(FLEXToggle2DIcon); } + (UIImage *)toggle3DIcon { return FLEXImage(FLEXToggle3DIcon); } + (UIImage *)rangeSliderLeftHandle { return FLEXImage(FLEXRangeSliderLeftHandle); } + (UIImage *)rangeSliderRightHandle { return FLEXImage(FLEXRangeSliderRightHandle); } + (UIImage *)rangeSliderTrack { UIEdgeInsets cap = UIEdgeInsetsMake(0, 5, 0, 4); return [FLEXImage(FLEXRangeSliderTrack) resizableImageWithCapInsets:cap]; } + (UIImage *)rangeSliderFill { UIEdgeInsets cap = UIEdgeInsetsMake(0, 5, 0, 4); return [FLEXImage(FLEXRangeSliderFill) resizableImageWithCapInsets:cap]; } #pragma mark - Misc Icons + (UIImage *)checkerPattern { return FLEXImage(FLEXCheckerPattern); } + (UIColor *)checkerPatternColor { return [UIColor colorWithPatternImage:FLEXResources.checkerPattern]; } + (UIImage *)hierarchyIndentPattern { return FLEXImageTemplate(FLEXHierarchyIndentPattern); } #undef FLEXImage #undef FLEXRetinaOnlyImage #pragma mark - Helpers + (UIImage *)imageWithBytesNoCopy:(void *)bytes length:(NSUInteger)length scale:(CGFloat)scale { NSData *data = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:NO]; return [UIImage imageWithData:data scale:scale]; } @end ================================================ FILE: Classes/Utility/FLEXUtility.h ================================================ // // FLEXUtility.h // Flipboard // // Created by Ryan Olson on 4/18/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import #import #import #import #import "FLEXTypeEncodingParser.h" #import "FLEXAlert.h" #import "NSArray+FLEX.h" #import "UIFont+FLEX.h" #import "FLEXMacros.h" @interface FLEXUtility : NSObject /// The key window of the app, if it is not a \c FLEXWindow. /// If it is, then \c FLEXWindow.previousKeyWindow is returned. @property (nonatomic, readonly, class) UIWindow *appKeyWindow; /// @return the result of +[UIWindow allWindowsIncludingInternalWindows:onlyVisibleWindows:] @property (nonatomic, readonly, class) NSArray *allWindows; /// The first active \c UIWindowScene of the app. @property (nonatomic, readonly, class) UIWindowScene *activeScene API_AVAILABLE(ios(13.0)); /// @return top-most view controller of the given window + (UIViewController *)topViewControllerInWindow:(UIWindow *)window; + (UIColor *)consistentRandomColorForObject:(id)object; + (NSString *)descriptionForView:(UIView *)view includingFrame:(BOOL)includeFrame; + (NSString *)stringForCGRect:(CGRect)rect; + (UIViewController *)viewControllerForView:(UIView *)view; + (UIViewController *)viewControllerForAncestralView:(UIView *)view; + (UIImage *)previewImageForView:(UIView *)view; + (UIImage *)previewImageForLayer:(CALayer *)layer; + (NSString *)detailDescriptionForView:(UIView *)view; + (UIImage *)circularImageWithColor:(UIColor *)color radius:(CGFloat)radius; + (UIColor *)hierarchyIndentPatternColor; + (NSString *)pointerToString:(void *)ptr; + (NSString *)addressOfObject:(id)object; + (NSString *)stringByEscapingHTMLEntitiesInString:(NSString *)originalString; + (UIInterfaceOrientationMask)infoPlistSupportedInterfaceOrientationsMask; + (UIImage *)thumbnailedImageWithMaxPixelDimension:(NSInteger)dimension fromImageData:(NSData *)data; + (NSString *)stringFromRequestDuration:(NSTimeInterval)duration; + (NSString *)statusCodeStringFromURLResponse:(NSURLResponse *)response; + (BOOL)isErrorStatusCodeFromURLResponse:(NSURLResponse *)response; + (NSArray *)itemsFromQueryString:(NSString *)query; + (NSString *)prettyJSONStringFromData:(NSData *)data; + (BOOL)isValidJSONData:(NSData *)data; + (NSData *)inflatedDataFromCompressedData:(NSData *)compressedData; + (BOOL)hasCompressedContentEncoding:(NSURLRequest *)request; // Swizzling utilities + (SEL)swizzledSelectorForSelector:(SEL)selector; + (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector class:(Class)cls; + (void)replaceImplementationOfKnownSelector:(SEL)originalSelector onClass:(Class)cls withBlock:(id)block swizzledSelector:(SEL)swizzledSelector; + (void)replaceImplementationOfSelector:(SEL)selector withSelector:(SEL)swizzledSelector forClass:(Class)cls withMethodDescription:(struct objc_method_description)methodDescription implementationBlock:(id)implementationBlock undefinedBlock:(id)undefinedBlock; @end ================================================ FILE: Classes/Utility/FLEXUtility.m ================================================ // // FLEXUtility.m // Flipboard // // Created by Ryan Olson on 4/18/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXUtility.h" #import "FLEXResources.h" #import "FLEXWindow.h" #import #import #import BOOL FLEXConstructorsShouldRun(void) { #if FLEX_DISABLE_CTORS return NO; #else static BOOL _FLEXConstructorsShouldRun_storage = YES; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *key = @"FLEX_SKIP_INIT"; if (getenv(key.UTF8String) || [NSUserDefaults.standardUserDefaults boolForKey:key]) { _FLEXConstructorsShouldRun_storage = NO; } }); return _FLEXConstructorsShouldRun_storage; #endif } @implementation FLEXUtility + (UIWindow *)appKeyWindow { // First, check UIApplication.keyWindow FLEXWindow *window = (id)UIApplication.sharedApplication.keyWindow; if (window) { if ([window isKindOfClass:[FLEXWindow class]]) { return window.previousKeyWindow; } return window; } // As of iOS 13, UIApplication.keyWindow does not return nil, // so this is more of a safeguard against it returning nil in the future. // // Also, these are obviously not all FLEXWindows; FLEXWindow is used // so we can call window.previousKeyWindow without an ugly cast for (FLEXWindow *window in UIApplication.sharedApplication.windows) { if (window.isKeyWindow) { if ([window isKindOfClass:[FLEXWindow class]]) { return window.previousKeyWindow; } return window; } } return nil; } + (UIWindowScene *)activeScene { for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { // Look for an active UIWindowScene if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) { return (UIWindowScene *)scene; } } return nil; } + (UIViewController *)topViewControllerInWindow:(UIWindow *)window { UIViewController *topViewController = window.rootViewController; while (topViewController.presentedViewController) { topViewController = topViewController.presentedViewController; } return topViewController; } + (UIColor *)consistentRandomColorForObject:(id)object { CGFloat hue = (((NSUInteger)object >> 4) % 256) / 255.0; return [UIColor colorWithHue:hue saturation:1.0 brightness:1.0 alpha:1.0]; } + (NSString *)descriptionForView:(UIView *)view includingFrame:(BOOL)includeFrame { NSString *description = [[view class] description]; NSString *viewControllerDescription = [[[self viewControllerForView:view] class] description]; if (viewControllerDescription.length > 0) { description = [description stringByAppendingFormat:@" (%@)", viewControllerDescription]; } if (includeFrame) { description = [description stringByAppendingFormat:@" %@", [self stringForCGRect:view.frame]]; } if (view.accessibilityLabel.length > 0 || view.accessibilityIdentifier.length > 0) { description = [description stringByAppendingFormat:@" · %@", view.accessibilityLabel.length > 0 ? view.accessibilityLabel : view.accessibilityIdentifier]; } return description; } + (NSString *)stringForCGRect:(CGRect)rect { return [NSString stringWithFormat:@"{(%g, %g), (%g, %g)}", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height ]; } + (UIViewController *)viewControllerForView:(UIView *)view { NSString *viewDelegate = @"_viewDelegate"; if ([view respondsToSelector:NSSelectorFromString(viewDelegate)]) { return [view valueForKey:viewDelegate]; } return nil; } + (UIViewController *)viewControllerForAncestralView:(UIView *)view { NSString *_viewControllerForAncestor = @"_viewControllerForAncestor"; if ([view respondsToSelector:NSSelectorFromString(_viewControllerForAncestor)]) { return [view valueForKey:_viewControllerForAncestor]; } return nil; } + (UIImage *)previewImageForView:(UIView *)view { if (CGRectIsEmpty(view.bounds)) { return [UIImage new]; } CGSize viewSize = view.bounds.size; UIGraphicsBeginImageContextWithOptions(viewSize, NO, 0.0); [view drawViewHierarchyInRect:CGRectMake(0, 0, viewSize.width, viewSize.height) afterScreenUpdates:YES]; UIImage *previewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return previewImage; } + (UIImage *)previewImageForLayer:(CALayer *)layer { if (CGRectIsEmpty(layer.bounds)) { return nil; } UIGraphicsBeginImageContextWithOptions(layer.bounds.size, NO, 0.0); CGContextRef imageContext = UIGraphicsGetCurrentContext(); [layer renderInContext:imageContext]; UIImage *previewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return previewImage; } + (NSString *)detailDescriptionForView:(UIView *)view { return [NSString stringWithFormat:@"frame %@", [self stringForCGRect:view.frame]]; } + (UIImage *)circularImageWithColor:(UIColor *)color radius:(CGFloat)radius { CGFloat diameter = radius * 2.0; UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), NO, 0.0); CGContextRef imageContext = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(imageContext, color.CGColor); CGContextFillEllipseInRect(imageContext, CGRectMake(0, 0, diameter, diameter)); UIImage *circularImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return circularImage; } + (UIColor *)hierarchyIndentPatternColor { static UIColor *patternColor = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ UIImage *indentationPatternImage = FLEXResources.hierarchyIndentPattern; patternColor = [UIColor colorWithPatternImage:indentationPatternImage]; if (@available(iOS 13.0, *)) { // Create a dark mode version UIGraphicsBeginImageContextWithOptions( indentationPatternImage.size, NO, indentationPatternImage.scale ); [FLEXColor.iconColor set]; [indentationPatternImage drawInRect:CGRectMake( 0, 0, indentationPatternImage.size.width, indentationPatternImage.size.height )]; UIImage *darkModePatternImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // Create dynamic color provider patternColor = [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traitCollection) { return (traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight ? [UIColor colorWithPatternImage:indentationPatternImage] : [UIColor colorWithPatternImage:darkModePatternImage]); }]; } }); return patternColor; } + (NSString *)applicationImageName { return NSBundle.mainBundle.executablePath; } + (NSString *)applicationName { return FLEXUtility.applicationImageName.lastPathComponent; } + (NSString *)pointerToString:(void *)ptr { return [NSString stringWithFormat:@"%p", ptr]; } + (NSString *)addressOfObject:(id)object { return [NSString stringWithFormat:@"%p", object]; } + (NSString *)stringByEscapingHTMLEntitiesInString:(NSString *)originalString { static NSDictionary *escapingDictionary = nil; static NSRegularExpression *regex = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ escapingDictionary = @{ @" " : @" ", @">" : @">", @"<" : @"<", @"&" : @"&", @"'" : @"'", @"\"" : @""", @"«" : @"«", @"»" : @"»" }; regex = [NSRegularExpression regularExpressionWithPattern:@"(&|>|<|'|\"|«|»)" options:0 error:NULL]; }); NSMutableString *mutableString = originalString.mutableCopy; NSArray *matches = [regex matchesInString:mutableString options:0 range:NSMakeRange(0, mutableString.length) ]; for (NSTextCheckingResult *result in matches.reverseObjectEnumerator) { NSString *foundString = [mutableString substringWithRange:result.range]; NSString *replacementString = escapingDictionary[foundString]; if (replacementString) { [mutableString replaceCharactersInRange:result.range withString:replacementString]; } } return [mutableString copy]; } + (UIInterfaceOrientationMask)infoPlistSupportedInterfaceOrientationsMask { NSArray *supportedOrientations = NSBundle.mainBundle.infoDictionary[@"UISupportedInterfaceOrientations"]; UIInterfaceOrientationMask supportedOrientationsMask = 0; if ([supportedOrientations containsObject:@"UIInterfaceOrientationPortrait"]) { supportedOrientationsMask |= UIInterfaceOrientationMaskPortrait; } if ([supportedOrientations containsObject:@"UIInterfaceOrientationMaskLandscapeRight"]) { supportedOrientationsMask |= UIInterfaceOrientationMaskLandscapeRight; } if ([supportedOrientations containsObject:@"UIInterfaceOrientationMaskPortraitUpsideDown"]) { supportedOrientationsMask |= UIInterfaceOrientationMaskPortraitUpsideDown; } if ([supportedOrientations containsObject:@"UIInterfaceOrientationLandscapeLeft"]) { supportedOrientationsMask |= UIInterfaceOrientationMaskLandscapeLeft; } return supportedOrientationsMask; } + (UIImage *)thumbnailedImageWithMaxPixelDimension:(NSInteger)dimension fromImageData:(NSData *)data { UIImage *thumbnail = nil; CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, 0); if (imageSource) { NSDictionary *options = @{ (__bridge id)kCGImageSourceCreateThumbnailWithTransform : @YES, (__bridge id)kCGImageSourceCreateThumbnailFromImageAlways : @YES, (__bridge id)kCGImageSourceThumbnailMaxPixelSize : @(dimension) }; CGImageRef scaledImageRef = CGImageSourceCreateThumbnailAtIndex( imageSource, 0, (__bridge CFDictionaryRef)options ); if (scaledImageRef) { thumbnail = [UIImage imageWithCGImage:scaledImageRef]; CFRelease(scaledImageRef); } CFRelease(imageSource); } return thumbnail; } + (NSString *)stringFromRequestDuration:(NSTimeInterval)duration { NSString *string = @"0s"; if (duration > 0.0) { if (duration < 1.0) { string = [NSString stringWithFormat:@"%dms", (int)(duration * 1000)]; } else if (duration < 10.0) { string = [NSString stringWithFormat:@"%.2fs", duration]; } else { string = [NSString stringWithFormat:@"%.1fs", duration]; } } return string; } + (NSString *)statusCodeStringFromURLResponse:(NSURLResponse *)response { NSString *httpResponseString = nil; if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSString *statusCodeDescription = nil; if (httpResponse.statusCode == 200) { // Prefer OK to the default "no error" statusCodeDescription = @"OK"; } else { statusCodeDescription = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]; } httpResponseString = [NSString stringWithFormat:@"%ld %@", (long)httpResponse.statusCode, statusCodeDescription]; } return httpResponseString; } + (BOOL)isErrorStatusCodeFromURLResponse:(NSURLResponse *)response { if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; return httpResponse.statusCode >= 400; } return NO; } + (NSArray *)itemsFromQueryString:(NSString *)query { NSMutableArray *items = [NSMutableArray new]; // [a=1, b=2, c=3] NSArray *queryComponents = [query componentsSeparatedByString:@"&"]; for (NSString *keyValueString in queryComponents) { // [a, 1] NSArray *components = [keyValueString componentsSeparatedByString:@"="]; if (components.count == 2) { NSString *key = components.firstObject.stringByRemovingPercentEncoding; NSString *value = components.lastObject.stringByRemovingPercentEncoding; [items addObject:[NSURLQueryItem queryItemWithName:key value:value]]; } } return items.copy; } + (NSString *)prettyJSONStringFromData:(NSData *)data { NSString *prettyString = nil; id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; if ([NSJSONSerialization isValidJSONObject:jsonObject]) { // Thanks RaziPour1993 prettyString = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:NULL ] encoding:NSUTF8StringEncoding ]; // NSJSONSerialization escapes forward slashes. // We want pretty json, so run through and unescape the slashes. prettyString = [prettyString stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]; } else { prettyString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } return prettyString; } + (BOOL)isValidJSONData:(NSData *)data { return [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL] ? YES : NO; } // Thanks to the following links for help with this method // https://www.cocoanetics.com/2012/02/decompressing-files-into-memory/ // https://github.com/nicklockwood/GZIP + (NSData *)inflatedDataFromCompressedData:(NSData *)compressedData { NSData *inflatedData = nil; NSUInteger compressedDataLength = compressedData.length; if (compressedDataLength > 0) { z_stream stream; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.avail_in = (uInt)compressedDataLength; stream.next_in = (void *)compressedData.bytes; stream.total_out = 0; stream.avail_out = 0; NSMutableData *mutableData = [NSMutableData dataWithLength:compressedDataLength * 1.5]; if (inflateInit2(&stream, 15 + 32) == Z_OK) { int status = Z_OK; while (status == Z_OK) { if (stream.total_out >= mutableData.length) { mutableData.length += compressedDataLength / 2; } stream.next_out = (uint8_t *)[mutableData mutableBytes] + stream.total_out; stream.avail_out = (uInt)(mutableData.length - stream.total_out); status = inflate(&stream, Z_SYNC_FLUSH); } if (inflateEnd(&stream) == Z_OK) { if (status == Z_STREAM_END) { mutableData.length = stream.total_out; inflatedData = [mutableData copy]; } } } } return inflatedData; } + (BOOL)hasCompressedContentEncoding:(NSURLRequest *)request { NSString *contentEncoding = [request valueForHTTPHeaderField:@"Content-Encoding"]; return ([contentEncoding rangeOfString:@"deflate" options:NSCaseInsensitiveSearch].length > 0 || [contentEncoding rangeOfString:@"gzip" options:NSCaseInsensitiveSearch].length > 0); } + (NSArray *)allWindows { BOOL includeInternalWindows = YES; BOOL onlyVisibleWindows = NO; // Obfuscating selector allWindowsIncludingInternalWindows:onlyVisibleWindows: NSArray *allWindowsComponents = @[ @"al", @"lWindo", @"wsIncl", @"udingInt", @"ernalWin", @"dows:o", @"nlyVisi", @"bleWin", @"dows:" ]; SEL allWindowsSelector = NSSelectorFromString([allWindowsComponents componentsJoinedByString:@""]); NSMethodSignature *methodSignature = [[UIWindow class] methodSignatureForSelector:allWindowsSelector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; invocation.target = [UIWindow class]; invocation.selector = allWindowsSelector; [invocation setArgument:&includeInternalWindows atIndex:2]; [invocation setArgument:&onlyVisibleWindows atIndex:3]; [invocation invoke]; __unsafe_unretained NSArray *windows = nil; [invocation getReturnValue:&windows]; return windows; } + (UIAlertController *)alert:(NSString *)title message:(NSString *)message { return [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert ]; } + (SEL)swizzledSelectorForSelector:(SEL)selector { return NSSelectorFromString([NSString stringWithFormat: @"_flex_swizzle_%x_%@", arc4random(), NSStringFromSelector(selector) ]); } + (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector class:(Class)cls { if ([cls instancesRespondToSelector:selector]) { unsigned int numMethods = 0; Method *methods = class_copyMethodList(cls, &numMethods); BOOL implementsSelector = NO; for (int index = 0; index < numMethods; index++) { SEL methodSelector = method_getName(methods[index]); if (selector == methodSelector) { implementsSelector = YES; break; } } free(methods); if (!implementsSelector) { return YES; } } return NO; } + (void)replaceImplementationOfKnownSelector:(SEL)originalSelector onClass:(Class)class withBlock:(id)block swizzledSelector:(SEL)swizzledSelector { // This method is only intended for swizzling methods that are know to exist on the class. // Bail if that isn't the case. Method originalMethod = class_getInstanceMethod(class, originalSelector); if (!originalMethod) { return; } IMP implementation = imp_implementationWithBlock(block); class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalMethod)); Method newMethod = class_getInstanceMethod(class, swizzledSelector); method_exchangeImplementations(originalMethod, newMethod); } + (void)replaceImplementationOfSelector:(SEL)selector withSelector:(SEL)swizzledSelector forClass:(Class)cls withMethodDescription:(struct objc_method_description)methodDescription implementationBlock:(id)implementationBlock undefinedBlock:(id)undefinedBlock { if ([self instanceRespondsButDoesNotImplementSelector:selector class:cls]) { return; } IMP implementation = imp_implementationWithBlock((id)( [cls instancesRespondToSelector:selector] ? implementationBlock : undefinedBlock) ); Method oldMethod = class_getInstanceMethod(cls, selector); const char *types = methodDescription.types; if (oldMethod) { if (!types) { types = method_getTypeEncoding(oldMethod); } class_addMethod(cls, swizzledSelector, implementation, types); Method newMethod = class_getInstanceMethod(cls, swizzledSelector); method_exchangeImplementations(oldMethod, newMethod); } else { if (!types) { // Some protocol method descriptions don't have .types populated // Set the return type to void and ignore arguments types = "v@:"; } class_addMethod(cls, selector, implementation, types); } } @end ================================================ FILE: Classes/Utility/Keyboard/FLEXKeyboardHelpViewController.h ================================================ // // FLEXKeyboardHelpViewController.h // FLEX // // Created by Ryan Olson on 9/19/15. // Copyright © 2015 f. All rights reserved. // #import @interface FLEXKeyboardHelpViewController : UIViewController @end ================================================ FILE: Classes/Utility/Keyboard/FLEXKeyboardHelpViewController.m ================================================ // // FLEXKeyboardHelpViewController.m // FLEX // // Created by Ryan Olson on 9/19/15. // Copyright © 2015 f. All rights reserved. // #import "FLEXKeyboardHelpViewController.h" #import "FLEXKeyboardShortcutManager.h" @interface FLEXKeyboardHelpViewController () @property (nonatomic) UITextView *textView; @end @implementation FLEXKeyboardHelpViewController - (void)viewDidLoad { [super viewDidLoad]; self.textView = [[UITextView alloc] initWithFrame:self.view.bounds]; self.textView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; [self.view addSubview:self.textView]; #if TARGET_OS_SIMULATOR self.textView.text = FLEXKeyboardShortcutManager.sharedManager.keyboardShortcutsDescription; #endif self.textView.backgroundColor = UIColor.blackColor; self.textView.textColor = UIColor.whiteColor; self.textView.font = [UIFont boldSystemFontOfSize:14.0]; self.navigationController.navigationBar.barStyle = UIBarStyleBlack; self.title = @"Simulator Shortcuts"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed:)]; } - (void)donePressed:(id)sender { [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } @end ================================================ FILE: Classes/Utility/Keyboard/FLEXKeyboardShortcutManager.h ================================================ // // FLEXKeyboardShortcutManager.h // FLEX // // Created by Ryan Olson on 9/19/15. // Copyright © 2020 FLEX Team. All rights reserved. // #import @interface FLEXKeyboardShortcutManager : NSObject @property (nonatomic, readonly, class) FLEXKeyboardShortcutManager *sharedManager; /// @param key A single character string matching a key on the keyboard /// @param modifiers Modifier keys such as shift, command, or alt/option /// @param action The block to run on the main thread when the key & modifier combination is recognized. /// @param description Shown the the keyboard shortcut help menu, which is accessed via the '?' key. /// @param allowOverride Allow registering even if there's an existing action associated with that key/modifier. - (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description allowOverride:(BOOL)allowOverride; @property (nonatomic, getter=isEnabled) BOOL enabled; @property (nonatomic, readonly) NSString *keyboardShortcutsDescription; @end ================================================ FILE: Classes/Utility/Keyboard/FLEXKeyboardShortcutManager.m ================================================ // // FLEXKeyboardShortcutManager.m // FLEX // // Created by Ryan Olson on 9/19/15. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXKeyboardShortcutManager.h" #import "FLEXUtility.h" #import #import #if TARGET_OS_SIMULATOR @interface UIEvent (UIPhysicalKeyboardEvent) @property (nonatomic) NSString *_modifiedInput; @property (nonatomic) NSString *_unmodifiedInput; @property (nonatomic) UIKeyModifierFlags _modifierFlags; @property (nonatomic) BOOL _isKeyDown; @property (nonatomic) long _keyCode; @end @interface FLEXKeyInput : NSObject @property (nonatomic, copy, readonly) NSString *key; @property (nonatomic, readonly) UIKeyModifierFlags flags; @property (nonatomic, copy, readonly) NSString *helpDescription; @end @implementation FLEXKeyInput - (BOOL)isEqual:(id)object { BOOL isEqual = NO; if ([object isKindOfClass:[FLEXKeyInput class]]) { FLEXKeyInput *keyCommand = (FLEXKeyInput *)object; BOOL equalKeys = self.key == keyCommand.key || [self.key isEqual:keyCommand.key]; BOOL equalFlags = self.flags == keyCommand.flags; isEqual = equalKeys && equalFlags; } return isEqual; } - (NSUInteger)hash { return self.key.hash ^ self.flags; } - (id)copyWithZone:(NSZone *)zone { return [[self class] keyInputForKey:self.key flags:self.flags helpDescription:self.helpDescription]; } - (NSString *)description { NSDictionary *keyMappings = @{ UIKeyInputUpArrow : @"↑", UIKeyInputDownArrow : @"↓", UIKeyInputLeftArrow : @"←", UIKeyInputRightArrow : @"→", UIKeyInputEscape : @"␛", @" " : @"␠" }; NSString *prettyKey = nil; if (self.key && keyMappings[self.key]) { prettyKey = keyMappings[self.key]; } else { prettyKey = [self.key uppercaseString]; } NSString *prettyFlags = @""; if (self.flags & UIKeyModifierControl) { prettyFlags = [prettyFlags stringByAppendingString:@"⌃"]; } if (self.flags & UIKeyModifierAlternate) { prettyFlags = [prettyFlags stringByAppendingString:@"⌥"]; } if (self.flags & UIKeyModifierShift) { prettyFlags = [prettyFlags stringByAppendingString:@"⇧"]; } if (self.flags & UIKeyModifierCommand) { prettyFlags = [prettyFlags stringByAppendingString:@"⌘"]; } // Fudging to get easy columns with tabs if (prettyFlags.length < 2) { prettyKey = [prettyKey stringByAppendingString:@"\t"]; } return [NSString stringWithFormat:@"%@%@\t%@", prettyFlags, prettyKey, self.helpDescription]; } + (instancetype)keyInputForKey:(NSString *)key flags:(UIKeyModifierFlags)flags { return [self keyInputForKey:key flags:flags helpDescription:nil]; } + (instancetype)keyInputForKey:(NSString *)key flags:(UIKeyModifierFlags)flags helpDescription:(NSString *)helpDescription { FLEXKeyInput *keyInput = [self new]; if (keyInput) { keyInput->_key = key; keyInput->_flags = flags; keyInput->_helpDescription = helpDescription; } return keyInput; } @end @interface FLEXKeyboardShortcutManager () @property (nonatomic) NSMutableDictionary *actionsForKeyInputs; @property (nonatomic, getter=isPressingShift) BOOL pressingShift; @property (nonatomic, getter=isPressingCommand) BOOL pressingCommand; @property (nonatomic, getter=isPressingControl) BOOL pressingControl; @end @implementation FLEXKeyboardShortcutManager + (instancetype)sharedManager { static FLEXKeyboardShortcutManager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedManager = [self new]; }); return sharedManager; } + (void)load { SEL originalKeyEventSelector = NSSelectorFromString(@"handleKeyUIEvent:"); SEL swizzledKeyEventSelector = [FLEXUtility swizzledSelectorForSelector:originalKeyEventSelector]; void (^handleKeyUIEventSwizzleBlock)(UIApplication *, UIEvent *) = ^(UIApplication *slf, UIEvent *event) { [[[self class] sharedManager] handleKeyboardEvent:event]; ((void(*)(id, SEL, id))objc_msgSend)(slf, swizzledKeyEventSelector, event); }; [FLEXUtility replaceImplementationOfKnownSelector:originalKeyEventSelector onClass:[UIApplication class] withBlock:handleKeyUIEventSwizzleBlock swizzledSelector:swizzledKeyEventSelector ]; if ([[UITouch class] instancesRespondToSelector:@selector(maximumPossibleForce)]) { SEL originalSendEventSelector = NSSelectorFromString(@"sendEvent:"); SEL swizzledSendEventSelector = [FLEXUtility swizzledSelectorForSelector:originalSendEventSelector]; void (^sendEventSwizzleBlock)(UIApplication *, UIEvent *) = ^(UIApplication *slf, UIEvent *event) { if (event.type == UIEventTypeTouches) { FLEXKeyboardShortcutManager *keyboardManager = FLEXKeyboardShortcutManager.sharedManager; NSInteger pressureLevel = 0; if (keyboardManager.isPressingShift) { pressureLevel++; } if (keyboardManager.isPressingCommand) { pressureLevel++; } if (keyboardManager.isPressingControl) { pressureLevel++; } if (pressureLevel > 0) { if (@available(iOS 9.0, *)) { for (UITouch *touch in [event allTouches]) { double adjustedPressureLevel = pressureLevel * 20 * touch.maximumPossibleForce; [touch setValue:@(adjustedPressureLevel) forKey:@"_pressure"]; } } } } ((void(*)(id, SEL, id))objc_msgSend)(slf, swizzledSendEventSelector, event); }; [FLEXUtility replaceImplementationOfKnownSelector:originalSendEventSelector onClass:[UIApplication class] withBlock:sendEventSwizzleBlock swizzledSelector:swizzledSendEventSelector ]; SEL originalSupportsTouchPressureSelector = NSSelectorFromString(@"_supportsForceTouch"); SEL swizzledSupportsTouchPressureSelector = [FLEXUtility swizzledSelectorForSelector:originalSupportsTouchPressureSelector]; BOOL (^supportsTouchPressureSwizzleBlock)(UIDevice *) = ^BOOL(UIDevice *slf) { return YES; }; [FLEXUtility replaceImplementationOfKnownSelector:originalSupportsTouchPressureSelector onClass:[UIDevice class] withBlock:supportsTouchPressureSwizzleBlock swizzledSelector:swizzledSupportsTouchPressureSelector ]; } } - (instancetype)init { self = [super init]; if (self) { _actionsForKeyInputs = [NSMutableDictionary new]; _enabled = YES; } return self; } - (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description allowOverride:(BOOL)allowOverride { FLEXKeyInput *keyInput = [FLEXKeyInput keyInputForKey:key flags:modifiers helpDescription:description]; if (!allowOverride && self.actionsForKeyInputs[keyInput] != nil) { return; } else { [self.actionsForKeyInputs setObject:action forKey:keyInput]; } } static const long kFLEXControlKeyCode = 0xe0; static const long kFLEXShiftKeyCode = 0xe1; static const long kFLEXCommandKeyCode = 0xe3; - (void)handleKeyboardEvent:(UIEvent *)event { if (!self.enabled) { return; } NSString *modifiedInput = nil; NSString *unmodifiedInput = nil; UIKeyModifierFlags flags = 0; BOOL isKeyDown = NO; if ([event respondsToSelector:@selector(_modifiedInput)]) { modifiedInput = [event _modifiedInput]; } if ([event respondsToSelector:@selector(_unmodifiedInput)]) { unmodifiedInput = [event _unmodifiedInput]; } if ([event respondsToSelector:@selector(_modifierFlags)]) { flags = [event _modifierFlags]; } if ([event respondsToSelector:@selector(_isKeyDown)]) { isKeyDown = [event _isKeyDown]; } BOOL interactionEnabled = !UIApplication.sharedApplication.isIgnoringInteractionEvents; BOOL hasFirstResponder = NO; if (isKeyDown && modifiedInput.length > 0 && interactionEnabled) { UIResponder *firstResponder = nil; for (UIWindow *window in FLEXUtility.allWindows) { firstResponder = [window valueForKey:@"firstResponder"]; if (firstResponder) { hasFirstResponder = YES; break; } } // Ignore key commands (except escape) when there's an active responder if (firstResponder) { if ([unmodifiedInput isEqual:UIKeyInputEscape]) { [firstResponder resignFirstResponder]; } } else { FLEXKeyInput *exactMatch = [FLEXKeyInput keyInputForKey:unmodifiedInput flags:flags]; dispatch_block_t actionBlock = self.actionsForKeyInputs[exactMatch]; if (!actionBlock) { FLEXKeyInput *shiftMatch = [FLEXKeyInput keyInputForKey:modifiedInput flags:flags&(~UIKeyModifierShift) ]; actionBlock = self.actionsForKeyInputs[shiftMatch]; } if (!actionBlock) { FLEXKeyInput *capitalMatch = [FLEXKeyInput keyInputForKey:[unmodifiedInput uppercaseString] flags:flags ]; actionBlock = self.actionsForKeyInputs[capitalMatch]; } if (actionBlock) { actionBlock(); } } } // Calling _keyCode on events from the simulator keyboard will crash. // It is only safe to call _keyCode when there's not an active responder. if (!hasFirstResponder && [event respondsToSelector:@selector(_keyCode)]) { long keyCode = [event _keyCode]; if (keyCode == kFLEXControlKeyCode) { self.pressingControl = isKeyDown; } else if (keyCode == kFLEXCommandKeyCode) { self.pressingCommand = isKeyDown; } else if (keyCode == kFLEXShiftKeyCode) { self.pressingShift = isKeyDown; } } } - (NSString *)keyboardShortcutsDescription { NSMutableString *description = [NSMutableString new]; NSArray *keyInputs = [self.actionsForKeyInputs.allKeys sortedArrayUsingComparator:^NSComparisonResult(FLEXKeyInput *input1, FLEXKeyInput *input2) { return [input1.key caseInsensitiveCompare:input2.key]; } ]; for (FLEXKeyInput *keyInput in keyInputs) { [description appendFormat:@"%@\n", keyInput]; } return [description copy]; } @end #endif ================================================ FILE: Classes/Utility/Runtime/FLEXRuntimeUtility.h ================================================ // // FLEXRuntimeUtility.h // Flipboard // // Created by Ryan Olson on 6/8/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeConstants.h" @class FLEXObjectRef; #define PropertyKey(suffix) kFLEXPropertyAttributeKey##suffix : @"" #define PropertyKeyGetter(getter) kFLEXPropertyAttributeKeyCustomGetter : NSStringFromSelector(@selector(getter)) #define PropertyKeySetter(setter) kFLEXPropertyAttributeKeyCustomSetter : NSStringFromSelector(@selector(setter)) /// Takes: min iOS version, property name, target class, property type, and a list of attributes #define FLEXRuntimeUtilityTryAddProperty(iOS_atLeast, name, cls, type, ...) ({ \ if (@available(iOS iOS_atLeast, *)) { \ NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithDictionary:@{ \ kFLEXPropertyAttributeKeyTypeEncoding : @(type), \ __VA_ARGS__ \ }]; \ [FLEXRuntimeUtility \ tryAddPropertyWithName:#name \ attributes:attrs \ toClass:cls \ ]; \ } \ }) /// Takes: min iOS version, property name, target class, property type, and a list of attributes #define FLEXRuntimeUtilityTryAddNonatomicProperty(iOS_atLeast, name, cls, type, ...) \ FLEXRuntimeUtilityTryAddProperty(iOS_atLeast, name, cls, @encode(type), PropertyKey(NonAtomic), __VA_ARGS__); /// Takes: min iOS version, property name, target class, property type (class name), and a list of attributes #define FLEXRuntimeUtilityTryAddObjectProperty(iOS_atLeast, name, cls, type, ...) \ FLEXRuntimeUtilityTryAddProperty(iOS_atLeast, name, cls, FLEXEncodeClass(type), PropertyKey(NonAtomic), __VA_ARGS__); extern NSString * const FLEXRuntimeUtilityErrorDomain; typedef NS_ENUM(NSInteger, FLEXRuntimeUtilityErrorCode) { // Start at a random value instead of 0 to avoid confusion with an absent code FLEXRuntimeUtilityErrorCodeDoesNotRecognizeSelector = 0xbabe, FLEXRuntimeUtilityErrorCodeInvocationFailed, FLEXRuntimeUtilityErrorCodeArgumentTypeMismatch }; @interface FLEXRuntimeUtility : NSObject #pragma mark - General Helpers /// Calls into \c FLEXPointerIsValidObjcObject() + (BOOL)pointerIsValidObjcObject:(const void *)pointer; /// Unwraps raw pointers to objects stored in NSValue, and re-boxes C strings into NSStrings. + (id)potentiallyUnwrapBoxedPointer:(id)returnedObjectOrNil type:(const FLEXTypeEncoding *)returnType; /// Some fields have a name in their encoded string (e.g. \"width\"d) /// @return the offset to skip the field name, 0 if there is no name + (NSUInteger)fieldNameOffsetForTypeEncoding:(const FLEXTypeEncoding *)typeEncoding; /// Given name "foo" and type "int" this would return "int foo", but /// given name "foo" and type "T *" it would return "T *foo" + (NSString *)appendName:(NSString *)name toType:(NSString *)typeEncoding; /// @return The class hierarchy for the given object or class, /// from the current class to the root-most class. + (NSArray *)classHierarchyOfObject:(id)objectOrClass; /// @return Every subclass of the given class name. + (NSArray *)subclassesOfClassWithName:(NSString *)className; /// Used to describe an object in brief within an explorer row + (NSString *)summaryForObject:(id)value; + (NSString *)safeClassNameForObject:(id)object; + (NSString *)safeDescriptionForObject:(id)object; + (NSString *)safeDebugDescriptionForObject:(id)object; + (BOOL)safeObject:(id)object isKindOfClass:(Class)cls; + (BOOL)safeObject:(id)object respondsToSelector:(SEL)sel; #pragma mark - Property Helpers + (BOOL)tryAddPropertyWithName:(const char *)name attributes:(NSDictionary *)attributePairs toClass:(__unsafe_unretained Class)theClass; + (NSArray *)allPropertyAttributeKeys; #pragma mark - Method Helpers + (NSArray *)prettyArgumentComponentsForMethod:(Method)method; #pragma mark - Method Calling/Field Editing + (id)performSelector:(SEL)selector onObject:(id)object; + (id)performSelector:(SEL)selector onObject:(id)object withArguments:(NSArray *)arguments error:(NSError * __autoreleasing *)error; + (id)performSelector:(SEL)selector onObject:(id)object withArguments:(NSArray *)arguments allowForwarding:(BOOL)mightForwardMsgSend error:(NSError * __autoreleasing *)error; + (NSString *)editableJSONStringForObject:(id)object; + (id)objectValueFromEditableJSONString:(NSString *)string; + (NSValue *)valueForNumberWithObjCType:(const char *)typeEncoding fromInputString:(NSString *)inputString; + (void)enumerateTypesInStructEncoding:(const char *)structEncoding usingBlock:(void (^)(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset))typeBlock; + (NSValue *)valueForPrimitivePointer:(void *)pointer objCType:(const char *)type; #pragma mark - Metadata Helpers + (NSString *)readableTypeForEncoding:(NSString *)encodingString; @end ================================================ FILE: Classes/Utility/Runtime/FLEXRuntimeUtility.m ================================================ // // FLEXRuntimeUtility.m // Flipboard // // Created by Ryan Olson on 6/8/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import "FLEXRuntimeUtility.h" #import "FLEXObjcInternal.h" #import "FLEXObjectRef.h" #import "NSObject+FLEX_Reflection.h" #import "FLEXTypeEncodingParser.h" #import "FLEXMethod.h" NSString * const FLEXRuntimeUtilityErrorDomain = @"FLEXRuntimeUtilityErrorDomain"; @implementation FLEXRuntimeUtility #pragma mark - General Helpers (Public) + (BOOL)pointerIsValidObjcObject:(const void *)pointer { return FLEXPointerIsValidObjcObject(pointer); } + (id)potentiallyUnwrapBoxedPointer:(id)returnedObjectOrNil type:(const FLEXTypeEncoding *)returnType { if (!returnedObjectOrNil) { return nil; } NSInteger i = 0; if (returnType[i] == FLEXTypeEncodingConst) { i++; } BOOL returnsObjectOrClass = returnType[i] == FLEXTypeEncodingObjcObject || returnType[i] == FLEXTypeEncodingObjcClass; BOOL returnsVoidPointer = returnType[i] == FLEXTypeEncodingPointer && returnType[i+1] == FLEXTypeEncodingVoid; BOOL returnsCString = returnType[i] == FLEXTypeEncodingCString; // If we got back an NSValue and the return type is not an object, // we check to see if the pointer is of a valid object. If not, // we just display the NSValue. if (!returnsObjectOrClass) { // Skip NSNumber instances if ([returnedObjectOrNil isKindOfClass:[NSNumber class]]) { return returnedObjectOrNil; } // Can only be NSValue since return type is not an object, // so we bail if this doesn't add up if (![returnedObjectOrNil isKindOfClass:[NSValue class]]) { return returnedObjectOrNil; } NSValue *value = (NSValue *)returnedObjectOrNil; if (returnsCString) { // Wrap char * in NSString const char *string = (const char *)value.pointerValue; returnedObjectOrNil = string ? [NSString stringWithCString:string encoding:NSUTF8StringEncoding] : NULL; } else if (returnsVoidPointer) { // Cast valid objects disguised as void * to id if ([FLEXRuntimeUtility pointerIsValidObjcObject:value.pointerValue]) { returnedObjectOrNil = (__bridge id)value.pointerValue; } } } return returnedObjectOrNil; } + (NSUInteger)fieldNameOffsetForTypeEncoding:(const FLEXTypeEncoding *)typeEncoding { NSUInteger beginIndex = 0; while (typeEncoding[beginIndex] == FLEXTypeEncodingQuote) { NSUInteger endIndex = beginIndex + 1; while (typeEncoding[endIndex] != FLEXTypeEncodingQuote) { ++endIndex; } beginIndex = endIndex + 1; } return beginIndex; } + (NSArray *)classHierarchyOfObject:(id)objectOrClass { NSMutableArray *superClasses = [NSMutableArray new]; id cls = [objectOrClass class]; do { [superClasses addObject:cls]; } while ((cls = [cls superclass])); return superClasses; } + (NSArray *)subclassesOfClassWithName:(NSString *)className { NSArray *classes = FLEXGetAllSubclasses(NSClassFromString(className), NO); NSArray *references = [FLEXObjectRef referencingClasses:classes]; return references; } + (NSString *)safeClassNameForObject:(id)object { // Don't assume that we have an NSObject subclass if ([self safeObject:object respondsToSelector:@selector(class)]) { return NSStringFromClass([object class]); } return NSStringFromClass(object_getClass(object)); } /// Could be nil + (NSString *)safeDescriptionForObject:(id)object { // Don't assume that we have an NSObject subclass; not all objects respond to -description if ([self safeObject:object respondsToSelector:@selector(description)]) { @try { return [object description]; } @catch (NSException *exception) { return nil; } } return nil; } /// Never nil + (NSString *)safeDebugDescriptionForObject:(id)object { NSString *description = nil; if ([self safeObject:object respondsToSelector:@selector(debugDescription)]) { @try { description = [object debugDescription]; } @catch (NSException *exception) { } } else { description = [self safeDescriptionForObject:object]; } if (!description.length) { NSString *cls = NSStringFromClass(object_getClass(object)); if (object_isClass(object)) { description = [cls stringByAppendingString:@" class (no description)"]; } else { description = [cls stringByAppendingString:@" instance (no description)"]; } } return description; } + (NSString *)summaryForObject:(id)value { NSString *description = nil; // Special case BOOL for better readability. if ([self safeObject:value isKindOfClass:[NSValue class]]) { const char *type = [value objCType]; if (strcmp(type, @encode(BOOL)) == 0) { BOOL boolValue = NO; [value getValue:&boolValue]; return boolValue ? @"YES" : @"NO"; } else if (strcmp(type, @encode(SEL)) == 0) { SEL selector = NULL; [value getValue:&selector]; return NSStringFromSelector(selector); } } @try { // Single line display - replace newlines and tabs with spaces. description = [[self safeDescriptionForObject:value] stringByReplacingOccurrencesOfString:@"\n" withString:@" "]; description = [description stringByReplacingOccurrencesOfString:@"\t" withString:@" "]; description = [description stringByReplacingOccurrencesOfString:@" " withString:@" "]; } @catch (NSException *e) { description = [@"Thrown: " stringByAppendingString:e.reason ?: @"(nil exception reason)"]; } if (!description) { description = @"nil"; } return description; } + (BOOL)safeObject:(id)object isKindOfClass:(Class)cls { static BOOL (*isKindOfClass)(id, SEL, Class) = nil; static BOOL (*isKindOfClass_meta)(id, SEL, Class) = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ isKindOfClass = (BOOL(*)(id, SEL, Class))[NSObject instanceMethodForSelector:@selector(isKindOfClass:)]; isKindOfClass_meta = (BOOL(*)(id, SEL, Class))[NSObject methodForSelector:@selector(isKindOfClass:)]; }); BOOL isClass = object_isClass(object); return (isClass ? isKindOfClass_meta : isKindOfClass)(object, @selector(isKindOfClass:), cls); } + (BOOL)safeObject:(id)object respondsToSelector:(SEL)sel { // If we're given a class, we want to know if classes respond to this selector. // Similarly, if we're given an instance, we want to know if instances respond. BOOL isClass = object_isClass(object); Class cls = isClass ? object : object_getClass(object); // BOOL isMetaclass = class_isMetaClass(cls); if (isClass) { // In theory, this should also work for metaclasses... return class_getClassMethod(cls, sel) != nil; } else { return class_getInstanceMethod(cls, sel) != nil; } } #pragma mark - Property Helpers (Public) + (BOOL)tryAddPropertyWithName:(const char *)name attributes:(NSDictionary *)attributePairs toClass:(__unsafe_unretained Class)theClass { objc_property_t property = class_getProperty(theClass, name); if (!property) { unsigned int totalAttributesCount = (unsigned int)attributePairs.count; objc_property_attribute_t *attributes = malloc(sizeof(objc_property_attribute_t) * totalAttributesCount); if (attributes) { unsigned int attributeIndex = 0; for (NSString *attributeName in attributePairs.allKeys) { objc_property_attribute_t attribute; attribute.name = attributeName.UTF8String; attribute.value = attributePairs[attributeName].UTF8String; attributes[attributeIndex++] = attribute; } BOOL success = class_addProperty(theClass, name, attributes, totalAttributesCount); free(attributes); return success; } else { return NO; } } return YES; } + (NSArray *)allPropertyAttributeKeys { static NSArray *allPropertyAttributeKeys = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ allPropertyAttributeKeys = @[ kFLEXPropertyAttributeKeyTypeEncoding, kFLEXPropertyAttributeKeyBackingIvarName, kFLEXPropertyAttributeKeyReadOnly, kFLEXPropertyAttributeKeyCopy, kFLEXPropertyAttributeKeyRetain, kFLEXPropertyAttributeKeyNonAtomic, kFLEXPropertyAttributeKeyCustomGetter, kFLEXPropertyAttributeKeyCustomSetter, kFLEXPropertyAttributeKeyDynamic, kFLEXPropertyAttributeKeyWeak, kFLEXPropertyAttributeKeyGarbageCollectable, kFLEXPropertyAttributeKeyOldStyleTypeEncoding, ]; }); return allPropertyAttributeKeys; } #pragma mark - Method Helpers (Public) + (NSArray *)prettyArgumentComponentsForMethod:(Method)method { NSMutableArray *components = [NSMutableArray new]; NSString *selectorName = NSStringFromSelector(method_getName(method)); NSMutableArray *selectorComponents = [selectorName componentsSeparatedByString:@":"].mutableCopy; // this is a workaround cause method_getNumberOfArguments() returns wrong number for some methods if (selectorComponents.count == 1) { return @[]; } if ([selectorComponents.lastObject isEqualToString:@""]) { [selectorComponents removeLastObject]; } for (unsigned int argIndex = 0; argIndex < selectorComponents.count; argIndex++) { char *argType = method_copyArgumentType(method, argIndex + kFLEXNumberOfImplicitArgs); NSString *readableArgType = (argType != NULL) ? [self readableTypeForEncoding:@(argType)] : nil; free(argType); NSString *prettyComponent = [NSString stringWithFormat:@"%@:(%@) ", selectorComponents[argIndex], readableArgType ]; [components addObject:prettyComponent]; } return components; } #pragma mark - Method Calling/Field Editing (Public) + (id)performSelector:(SEL)selector onObject:(id)object { return [self performSelector:selector onObject:object withArguments:@[] error:nil]; } + (id)performSelector:(SEL)selector onObject:(id)object withArguments:(NSArray *)arguments error:(NSError * __autoreleasing *)error { return [self performSelector:selector onObject:object withArguments:arguments allowForwarding:NO error:error ]; } + (id)performSelector:(SEL)selector onObject:(id)object withArguments:(NSArray *)arguments allowForwarding:(BOOL)mightForwardMsgSend error:(NSError * __autoreleasing *)error { static dispatch_once_t onceToken; static SEL stdStringExclusion = nil; dispatch_once(&onceToken, ^{ stdStringExclusion = NSSelectorFromString(@"stdString"); }); // Bail if the object won't respond to this selector if (mightForwardMsgSend || ![self safeObject:object respondsToSelector:selector]) { if (error) { NSString *msg = [NSString stringWithFormat:@"This object does not respond to the selector %@", NSStringFromSelector(selector) ]; NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : msg }; *error = [NSError errorWithDomain:FLEXRuntimeUtilityErrorDomain code:FLEXRuntimeUtilityErrorCodeDoesNotRecognizeSelector userInfo:userInfo ]; } return nil; } // It is important to use object_getClass and not -class here, as // object_getClass will return a different result for class objects Class cls = object_getClass(object); NSMethodSignature *methodSignature = [FLEXMethod selector:selector class:cls].signature; if (!methodSignature) { // Unsupported type encoding return nil; } // Probably an unsupported type encoding, like bitfields. // In the future, we could calculate the return length // on our own. For now, we abort. // // For future reference, the code here will get the true type encoding. // NSMethodSignature will convert {?=b8b4b1b1b18[8S]} to {?} // // returnType = method_getTypeEncoding(class_getInstanceMethod([object class], selector)); if (!methodSignature.methodReturnLength && methodSignature.methodReturnType[0] != FLEXTypeEncodingVoid) { return nil; } // Build the invocation NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; [invocation setSelector:selector]; [invocation setTarget:object]; [invocation retainArguments]; // Always self and _cmd NSUInteger numberOfArguments = methodSignature.numberOfArguments; for (NSUInteger argumentIndex = kFLEXNumberOfImplicitArgs; argumentIndex < numberOfArguments; argumentIndex++) { NSUInteger argumentsArrayIndex = argumentIndex - kFLEXNumberOfImplicitArgs; id argumentObject = arguments.count > argumentsArrayIndex ? arguments[argumentsArrayIndex] : nil; // NSNull in the arguments array can be passed as a placeholder to indicate nil. // We only need to set the argument if it will be non-nil. if (argumentObject && ![argumentObject isKindOfClass:[NSNull class]]) { const char *typeEncodingCString = [methodSignature getArgumentTypeAtIndex:argumentIndex]; if (typeEncodingCString[0] == FLEXTypeEncodingObjcObject || typeEncodingCString[0] == FLEXTypeEncodingObjcClass || [self isTollFreeBridgedValue:argumentObject forCFType:typeEncodingCString]) { // Object [invocation setArgument:&argumentObject atIndex:argumentIndex]; } else if (strcmp(typeEncodingCString, @encode(CGColorRef)) == 0 && [argumentObject isKindOfClass:[UIColor class]]) { // Bridging UIColor to CGColorRef CGColorRef colorRef = [argumentObject CGColor]; [invocation setArgument:&colorRef atIndex:argumentIndex]; } else if ([argumentObject isKindOfClass:[NSValue class]]) { // Primitive boxed in NSValue NSValue *argumentValue = (NSValue *)argumentObject; // Ensure that the type encoding on the NSValue matches the type encoding of the argument in the method signature if (strcmp([argumentValue objCType], typeEncodingCString) != 0) { if (error) { NSString *msg = [NSString stringWithFormat:@"Type encoding mismatch for argument at index %lu. " "Value type: %s; Method argument type: %s.", (unsigned long)argumentsArrayIndex, argumentValue.objCType, typeEncodingCString ]; NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : msg }; *error = [NSError errorWithDomain:FLEXRuntimeUtilityErrorDomain code:FLEXRuntimeUtilityErrorCodeArgumentTypeMismatch userInfo:userInfo ]; } return nil; } @try { NSUInteger bufferSize = 0; FLEXGetSizeAndAlignment(typeEncodingCString, &bufferSize, NULL); if (bufferSize > 0) { void *buffer = alloca(bufferSize); [argumentValue getValue:buffer]; [invocation setArgument:buffer atIndex:argumentIndex]; } } @catch (NSException *exception) { } } } } // Try to invoke the invocation but guard against an exception being thrown. id returnObject = nil; @try { [invocation invoke]; // Retrieve the return value and box if necessary. const char *returnType = methodSignature.methodReturnType; if (returnType[0] == FLEXTypeEncodingObjcObject || returnType[0] == FLEXTypeEncodingObjcClass) { // Return value is an object. __unsafe_unretained id objectReturnedFromMethod = nil; [invocation getReturnValue:&objectReturnedFromMethod]; returnObject = objectReturnedFromMethod; } else if (returnType[0] != FLEXTypeEncodingVoid) { NSAssert(methodSignature.methodReturnLength, @"Memory corruption lies ahead"); if (returnType[0] == FLEXTypeEncodingStructBegin) { if (selector == stdStringExclusion && [object isKindOfClass:[NSString class]]) { // stdString is a C++ object and we will crash if we try to access it if (error) { *error = [NSError errorWithDomain:FLEXRuntimeUtilityErrorDomain code:FLEXRuntimeUtilityErrorCodeInvocationFailed userInfo:@{ NSLocalizedDescriptionKey : @"Skipping -[NSString stdString]" } ]; } return nil; } } // Will use arbitrary buffer for return value and box it. void *returnValue = malloc(methodSignature.methodReturnLength); [invocation getReturnValue:returnValue]; returnObject = [self valueForPrimitivePointer:returnValue objCType:returnType]; free(returnValue); } } @catch (NSException *exception) { // Bummer... if (error) { // "… on " / "… on instance of " NSString *class = NSStringFromClass([object class]); NSString *calledOn = object == [object class] ? class : [@"an instance of " stringByAppendingString:class]; NSString *message = [NSString stringWithFormat:@"Exception '%@' thrown while performing selector '%@' on %@.\nReason:\n\n%@", exception.name, NSStringFromSelector(selector), calledOn, exception.reason ]; *error = [NSError errorWithDomain:FLEXRuntimeUtilityErrorDomain code:FLEXRuntimeUtilityErrorCodeInvocationFailed userInfo:@{ NSLocalizedDescriptionKey : message } ]; } } return returnObject; } + (BOOL)isTollFreeBridgedValue:(id)value forCFType:(const char *)typeEncoding { // See https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Toll-FreeBridgin/Toll-FreeBridgin.html #define CASE(cftype, foundationClass) \ if (strcmp(typeEncoding, @encode(cftype)) == 0) { \ return [value isKindOfClass:[foundationClass class]]; \ } CASE(CFArrayRef, NSArray); CASE(CFAttributedStringRef, NSAttributedString); CASE(CFCalendarRef, NSCalendar); CASE(CFCharacterSetRef, NSCharacterSet); CASE(CFDataRef, NSData); CASE(CFDateRef, NSDate); CASE(CFDictionaryRef, NSDictionary); CASE(CFErrorRef, NSError); CASE(CFLocaleRef, NSLocale); CASE(CFMutableArrayRef, NSMutableArray); CASE(CFMutableAttributedStringRef, NSMutableAttributedString); CASE(CFMutableCharacterSetRef, NSMutableCharacterSet); CASE(CFMutableDataRef, NSMutableData); CASE(CFMutableDictionaryRef, NSMutableDictionary); CASE(CFMutableSetRef, NSMutableSet); CASE(CFMutableStringRef, NSMutableString); CASE(CFNumberRef, NSNumber); CASE(CFReadStreamRef, NSInputStream); CASE(CFRunLoopTimerRef, NSTimer); CASE(CFSetRef, NSSet); CASE(CFStringRef, NSString); CASE(CFTimeZoneRef, NSTimeZone); CASE(CFURLRef, NSURL); CASE(CFWriteStreamRef, NSOutputStream); #undef CASE return NO; } + (NSString *)editableJSONStringForObject:(id)object { NSString *editableDescription = nil; if (object) { // This is a hack to use JSON serialization for our editable objects. // NSJSONSerialization doesn't allow writing fragments - the top level object must be an array or dictionary. // We always wrap the object inside an array and then strip the outer square braces off the final string. NSArray *wrappedObject = @[object]; if ([NSJSONSerialization isValidJSONObject:wrappedObject]) { NSData *jsonData = [NSJSONSerialization dataWithJSONObject:wrappedObject options:0 error:NULL]; NSString *wrappedDescription = [NSString stringWithUTF8String:jsonData.bytes]; editableDescription = [wrappedDescription substringWithRange:NSMakeRange(1, wrappedDescription.length - 2)]; } } return editableDescription; } + (id)objectValueFromEditableJSONString:(NSString *)string { id value = nil; // nil for empty string/whitespace if ([string stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet].length) { value = [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:NULL ]; } return value; } + (NSValue *)valueForNumberWithObjCType:(const char *)typeEncoding fromInputString:(NSString *)inputString { NSNumberFormatter *formatter = [NSNumberFormatter new]; [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; NSNumber *number = [formatter numberFromString:inputString]; // Is the type encoding longer than one character? if (strlen(typeEncoding) > 1) { NSString *type = @(typeEncoding); // Is it NSDecimalNumber or NSNumber? if ([type isEqualToString:@FLEXEncodeClass(NSDecimalNumber)]) { return [NSDecimalNumber decimalNumberWithString:inputString]; } else if ([type isEqualToString:@FLEXEncodeClass(NSNumber)]) { return number; } return nil; } // Type encoding is one character, switch on the type FLEXTypeEncoding type = typeEncoding[0]; uint8_t value[32]; void *bufferStart = &value[0]; // Make sure we box the number with the correct type encoding // so it can be properly unboxed later via getValue: switch (type) { case FLEXTypeEncodingChar: *(char *)bufferStart = number.charValue; break; case FLEXTypeEncodingInt: *(int *)bufferStart = number.intValue; break; case FLEXTypeEncodingShort: *(short *)bufferStart = number.shortValue; break; case FLEXTypeEncodingLong: *(long *)bufferStart = number.longValue; break; case FLEXTypeEncodingLongLong: *(long long *)bufferStart = number.longLongValue; break; case FLEXTypeEncodingUnsignedChar: *(unsigned char *)bufferStart = number.unsignedCharValue; break; case FLEXTypeEncodingUnsignedInt: *(unsigned int *)bufferStart = number.unsignedIntValue; break; case FLEXTypeEncodingUnsignedShort: *(unsigned short *)bufferStart = number.unsignedShortValue; break; case FLEXTypeEncodingUnsignedLong: *(unsigned long *)bufferStart = number.unsignedLongValue; break; case FLEXTypeEncodingUnsignedLongLong: *(unsigned long long *)bufferStart = number.unsignedLongLongValue; break; case FLEXTypeEncodingFloat: *(float *)bufferStart = number.floatValue; break; case FLEXTypeEncodingDouble: *(double *)bufferStart = number.doubleValue; break; case FLEXTypeEncodingLongDouble: // NSNumber does not support long double default: return nil; } return [NSValue value:value withObjCType:typeEncoding]; } + (void)enumerateTypesInStructEncoding:(const char *)structEncoding usingBlock:(void (^)(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset))typeBlock { if (structEncoding && structEncoding[0] == FLEXTypeEncodingStructBegin) { const char *equals = strchr(structEncoding, '='); if (equals) { const char *nameStart = structEncoding + 1; NSString *structName = [@(structEncoding) substringWithRange:NSMakeRange(nameStart - structEncoding, equals - nameStart) ]; NSUInteger fieldAlignment = 0, structSize = 0; if (FLEXGetSizeAndAlignment(structEncoding, &structSize, &fieldAlignment)) { NSUInteger runningFieldIndex = 0; NSUInteger runningFieldOffset = 0; const char *typeStart = equals + 1; while (*typeStart != FLEXTypeEncodingStructEnd) { NSUInteger fieldSize = 0; // If the struct type encoding was successfully handled by // FLEXGetSizeAndAlignment above, we *should* be ok with the field here. const char *nextTypeStart = NSGetSizeAndAlignment(typeStart, &fieldSize, NULL); NSString *typeEncoding = [@(structEncoding) substringWithRange:NSMakeRange(typeStart - structEncoding, nextTypeStart - typeStart) ]; // Padding to keep proper alignment. __attribute((packed)) structs // will break here. The type encoding is no different for packed structs, // so it's not clear there's anything we can do for those. const NSUInteger currentSizeSum = runningFieldOffset % fieldAlignment; if (currentSizeSum != 0 && currentSizeSum + fieldSize > fieldAlignment) { runningFieldOffset += fieldAlignment - currentSizeSum; } typeBlock( structName, typeEncoding.UTF8String, [self readableTypeForEncoding:typeEncoding], runningFieldIndex, runningFieldOffset ); runningFieldOffset += fieldSize; runningFieldIndex++; typeStart = nextTypeStart; } } } } } #pragma mark - Metadata Helpers + (NSDictionary *)attributesForProperty:(objc_property_t)property { NSString *attributes = @(property_getAttributes(property) ?: ""); // Thanks to MAObjcRuntime for inspiration here. NSArray *attributePairs = [attributes componentsSeparatedByString:@","]; NSMutableDictionary *attributesDictionary = [NSMutableDictionary new]; for (NSString *attributePair in attributePairs) { attributesDictionary[[attributePair substringToIndex:1]] = [attributePair substringFromIndex:1]; } return attributesDictionary; } + (NSString *)appendName:(NSString *)name toType:(NSString *)type { if (!type.length) { type = @"(?)"; } NSString *combined = nil; if ([type characterAtIndex:type.length - 1] == FLEXTypeEncodingCString) { combined = [type stringByAppendingString:name]; } else { combined = [type stringByAppendingFormat:@" %@", name]; } return combined; } + (NSString *)readableTypeForEncoding:(NSString *)encodingString { if (!encodingString.length) { return @"?"; } // See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html // class-dump has a much nicer and much more complete implementation for this task, but it is distributed under GPLv2 :/ // See https://github.com/nygard/class-dump/blob/master/Source/CDType.m // Warning: this method uses multiple middle returns and macros to cut down on boilerplate. // The use of macros here was inspired by https://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html const char *encodingCString = encodingString.UTF8String; // Some fields have a name, such as {Size=\"width\"d\"height\"d}, we need to extract the name out and recursive const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:encodingCString]; if (fieldNameOffset > 0) { // According to https://github.com/nygard/class-dump/commit/33fb5ed221810685f57c192e1ce8ab6054949a7c, // there are some consecutive quoted strings, so use `_` to concatenate the names. NSString *const fieldNamesString = [encodingString substringWithRange:NSMakeRange(0, fieldNameOffset)]; NSArray *const fieldNames = [fieldNamesString componentsSeparatedByString:[NSString stringWithFormat:@"%c", FLEXTypeEncodingQuote] ]; NSMutableString *finalFieldNamesString = [NSMutableString new]; for (NSString *const fieldName in fieldNames) { if (fieldName.length > 0) { if (finalFieldNamesString.length > 0) { [finalFieldNamesString appendString:@"_"]; } [finalFieldNamesString appendString:fieldName]; } } NSString *const recursiveType = [self readableTypeForEncoding:[encodingString substringFromIndex:fieldNameOffset]]; return [NSString stringWithFormat:@"%@ %@", recursiveType, finalFieldNamesString]; } // Objects if (encodingCString[0] == FLEXTypeEncodingObjcObject) { NSString *class = [encodingString substringFromIndex:1]; class = [class stringByReplacingOccurrencesOfString:@"\"" withString:@""]; if (class.length == 0 || (class.length == 1 && [class characterAtIndex:0] == FLEXTypeEncodingUnknown)) { class = @"id"; } else { class = [class stringByAppendingString:@" *"]; } return class; } // Qualifier Prefixes // Do this first since some of the direct translations (i.e. Method) contain a prefix. #define RECURSIVE_TRANSLATE(prefix, formatString) \ if (encodingCString[0] == prefix) { \ NSString *recursiveType = [self readableTypeForEncoding:[encodingString substringFromIndex:1]]; \ return [NSString stringWithFormat:formatString, recursiveType]; \ } // If there's a qualifier prefix on the encoding, translate it and then // recursively call this method with the rest of the encoding string. RECURSIVE_TRANSLATE('^', @"%@ *"); RECURSIVE_TRANSLATE('r', @"const %@"); RECURSIVE_TRANSLATE('n', @"in %@"); RECURSIVE_TRANSLATE('N', @"inout %@"); RECURSIVE_TRANSLATE('o', @"out %@"); RECURSIVE_TRANSLATE('O', @"bycopy %@"); RECURSIVE_TRANSLATE('R', @"byref %@"); RECURSIVE_TRANSLATE('V', @"oneway %@"); RECURSIVE_TRANSLATE('b', @"bitfield(%@)"); #undef RECURSIVE_TRANSLATE // C Types #define TRANSLATE(ctype) \ if (strcmp(encodingCString, @encode(ctype)) == 0) { \ return (NSString *)CFSTR(#ctype); \ } // Order matters here since some of the cocoa types are typedefed to c types. // We can't recover the exact mapping, but we choose to prefer the cocoa types. // This is not an exhaustive list, but it covers the most common types TRANSLATE(CGRect); TRANSLATE(CGPoint); TRANSLATE(CGSize); TRANSLATE(CGVector); TRANSLATE(UIEdgeInsets); if (@available(iOS 11.0, *)) { TRANSLATE(NSDirectionalEdgeInsets); } TRANSLATE(UIOffset); TRANSLATE(NSRange); TRANSLATE(CGAffineTransform); TRANSLATE(CATransform3D); TRANSLATE(CGColorRef); TRANSLATE(CGPathRef); TRANSLATE(CGContextRef); TRANSLATE(NSInteger); TRANSLATE(NSUInteger); TRANSLATE(CGFloat); TRANSLATE(BOOL); TRANSLATE(int); TRANSLATE(short); TRANSLATE(long); TRANSLATE(long long); TRANSLATE(unsigned char); TRANSLATE(unsigned int); TRANSLATE(unsigned short); TRANSLATE(unsigned long); TRANSLATE(unsigned long long); TRANSLATE(float); TRANSLATE(double); TRANSLATE(long double); TRANSLATE(char *); TRANSLATE(Class); TRANSLATE(objc_property_t); TRANSLATE(Ivar); TRANSLATE(Method); TRANSLATE(Category); TRANSLATE(NSZone *); TRANSLATE(SEL); TRANSLATE(void); #undef TRANSLATE // For structs, we only use the name of the structs if (encodingCString[0] == FLEXTypeEncodingStructBegin) { // Special case: std::string if ([encodingString hasPrefix:@"{basic_string d) const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:type]; if (fieldNameOffset > 0) { return [self valueForPrimitivePointer:pointer objCType:type + fieldNameOffset]; } // CASE macro inspired by https://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html #define CASE(ctype, selectorpart) \ if (strcmp(type, @encode(ctype)) == 0) { \ return [NSNumber numberWith ## selectorpart: *(ctype *)pointer]; \ } CASE(BOOL, Bool); CASE(unsigned char, UnsignedChar); CASE(short, Short); CASE(unsigned short, UnsignedShort); CASE(int, Int); CASE(unsigned int, UnsignedInt); CASE(long, Long); CASE(unsigned long, UnsignedLong); CASE(long long, LongLong); CASE(unsigned long long, UnsignedLongLong); CASE(float, Float); CASE(double, Double); CASE(long double, Double); #undef CASE NSValue *value = nil; if (FLEXGetSizeAndAlignment(type, nil, nil)) { @try { value = [NSValue valueWithBytes:pointer objCType:type]; } @catch (NSException *exception) { // Certain type encodings are not supported by valueWithBytes:objCType:. // Just fail silently if an exception is thrown. } } return value; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXObjcInternal.h ================================================ // // FLEXObjcInternal.h // FLEX // // Created by Tanner Bennett on 11/1/18. // #import #ifdef __cplusplus extern "C" { #endif // The macros below are copied straight from // objc-internal.h, objc-private.h, objc-object.h, and objc-config.h with // as few modifications as possible. Changes are noted in boxed comments. // https://opensource.apple.com/source/objc4/objc4-723/ // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-internal.h.auto.html // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html ///////////////////// // objc-internal.h // ///////////////////// #if __LP64__ #define OBJC_HAVE_TAGGED_POINTERS 1 #endif #if OBJC_HAVE_TAGGED_POINTERS #if TARGET_OS_OSX && __x86_64__ // 64-bit Mac - tag bit is LSB # define OBJC_MSB_TAGGED_POINTERS 0 #else // Everything else - tag bit is MSB # define OBJC_MSB_TAGGED_POINTERS 1 #endif #if OBJC_MSB_TAGGED_POINTERS # define _OBJC_TAG_MASK (1UL<<63) # define _OBJC_TAG_EXT_MASK (0xfUL<<60) #else # define _OBJC_TAG_MASK 1UL # define _OBJC_TAG_EXT_MASK 0xfUL #endif #endif // OBJC_HAVE_TAGGED_POINTERS ////////////////////////////////////// // originally _objc_isTaggedPointer // ////////////////////////////////////// NS_INLINE BOOL flex_isTaggedPointer(const void *ptr) { #if OBJC_HAVE_TAGGED_POINTERS return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; #else return NO; #endif } #define FLEXPointerIsTaggedPointer(obj) flex_isTaggedPointer((__bridge void *)obj) /// Whether the given pointer is a valid, readable address. BOOL FLEXPointerIsReadable(const void * ptr); /// @brief Assumes memory is valid and readable. /// @discussion objc-internal.h, objc-private.h, and objc-config.h /// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/ /// https://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/objc_runtime.py /// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/ BOOL FLEXPointerIsValidObjcObject(const void * ptr); #ifdef __cplusplus } #endif ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXObjcInternal.mm ================================================ // // FLEXObjcInternal.mm // FLEX // // Created by Tanner Bennett on 11/1/18. // /* * Copyright (c) 2005-2007 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #import "FLEXObjcInternal.h" #import // For malloc_size #import // For vm_region_64 #include #if __arm64e__ #include #endif #define ALWAYS_INLINE inline __attribute__((always_inline)) #define NEVER_INLINE inline __attribute__((noinline)) // The macros below are copied straight from // objc-internal.h, objc-private.h, objc-object.h, and objc-config.h with // as few modifications as possible. Changes are noted in boxed comments. // https://opensource.apple.com/source/objc4/objc4-723/ // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-internal.h.auto.html // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html ///////////////////// // objc-internal.h // ///////////////////// #if OBJC_HAVE_TAGGED_POINTERS /////////////////// // objc-object.h // /////////////////// //////////////////////////////////////////////// // originally objc_object::isExtTaggedPointer // //////////////////////////////////////////////// NS_INLINE BOOL flex_isExtTaggedPointer(const void *ptr) { return ((uintptr_t)ptr & _OBJC_TAG_EXT_MASK) == _OBJC_TAG_EXT_MASK; } #endif // OBJC_HAVE_TAGGED_POINTERS ///////////////////////////////////// // FLEXObjectInternal // // No Apple code beyond this point // ///////////////////////////////////// extern "C" { BOOL FLEXPointerIsReadable(const void *inPtr) { kern_return_t error = KERN_SUCCESS; vm_size_t vmsize; #if __arm64e__ // On arm64e, we need to strip the PAC from the pointer so the adress is readable vm_address_t address = (vm_address_t)ptrauth_strip(inPtr, ptrauth_key_function_pointer); #else vm_address_t address = (vm_address_t)inPtr; #endif vm_region_basic_info_data_t info; mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; memory_object_name_t object; error = vm_region_64( mach_task_self(), &address, &vmsize, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &info_count, &object ); if (error != KERN_SUCCESS) { // vm_region/vm_region_64 returned an error return NO; } else if (!(BOOL)(info.protection & VM_PROT_READ)) { return NO; } #if __arm64e__ address = (vm_address_t)ptrauth_strip(inPtr, ptrauth_key_function_pointer); #else address = (vm_address_t)inPtr; #endif // Read the memory vm_size_t size = 0; char buf[sizeof(uintptr_t)]; error = vm_read_overwrite(mach_task_self(), address, sizeof(uintptr_t), (vm_address_t)buf, &size); if (error != KERN_SUCCESS) { // vm_read_overwrite returned an error return NO; } return YES; } /// Accepts addresses that may or may not be readable. /// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/ BOOL FLEXPointerIsValidObjcObject(const void *ptr) { uintptr_t pointer = (uintptr_t)ptr; if (!ptr) { return NO; } #if OBJC_HAVE_TAGGED_POINTERS // Tagged pointers have 0x1 set, no other valid pointers do // objc-internal.h -> _objc_isTaggedPointer() if (flex_isTaggedPointer(ptr) || flex_isExtTaggedPointer(ptr)) { return YES; } #endif // Check pointer alignment if ((pointer % sizeof(uintptr_t)) != 0) { return NO; } // From LLDB: // Pointers in a class_t will only have bits 0 through 46 set, // so if any pointer has bits 47 through 63 high, we know that this is not a valid isa // https://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/objc_runtime.py if ((pointer & 0xFFFF800000000000) != 0) { return NO; } // Make sure dereferencing this address won't crash if (!FLEXPointerIsReadable(ptr)) { return NO; } // http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html // We check if the returned class is readable because object_getClass // can return a garbage value when given a non-nil pointer to a non-object Class cls = object_getClass((__bridge id)ptr); if (!cls || !FLEXPointerIsReadable((__bridge void *)cls)) { return NO; } // Just because this pointer is readable doesn't mean whatever is at // it's ISA offset is readable. We need to do the same checks on it's ISA. // Even this isn't perfect, because once we call object_isClass, we're // going to dereference a member of the metaclass, which may or may not // be readable itself. For the time being there is no way to access it // to check here, and I have yet to hard-code a solution. Class metaclass = object_getClass(cls); if (!metaclass || !FLEXPointerIsReadable((__bridge void *)metaclass)) { return NO; } // Does the class pointer we got appear as a class to the runtime? if (!object_isClass(cls)) { return NO; } // Is the allocation size at least as large as the expected instance size? ssize_t instanceSize = class_getInstanceSize(cls); if (malloc_size(ptr) < instanceSize) { return NO; } return YES; } } // End extern "C" ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXRuntimeConstants.h ================================================ // // FLEXRuntimeConstants.h // FLEX // // Created by Tanner on 3/11/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import #import #define FLEXEncodeClass(class) ("@\"" #class "\"") #define FLEXEncodeObject(obj) (obj ? [NSString stringWithFormat:@"@\"%@\"", [obj class]].UTF8String : @encode(id)) // Arguments 0 and 1 are self and _cmd always extern const unsigned int kFLEXNumberOfImplicitArgs; // See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6 extern NSString *const kFLEXPropertyAttributeKeyTypeEncoding; extern NSString *const kFLEXPropertyAttributeKeyBackingIvarName; extern NSString *const kFLEXPropertyAttributeKeyReadOnly; extern NSString *const kFLEXPropertyAttributeKeyCopy; extern NSString *const kFLEXPropertyAttributeKeyRetain; extern NSString *const kFLEXPropertyAttributeKeyNonAtomic; extern NSString *const kFLEXPropertyAttributeKeyCustomGetter; extern NSString *const kFLEXPropertyAttributeKeyCustomSetter; extern NSString *const kFLEXPropertyAttributeKeyDynamic; extern NSString *const kFLEXPropertyAttributeKeyWeak; extern NSString *const kFLEXPropertyAttributeKeyGarbageCollectable; extern NSString *const kFLEXPropertyAttributeKeyOldStyleTypeEncoding; typedef NS_ENUM(NSUInteger, FLEXPropertyAttribute) { FLEXPropertyAttributeTypeEncoding = 'T', FLEXPropertyAttributeBackingIvarName = 'V', FLEXPropertyAttributeCopy = 'C', FLEXPropertyAttributeCustomGetter = 'G', FLEXPropertyAttributeCustomSetter = 'S', FLEXPropertyAttributeDynamic = 'D', FLEXPropertyAttributeGarbageCollectible = 'P', FLEXPropertyAttributeNonAtomic = 'N', FLEXPropertyAttributeOldTypeEncoding = 't', FLEXPropertyAttributeReadOnly = 'R', FLEXPropertyAttributeRetain = '&', FLEXPropertyAttributeWeak = 'W' }; //NS_SWIFT_NAME(FLEX.PropertyAttribute); typedef NS_ENUM(char, FLEXTypeEncoding) { FLEXTypeEncodingNull = '\0', FLEXTypeEncodingUnknown = '?', FLEXTypeEncodingChar = 'c', FLEXTypeEncodingInt = 'i', FLEXTypeEncodingShort = 's', FLEXTypeEncodingLong = 'l', FLEXTypeEncodingLongLong = 'q', FLEXTypeEncodingUnsignedChar = 'C', FLEXTypeEncodingUnsignedInt = 'I', FLEXTypeEncodingUnsignedShort = 'S', FLEXTypeEncodingUnsignedLong = 'L', FLEXTypeEncodingUnsignedLongLong = 'Q', FLEXTypeEncodingFloat = 'f', FLEXTypeEncodingDouble = 'd', FLEXTypeEncodingLongDouble = 'D', FLEXTypeEncodingCBool = 'B', FLEXTypeEncodingVoid = 'v', FLEXTypeEncodingCString = '*', FLEXTypeEncodingObjcObject = '@', FLEXTypeEncodingObjcClass = '#', FLEXTypeEncodingSelector = ':', FLEXTypeEncodingArrayBegin = '[', FLEXTypeEncodingArrayEnd = ']', FLEXTypeEncodingStructBegin = '{', FLEXTypeEncodingStructEnd = '}', FLEXTypeEncodingUnionBegin = '(', FLEXTypeEncodingUnionEnd = ')', FLEXTypeEncodingQuote = '\"', FLEXTypeEncodingBitField = 'b', FLEXTypeEncodingPointer = '^', FLEXTypeEncodingConst = 'r' }; //NS_SWIFT_NAME(FLEX.TypeEncoding); ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXRuntimeConstants.m ================================================ // // FLEXRuntimeConstants.m // FLEX // // Created by Tanner on 3/11/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeConstants.h" const unsigned int kFLEXNumberOfImplicitArgs = 2; NSString *const kFLEXPropertyAttributeKeyTypeEncoding = @"T"; NSString *const kFLEXPropertyAttributeKeyBackingIvarName = @"V"; NSString *const kFLEXPropertyAttributeKeyReadOnly = @"R"; NSString *const kFLEXPropertyAttributeKeyCopy = @"C"; NSString *const kFLEXPropertyAttributeKeyRetain = @"&"; NSString *const kFLEXPropertyAttributeKeyNonAtomic = @"N"; NSString *const kFLEXPropertyAttributeKeyCustomGetter = @"G"; NSString *const kFLEXPropertyAttributeKeyCustomSetter = @"S"; NSString *const kFLEXPropertyAttributeKeyDynamic = @"D"; NSString *const kFLEXPropertyAttributeKeyWeak = @"W"; NSString *const kFLEXPropertyAttributeKeyGarbageCollectable = @"P"; NSString *const kFLEXPropertyAttributeKeyOldStyleTypeEncoding = @"t"; ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXRuntimeSafety.h ================================================ // // FLEXRuntimeSafety.h // FLEX // // Created by Tanner on 3/25/17. // #import #import #pragma mark - Classes extern NSUInteger const kFLEXKnownUnsafeClassCount; extern const Class * FLEXKnownUnsafeClassList(void); extern NSSet * FLEXKnownUnsafeClassNames(void); extern CFSetRef FLEXKnownUnsafeClasses; static Class cNSObject = nil, cNSProxy = nil; __attribute__((constructor)) static void FLEXInitKnownRootClasses(void) { cNSObject = [NSObject class]; cNSProxy = [NSProxy class]; } static inline BOOL FLEXClassIsSafe(Class cls) { // Is it nil or known to be unsafe? if (!cls || CFSetContainsValue(FLEXKnownUnsafeClasses, (__bridge void *)cls)) { return NO; } // Is it a known root class? if (!class_getSuperclass(cls)) { return cls == cNSObject || cls == cNSProxy; } // Probably safe return YES; } static inline BOOL FLEXClassNameIsSafe(NSString *cls) { if (!cls) return NO; NSSet *ignored = FLEXKnownUnsafeClassNames(); return ![ignored containsObject:cls]; } #pragma mark - Ivars extern CFSetRef FLEXKnownUnsafeIvars; static inline BOOL FLEXIvarIsSafe(Ivar ivar) { if (!ivar) return NO; return !CFSetContainsValue(FLEXKnownUnsafeIvars, ivar); } ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXRuntimeSafety.m ================================================ // // FLEXRuntimeSafety.m // FLEX // // Created by Tanner on 3/25/17. // #import "FLEXRuntimeSafety.h" NSUInteger const kFLEXKnownUnsafeClassCount = 19; Class * _UnsafeClasses = NULL; CFSetRef FLEXKnownUnsafeClasses = nil; CFSetRef FLEXKnownUnsafeIvars = nil; #define FLEXClassPointerOrCFNull(name) \ (NSClassFromString(name) ?: (__bridge id)kCFNull) #define FLEXIvarOrCFNull(cls, name) \ (class_getInstanceVariable([cls class], name) ?: (void *)kCFNull) __attribute__((constructor)) static void FLEXRuntimeSafteyInit(void) { FLEXKnownUnsafeClasses = CFSetCreate( kCFAllocatorDefault, (const void **)(uintptr_t)FLEXKnownUnsafeClassList(), kFLEXKnownUnsafeClassCount, nil ); Ivar unsafeIvars[] = { FLEXIvarOrCFNull(NSURL, "_urlString"), FLEXIvarOrCFNull(NSURL, "_baseURL"), }; FLEXKnownUnsafeIvars = CFSetCreate( kCFAllocatorDefault, (const void **)unsafeIvars, sizeof(unsafeIvars), nil ); } const Class * FLEXKnownUnsafeClassList(void) { if (!_UnsafeClasses) { const Class ignored[] = { FLEXClassPointerOrCFNull(@"__ARCLite__"), FLEXClassPointerOrCFNull(@"__NSCFCalendar"), FLEXClassPointerOrCFNull(@"__NSCFTimer"), FLEXClassPointerOrCFNull(@"NSCFTimer"), FLEXClassPointerOrCFNull(@"__NSGenericDeallocHandler"), FLEXClassPointerOrCFNull(@"NSAutoreleasePool"), FLEXClassPointerOrCFNull(@"NSPlaceholderNumber"), FLEXClassPointerOrCFNull(@"NSPlaceholderString"), FLEXClassPointerOrCFNull(@"NSPlaceholderValue"), FLEXClassPointerOrCFNull(@"Object"), FLEXClassPointerOrCFNull(@"VMUArchitecture"), FLEXClassPointerOrCFNull(@"JSExport"), FLEXClassPointerOrCFNull(@"__NSAtom"), FLEXClassPointerOrCFNull(@"_NSZombie_"), FLEXClassPointerOrCFNull(@"_CNZombie_"), FLEXClassPointerOrCFNull(@"__NSMessage"), FLEXClassPointerOrCFNull(@"__NSMessageBuilder"), FLEXClassPointerOrCFNull(@"FigIrisAutoTrimmerMotionSampleExport"), // Temporary until we have our own type encoding parser; // setVectors: has an invalid type encoding and crashes NSMethodSignature FLEXClassPointerOrCFNull(@"_UIPointVector"), }; assert((sizeof(ignored) / sizeof(Class)) == kFLEXKnownUnsafeClassCount); _UnsafeClasses = (Class *)malloc(sizeof(ignored)); memcpy(_UnsafeClasses, ignored, sizeof(ignored)); } return _UnsafeClasses; } NSSet * FLEXKnownUnsafeClassNames(void) { static NSSet *set = nil; if (!set) { NSArray *ignored = @[ @"__ARCLite__", @"__NSCFCalendar", @"__NSCFTimer", @"NSCFTimer", @"__NSGenericDeallocHandler", @"NSAutoreleasePool", @"NSPlaceholderNumber", @"NSPlaceholderString", @"NSPlaceholderValue", @"Object", @"VMUArchitecture", @"JSExport", @"__NSAtom", @"_NSZombie_", @"_CNZombie_", @"__NSMessage", @"__NSMessageBuilder", @"FigIrisAutoTrimmerMotionSampleExport", @"_UIPointVector", ]; set = [NSSet setWithArray:ignored]; assert(set.count == kFLEXKnownUnsafeClassCount); } return set; } ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXSwiftInternal.h ================================================ // // FLEXSwiftInternal.h // FLEX // // Created by Tanner Bennett on 10/28/21. // Copyright © 2021 Flipboard. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN #ifdef __cplusplus extern "C" { #endif BOOL FLEXIsSwiftObjectOrClass(id objOrClass); #ifdef __cplusplus } #endif NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXSwiftInternal.mm ================================================ // // FLEXSwiftInternal.m // FLEX // // Created by Tanner Bennett on 10/28/21. // Copyright © 2021 Flipboard. All rights reserved. // #import "FLEXSwiftInternal.h" #import #include // class is a Swift class from the pre-stable Swift ABI #define FAST_IS_SWIFT_LEGACY (1UL<<0) // class is a Swift class from the stable Swift ABI #define FAST_IS_SWIFT_STABLE (1UL<<1) // data pointer #define FAST_DATA_MASK 0xfffffffcUL typedef uintptr_t class_data_bits_t; #if __LP64__ typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits #else typedef uint16_t mask_t; #endif /* dyld_shared_cache_builder and obj-C agree on these definitions */ struct preopt_cache_entry_t { uint32_t sel_offs; uint32_t imp_offs; }; /* dyld_shared_cache_builder and obj-C agree on these definitions */ struct preopt_cache_t { int32_t fallback_class_offset; union { struct { uint16_t shift : 5; uint16_t mask : 11; }; uint16_t hash_params; }; uint16_t occupied : 14; uint16_t has_inlines : 1; uint16_t bit_one : 1; preopt_cache_entry_t entries[]; }; union isa_t { uintptr_t bits; // Accessing the class requires custom ptrauth operations Class cls; }; struct cache_t { std::atomic _bucketsAndMaybeMask; union { struct { std::atomic _maybeMask; #if __LP64__ uint16_t _flags; #endif uint16_t _occupied; }; std::atomic _originalPreoptCache; }; }; struct objc_object_ { union isa_t isa; }; struct objc_class_ : objc_object_ { Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; }; extern "C" BOOL FLEXIsSwiftObjectOrClass(id objOrClass) { Class cls = objOrClass; if (!object_isClass(objOrClass)) { cls = object_getClass(objOrClass); } class_data_bits_t rodata = ((__bridge objc_class_ *)(cls))->bits; if (@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *)) { return (rodata & FAST_IS_SWIFT_STABLE) != 0; } else { return (rodata & FAST_IS_SWIFT_LEGACY) != 0; } } ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXTypeEncodingParser.h ================================================ // // FLEXTypeEncodingParser.h // FLEX // // Created by Tanner Bennett on 8/22/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN /// @return \c YES if the type is supported, \c NO otherwise BOOL FLEXGetSizeAndAlignment(const char *type, NSUInteger * _Nullable sizep, NSUInteger * _Nullable alignp); @interface FLEXTypeEncodingParser : NSObject /// \c cleanedEncoding is necessary because a type encoding may contain a pointer /// to an unsupported type. \c NSMethodSignature will pass each type to \c NSGetSizeAndAlignment /// which will throw an exception on unsupported struct pointers, and this exception is caught /// by \c NSMethodSignature, but it still bothers anyone debugging with \c objc_exception_throw /// /// @param cleanedEncoding the "safe" type encoding you can pass to \c NSMethodSignature /// @return whether the given type encoding can be passed to /// \c NSMethodSignature without it throwing an exception. + (BOOL)methodTypeEncodingSupported:(NSString *)typeEncoding cleaned:(NSString *_Nonnull*_Nullable)cleanedEncoding; /// @return The type encoding of an individual argument in a method's type encoding string. /// Pass 0 to get the type of the return value. 1 and 2 are `self` and `_cmd` respectively. + (NSString *)type:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx; /// @return The size in bytes of the typeof an individual argument in a method's type encoding string. /// Pass 0 to get the size of the return value. 1 and 2 are `self` and `_cmd` respectively. + (ssize_t)size:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx; /// @param unaligned whether to compute the aligned or unaligned size. /// @return The size in bytes, or \c -1 if the type encoding is unsupported. /// Do not pass in the result of \c method_getTypeEncoding + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(nullable ssize_t *)alignOut unaligned:(BOOL)unaligned; /// Defaults to \C unaligned:NO + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(nullable ssize_t *)alignOut; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/FLEXTypeEncodingParser.m ================================================ // // FLEXTypeEncodingParser.m // FLEX // // Created by Tanner Bennett on 8/22/19. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXTypeEncodingParser.h" #import "FLEXRuntimeUtility.h" #define S(__ch) ({ \ unichar __c = __ch; \ [[NSString alloc] initWithCharacters:&__c length:1]; \ }) typedef struct FLEXTypeInfo { /// The size is unaligned. -1 if not supported at all. ssize_t size; ssize_t align; /// NO if the type cannot be supported at all /// YES if the type is either fully or partially supported. BOOL supported; /// YES if the type was only partially supported, such as in /// the case of unions in pointer types, or named structure /// types without member info. These can be corrected manually /// since they can be fixed or replaced with less info. BOOL fixesApplied; /// Whether this type is a union or one of its members /// recursively contains a union, exlcuding pointers. /// /// Unions are tricky because they're supported by /// \c NSGetSizeAndAlignment but not by \c NSMethodSignature /// so we need to track whenever a type contains a union /// so that we can clean it out of pointer types. BOOL containsUnion; /// size can only be 0 if not void BOOL isVoid; } FLEXTypeInfo; /// Type info for a completely unsupported type. static FLEXTypeInfo FLEXTypeInfoUnsupported = (FLEXTypeInfo){ -1, 0, NO, NO, NO, NO }; /// Type info for the void return type. static FLEXTypeInfo FLEXTypeInfoVoid = (FLEXTypeInfo){ 0, 0, YES, NO, NO, YES }; /// Builds type info for a fully or partially supported type. static inline FLEXTypeInfo FLEXTypeInfoMake(ssize_t size, ssize_t align, BOOL fixed) { return (FLEXTypeInfo){ size, align, YES, fixed, NO, NO }; } /// Builds type info for a fully or partially supported type. static inline FLEXTypeInfo FLEXTypeInfoMakeU(ssize_t size, ssize_t align, BOOL fixed, BOOL hasUnion) { return (FLEXTypeInfo){ size, align, YES, fixed, hasUnion, NO }; } BOOL FLEXGetSizeAndAlignment(const char *type, NSUInteger *sizep, NSUInteger *alignp) { NSInteger size = 0; ssize_t align = 0; size = [FLEXTypeEncodingParser sizeForTypeEncoding:@(type) alignment:&align]; if (size == -1) { return NO; } if (sizep) { *sizep = (NSUInteger)size; } if (alignp) { *alignp = (NSUInteger)size; } return YES; } @interface FLEXTypeEncodingParser () @property (nonatomic, readonly) NSScanner *scan; @property (nonatomic, readonly) NSString *scanned; @property (nonatomic, readonly) NSString *unscanned; @property (nonatomic, readonly) char nextChar; /// Replacements are made to this string as we scan as needed @property (nonatomic) NSMutableString *cleaned; /// Offset for \e further replacements to be made within \c cleaned @property (nonatomic, readonly) NSUInteger cleanedReplacingOffset; @end @implementation FLEXTypeEncodingParser - (NSString *)scanned { return [self.scan.string substringToIndex:self.scan.scanLocation]; } - (NSString *)unscanned { return [self.scan.string substringFromIndex:self.scan.scanLocation]; } #pragma mark Initialization - (id)initWithObjCTypes:(NSString *)typeEncoding { self = [super init]; if (self) { _scan = [NSScanner scannerWithString:typeEncoding]; _scan.caseSensitive = YES; _cleaned = typeEncoding.mutableCopy; } return self; } #pragma mark Public + (BOOL)methodTypeEncodingSupported:(NSString *)typeEncoding cleaned:(NSString * __autoreleasing *)cleanedEncoding { if (!typeEncoding.length) { return NO; } FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding]; while (!parser.scan.isAtEnd) { FLEXTypeInfo info = [parser parseNextType]; if (!info.supported || info.containsUnion || (info.size == 0 && !info.isVoid)) { return NO; } } if (cleanedEncoding) { *cleanedEncoding = parser.cleaned.copy; } return YES; } + (NSString *)type:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx { FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding]; // Scan up to the argument we want for (NSUInteger i = 0; i < idx; i++) { if (![parser scanPastArg]) { [NSException raise:NSRangeException format:@"Index %@ out of bounds for type encoding '%@'", @(idx), typeEncoding ]; } } return [parser scanArg]; } + (ssize_t)size:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx { return [self sizeForTypeEncoding:[self type:typeEncoding forMethodArgumentAtIndex:idx] alignment:nil]; } + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut { return [self sizeForTypeEncoding:type alignment:alignOut unaligned:NO]; } + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut unaligned:(BOOL)unaligned { FLEXTypeInfo info = [self parseType:type]; ssize_t size = info.size; ssize_t align = info.align; if (info.supported) { if (alignOut) { *alignOut = align; } if (!unaligned) { size += size % align; } } // size is -1 if not supported return size; } + (FLEXTypeInfo)parseType:(NSString *)type cleaned:(NSString * __autoreleasing *)cleanedEncoding { FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:type]; FLEXTypeInfo info = [parser parseNextType]; if (cleanedEncoding) { *cleanedEncoding = parser.cleaned; } return info; } + (FLEXTypeInfo)parseType:(NSString *)type { return [self parseType:type cleaned:nil]; } #pragma mark Private - (NSCharacterSet *)identifierFirstCharCharacterSet { static NSCharacterSet *identifierFirstSet = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *allowed = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$"; identifierFirstSet = [NSCharacterSet characterSetWithCharactersInString:allowed]; }); return identifierFirstSet; } - (NSCharacterSet *)identifierCharacterSet { static NSCharacterSet *identifierSet = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *allowed = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$1234567890"; identifierSet = [NSCharacterSet characterSetWithCharactersInString:allowed]; }); return identifierSet; } - (char)nextChar { NSScanner *scan = self.scan; return [scan.string characterAtIndex:scan.scanLocation]; } /// For scanning struct/class names - (NSString *)scanIdentifier { NSString *prefix = nil, *suffix = nil; // Identifiers cannot start with a number if (![self.scan scanCharactersFromSet:self.identifierFirstCharCharacterSet intoString:&prefix]) { return nil; } // Optional because identifier may just be one character [self.scan scanCharactersFromSet:self.identifierCharacterSet intoString:&suffix]; if (suffix) { return [prefix stringByAppendingString:suffix]; } return prefix; } /// @return the size in bytes - (ssize_t)sizeForType:(FLEXTypeEncoding)type { switch (type) { case FLEXTypeEncodingChar: return sizeof(char); case FLEXTypeEncodingInt: return sizeof(int); case FLEXTypeEncodingShort: return sizeof(short); case FLEXTypeEncodingLong: return sizeof(long); case FLEXTypeEncodingLongLong: return sizeof(long long); case FLEXTypeEncodingUnsignedChar: return sizeof(unsigned char); case FLEXTypeEncodingUnsignedInt: return sizeof(unsigned int); case FLEXTypeEncodingUnsignedShort: return sizeof(unsigned short); case FLEXTypeEncodingUnsignedLong: return sizeof(unsigned long); case FLEXTypeEncodingUnsignedLongLong: return sizeof(unsigned long long); case FLEXTypeEncodingFloat: return sizeof(float); case FLEXTypeEncodingDouble: return sizeof(double); case FLEXTypeEncodingLongDouble: return sizeof(long double); case FLEXTypeEncodingCBool: return sizeof(_Bool); case FLEXTypeEncodingVoid: return 0; case FLEXTypeEncodingCString: return sizeof(char *); case FLEXTypeEncodingObjcObject: return sizeof(id); case FLEXTypeEncodingObjcClass: return sizeof(Class); case FLEXTypeEncodingSelector: return sizeof(SEL); // Unknown / '?' is typically a pointer. In the rare case // it isn't, such as in '{?=...}', it is never passed here. case FLEXTypeEncodingUnknown: case FLEXTypeEncodingPointer: return sizeof(uintptr_t); default: return -1; } } - (FLEXTypeInfo)parseNextType { NSUInteger start = self.scan.scanLocation; // Check for void first if ([self scanChar:FLEXTypeEncodingVoid]) { // Skip argument frame for method signatures [self scanSize]; return FLEXTypeInfoVoid; } // Scan optional const [self scanChar:FLEXTypeEncodingConst]; // Check for pointer, then scan next if ([self scanChar:FLEXTypeEncodingPointer]) { // Recurse to scan something else NSUInteger pointerTypeStart = self.scan.scanLocation; if ([self scanPastArg]) { // Make sure the pointer type is supported, and clean it if not NSUInteger pointerTypeLength = self.scan.scanLocation - pointerTypeStart; NSString *pointerType = [self.scan.string substringWithRange:NSMakeRange(pointerTypeStart, pointerTypeLength) ]; // Deeeep nested cleaning info gets lost here NSString *cleaned = nil; FLEXTypeInfo info = [self.class parseType:pointerType cleaned:&cleaned]; BOOL needsCleaning = !info.supported || info.containsUnion || info.fixesApplied; // Clean the type if it is unsupported, malformed, or contains a union. // (Unions are supported by NSGetSizeAndAlignment but not // supported by NSMethodSignature for some reason) if (needsCleaning) { // If unsupported, no cleaning occurred in parseType:cleaned: above. // Otherwise, the type is partially supported and we did clean it, // and we will replace this type with the cleaned type from above. if (!info.supported || info.containsUnion) { cleaned = [self cleanPointeeTypeAtLocation:pointerTypeStart]; } NSInteger offset = self.cleanedReplacingOffset; NSInteger location = pointerTypeStart - offset; [self.cleaned replaceCharactersInRange:NSMakeRange( location, pointerTypeLength ) withString:cleaned]; } // Skip optional frame offset [self scanSize]; ssize_t size = [self sizeForType:FLEXTypeEncodingPointer]; return FLEXTypeInfoMake(size, size, !info.supported || info.fixesApplied); } else { // Scan failed, abort self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } } // Check for struct/union/array char next = self.nextChar; BOOL didScanSUA = YES, structOrUnion = NO, isUnion = NO; FLEXTypeEncoding opening = FLEXTypeEncodingNull, closing = FLEXTypeEncodingNull; switch (next) { case FLEXTypeEncodingStructBegin: structOrUnion = YES; opening = FLEXTypeEncodingStructBegin; closing = FLEXTypeEncodingStructEnd; break; case FLEXTypeEncodingUnionBegin: structOrUnion = isUnion = YES; opening = FLEXTypeEncodingUnionBegin; closing = FLEXTypeEncodingUnionEnd; break; case FLEXTypeEncodingArrayBegin: opening = FLEXTypeEncodingArrayBegin; closing = FLEXTypeEncodingArrayEnd; break; default: didScanSUA = NO; break; } if (didScanSUA) { BOOL containsUnion = isUnion; BOOL fixesApplied = NO; NSUInteger backup = self.scan.scanLocation; // Ensure we have a closing tag if (![self scanPair:opening close:closing]) { // Scan failed, abort self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } // Move cursor just after opening tag (struct/union/array) NSInteger arrayCount = -1; self.scan.scanLocation = backup + 1; if (!structOrUnion) { arrayCount = [self scanSize]; if (!arrayCount || self.nextChar == FLEXTypeEncodingArrayEnd) { // Malformed array type: // 1. Arrays must have a count after the opening brace // 2. Arrays must have an element type after the count self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } } else { // If we encounter the ?= portion of something like {?=b8b4b1b1b18[8S]} // then we skip over it, since it means nothing to us in this context. // It is completely optional, and if it fails, we go right back where we were. if (![self scanTypeName] && self.nextChar == FLEXTypeEncodingUnknown) { // Exception: we are trying to parse {?} which is invalid self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } } // Sum sizes of members together: // Scan for bitfields before checking for other members // // Arrays will only have one "member," but // this logic still works for them ssize_t sizeSoFar = 0; ssize_t maxAlign = 0; NSMutableString *cleanedBackup = self.cleaned.mutableCopy; while (![self scanChar:closing]) { next = self.nextChar; // Check for bitfields, which we cannot support because // type encodings for bitfields do not include alignment info if (next == FLEXTypeEncodingBitField) { self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } // Structure fields could be named if (next == FLEXTypeEncodingQuote) { [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote]; } FLEXTypeInfo info = [self parseNextType]; if (!info.supported || info.containsUnion) { // The above call is the only time in this method where // `cleaned` might be mutated recursively, so this is the // only place where we need to keep and restore a backup // // For instance, if we've been iterating over the members // of a struct and we've encountered a few pointers so far // that we needed to clean, and suddenly we come across an // unsupported member, we need to be able to "rewind" and // undo any changes to `self.cleaned` so that the parent // call in the call stack can wipe the current structure // clean entirely if needed. Example below: // // Initial: ^{foo=^{pair}{^pair}{invalid_type}} // v-- here // 1st clean: ^{foo=^{?=}{^pair}{invalid_type} // v-- here // 2nd clean: ^{foo=^{?=}{?=}{invalid_type} // v-- here // Can't clean: ^{foo=^{?=}{?=}{invalid_type} // v-- to here // Rewind: ^{foo=^{pair}{^pair}{invalid_type}} // Final clean: ^{foo=} self.cleaned = cleanedBackup; self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } // Unions are the size of their largest member, // arrays are element.size x length, and // structs are the sum of their members if (structOrUnion) { if (isUnion) { // Union sizeSoFar = MAX(sizeSoFar, info.size); } else { // Struct sizeSoFar += info.size; } } else { // Array sizeSoFar = info.size * arrayCount; } // Propogate the max alignment and other metadata maxAlign = MAX(maxAlign, info.align); containsUnion = containsUnion || info.containsUnion; fixesApplied = fixesApplied || info.fixesApplied; } // Skip optional frame offset [self scanSize]; return FLEXTypeInfoMakeU(sizeSoFar, maxAlign, fixesApplied, containsUnion); } // Scan single thing and possible size and return ssize_t size = -1; char t = self.nextChar; switch (t) { case FLEXTypeEncodingUnknown: case FLEXTypeEncodingChar: case FLEXTypeEncodingInt: case FLEXTypeEncodingShort: case FLEXTypeEncodingLong: case FLEXTypeEncodingLongLong: case FLEXTypeEncodingUnsignedChar: case FLEXTypeEncodingUnsignedInt: case FLEXTypeEncodingUnsignedShort: case FLEXTypeEncodingUnsignedLong: case FLEXTypeEncodingUnsignedLongLong: case FLEXTypeEncodingFloat: case FLEXTypeEncodingDouble: case FLEXTypeEncodingLongDouble: case FLEXTypeEncodingCBool: case FLEXTypeEncodingCString: case FLEXTypeEncodingSelector: case FLEXTypeEncodingBitField: { self.scan.scanLocation++; // Skip optional frame offset [self scanSize]; if (t == FLEXTypeEncodingBitField) { self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } else { // Compute size size = [self sizeForType:t]; } } break; case FLEXTypeEncodingObjcObject: case FLEXTypeEncodingObjcClass: { self.scan.scanLocation++; // These might have numbers OR quotes after them // Skip optional frame offset [self scanSize]; [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote]; size = sizeof(id); } break; default: break; } if (size > 0) { // Alignment of scalar types is its size return FLEXTypeInfoMake(size, size, NO); } self.scan.scanLocation = start; return FLEXTypeInfoUnsupported; } - (BOOL)scanString:(NSString *)str { return [self.scan scanString:str intoString:nil]; } - (BOOL)canScanString:(NSString *)str { NSScanner *scan = self.scan; NSUInteger len = str.length; unichar buff1[len], buff2[len]; [str getCharacters:buff1]; [scan.string getCharacters:buff2 range:NSMakeRange(scan.scanLocation, len)]; if (memcmp(buff1, buff2, len) == 0) { return YES; } return NO; } - (BOOL)canScanChar:(char)c { // By avoiding any ARC calls on these two objects which we know won't be // free'd out from under us, we're making HUGE performance savings in this // parser, because this method is one of the most-used methods of the parser. // This is probably the most performance-critical method in this class. __unsafe_unretained NSScanner *scan = self.scan; __unsafe_unretained NSString *string = scan.string; if (scan.scanLocation >= string.length) return NO; return [string characterAtIndex:scan.scanLocation] == c; } - (BOOL)scanChar:(char)c { if ([self canScanChar:c]) { self.scan.scanLocation++; return YES; } return NO; } - (BOOL)scanChar:(char)c into:(char *)ref { if ([self scanChar:c]) { *ref = c; return YES; } return NO; } - (ssize_t)scanSize { NSInteger size = 0; if ([self.scan scanInteger:&size]) { return size; } return 0; } - (NSString *)scanPair:(char)c1 close:(char)c2 { // Starting position and string variables NSUInteger start = self.scan.scanLocation; NSString *s1 = S(c1); // Scan opening tag if (![self scanChar:c1]) { self.scan.scanLocation = start; return nil; } // Character set for scanning up to either symbol NSCharacterSet *bothChars = ({ unichar buff[2] = { c1, c2 }; NSString *bothCharsStr = [[NSString alloc] initWithCharacters:buff length:2]; [NSCharacterSet characterSetWithCharactersInString:bothCharsStr]; }); // Stack for finding pairs, starting with the opening symbol NSMutableArray *stack = [NSMutableArray arrayWithObject:s1]; // Algorithm for scanning to the closing end of a pair of opening/closing symbols // scanUpToCharactersFromSet:intoString: returns NO if you're already at one of the chars, // so we need to check if we can actually scan one if it returns NO while ([self.scan scanUpToCharactersFromSet:bothChars intoString:nil] || [self canScanChar:c1] || [self canScanChar:c2]) { // Closing symbol found if ([self scanChar:c2]) { if (!stack.count) { // Abort, no matching opening symbol self.scan.scanLocation = start; return nil; } // Pair found, pop opening symbol [stack removeLastObject]; // Exit loop if we reached the closing brace we needed if (!stack.count) { break; } } // Opening symbol found if ([self scanChar:c1]) { // Begin pair [stack addObject:s1]; } } if (stack.count) { // Abort, no matching closing symbol self.scan.scanLocation = start; return nil; } // Slice out the string we just scanned return [self.scan.string substringWithRange:NSMakeRange(start, self.scan.scanLocation - start) ]; } - (BOOL)scanPastArg { NSUInteger start = self.scan.scanLocation; // Check for void first if ([self scanChar:FLEXTypeEncodingVoid]) { return YES; } // Scan optional const [self scanChar:FLEXTypeEncodingConst]; // Check for pointer, then scan next if ([self scanChar:FLEXTypeEncodingPointer]) { // Recurse to scan something else if ([self scanPastArg]) { return YES; } else { // Scan failed, abort self.scan.scanLocation = start; return NO; } } char next = self.nextChar; // Check for struct/union/array, scan past it FLEXTypeEncoding opening = FLEXTypeEncodingNull, closing = FLEXTypeEncodingNull; BOOL checkPair = YES; switch (next) { case FLEXTypeEncodingStructBegin: opening = FLEXTypeEncodingStructBegin; closing = FLEXTypeEncodingStructEnd; break; case FLEXTypeEncodingUnionBegin: opening = FLEXTypeEncodingUnionBegin; closing = FLEXTypeEncodingUnionEnd; break; case FLEXTypeEncodingArrayBegin: opening = FLEXTypeEncodingArrayBegin; closing = FLEXTypeEncodingArrayEnd; break; default: checkPair = NO; break; } if (checkPair && [self scanPair:opening close:closing]) { return YES; } // Scan single thing and possible size and return switch (next) { case FLEXTypeEncodingUnknown: case FLEXTypeEncodingChar: case FLEXTypeEncodingInt: case FLEXTypeEncodingShort: case FLEXTypeEncodingLong: case FLEXTypeEncodingLongLong: case FLEXTypeEncodingUnsignedChar: case FLEXTypeEncodingUnsignedInt: case FLEXTypeEncodingUnsignedShort: case FLEXTypeEncodingUnsignedLong: case FLEXTypeEncodingUnsignedLongLong: case FLEXTypeEncodingFloat: case FLEXTypeEncodingDouble: case FLEXTypeEncodingLongDouble: case FLEXTypeEncodingCBool: case FLEXTypeEncodingCString: case FLEXTypeEncodingSelector: case FLEXTypeEncodingBitField: { self.scan.scanLocation++; // Size is optional [self scanSize]; return YES; } case FLEXTypeEncodingObjcObject: case FLEXTypeEncodingObjcClass: { self.scan.scanLocation++; // These might have numbers OR quotes after them [self scanSize] || [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote]; return YES; } default: break; } self.scan.scanLocation = start; return NO; } - (NSString *)scanArg { NSUInteger start = self.scan.scanLocation; if (![self scanPastArg]) { return nil; } return [self.scan.string substringWithRange:NSMakeRange(start, self.scan.scanLocation - start) ]; } - (BOOL)scanTypeName { NSUInteger start = self.scan.scanLocation; // The ?= portion of something like {?=b8b4b1b1b18[8S]} if ([self scanChar:FLEXTypeEncodingUnknown]) { if (![self scanString:@"="]) { // No size information available for strings like {?=} self.scan.scanLocation = start; return NO; } } else { if (![self scanIdentifier] || ![self scanString:@"="]) { // 1. Not a valid identifier // 2. No size information available for strings like {CGPoint} self.scan.scanLocation = start; return NO; } } return YES; } - (NSString *)extractTypeNameFromScanLocation:(BOOL)allowMissingTypeInfo closing:(FLEXTypeEncoding)closeTag { NSUInteger start = self.scan.scanLocation; // The ?= portion of something like {?=b8b4b1b1b18[8S]} if ([self scanChar:FLEXTypeEncodingUnknown]) { return @"?"; } else { NSString *typeName = [self scanIdentifier]; char next = self.nextChar; if (!typeName) { // Did not scan an identifier self.scan.scanLocation = start; return nil; } switch (next) { case '=': return typeName; default: { // = is non-optional unless we allowMissingTypeInfo, in whcih // case the next character needs to be a closing brace if (allowMissingTypeInfo && next == closeTag) { return typeName; } else { // Not a valid identifier; possibly a generic C++ type // i.e. {pair} where `name` was found as `pair` self.scan.scanLocation = start; return nil; } } } } } - (NSString *)cleanPointeeTypeAtLocation:(NSUInteger)scanLocation { NSUInteger start = self.scan.scanLocation; self.scan.scanLocation = scanLocation; // The return / cleanup code for when the scanned type is already clean NSString * (^typeIsClean)(void) = ^NSString * { NSString *clean = [self.scan.string substringWithRange:NSMakeRange(scanLocation, self.scan.scanLocation - scanLocation) ]; // Reset scan location even on success, because this method is not supposed to change it self.scan.scanLocation = start; return clean; }; // No void, this is not a return type // Scan optional const [self scanChar:FLEXTypeEncodingConst]; char next = self.nextChar; switch (next) { case FLEXTypeEncodingPointer: // Recurse to scan something else [self scanChar:next]; return [self cleanPointeeTypeAtLocation:self.scan.scanLocation]; case FLEXTypeEncodingArrayBegin: // All arrays are supported, scan past them if ([self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) { return typeIsClean(); } break; case FLEXTypeEncodingUnionBegin: // Unions are not supported at all in NSMethodSignature // We could check for the closing token to be safe, but eh self.scan.scanLocation = start; return @"?"; case FLEXTypeEncodingStructBegin: { FLEXTypeInfo info = [self.class parseType:self.unscanned]; if (info.supported && !info.fixesApplied) { [self scanPastArg]; return typeIsClean(); } // The structure we just tried to scan is unsupported, so just return its name // if it has one. If not, just return a question mark. self.scan.scanLocation++; // Skip past { NSString *name = [self extractTypeNameFromScanLocation:YES closing:FLEXTypeEncodingStructEnd]; if (name) { // Got the name, scan past the closing token [self.scan scanUpToString:@"}" intoString:nil]; if (![self scanChar:FLEXTypeEncodingStructEnd]) { // Missing struct close token self.scan.scanLocation = start; return nil; } } else { // Did not scan valid identifier, possibly a C++ type self.scan.scanLocation = start; return @"{?=}"; } // Reset scan location even on success, because this method is not supposed to change it self.scan.scanLocation = start; return ({ // "{name=}" NSMutableString *format = @"{".mutableCopy; [format appendString:name]; [format appendString:@"=}"]; format; }); } default: break; } // Check for other types, which in theory are all valid but whatever FLEXTypeInfo info = [self parseNextType]; if (info.supported && !info.fixesApplied) { return typeIsClean(); } self.scan.scanLocation = start; return @"?"; } - (NSUInteger)cleanedReplacingOffset { return self.scan.string.length - self.cleaned.length; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXBlockDescription.h ================================================ // // FLEXBlockDescription.h // FLEX // // Created by Oliver Letterer on 2012-09-01 // Forked from CTObjectiveCRuntimeAdditions (MIT License) // https://github.com/ebf/CTObjectiveCRuntimeAdditions // // Copyright (c) 2020 FLEX Team-EDV Beratung Föllmer GmbH // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import typedef NS_OPTIONS(NSUInteger, FLEXBlockOptions) { FLEXBlockOptionHasCopyDispose = (1 << 25), FLEXBlockOptionHasCtor = (1 << 26), // helpers have C++ code FLEXBlockOptionIsGlobal = (1 << 28), FLEXBlockOptionHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE FLEXBlockOptionHasSignature = (1 << 30), }; NS_ASSUME_NONNULL_BEGIN #pragma mark - @interface FLEXBlockDescription : NSObject + (instancetype)describing:(id)block; @property (nonatomic, readonly, nullable) NSMethodSignature *signature; @property (nonatomic, readonly, nullable) NSString *signatureString; @property (nonatomic, readonly, nullable) NSString *sourceDeclaration; @property (nonatomic, readonly) FLEXBlockOptions flags; @property (nonatomic, readonly) NSUInteger size; @property (nonatomic, readonly) NSString *summary; @property (nonatomic, readonly) id block; - (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature; @end #pragma mark - @interface NSBlock : NSObject - (void)invoke; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXBlockDescription.m ================================================ // // FLEXBlockDescription.m // FLEX // // Created by Oliver Letterer on 2012-09-01 // Forked from CTObjectiveCRuntimeAdditions (MIT License) // https://github.com/ebf/CTObjectiveCRuntimeAdditions // // Copyright (c) 2020 FLEX Team-EDV Beratung Föllmer GmbH // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "FLEXBlockDescription.h" #import "FLEXRuntimeUtility.h" struct block_object { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct block_descriptor { unsigned long int reserved; // NULL unsigned long int size; // sizeof(struct Block_literal_1) // optional helper functions void (*copy_helper)(void *dst, void *src); // IFF (1<<25) void (*dispose_helper)(void *src); // IFF (1<<25) // required ABI.2010.3.16 const char *signature; // IFF (1<<30) } *descriptor; // imported variables }; @implementation FLEXBlockDescription + (instancetype)describing:(id)block { return [[self alloc] initWithObjcBlock:block]; } - (id)initWithObjcBlock:(id)block { self = [super init]; if (self) { _block = block; struct block_object *blockRef = (__bridge struct block_object *)block; _flags = blockRef->flags; _size = blockRef->descriptor->size; if (_flags & FLEXBlockOptionHasSignature) { void *signatureLocation = blockRef->descriptor; signatureLocation += sizeof(unsigned long int); signatureLocation += sizeof(unsigned long int); if (_flags & FLEXBlockOptionHasCopyDispose) { signatureLocation += sizeof(void(*)(void *dst, void *src)); signatureLocation += sizeof(void (*)(void *src)); } const char *signature = (*(const char **)signatureLocation); _signatureString = @(signature); @try { _signature = [NSMethodSignature signatureWithObjCTypes:signature]; } @catch (NSException *exception) { } } NSMutableString *summary = [NSMutableString stringWithFormat: @"Type signature: %@\nSize: %@\nIs global: %@\nHas constructor: %@\nIs stret: %@", self.signatureString ?: @"nil", @(self.size), @((BOOL)(_flags & FLEXBlockOptionIsGlobal)), @((BOOL)(_flags & FLEXBlockOptionHasCtor)), @((BOOL)(_flags & FLEXBlockOptionHasStret)) ]; if (!self.signature) { [summary appendFormat:@"\nNumber of arguments: %@", @(self.signature.numberOfArguments)]; } _summary = summary.copy; _sourceDeclaration = [self buildLikelyDeclaration]; } return self; } - (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature { if (!self.signature) { return NO; } if (self.signature.numberOfArguments != methodSignature.numberOfArguments + 1) { return NO; } if (strcmp(self.signature.methodReturnType, methodSignature.methodReturnType) != 0) { return NO; } for (int i = 0; i < methodSignature.numberOfArguments; i++) { if (i == 1) { // SEL in method, IMP in block if (strcmp([methodSignature getArgumentTypeAtIndex:i], ":") != 0) { return NO; } if (strcmp([self.signature getArgumentTypeAtIndex:i + 1], "^?") != 0) { return NO; } } else { if (strcmp([self.signature getArgumentTypeAtIndex:i], [self.signature getArgumentTypeAtIndex:i + 1]) != 0) { return NO; } } } return YES; } - (NSString *)buildLikelyDeclaration { NSMethodSignature *signature = self.signature; NSUInteger numberOfArguments = signature.numberOfArguments; const char *returnType = signature.methodReturnType; // Return type NSMutableString *decl = [NSMutableString stringWithString:@"^"]; if (returnType[0] != FLEXTypeEncodingVoid) { [decl appendString:[FLEXRuntimeUtility readableTypeForEncoding:@(returnType)]]; [decl appendString:@" "]; } // Arguments if (numberOfArguments) { [decl appendString:@"("]; for (NSUInteger i = 1; i < numberOfArguments; i++) { const char *argType = [self.signature getArgumentTypeAtIndex:i] ?: "?"; NSString *readableArgType = [FLEXRuntimeUtility readableTypeForEncoding:@(argType)]; [decl appendFormat:@"%@ arg%@, ", readableArgType, @(i)]; } [decl deleteCharactersInRange:NSMakeRange(decl.length-2, 2)]; [decl appendString:@")"]; } return decl.copy; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXClassBuilder.h ================================================ // // FLEXClassBuilder.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/3/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXIvarBuilder, FLEXMethodBase, FLEXProperty, FLEXProtocol; #pragma mark FLEXClassBuilder @interface FLEXClassBuilder : NSObject @property (nonatomic, readonly) Class workingClass; /// Begins constructing a class with the given name. /// /// This new class will implicitly inherits from \c NSObject with \c 0 extra bytes. /// Classes created this way must be registered with \c -registerClass before being used. + (instancetype)allocateClass:(NSString *)name; /// Begins constructing a class with the given name and superclass. /// @discussion Calls \c -allocateClass:superclass:extraBytes: with \c 0 extra bytes. /// Classes created this way must be registered with \c -registerClass before being used. + (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass; /// Begins constructing a new class object with the given name and superclass. /// @discussion Pass \c nil to \e superclass to create a new root class. /// Classes created this way must be registered with \c -registerClass before being used. + (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass extraBytes:(size_t)bytes; /// Begins constructing a new root class object with the given name and \c 0 extra bytes. /// @discussion Classes created this way must be registered with \c -registerClass before being used. + (instancetype)allocateRootClass:(NSString *)name; /// Use this to modify existing classes. @warning You cannot add instance variables to existing classes. + (instancetype)builderForClass:(Class)cls; /// @return Any methods that failed to be added. - (NSArray *)addMethods:(NSArray *)methods; /// @return Any properties that failed to be added. - (NSArray *)addProperties:(NSArray *)properties; /// @return Any protocols that failed to be added. - (NSArray *)addProtocols:(NSArray *)protocols; /// @warning Adding Ivars to existing classes is not supported and will always fail. - (NSArray *)addIvars:(NSArray *)ivars; /// Finalizes construction of a new class. /// @discussion Once a class is registered, instance variables cannot be added. /// @note Raises an exception if called on a previously registered class. - (Class)registerClass; /// Uses \c objc_lookupClass to determine if the working class is registered. @property (nonatomic, readonly) BOOL isRegistered; @end #pragma mark FLEXIvarBuilder @interface FLEXIvarBuilder : NSObject /// Consider using the \c FLEXIvarBuilderWithNameAndType() macro below. /// @param name The name of the Ivar, such as \c \@"_value". /// @param size The size of the Ivar. Usually \c sizeof(type). For objects, this is \c sizeof(id). /// @param alignment The alignment of the Ivar. Usually \c log2(sizeof(type)). /// @param encoding The type encoding of the Ivar. For objects, this is \c \@(\@encode(id)), and for others it is \c \@(\@encode(type)). + (instancetype)name:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment typeEncoding:(NSString *)encoding; @property (nonatomic, readonly) NSString *name; @property (nonatomic, readonly) NSString *encoding; @property (nonatomic, readonly) size_t size; @property (nonatomic, readonly) uint8_t alignment; @end #define FLEXIvarBuilderWithNameAndType(nameString, type) [FLEXIvarBuilder \ name:nameString \ size:sizeof(type) \ alignment:log2(sizeof(type)) \ typeEncoding:@(@encode(type)) \ ] ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXClassBuilder.m ================================================ // // FLEXClassBuilder.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/3/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXClassBuilder.h" #import "FLEXProperty.h" #import "FLEXMethodBase.h" #import "FLEXProtocol.h" #import #pragma mark FLEXClassBuilder @interface FLEXClassBuilder () @property (nonatomic) NSString *name; @end @implementation FLEXClassBuilder - (id)init { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with -init" ]; return nil; } #pragma mark Initializers + (instancetype)allocateClass:(NSString *)name { return [self allocateClass:name superclass:NSObject.class]; } + (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass { return [self allocateClass:name superclass:superclass extraBytes:0]; } + (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass extraBytes:(size_t)bytes { NSParameterAssert(name); return [[self alloc] initWithClass:objc_allocateClassPair(superclass, name.UTF8String, bytes)]; } + (instancetype)allocateRootClass:(NSString *)name { NSParameterAssert(name); return [[self alloc] initWithClass:objc_allocateClassPair(Nil, name.UTF8String, 0)]; } + (instancetype)builderForClass:(Class)cls { return [[self alloc] initWithClass:cls]; } - (id)initWithClass:(Class)cls { NSParameterAssert(cls); self = [super init]; if (self) { _workingClass = cls; _name = NSStringFromClass(_workingClass); } return self; } - (NSString *)description { return [NSString stringWithFormat:@"<%@ name=%@, registered=%d>", NSStringFromClass(self.class), self.name, self.isRegistered]; } #pragma mark Building - (NSArray *)addMethods:(NSArray *)methods { NSParameterAssert(methods.count); NSMutableArray *failed = [NSMutableArray new]; for (FLEXMethodBase *m in methods) { if (!class_addMethod(self.workingClass, m.selector, m.implementation, m.typeEncoding.UTF8String)) { [failed addObject:m]; } } return failed; } - (NSArray *)addProperties:(NSArray *)properties { NSParameterAssert(properties.count); NSMutableArray *failed = [NSMutableArray new]; for (FLEXProperty *p in properties) { unsigned int pcount; objc_property_attribute_t *attributes = [p copyAttributesList:&pcount]; if (!class_addProperty(self.workingClass, p.name.UTF8String, attributes, pcount)) { [failed addObject:p]; } free(attributes); } return failed; } - (NSArray *)addProtocols:(NSArray *)protocols { NSParameterAssert(protocols.count); NSMutableArray *failed = [NSMutableArray new]; for (FLEXProtocol *p in protocols) { if (!class_addProtocol(self.workingClass, p.objc_protocol)) { [failed addObject:p]; } } return failed; } - (NSArray *)addIvars:(NSArray *)ivars { NSParameterAssert(ivars.count); NSMutableArray *failed = [NSMutableArray new]; for (FLEXIvarBuilder *ivar in ivars) { if (!class_addIvar(self.workingClass, ivar.name.UTF8String, ivar.size, ivar.alignment, ivar.encoding.UTF8String)) { [failed addObject:ivar]; } } return failed; } - (Class)registerClass { if (self.isRegistered) { [NSException raise:NSInternalInconsistencyException format:@"Class is already registered"]; } objc_registerClassPair(self.workingClass); return self.workingClass; } - (BOOL)isRegistered { return objc_lookUpClass(self.name.UTF8String) != nil; } @end #pragma mark FLEXIvarBuilder @implementation FLEXIvarBuilder + (instancetype)name:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment typeEncoding:(NSString *)encoding { return [[self alloc] initWithName:name size:size alignment:alignment typeEncoding:encoding]; } - (id)initWithName:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment typeEncoding:(NSString *)encoding { NSParameterAssert(name); NSParameterAssert(encoding); NSParameterAssert(size > 0); NSParameterAssert(alignment > 0); self = [super init]; if (self) { _name = name; _encoding = encoding; _size = size; _alignment = alignment; } return self; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXIvar.h ================================================ // // FLEXIvar.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeConstants.h" NS_ASSUME_NONNULL_BEGIN @interface FLEXIvar : NSObject + (instancetype)ivar:(Ivar)ivar; + (instancetype)named:(NSString *)name onClass:(Class)cls; /// The underlying \c Ivar data structure. @property (nonatomic, readonly) Ivar objc_ivar; /// The name of the instance variable. @property (nonatomic, readonly) NSString *name; /// The type of the instance variable. @property (nonatomic, readonly) FLEXTypeEncoding type; /// The type encoding string of the instance variable. @property (nonatomic, readonly) NSString *typeEncoding; /// The offset of the instance variable. @property (nonatomic, readonly) NSInteger offset; /// The size of the instance variable. 0 if unknown. @property (nonatomic, readonly) NSUInteger size; /// Describes the type encoding, size, offset, and objc_ivar @property (nonatomic, readonly) NSString *details; /// The full path of the image that contains this ivar definition, /// or \c nil if this ivar was probably defined at runtime. @property (nonatomic, readonly, nullable) NSString *imagePath; /// For internal use @property (nonatomic) id tag; - (nullable id)getValue:(id)target; - (void)setValue:(nullable id)value onObject:(id)target; /// Calls into -getValue: and passes that value into /// -[FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:type:] /// and returns the result - (nullable id)getPotentiallyUnboxedValue:(id)target; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXIvar.m ================================================ // // FLEXIvar.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXIvar.h" #import "FLEXRuntimeUtility.h" #import "FLEXRuntimeSafety.h" #import "FLEXTypeEncodingParser.h" #import "NSString+FLEX.h" #include "FLEXObjcInternal.h" #include @interface FLEXIvar () { NSString *_flex_description; } @end @implementation FLEXIvar #pragma mark Initializers + (instancetype)ivar:(Ivar)ivar { return [[self alloc] initWithIvar:ivar]; } + (instancetype)named:(NSString *)name onClass:(Class)cls { Ivar _Nullable ivar = class_getInstanceVariable(cls, name.UTF8String); NSAssert(ivar, @"Cannot find ivar with name %@ on class %@", name, cls); return [self ivar:ivar]; } - (id)initWithIvar:(Ivar)ivar { NSParameterAssert(ivar); self = [super init]; if (self) { _objc_ivar = ivar; [self examine]; } return self; } #pragma mark Other - (NSString *)description { if (!_flex_description) { NSString *readableType = [FLEXRuntimeUtility readableTypeForEncoding:self.typeEncoding]; _flex_description = [FLEXRuntimeUtility appendName:self.name toType:readableType]; } return _flex_description; } - (NSString *)debugDescription { return [NSString stringWithFormat:@"<%@ name=%@, encoding=%@, offset=%ld>", NSStringFromClass(self.class), self.name, self.typeEncoding, (long)self.offset]; } - (void)examine { _name = @(ivar_getName(self.objc_ivar) ?: "(nil)"); _offset = ivar_getOffset(self.objc_ivar); _typeEncoding = @(ivar_getTypeEncoding(self.objc_ivar) ?: ""); NSString *typeForDetails = _typeEncoding; NSString *sizeForDetails = nil; if (_typeEncoding.length) { _type = (FLEXTypeEncoding)[_typeEncoding characterAtIndex:0]; FLEXGetSizeAndAlignment(_typeEncoding.UTF8String, &_size, nil); sizeForDetails = [@(_size).stringValue stringByAppendingString:@" bytes"]; } else { _type = FLEXTypeEncodingNull; typeForDetails = @"no type info"; sizeForDetails = @"unknown size"; } Dl_info exeInfo; if (dladdr(_objc_ivar, &exeInfo)) { _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil; } _details = [NSString stringWithFormat: @"%@, offset %@ — %@", sizeForDetails, @(_offset), typeForDetails ]; } - (id)getValue:(id)target { id value = nil; if (!FLEXIvarIsSafe(_objc_ivar) || _type == FLEXTypeEncodingNull || FLEXPointerIsTaggedPointer(target)) { return nil; } #ifdef __arm64__ // See http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html if (self.type == FLEXTypeEncodingObjcClass && [self.name isEqualToString:@"isa"]) { value = object_getClass(target); } else #endif if (self.type == FLEXTypeEncodingObjcObject || self.type == FLEXTypeEncodingObjcClass) { value = object_getIvar(target, self.objc_ivar); } else { void *pointer = (__bridge void *)target + self.offset; value = [FLEXRuntimeUtility valueForPrimitivePointer:pointer objCType:self.typeEncoding.UTF8String ]; } return value; } - (void)setValue:(id)value onObject:(id)target { const char *typeEncodingCString = self.typeEncoding.UTF8String; if (self.type == FLEXTypeEncodingObjcObject) { object_setIvar(target, self.objc_ivar, value); } else if ([value isKindOfClass:[NSValue class]]) { // Primitive - unbox the NSValue. NSValue *valueValue = (NSValue *)value; // Make sure that the box contained the correct type. NSAssert( strcmp(valueValue.objCType, typeEncodingCString) == 0, @"Type encoding mismatch (value: %s; ivar: %s) in setting ivar named: %@ on object: %@", valueValue.objCType, typeEncodingCString, self.name, target ); NSUInteger bufferSize = 0; if (FLEXGetSizeAndAlignment(typeEncodingCString, &bufferSize, NULL)) { void *buffer = calloc(bufferSize, 1); [valueValue getValue:buffer]; void *pointer = (__bridge void *)target + self.offset; memcpy(pointer, buffer, bufferSize); free(buffer); } } } - (id)getPotentiallyUnboxedValue:(id)target { NSString *type = self.typeEncoding; if (type.flex_typeIsNonObjcPointer && type.flex_pointeeType != FLEXTypeEncodingVoid) { return [self getValue:target]; } return [FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:[self getValue:target] type:type.UTF8String ]; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMetadataExtras.h ================================================ // // FLEXMetadataExtras.h // FLEX // // Created by Tanner Bennett on 4/26/22. // #import #import "FLEXMethodBase.h" #import "FLEXProperty.h" #import "FLEXIvar.h" NS_ASSUME_NONNULL_BEGIN /// A dictionary mapping type encoding strings to an array of field titles extern NSString * const FLEXAuxiliarynfoKeyFieldLabels; @protocol FLEXMetadataAuxiliaryInfo /// Used to supply arbitrary additional data that need not be exposed by their own properties - (nullable id)auxiliaryInfoForKey:(NSString *)key; @end @interface FLEXMethodBase (Auxiliary) @end @interface FLEXProperty (Auxiliary) @end @interface FLEXIvar (Auxiliary) @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMetadataExtras.m ================================================ // // FLEXMetadataExtras.m // FLEX // // Created by Tanner Bennett on 4/26/22. // #import "FLEXMetadataExtras.h" NSString * const FLEXAuxiliarynfoKeyFieldLabels = @"FLEXAuxiliarynfoKeyFieldLabels"; @implementation FLEXMethodBase (Auxiliary) - (id)auxiliaryInfoForKey:(NSString *)key { return nil; } @end @implementation FLEXProperty (Auxiliary) - (id)auxiliaryInfoForKey:(NSString *)key { return nil; } @end @implementation FLEXIvar (Auxiliary) - (id)auxiliaryInfoForKey:(NSString *)key { return nil; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMethod.h ================================================ // // FLEXMethod.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeConstants.h" #import "FLEXMethodBase.h" NS_ASSUME_NONNULL_BEGIN /// A class representing a concrete method which already exists in a class. /// This class contains helper methods for swizzling or invoking the method. /// /// Any of the initializers will return nil if the type encoding /// of the method is unsupported by `NSMethodSignature`. In general, /// any method whose return type or parameters involve a struct with /// bitfields or arrays is unsupported. /// /// I do not remember why I didn't include \c signature in the base class /// when I originally wrote this, but I probably had a good reason. We can /// always go back and move it to \c FLEXMethodBase if we find we need to. @interface FLEXMethod : FLEXMethodBase /// Defaults to instance method + (nullable instancetype)method:(Method)method; + (nullable instancetype)method:(Method)method isInstanceMethod:(BOOL)isInstanceMethod; /// Constructs an \c FLEXMethod for the given method on the given class. /// @param cls the class, or metaclass if this is a class method /// @return The newly constructed \c FLEXMethod object, or \c nil if the /// specified class or its superclasses do not contain a method with the specified selector. + (nullable instancetype)selector:(SEL)selector class:(Class)cls; /// Constructs an \c FLEXMethod for the given method on the given class, /// only if the given class itself defines or overrides the desired method. /// @param cls the class, or metaclass if this is a class method /// @return The newly constructed \c FLEXMethod object, or \c nil \e if the /// specified class does not define or override, or if the specified class /// or its superclasses do not contain, a method with the specified selector. + (nullable instancetype)selector:(SEL)selector implementedInClass:(Class)cls; @property (nonatomic, readonly) Method objc_method; /// The implementation of the method. /// @discussion Setting \c implementation will change the implementation of this method /// for the entire class which implements said method. It will also not modify the selector of said method. @property (nonatomic ) IMP implementation; /// Whether the method is an instance method or not. @property (nonatomic, readonly) BOOL isInstanceMethod; /// The number of arguments to the method. @property (nonatomic, readonly) NSUInteger numberOfArguments; /// The \c NSMethodSignature object corresponding to the method's type encoding. @property (nonatomic, readonly) NSMethodSignature *signature; /// Same as \e typeEncoding but with parameter sizes up front and offsets after the types. @property (nonatomic, readonly) NSString *signatureString; /// The return type of the method. @property (nonatomic, readonly) FLEXTypeEncoding *returnType; /// The return size of the method. @property (nonatomic, readonly) NSUInteger returnSize; /// The full path of the image that contains this method definition, /// or \c nil if this ivar was probably defined at runtime. @property (nonatomic, readonly) NSString *imagePath; /// Like @code - (void)foo:(int)bar @endcode @property (nonatomic, readonly) NSString *description; /// Like @code -[Class foo:] @endcode - (NSString *)debugNameGivenClassName:(NSString *)name; /// Swizzles the recieving method with the given method. - (void)swapImplementations:(FLEXMethod *)method; #define FLEXMagicNumber 0xdeadbeef #define FLEXArg(expr) FLEXMagicNumber,/// @encode(__typeof__(expr)), (__typeof__(expr) []){ expr } /// Sends a message to \e target, and returns it's value, or \c nil if not applicable. /// @discussion You may send any message with this method. Primitive return values will be wrapped /// in instances of \c NSNumber and \c NSValue. \c void and bitfield returning methods return \c nil. /// \c SEL return types are converted to strings using \c NSStringFromSelector. /// @return The object returned by this method, or an instance of \c NSValue or \c NSNumber containing /// the primitive return type, or a string for \c SEL return types. - (id)sendMessage:(id)target, ...; /// Used internally by \c sendMessage:target,. Pass \c NULL to the first parameter for void methods. - (void)getReturnValue:(void *)retPtr forMessageSend:(id)target, ...; @end @interface FLEXMethod (Comparison) - (NSComparisonResult)compare:(FLEXMethod *)method; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMethod.m ================================================ // // FLEXMethod.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXMethod.h" #import "FLEXMirror.h" #import "FLEXTypeEncodingParser.h" #import "FLEXRuntimeUtility.h" #include @implementation FLEXMethod @synthesize imagePath = _imagePath; @dynamic implementation; + (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with +buildMethodNamed:withTypes:implementation"]; return nil; } - (id)init { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with -init" ]; return nil; } #pragma mark Initializers + (instancetype)method:(Method)method { return [[self alloc] initWithMethod:method isInstanceMethod:YES]; } + (instancetype)method:(Method)method isInstanceMethod:(BOOL)isInstanceMethod { return [[self alloc] initWithMethod:method isInstanceMethod:isInstanceMethod]; } + (instancetype)selector:(SEL)selector class:(Class)cls { BOOL instance = !class_isMetaClass(cls); // class_getInstanceMethod will return an instance method if not given // not given a metaclass, or a class method if given a metaclass, but // this isn't documented so we just want to be safe here. Method m = instance ? class_getInstanceMethod(cls, selector) : class_getClassMethod(cls, selector); if (m == NULL) return nil; return [self method:m isInstanceMethod:instance]; } + (instancetype)selector:(SEL)selector implementedInClass:(Class)cls { if (![cls superclass]) { return [self selector:selector class:cls]; } BOOL unique = [cls methodForSelector:selector] != [[cls superclass] methodForSelector:selector]; if (unique) { return [self selector:selector class:cls]; } return nil; } - (id)initWithMethod:(Method)method isInstanceMethod:(BOOL)isInstanceMethod { NSParameterAssert(method); self = [super init]; if (self) { _objc_method = method; _isInstanceMethod = isInstanceMethod; _signatureString = @(method_getTypeEncoding(method) ?: "?@:"); NSString *cleanSig = nil; if ([FLEXTypeEncodingParser methodTypeEncodingSupported:_signatureString cleaned:&cleanSig]) { _signature = [NSMethodSignature signatureWithObjCTypes:cleanSig.UTF8String]; } [self examine]; } return self; } #pragma mark Other - (NSString *)description { if (!_flex_description) { _flex_description = [self prettyName]; } return _flex_description; } - (NSString *)debugNameGivenClassName:(NSString *)name { NSMutableString *string = [NSMutableString stringWithString:_isInstanceMethod ? @"-[" : @"+["]; [string appendString:name]; [string appendString:@" "]; [string appendString:self.selectorString]; [string appendString:@"]"]; return string; } - (NSString *)prettyName { NSString *methodTypeString = self.isInstanceMethod ? @"-" : @"+"; NSString *readableReturnType = [FLEXRuntimeUtility readableTypeForEncoding:@(self.signature.methodReturnType ?: "")]; NSString *prettyName = [NSString stringWithFormat:@"%@ (%@)", methodTypeString, readableReturnType]; NSArray *components = [self prettyArgumentComponents]; if (components.count) { return [prettyName stringByAppendingString:[components componentsJoinedByString:@" "]]; } else { return [prettyName stringByAppendingString:self.selectorString]; } } - (NSArray *)prettyArgumentComponents { // NSMethodSignature can't handle some type encodings // like ^AI@:ir* which happen to very much exist if (self.signature.numberOfArguments < self.numberOfArguments) { return nil; } NSMutableArray *components = [NSMutableArray new]; NSArray *selectorComponents = [self.selectorString componentsSeparatedByString:@":"]; NSUInteger numberOfArguments = self.numberOfArguments; for (NSUInteger argIndex = 2; argIndex < numberOfArguments; argIndex++) { assert(argIndex < self.signature.numberOfArguments); const char *argType = [self.signature getArgumentTypeAtIndex:argIndex] ?: "?"; NSString *readableArgType = [FLEXRuntimeUtility readableTypeForEncoding:@(argType)]; NSString *prettyComponent = [NSString stringWithFormat:@"%@:(%@) ", selectorComponents[argIndex - 2], readableArgType ]; [components addObject:prettyComponent]; } return components; } - (NSString *)debugDescription { return [NSString stringWithFormat:@"<%@ selector=%@, signature=%@>", NSStringFromClass(self.class), self.selectorString, self.signatureString]; } - (void)examine { _implementation = method_getImplementation(_objc_method); _selector = method_getName(_objc_method); _numberOfArguments = method_getNumberOfArguments(_objc_method); _name = NSStringFromSelector(_selector); _returnType = (FLEXTypeEncoding *)_signature.methodReturnType ?: ""; _returnSize = _signature.methodReturnLength; } #pragma mark Public - (void)setImplementation:(IMP)implementation { NSParameterAssert(implementation); method_setImplementation(self.objc_method, implementation); [self examine]; } - (NSString *)typeEncoding { if (!_typeEncoding) { _typeEncoding = [_signatureString stringByReplacingOccurrencesOfString:@"[0-9]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, _signatureString.length) ]; } return _typeEncoding; } - (NSString *)imagePath { if (!_imagePath) { Dl_info exeInfo; if (dladdr(_implementation, &exeInfo)) { _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : @""; } } return _imagePath; } #pragma mark Misc - (void)swapImplementations:(FLEXMethod *)method { method_exchangeImplementations(self.objc_method, method.objc_method); [self examine]; [method examine]; } // Some code borrowed from MAObjcRuntime, by Mike Ash. - (id)sendMessage:(id)target, ... { id ret = nil; va_list args; va_start(args, target); switch (self.returnType[0]) { case FLEXTypeEncodingUnknown: { [self getReturnValue:NULL forMessageSend:target arguments:args]; break; } case FLEXTypeEncodingChar: { char val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingInt: { int val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingShort: { short val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingLong: { long val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingLongLong: { long long val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingUnsignedChar: { unsigned char val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingUnsignedInt: { unsigned int val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingUnsignedShort: { unsigned short val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingUnsignedLong: { unsigned long val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingUnsignedLongLong: { unsigned long long val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingFloat: { float val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingDouble: { double val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingLongDouble: { long double val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = [NSValue value:&val withObjCType:self.returnType]; break; } case FLEXTypeEncodingCBool: { bool val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingVoid: { [self getReturnValue:NULL forMessageSend:target arguments:args]; return nil; break; } case FLEXTypeEncodingCString: { char *val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = @(val); break; } case FLEXTypeEncodingObjcObject: { id val = nil; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = val; break; } case FLEXTypeEncodingObjcClass: { Class val = Nil; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = val; break; } case FLEXTypeEncodingSelector: { SEL val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = NSStringFromSelector(val); break; } case FLEXTypeEncodingArrayBegin: { void *val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = [NSValue valueWithBytes:val objCType:self.signature.methodReturnType]; break; } case FLEXTypeEncodingUnionBegin: case FLEXTypeEncodingStructBegin: { if (self.signature.methodReturnLength) { void * val = malloc(self.signature.methodReturnLength); [self getReturnValue:val forMessageSend:target arguments:args]; ret = [NSValue valueWithBytes:val objCType:self.signature.methodReturnType]; } else { [self getReturnValue:NULL forMessageSend:target arguments:args]; } break; } case FLEXTypeEncodingBitField: { [self getReturnValue:NULL forMessageSend:target arguments:args]; break; } case FLEXTypeEncodingPointer: { void * val = 0; [self getReturnValue:&val forMessageSend:target arguments:args]; ret = [NSValue valueWithPointer:val]; break; } default: { [NSException raise:NSInvalidArgumentException format:@"Unsupported type encoding: %s", (char *)self.returnType]; } } va_end(args); return ret; } // Code borrowed from MAObjcRuntime, by Mike Ash. - (void)getReturnValue:(void *)retPtr forMessageSend:(id)target, ... { va_list args; va_start(args, target); [self getReturnValue:retPtr forMessageSend:target arguments:args]; va_end(args); } // Code borrowed from MAObjcRuntime, by Mike Ash. - (void)getReturnValue:(void *)retPtr forMessageSend:(id)target arguments:(va_list)args { if (!_signature) { return; } NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_signature]; NSUInteger argumentCount = _signature.numberOfArguments; invocation.target = target; for (NSUInteger i = 2; i < argumentCount; i++) { int cookie = va_arg(args, int); if (cookie != FLEXMagicNumber) { [NSException raise:NSInternalInconsistencyException format:@"%s: incorrect magic cookie %08x; make sure you didn't forget " "any arguments and that all arguments are wrapped in FLEXArg().", __func__, cookie ]; } const char *typeString = va_arg(args, char *); void *argPointer = va_arg(args, void *); NSUInteger inSize, sigSize; NSGetSizeAndAlignment(typeString, &inSize, NULL); NSGetSizeAndAlignment([_signature getArgumentTypeAtIndex:i], &sigSize, NULL); if (inSize != sigSize) { [NSException raise:NSInternalInconsistencyException format:@"%s:size mismatch between passed-in argument and " "required argument; in type:%s (%lu) requested:%s (%lu)", __func__, typeString, (long)inSize, [_signature getArgumentTypeAtIndex:i], (long)sigSize ]; } [invocation setArgument:argPointer atIndex:i]; } // Hack to make NSInvocation invoke the desired implementation IMP imp = [invocation methodForSelector:NSSelectorFromString(@"invokeUsingIMP:")]; void (*invokeWithIMP)(id, SEL, IMP) = (void *)imp; invokeWithIMP(invocation, 0, _implementation); if (_signature.methodReturnLength && retPtr) { [invocation getReturnValue:retPtr]; } } @end @implementation FLEXMethod (Comparison) - (NSComparisonResult)compare:(FLEXMethod *)method { return [self.selectorString compare:method.selectorString]; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMethodBase.h ================================================ // // FLEXMethodBase.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import /// A base class for methods which encompasses those that may not /// have been added to a class yet. Useful on it's own for adding /// methods to a class, or building a new class from the ground up. @interface FLEXMethodBase : NSObject { @protected SEL _selector; NSString *_name; NSString *_typeEncoding; IMP _implementation; NSString *_flex_description; } /// Constructs and returns a \c FLEXSimpleMethod instance with the given name, type encoding, and implementation. + (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation; /// The selector of the method. @property (nonatomic, readonly) SEL selector; /// The selector string of the method. @property (nonatomic, readonly) NSString *selectorString; /// Same as selectorString. @property (nonatomic, readonly) NSString *name; /// The type encoding of the method. @property (nonatomic, readonly) NSString *typeEncoding; /// The implementation of the method. @property (nonatomic, readonly) IMP implementation; /// For internal use @property (nonatomic) id tag; @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMethodBase.m ================================================ // // FLEXMethodBase.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXMethodBase.h" @implementation FLEXMethodBase #pragma mark Initializers + (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation { return [[self alloc] initWithSelector:sel_registerName(name.UTF8String) types:typeEncoding imp:implementation]; } - (id)initWithSelector:(SEL)selector types:(NSString *)types imp:(IMP)imp { NSParameterAssert(selector); NSParameterAssert(types); NSParameterAssert(imp); self = [super init]; if (self) { _selector = selector; _typeEncoding = types; _implementation = imp; _name = NSStringFromSelector(self.selector); } return self; } - (NSString *)selectorString { return _name; } #pragma mark Overrides - (NSString *)description { if (!_flex_description) { _flex_description = [NSString stringWithFormat:@"%@ '%@'", _name, _typeEncoding]; } return _flex_description; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMirror.h ================================================ // // FLEXMirror.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/29/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import @class FLEXMethod, FLEXProperty, FLEXIvar, FLEXProtocol; NS_ASSUME_NONNULL_BEGIN #pragma mark FLEXMirror Protocol NS_SWIFT_NAME(FLEXMirrorProtocol) @protocol FLEXMirror /// Swift initializer /// @throws If a metaclass object is passed in. - (instancetype)initWithSubject:(id)objectOrClass NS_SWIFT_NAME(init(reflecting:)); /// The underlying object or \c Class used to create this \c FLEXMirror. @property (nonatomic, readonly) id value; /// Whether \c value was a class or a class instance. @property (nonatomic, readonly) BOOL isClass; /// The name of the \c Class of the \c value property. @property (nonatomic, readonly) NSString *className; @property (nonatomic, readonly) NSArray *properties; @property (nonatomic, readonly) NSArray *classProperties; @property (nonatomic, readonly) NSArray *ivars; @property (nonatomic, readonly) NSArray *methods; @property (nonatomic, readonly) NSArray *classMethods; @property (nonatomic, readonly) NSArray *protocols; /// Super mirrors are initialized with the class that corresponds to the value passed in. /// If you passed in an instance of a class, it's superclass is used to create this mirror. /// If you passed in a class, then that class's superclass is used. /// /// @note This property should be computed, not cached. @property (nonatomic, readonly, nullable) id superMirror NS_SWIFT_NAME(superMirror); @end #pragma mark FLEXMirror Class @interface FLEXMirror : NSObject /// Reflects an instance of an object or \c Class. /// @discussion \c FLEXMirror will immediately gather all useful information. Consider using the /// \c NSObject categories provided if your code will only use a few pieces of information, /// or if your code needs to run faster. /// /// Regardless of whether you reflect an instance or a class object, \c methods and \c properties /// will be populated with instance methods and properties, and \c classMethods and \c classProperties /// will be populated with class methods and properties. /// /// @param objectOrClass An instance of an objct or a \c Class object. /// @throws If a metaclass object is passed in. /// @return An instance of \c FLEXMirror. + (instancetype)reflect:(id)objectOrClass; @property (nonatomic, readonly) id value; @property (nonatomic, readonly) BOOL isClass; @property (nonatomic, readonly) NSString *className; @property (nonatomic, readonly) NSArray *properties; @property (nonatomic, readonly) NSArray *classProperties; @property (nonatomic, readonly) NSArray *ivars; @property (nonatomic, readonly) NSArray *methods; @property (nonatomic, readonly) NSArray *classMethods; @property (nonatomic, readonly) NSArray *protocols; @property (nonatomic, readonly, nullable) FLEXMirror *superMirror NS_SWIFT_NAME(superMirror); @end @interface FLEXMirror (ExtendedMirror) /// @return The instance method with the given name, or \c nil if one does not exist. - (nullable FLEXMethod *)methodNamed:(nullable NSString *)name; /// @return The class method with the given name, or \c nil if one does not exist. - (nullable FLEXMethod *)classMethodNamed:(nullable NSString *)name; /// @return The instance property with the given name, or \c nil if one does not exist. - (nullable FLEXProperty *)propertyNamed:(nullable NSString *)name; /// @return The class property with the given name, or \c nil if one does not exist. - (nullable FLEXProperty *)classPropertyNamed:(nullable NSString *)name; /// @return The instance variable with the given name, or \c nil if one does not exist. - (nullable FLEXIvar *)ivarNamed:(nullable NSString *)name; /// @return The protocol with the given name, or \c nil if one does not exist. - (nullable FLEXProtocol *)protocolNamed:(nullable NSString *)name; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXMirror.m ================================================ // // FLEXMirror.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/29/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXMirror.h" #import "FLEXProperty.h" #import "FLEXMethod.h" #import "FLEXIvar.h" #import "FLEXProtocol.h" #import "FLEXUtility.h" #pragma mark FLEXMirror @implementation FLEXMirror - (id)init { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with -init" ]; return nil; } #pragma mark Initialization + (instancetype)reflect:(id)objectOrClass { return [[self alloc] initWithSubject:objectOrClass]; } - (id)initWithSubject:(id)objectOrClass { NSParameterAssert(objectOrClass); self = [super init]; if (self) { _value = objectOrClass; [self examine]; } return self; } - (NSString *)description { return [NSString stringWithFormat:@"<%@ %@=%@>", NSStringFromClass(self.class), self.isClass ? @"metaclass" : @"class", self.className ]; } - (void)examine { BOOL isClass = object_isClass(self.value); Class cls = isClass ? self.value : object_getClass(self.value); Class meta = object_getClass(cls); _className = NSStringFromClass(cls); _isClass = isClass; unsigned int pcount, cpcount, mcount, cmcount, ivcount, pccount; Ivar *objcIvars = class_copyIvarList(cls, &ivcount); Method *objcMethods = class_copyMethodList(cls, &mcount); Method *objcClsMethods = class_copyMethodList(meta, &cmcount); objc_property_t *objcProperties = class_copyPropertyList(cls, &pcount); objc_property_t *objcClsProperties = class_copyPropertyList(meta, &cpcount); Protocol *__unsafe_unretained *protos = class_copyProtocolList(cls, &pccount); _ivars = [NSArray flex_forEachUpTo:ivcount map:^id(NSUInteger i) { return [FLEXIvar ivar:objcIvars[i]]; }]; _methods = [NSArray flex_forEachUpTo:mcount map:^id(NSUInteger i) { return [FLEXMethod method:objcMethods[i] isInstanceMethod:YES]; }]; _classMethods = [NSArray flex_forEachUpTo:cmcount map:^id(NSUInteger i) { return [FLEXMethod method:objcClsMethods[i] isInstanceMethod:NO]; }]; _properties = [NSArray flex_forEachUpTo:pcount map:^id(NSUInteger i) { return [FLEXProperty property:objcProperties[i] onClass:cls]; }]; _classProperties = [NSArray flex_forEachUpTo:cpcount map:^id(NSUInteger i) { return [FLEXProperty property:objcClsProperties[i] onClass:meta]; }]; _protocols = [NSArray flex_forEachUpTo:pccount map:^id(NSUInteger i) { return [FLEXProtocol protocol:protos[i]]; }]; // Cleanup free(objcClsProperties); free(objcProperties); free(objcClsMethods); free(objcMethods); free(objcIvars); free(protos); protos = NULL; } #pragma mark Misc - (FLEXMirror *)superMirror { Class cls = _isClass ? _value : object_getClass(_value); return [FLEXMirror reflect:class_getSuperclass(cls)]; } @end #pragma mark ExtendedMirror @implementation FLEXMirror (ExtendedMirror) - (id)filter:(NSArray *)array forName:(NSString *)name { NSPredicate *filter = [NSPredicate predicateWithFormat:@"%K = %@", @"name", name]; return [array filteredArrayUsingPredicate:filter].firstObject; } - (FLEXMethod *)methodNamed:(NSString *)name { return [self filter:self.methods forName:name]; } - (FLEXMethod *)classMethodNamed:(NSString *)name { return [self filter:self.classMethods forName:name]; } - (FLEXProperty *)propertyNamed:(NSString *)name { return [self filter:self.properties forName:name]; } - (FLEXProperty *)classPropertyNamed:(NSString *)name { return [self filter:self.classProperties forName:name]; } - (FLEXIvar *)ivarNamed:(NSString *)name { return [self filter:self.ivars forName:name]; } - (FLEXProtocol *)protocolNamed:(NSString *)name { return [self filter:self.protocols forName:name]; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXProperty.h ================================================ // // FLEXProperty.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeConstants.h" @class FLEXPropertyAttributes, FLEXMethodBase; #pragma mark FLEXProperty @interface FLEXProperty : NSObject /// You may use this initializer instead of \c property:onClass: if you don't need /// to know anything about the uniqueness of this property or where it comes from. + (instancetype)property:(objc_property_t)property; /// This initializer can be used to access additional information /// in an efficient manner. That information being whether this property /// is certainly not unique and the name of the binary image which declares it. /// @param cls the class, or metaclass if this is a class property. + (instancetype)property:(objc_property_t)property onClass:(Class)cls; /// @param cls the class, or metaclass if this is a class property + (instancetype)named:(NSString *)name onClass:(Class)cls; /// Constructs a new property with the given name and attributes. + (instancetype)propertyWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes; /// \c 0 if the instance was created via \c +propertyWithName:attributes, /// otherwise this is the first property in \c objc_properties @property (nonatomic, readonly) objc_property_t objc_property; @property (nonatomic, readonly) objc_property_t *objc_properties; @property (nonatomic, readonly) NSInteger objc_propertyCount; @property (nonatomic, readonly) BOOL isClassProperty; /// The name of the property. @property (nonatomic, readonly) NSString *name; /// The type of the property. Get the full type from the attributes. @property (nonatomic, readonly) FLEXTypeEncoding type; /// The property's attributes. @property (nonatomic ) FLEXPropertyAttributes *attributes; /// The (likely) setter, regardless of whether the property is readonly. /// For example, this might be the custom setter. @property (nonatomic, readonly) SEL likelySetter; @property (nonatomic, readonly) NSString *likelySetterString; /// Not valid unless initialized with the owning class. @property (nonatomic, readonly) BOOL likelySetterExists; /// The (likely) getter. For example, this might be the custom getter. @property (nonatomic, readonly) SEL likelyGetter; @property (nonatomic, readonly) NSString *likelyGetterString; /// Not valid unless initialized with the owning class. @property (nonatomic, readonly) BOOL likelyGetterExists; /// Always \c nil for class properties. @property (nonatomic, readonly) NSString *likelyIvarName; /// Not valid unless initialized with the owning class. @property (nonatomic, readonly) BOOL likelyIvarExists; /// Whether there are certainly multiple definitions of this property, /// such as in categories in other binary images or something. /// @return Whether \c objc_property matches the return value of \c class_getProperty, /// or \c NO if this property was not created with \c property:onClass @property (nonatomic, readonly) BOOL multiple; /// @return The bundle of the image that contains this property definition, /// or \c nil if this property was not created with \c property:onClass or /// if this property was probably defined at runtime. @property (nonatomic, readonly) NSString *imageName; /// The full path of the image that contains this property definition, /// or \c nil if this property was not created with \c property:onClass or /// if this property was probably defined at runtime. @property (nonatomic, readonly) NSString *imagePath; /// For internal use @property (nonatomic) id tag; /// @return The value of this property on \c target as given by \c -valueForKey: /// A source-like description of the property, with all of its attributes. @property (nonatomic, readonly) NSString *fullDescription; /// If this is a class property, you must class the class object. - (id)getValue:(id)target; /// Calls into -getValue: and passes that value into /// -[FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:type:] /// and returns the result. /// /// If this is a class property, you must class the class object. - (id)getPotentiallyUnboxedValue:(id)target; /// Safe to use regardless of how the \c FLEXProperty instance was initialized. /// /// This uses \c self.objc_property if it exists, otherwise it uses \c self.attributes - (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount; /// Replace the attributes of the current property in the given class, /// using the attributes in \c self.attributes /// /// What happens when the property does not exist is undocumented. - (void)replacePropertyOnClass:(Class)cls; #pragma mark Convenience getters and setters /// @return A getter for the property with the given implementation. /// @discussion Consider using the \c FLEXPropertyGetter macros. - (FLEXMethodBase *)getterWithImplementation:(IMP)implementation; /// @return A setter for the property with the given implementation. /// @discussion Consider using the \c FLEXPropertySetter macros. - (FLEXMethodBase *)setterWithImplementation:(IMP)implementation; #pragma mark FLEXMethod property getter / setter macros // Easier than using the above methods yourself in most cases /// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and /// uses the \c FLEXProperty's \c attribute's \c backingIvarName to get the Ivar. #define FLEXPropertyGetter(FLEXProperty, type) [FLEXProperty \ getterWithImplementation:imp_implementationWithBlock(^(id self) { \ return *(type *)[self getIvarAddressByName:FLEXProperty.attributes.backingIvar]; \ }) \ ]; /// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and /// uses the \c FLEXProperty's \c attribute's \c backingIvarName to set the Ivar. #define FLEXPropertySetter(FLEXProperty, type) [FLEXProperty \ setterWithImplementation:imp_implementationWithBlock(^(id self, type value) { \ [self setIvarByName:FLEXProperty.attributes.backingIvar value:&value size:sizeof(type)]; \ }) \ ]; /// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and an Ivar name string to get the Ivar. #define FLEXPropertyGetterWithIvar(FLEXProperty, ivarName, type) [FLEXProperty \ getterWithImplementation:imp_implementationWithBlock(^(id self) { \ return *(type *)[self getIvarAddressByName:ivarName]; \ }) \ ]; /// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and an Ivar name string to set the Ivar. #define FLEXPropertySetterWithIvar(FLEXProperty, ivarName, type) [FLEXProperty \ setterWithImplementation:imp_implementationWithBlock(^(id self, type value) { \ [self setIvarByName:ivarName value:&value size:sizeof(type)]; \ }) \ ]; @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXProperty.m ================================================ // // FLEXProperty.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXProperty.h" #import "FLEXPropertyAttributes.h" #import "FLEXMethodBase.h" #import "FLEXRuntimeUtility.h" #include @interface FLEXProperty () { NSString *_flex_description; } @property (nonatomic ) BOOL uniqueCheckFlag; @property (nonatomic, readonly) Class cls; @end @implementation FLEXProperty @synthesize multiple = _multiple; @synthesize imageName = _imageName; @synthesize imagePath = _imagePath; #pragma mark Initializers - (id)init { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with -init" ]; return nil; } + (instancetype)property:(objc_property_t)property { return [[self alloc] initWithProperty:property onClass:nil]; } + (instancetype)property:(objc_property_t)property onClass:(Class)cls { return [[self alloc] initWithProperty:property onClass:cls]; } + (instancetype)named:(NSString *)name onClass:(Class)cls { objc_property_t _Nullable property = class_getProperty(cls, name.UTF8String); NSAssert(property, @"Cannot find property with name %@ on class %@", name, cls); return [self property:property onClass:cls]; } + (instancetype)propertyWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes { return [[self alloc] initWithName:name attributes:attributes]; } - (id)initWithProperty:(objc_property_t)property onClass:(Class)cls { NSParameterAssert(property); self = [super init]; if (self) { _objc_property = property; _attributes = [FLEXPropertyAttributes attributesForProperty:property]; _name = @(property_getName(property) ?: "(nil)"); _cls = cls; if (!_attributes) [NSException raise:NSInternalInconsistencyException format:@"Error retrieving property attributes"]; if (!_name) [NSException raise:NSInternalInconsistencyException format:@"Error retrieving property name"]; [self examine]; } return self; } - (id)initWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes { NSParameterAssert(name); NSParameterAssert(attributes); self = [super init]; if (self) { _attributes = attributes; _name = name; [self examine]; } return self; } #pragma mark Private - (void)examine { if (self.attributes.typeEncoding.length) { _type = (FLEXTypeEncoding)[self.attributes.typeEncoding characterAtIndex:0]; } // Return the given selector if the class responds to it Class cls = _cls; SEL (^selectorIfValid)(SEL) = ^SEL(SEL sel) { if (!sel || !cls) return nil; return [cls instancesRespondToSelector:sel] ? sel : nil; }; SEL customGetter = self.attributes.customGetter; SEL customSetter = self.attributes.customSetter; SEL defaultGetter = NSSelectorFromString(self.name); SEL defaultSetter = NSSelectorFromString([NSString stringWithFormat:@"set%c%@:", (char)toupper([self.name characterAtIndex:0]), [self.name substringFromIndex:1] ]); // Check if the likely getters/setters exist SEL validGetter = selectorIfValid(customGetter) ?: selectorIfValid(defaultGetter); SEL validSetter = selectorIfValid(customSetter) ?: selectorIfValid(defaultSetter); _likelyGetterExists = validGetter != nil; _likelySetterExists = validSetter != nil; // Assign likely getters and setters to the valid one, // or the default, regardless of whether the default exists _likelyGetter = validGetter ?: defaultGetter; _likelySetter = validSetter ?: defaultSetter; _likelyGetterString = NSStringFromSelector(_likelyGetter); _likelySetterString = NSStringFromSelector(_likelySetter); _isClassProperty = _cls ? class_isMetaClass(_cls) : NO; _likelyIvarName = _isClassProperty ? nil : ( self.attributes.backingIvar ?: [@"_" stringByAppendingString:_name] ); } #pragma mark Overrides - (NSString *)description { if (!_flex_description) { NSString *readableType = [FLEXRuntimeUtility readableTypeForEncoding:self.attributes.typeEncoding]; _flex_description = [FLEXRuntimeUtility appendName:self.name toType:readableType]; } return _flex_description; } - (NSString *)debugDescription { return [NSString stringWithFormat:@"<%@ name=%@, property=%p, attributes:\n\t%@\n>", NSStringFromClass(self.class), self.name, self.objc_property, self.attributes]; } #pragma mark Public - (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount { if (self.objc_property) { return property_copyAttributeList(self.objc_property, attributesCount); } else { return [self.attributes copyAttributesList:attributesCount]; } } - (void)replacePropertyOnClass:(Class)cls { class_replaceProperty(cls, self.name.UTF8String, self.attributes.list, (unsigned int)self.attributes.count); } - (void)computeSymbolInfo:(BOOL)forceBundle { Dl_info exeInfo; if (dladdr(_objc_property, &exeInfo)) { _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil; } if ((!_multiple || !_uniqueCheckFlag) && _cls) { _multiple = _objc_property != class_getProperty(_cls, self.name.UTF8String); if (_multiple || forceBundle) { NSString *path = _imagePath.stringByDeletingLastPathComponent; _imageName = [NSBundle bundleWithPath:path].executablePath.lastPathComponent; } } } - (BOOL)multiple { [self computeSymbolInfo:NO]; return _multiple; } - (NSString *)imagePath { [self computeSymbolInfo:YES]; return _imagePath; } - (NSString *)imageName { [self computeSymbolInfo:YES]; return _imageName; } - (BOOL)likelyIvarExists { if (_likelyIvarName && _cls) { return class_getInstanceVariable(_cls, _likelyIvarName.UTF8String) != nil; } return NO; } - (NSString *)fullDescription { NSMutableArray *attributesStrings = [NSMutableArray new]; FLEXPropertyAttributes *attributes = self.attributes; // Atomicity if (attributes.isNonatomic) { [attributesStrings addObject:@"nonatomic"]; } else { [attributesStrings addObject:@"atomic"]; } // Storage if (attributes.isRetained) { [attributesStrings addObject:@"strong"]; } else if (attributes.isCopy) { [attributesStrings addObject:@"copy"]; } else if (attributes.isWeak) { [attributesStrings addObject:@"weak"]; } else { [attributesStrings addObject:@"assign"]; } // Mutability if (attributes.isReadOnly) { [attributesStrings addObject:@"readonly"]; } else { [attributesStrings addObject:@"readwrite"]; } // Class or not if (self.isClassProperty) { [attributesStrings addObject:@"class"]; } // Custom getter/setter SEL customGetter = attributes.customGetter; SEL customSetter = attributes.customSetter; if (customGetter) { [attributesStrings addObject:[NSString stringWithFormat:@"getter=%s", sel_getName(customGetter)]]; } if (customSetter) { [attributesStrings addObject:[NSString stringWithFormat:@"setter=%s", sel_getName(customSetter)]]; } NSString *attributesString = [attributesStrings componentsJoinedByString:@", "]; return [NSString stringWithFormat:@"@property (%@) %@", attributesString, self.description]; } - (id)getValue:(id)target { if (!target) return nil; // We don't care about checking dynamically whether the getter // _now_ exists on this object. If the getter doesn't exist // when this property is initialized, it will never call it. // Just re-create the property object if you need to call it. if (self.likelyGetterExists) { BOOL objectIsClass = object_isClass(target); BOOL instanceAndInstanceProperty = !objectIsClass && !self.isClassProperty; BOOL classAndClassProperty = objectIsClass && self.isClassProperty; if (instanceAndInstanceProperty || classAndClassProperty) { return [FLEXRuntimeUtility performSelector:self.likelyGetter onObject:target]; } } return nil; } - (id)getPotentiallyUnboxedValue:(id)target { if (!target) return nil; return [FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:[self getValue:target] type:self.attributes.typeEncoding.UTF8String ]; } #pragma mark Suggested getters and setters - (FLEXMethodBase *)getterWithImplementation:(IMP)implementation { NSString *types = [NSString stringWithFormat:@"%@%s%s", self.attributes.typeEncoding, @encode(id), @encode(SEL)]; NSString *name = [NSString stringWithFormat:@"%@", self.name]; FLEXMethodBase *getter = [FLEXMethodBase buildMethodNamed:name withTypes:types implementation:implementation]; return getter; } - (FLEXMethodBase *)setterWithImplementation:(IMP)implementation { NSString *types = [NSString stringWithFormat:@"%s%s%s%@", @encode(void), @encode(id), @encode(SEL), self.attributes.typeEncoding]; NSString *name = [NSString stringWithFormat:@"set%@:", self.name.capitalizedString]; FLEXMethodBase *setter = [FLEXMethodBase buildMethodNamed:name withTypes:types implementation:implementation]; return setter; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXPropertyAttributes.h ================================================ // // FLEXPropertyAttributes.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import #import NS_ASSUME_NONNULL_BEGIN #pragma mark FLEXPropertyAttributes /// See \e FLEXRuntimeUtilitiy.h for valid string tokens. /// See this link on how to construct a proper attributes string: /// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html @interface FLEXPropertyAttributes : NSObject { // These are necessary for the mutable subclass to function @protected NSUInteger _count; NSString *_string, *_backingIvar, *_typeEncoding, *_oldTypeEncoding, *_fullDeclaration; NSDictionary *_dictionary; objc_property_attribute_t *_list; SEL _customGetter, _customSetter; BOOL _isReadOnly, _isCopy, _isRetained, _isNonatomic, _isDynamic, _isWeak, _isGarbageCollectable; } + (instancetype)attributesForProperty:(objc_property_t)property; /// @warning Raises an exception if \e attributes is invalid, \c nil, or contains unsupported keys. + (instancetype)attributesFromDictionary:(NSDictionary *)attributes; /// Copies the attributes list to a buffer you must \c free() yourself. /// Use \c list instead if you do not need more control over the lifetime of the list. /// @param attributesCountOut the number of attributes is returned in this parameter. - (objc_property_attribute_t *)copyAttributesList:(nullable unsigned int *)attributesCountOut; /// The number of property attributes. @property (nonatomic, readonly) NSUInteger count; /// For use with \c class_replaceProperty and the like. @property (nonatomic, readonly) objc_property_attribute_t *list; /// The string value of the property attributes. @property (nonatomic, readonly) NSString *string; /// A human-readable version of the property attributes. @property (nonatomic, readonly) NSString *fullDeclaration; /// A dictionary of the property attributes. /// Values are either a string or \c YES. Boolean attributes /// which are false will not be present in the dictionary. @property (nonatomic, readonly) NSDictionary *dictionary; /// The name of the instance variable backing the property. @property (nonatomic, readonly, nullable) NSString *backingIvar; /// The type encoding of the property. @property (nonatomic, readonly, nullable) NSString *typeEncoding; /// The \e old type encoding of the property. @property (nonatomic, readonly, nullable) NSString *oldTypeEncoding; /// The property's custom getter, if any. @property (nonatomic, readonly, nullable) SEL customGetter; /// The property's custom setter, if any. @property (nonatomic, readonly, nullable) SEL customSetter; /// The property's custom getter as a string, if any. @property (nonatomic, readonly, nullable) NSString *customGetterString; /// The property's custom setter as a string, if any. @property (nonatomic, readonly, nullable) NSString *customSetterString; @property (nonatomic, readonly) BOOL isReadOnly; @property (nonatomic, readonly) BOOL isCopy; @property (nonatomic, readonly) BOOL isRetained; @property (nonatomic, readonly) BOOL isNonatomic; @property (nonatomic, readonly) BOOL isDynamic; @property (nonatomic, readonly) BOOL isWeak; @property (nonatomic, readonly) BOOL isGarbageCollectable; @end #pragma mark FLEXPropertyAttributes @interface FLEXMutablePropertyAttributes : FLEXPropertyAttributes /// Creates and returns an empty property attributes object. + (instancetype)attributes; /// The name of the instance variable backing the property. @property (nonatomic, nullable) NSString *backingIvar; /// The type encoding of the property. @property (nonatomic, nullable) NSString *typeEncoding; /// The \e old type encoding of the property. @property (nonatomic, nullable) NSString *oldTypeEncoding; /// The property's custom getter, if any. @property (nonatomic, nullable) SEL customGetter; /// The property's custom setter, if any. @property (nonatomic, nullable) SEL customSetter; @property (nonatomic) BOOL isReadOnly; @property (nonatomic) BOOL isCopy; @property (nonatomic) BOOL isRetained; @property (nonatomic) BOOL isNonatomic; @property (nonatomic) BOOL isDynamic; @property (nonatomic) BOOL isWeak; @property (nonatomic) BOOL isGarbageCollectable; /// A more convenient method of setting the \c typeEncoding property. /// @discussion This will not work for complex types like structs and primitive pointers. - (void)setTypeEncodingChar:(char)type; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXPropertyAttributes.m ================================================ // // FLEXPropertyAttributes.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXPropertyAttributes.h" #import "FLEXRuntimeUtility.h" #import "NSString+ObjcRuntime.h" #import "NSDictionary+ObjcRuntime.h" #pragma mark FLEXPropertyAttributes @interface FLEXPropertyAttributes () @property (nonatomic) NSString *backingIvar; @property (nonatomic) NSString *typeEncoding; @property (nonatomic) NSString *oldTypeEncoding; @property (nonatomic) SEL customGetter; @property (nonatomic) SEL customSetter; @property (nonatomic) BOOL isReadOnly; @property (nonatomic) BOOL isCopy; @property (nonatomic) BOOL isRetained; @property (nonatomic) BOOL isNonatomic; @property (nonatomic) BOOL isDynamic; @property (nonatomic) BOOL isWeak; @property (nonatomic) BOOL isGarbageCollectable; - (NSString *)buildFullDeclaration; @end @implementation FLEXPropertyAttributes @synthesize list = _list; #pragma mark Initializers + (instancetype)attributesForProperty:(objc_property_t)property { return [self attributesFromDictionary:[NSDictionary attributesDictionaryForProperty:property]]; } + (instancetype)attributesFromDictionary:(NSDictionary *)attributes { return [[self alloc] initWithAttributesDictionary:attributes]; } - (id)initWithAttributesDictionary:(NSDictionary *)attributes { NSParameterAssert(attributes); self = [super init]; if (self) { _dictionary = attributes; _string = attributes.propertyAttributesString; _count = attributes.count; _typeEncoding = attributes[kFLEXPropertyAttributeKeyTypeEncoding]; _backingIvar = attributes[kFLEXPropertyAttributeKeyBackingIvarName]; _oldTypeEncoding = attributes[kFLEXPropertyAttributeKeyOldStyleTypeEncoding]; _customGetterString = attributes[kFLEXPropertyAttributeKeyCustomGetter]; _customSetterString = attributes[kFLEXPropertyAttributeKeyCustomSetter]; _customGetter = NSSelectorFromString(_customGetterString); _customSetter = NSSelectorFromString(_customSetterString); _isReadOnly = attributes[kFLEXPropertyAttributeKeyReadOnly] != nil; _isCopy = attributes[kFLEXPropertyAttributeKeyCopy] != nil; _isRetained = attributes[kFLEXPropertyAttributeKeyRetain] != nil; _isNonatomic = attributes[kFLEXPropertyAttributeKeyNonAtomic] != nil; _isWeak = attributes[kFLEXPropertyAttributeKeyWeak] != nil; _isGarbageCollectable = attributes[kFLEXPropertyAttributeKeyGarbageCollectable] != nil; _fullDeclaration = [self buildFullDeclaration]; } return self; } #pragma mark Misc - (NSString *)description { return [NSString stringWithFormat:@"<%@ \"%@\", ivar=%@, readonly=%d, nonatomic=%d, getter=%@, setter=%@>", NSStringFromClass(self.class), self.string, self.backingIvar ?: @"none", self.isReadOnly, self.isNonatomic, NSStringFromSelector(self.customGetter) ?: @"none", NSStringFromSelector(self.customSetter) ?: @"none" ]; } - (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount { NSDictionary *attrs = self.string.propertyAttributes; objc_property_attribute_t *propertyAttributes = malloc(attrs.count * sizeof(objc_property_attribute_t)); if (attributesCount) { *attributesCount = (unsigned int)attrs.count; } NSUInteger i = 0; for (NSString *key in attrs.allKeys) { FLEXPropertyAttribute c = (FLEXPropertyAttribute)[key characterAtIndex:0]; switch (c) { case FLEXPropertyAttributeTypeEncoding: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyTypeEncoding.UTF8String, self.typeEncoding.UTF8String }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeBackingIvarName: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyBackingIvarName.UTF8String, self.backingIvar.UTF8String }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeCopy: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyCopy.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeCustomGetter: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyCustomGetter.UTF8String, NSStringFromSelector(self.customGetter).UTF8String ?: "" }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeCustomSetter: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyCustomSetter.UTF8String, NSStringFromSelector(self.customSetter).UTF8String ?: "" }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeDynamic: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyDynamic.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeGarbageCollectible: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyGarbageCollectable.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeNonAtomic: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyNonAtomic.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeOldTypeEncoding: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyOldStyleTypeEncoding.UTF8String, self.oldTypeEncoding.UTF8String ?: "" }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeReadOnly: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyReadOnly.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeRetain: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyRetain.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeWeak: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyWeak.UTF8String, ""}; propertyAttributes[i] = pa; break; } } i++; } return propertyAttributes; } - (objc_property_attribute_t *)list { if (!_list) { _list = [self copyAttributesList:nil]; } return _list; } - (NSString *)buildFullDeclaration { NSMutableString *decl = [NSMutableString new]; [decl appendFormat:@"%@, ", _isNonatomic ? @"nonatomic" : @"atomic"]; [decl appendFormat:@"%@, ", _isReadOnly ? @"readonly" : @"readwrite"]; BOOL noExplicitMemorySemantics = YES; if (_isCopy) { noExplicitMemorySemantics = NO; [decl appendString:@"copy, "]; } if (_isRetained) { noExplicitMemorySemantics = NO; [decl appendString:@"strong, "]; } if (_isWeak) { noExplicitMemorySemantics = NO; [decl appendString:@"weak, "]; } if ([_typeEncoding hasPrefix:@"@"] && noExplicitMemorySemantics) { // *probably* strong if this is an object; strong is the default. [decl appendString:@"strong, "]; } else if (noExplicitMemorySemantics) { // *probably* assign if this is not an object [decl appendString:@"assign, "]; } if (_customGetter) { [decl appendFormat:@"getter=%@, ", NSStringFromSelector(_customGetter)]; } if (_customSetter) { [decl appendFormat:@"setter=%@, ", NSStringFromSelector(_customSetter)]; } [decl deleteCharactersInRange:NSMakeRange(decl.length-2, 2)]; return decl.copy; } - (void)dealloc { if (_list) { free(_list); _list = nil; } } #pragma mark Copying - (id)copyWithZone:(NSZone *)zone { return [[FLEXPropertyAttributes class] attributesFromDictionary:self.dictionary]; } - (id)mutableCopyWithZone:(NSZone *)zone { return [[FLEXMutablePropertyAttributes class] attributesFromDictionary:self.dictionary]; } @end #pragma mark FLEXMutablePropertyAttributes @interface FLEXMutablePropertyAttributes () @property (nonatomic) BOOL countDelta; @property (nonatomic) BOOL stringDelta; @property (nonatomic) BOOL dictDelta; @property (nonatomic) BOOL listDelta; @property (nonatomic) BOOL declDelta; @end #define PropertyWithDeltaFlag(type, name, Name) @dynamic name; \ - (void)set ## Name:(type)name { \ if (name != _ ## name) { \ _countDelta = _stringDelta = _dictDelta = _listDelta = _declDelta = YES; \ _ ## name = name; \ } \ } @implementation FLEXMutablePropertyAttributes PropertyWithDeltaFlag(NSString *, backingIvar, BackingIvar); PropertyWithDeltaFlag(NSString *, typeEncoding, TypeEncoding); PropertyWithDeltaFlag(NSString *, oldTypeEncoding, OldTypeEncoding); PropertyWithDeltaFlag(SEL, customGetter, CustomGetter); PropertyWithDeltaFlag(SEL, customSetter, CustomSetter); PropertyWithDeltaFlag(BOOL, isReadOnly, IsReadOnly); PropertyWithDeltaFlag(BOOL, isCopy, IsCopy); PropertyWithDeltaFlag(BOOL, isRetained, IsRetained); PropertyWithDeltaFlag(BOOL, isNonatomic, IsNonatomic); PropertyWithDeltaFlag(BOOL, isDynamic, IsDynamic); PropertyWithDeltaFlag(BOOL, isWeak, IsWeak); PropertyWithDeltaFlag(BOOL, isGarbageCollectable, IsGarbageCollectable); + (instancetype)attributes { return [self new]; } - (void)setTypeEncodingChar:(char)type { self.typeEncoding = [NSString stringWithFormat:@"%c", type]; } - (NSUInteger)count { // Recalculate attribute count after mutations if (self.countDelta) { self.countDelta = NO; _count = self.dictionary.count; } return _count; } - (objc_property_attribute_t *)list { // Regenerate list after mutations if (self.listDelta) { self.listDelta = NO; if (_list) { free(_list); _list = nil; } } // Super will generate the list if it isn't set return super.list; } - (NSString *)string { // Regenerate string after mutations if (self.stringDelta || !_string) { self.stringDelta = NO; _string = self.dictionary.propertyAttributesString; } return _string; } - (NSDictionary *)dictionary { // Regenerate dictionary after mutations if (self.dictDelta || !_dictionary) { // _stringa nd _dictionary depend on each other, // so we must generate ONE by hand using our properties. // We arbitrarily choose to generate the dictionary. NSMutableDictionary *attrs = [NSMutableDictionary new]; if (self.typeEncoding) attrs[kFLEXPropertyAttributeKeyTypeEncoding] = self.typeEncoding; if (self.backingIvar) attrs[kFLEXPropertyAttributeKeyBackingIvarName] = self.backingIvar; if (self.oldTypeEncoding) attrs[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] = self.oldTypeEncoding; if (self.customGetter) attrs[kFLEXPropertyAttributeKeyCustomGetter] = NSStringFromSelector(self.customGetter); if (self.customSetter) attrs[kFLEXPropertyAttributeKeyCustomSetter] = NSStringFromSelector(self.customSetter); if (self.isReadOnly) attrs[kFLEXPropertyAttributeKeyReadOnly] = @YES; if (self.isCopy) attrs[kFLEXPropertyAttributeKeyCopy] = @YES; if (self.isRetained) attrs[kFLEXPropertyAttributeKeyRetain] = @YES; if (self.isNonatomic) attrs[kFLEXPropertyAttributeKeyNonAtomic] = @YES; if (self.isDynamic) attrs[kFLEXPropertyAttributeKeyDynamic] = @YES; if (self.isWeak) attrs[kFLEXPropertyAttributeKeyWeak] = @YES; if (self.isGarbageCollectable) attrs[kFLEXPropertyAttributeKeyGarbageCollectable] = @YES; _dictionary = attrs.copy; } return _dictionary; } - (NSString *)fullDeclaration { if (self.declDelta || !_fullDeclaration) { _declDelta = NO; _fullDeclaration = [self buildFullDeclaration]; } return _fullDeclaration; } - (NSString *)customGetterString { return _customGetter ? NSStringFromSelector(_customGetter) : nil; } - (NSString *)customSetterString { return _customSetter ? NSStringFromSelector(_customSetter) : nil; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXProtocol.h ================================================ // // FLEXProtocol.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXRuntimeConstants.h" @class FLEXProperty, FLEXMethodDescription; NS_ASSUME_NONNULL_BEGIN #pragma mark FLEXProtocol @interface FLEXProtocol : NSObject /// Every protocol registered with the runtime. + (NSArray *)allProtocols; + (instancetype)protocol:(Protocol *)protocol; /// The underlying protocol data structure. @property (nonatomic, readonly) Protocol *objc_protocol; /// The name of the protocol. @property (nonatomic, readonly) NSString *name; /// The required methods of the protocol, if any. This includes property getters and setters. @property (nonatomic, readonly) NSArray *requiredMethods; /// The optional methods of the protocol, if any. This includes property getters and setters. @property (nonatomic, readonly) NSArray *optionalMethods; /// All protocols that this protocol conforms to, if any. @property (nonatomic, readonly) NSArray *protocols; /// The full path of the image that contains this protocol definition, /// or \c nil if this protocol was probably defined at runtime. @property (nonatomic, readonly, nullable) NSString *imagePath; /// The properties in the protocol, if any. \c nil on iOS 10+ @property (nonatomic, readonly, nullable) NSArray *properties API_DEPRECATED("Use the more specific accessors below", ios(2.0, 10.0)); /// The required properties in the protocol, if any. @property (nonatomic, readonly) NSArray *requiredProperties API_AVAILABLE(ios(10.0)); /// The optional properties in the protocol, if any. @property (nonatomic, readonly) NSArray *optionalProperties API_AVAILABLE(ios(10.0)); /// For internal use @property (nonatomic) id tag; /// Not to be confused with \c -conformsToProtocol:, which refers to the current /// \c FLEXProtocol instance and not the underlying \c Protocol object. - (BOOL)conformsTo:(Protocol *)protocol; @end #pragma mark Method descriptions @interface FLEXMethodDescription : NSObject + (instancetype)description:(struct objc_method_description)description; + (instancetype)description:(struct objc_method_description)description instance:(BOOL)isInstance; /// The underlying method description data structure. @property (nonatomic, readonly) struct objc_method_description objc_description; /// The method's selector. @property (nonatomic, readonly) SEL selector; /// The method's type encoding. @property (nonatomic, readonly) NSString *typeEncoding; /// The method's return type. @property (nonatomic, readonly) FLEXTypeEncoding returnType; /// \c YES if this is an instance method, \c NO if it is a class method, or \c nil if unspecified @property (nonatomic, readonly) NSNumber *instance; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXProtocol.m ================================================ // // FLEXProtocol.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 6/30/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXProtocol.h" #import "FLEXProperty.h" #import "FLEXRuntimeUtility.h" #import "NSArray+FLEX.h" #include @implementation FLEXProtocol #pragma mark Initializers + (NSArray *)allProtocols { unsigned int prcount; Protocol *__unsafe_unretained*protocols = objc_copyProtocolList(&prcount); NSMutableArray *all = [NSMutableArray new]; for(NSUInteger i = 0; i < prcount; i++) [all addObject:[self protocol:protocols[i]]]; free(protocols); return all; } + (instancetype)protocol:(Protocol *)protocol { return [[self alloc] initWithProtocol:protocol]; } - (id)initWithProtocol:(Protocol *)protocol { NSParameterAssert(protocol); self = [super init]; if (self) { _objc_protocol = protocol; [self examine]; } return self; } #pragma mark Other - (NSString *)description { return self.name; } - (NSString *)debugDescription { if (@available(iOS 10.0, *)) { return [NSString stringWithFormat:@"<%@ name=%@, %lu required properties, %lu optional properties %lu required methods, %lu optional methods, %lu protocols>", NSStringFromClass(self.class), self.name, (unsigned long)self.requiredProperties.count, (unsigned long)self.optionalProperties.count, (unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count]; } else { return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>", NSStringFromClass(self.class), self.name, (unsigned long)self.properties.count, (unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count]; } } - (void)examine { _name = @(protocol_getName(self.objc_protocol)); // imagePath Dl_info exeInfo; if (dladdr((__bridge const void *)(_objc_protocol), &exeInfo)) { _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil; } // Conformances and methods // unsigned int pccount, mdrcount, mdocount; struct objc_method_description *objcrMethods, *objcoMethods; Protocol *protocol = _objc_protocol; Protocol * __unsafe_unretained *protocols = protocol_copyProtocolList(protocol, &pccount); // Protocols _protocols = [NSArray flex_forEachUpTo:pccount map:^id(NSUInteger i) { return [FLEXProtocol protocol:protocols[i]]; }]; free(protocols); // Required instance methods objcrMethods = protocol_copyMethodDescriptionList(protocol, YES, YES, &mdrcount); NSArray *rMethods = [NSArray flex_forEachUpTo:mdrcount map:^id(NSUInteger i) { return [FLEXMethodDescription description:objcrMethods[i] instance:YES]; }]; free(objcrMethods); // Required class methods objcrMethods = protocol_copyMethodDescriptionList(protocol, YES, NO, &mdrcount); _requiredMethods = [[NSArray flex_forEachUpTo:mdrcount map:^id(NSUInteger i) { return [FLEXMethodDescription description:objcrMethods[i] instance:NO]; }] arrayByAddingObjectsFromArray:rMethods]; free(objcrMethods); // Optional instance methods objcoMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &mdocount); NSArray *oMethods = [NSArray flex_forEachUpTo:mdocount map:^id(NSUInteger i) { return [FLEXMethodDescription description:objcoMethods[i] instance:YES]; }]; free(objcoMethods); // Optional class methods objcoMethods = protocol_copyMethodDescriptionList(protocol, NO, NO, &mdocount); _optionalMethods = [[NSArray flex_forEachUpTo:mdocount map:^id(NSUInteger i) { return [FLEXMethodDescription description:objcoMethods[i] instance:NO]; }] arrayByAddingObjectsFromArray:oMethods]; free(objcoMethods); // Properties is a hassle because they didn't fix the API until iOS 10 // if (@available(iOS 10.0, *)) { unsigned int prrcount, procount; Class instance = [NSObject class], meta = objc_getMetaClass("NSObject"); // Required class and instance properties // // Instance first objc_property_t *rProps = protocol_copyPropertyList2(protocol, &prrcount, YES, YES); NSArray *rProperties = [NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) { return [FLEXProperty property:rProps[i] onClass:instance]; }]; free(rProps); // Then class rProps = protocol_copyPropertyList2(protocol, &prrcount, NO, YES); _requiredProperties = [[NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) { return [FLEXProperty property:rProps[i] onClass:instance]; }] arrayByAddingObjectsFromArray:rProperties]; free(rProps); // Optional class and instance properties // // Instance first objc_property_t *oProps = protocol_copyPropertyList2(protocol, &procount, YES, YES); NSArray *oProperties = [NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) { return [FLEXProperty property:oProps[i] onClass:meta]; }]; free(oProps); // Then class oProps = protocol_copyPropertyList2(protocol, &procount, NO, YES); _optionalProperties = [[NSArray flex_forEachUpTo:procount map:^id(NSUInteger i) { return [FLEXProperty property:oProps[i] onClass:meta]; }] arrayByAddingObjectsFromArray:oProperties]; free(oProps); } else { unsigned int prcount; objc_property_t *objcproperties = protocol_copyPropertyList(protocol, &prcount); _properties = [NSArray flex_forEachUpTo:prcount map:^id(NSUInteger i) { return [FLEXProperty property:objcproperties[i]]; }]; _requiredProperties = @[]; _optionalProperties = @[]; free(objcproperties); } } - (BOOL)conformsTo:(Protocol *)protocol { return protocol_conformsToProtocol(self.objc_protocol, protocol); } @end #pragma mark FLEXMethodDescription @implementation FLEXMethodDescription - (id)init { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with -init" ]; return nil; } + (instancetype)description:(struct objc_method_description)description { return [[self alloc] initWithDescription:description instance:nil]; } + (instancetype)description:(struct objc_method_description)description instance:(BOOL)isInstance { return [[self alloc] initWithDescription:description instance:@(isInstance)]; } - (id)initWithDescription:(struct objc_method_description)md instance:(NSNumber *)instance { NSParameterAssert(md.name != NULL); self = [super init]; if (self) { _objc_description = md; _selector = md.name; _typeEncoding = @(md.types); _returnType = (FLEXTypeEncoding)[self.typeEncoding characterAtIndex:0]; _instance = instance; } return self; } - (NSString *)description { return NSStringFromSelector(self.selector); } - (NSString *)debugDescription { return [NSString stringWithFormat:@"<%@ name=%@, type=%@>", NSStringFromClass(self.class), NSStringFromSelector(self.selector), self.typeEncoding]; } @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXProtocolBuilder.h ================================================ // // FLEXProtocolBuilder.h // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/4/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @class FLEXProperty, FLEXProtocol, Protocol; @interface FLEXProtocolBuilder : NSObject /// Begins to construct a new protocol with the given name. /// @discussion You must register the protocol with the /// \c registerProtocol method before you can use it. + (instancetype)allocateProtocol:(NSString *)name; /// Adds a property to a protocol. /// @param property The property to add. /// @param isRequired Whether the property is required to implement the protocol. - (void)addProperty:(FLEXProperty *)property isRequired:(BOOL)isRequired; /// Adds a property to a protocol. /// @param selector The selector of the method to add. /// @param typeEncoding The type encoding of the method to add. /// @param isRequired Whether the method is required to implement the protocol. /// @param isInstanceMethod \c YES if the method is an instance method, \c NO if it is a class method. - (void)addMethod:(SEL)selector typeEncoding:(NSString *)typeEncoding isRequired:(BOOL)isRequired isInstanceMethod:(BOOL)isInstanceMethod; /// Makes the recieving protocol conform to the given protocol. - (void)addProtocol:(Protocol *)protocol; /// Registers and returns the recieving protocol, which was previously under construction. - (FLEXProtocol *)registerProtocol; /// Whether the protocol is still under construction or already registered. @property (nonatomic, readonly) BOOL isRegistered; @end ================================================ FILE: Classes/Utility/Runtime/Objc/Reflection/FLEXProtocolBuilder.m ================================================ // // FLEXProtocolBuilder.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/4/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXProtocolBuilder.h" #import "FLEXProtocol.h" #import "FLEXProperty.h" #import #define MutationAssertion(msg) if (self.isRegistered) { \ [NSException \ raise:NSInternalInconsistencyException \ format:msg \ ]; \ } @interface FLEXProtocolBuilder () @property (nonatomic) Protocol *workingProtocol; @property (nonatomic) NSString *name; @end @implementation FLEXProtocolBuilder - (id)init { [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with -init" ]; return nil; } #pragma mark Initializers + (instancetype)allocateProtocol:(NSString *)name { NSParameterAssert(name); return [[self alloc] initWithProtocol:objc_allocateProtocol(name.UTF8String)]; } - (id)initWithProtocol:(Protocol *)protocol { NSParameterAssert(protocol); self = [super init]; if (self) { _workingProtocol = protocol; _name = NSStringFromProtocol(self.workingProtocol); } return self; } - (NSString *)description { return [NSString stringWithFormat:@"<%@ name=%@, registered=%d>", NSStringFromClass(self.class), self.name, self.isRegistered]; } #pragma mark Building - (void)addProperty:(FLEXProperty *)property isRequired:(BOOL)isRequired { MutationAssertion(@"Properties cannot be added once a protocol has been registered"); unsigned int count; objc_property_attribute_t *attributes = [property copyAttributesList:&count]; protocol_addProperty(self.workingProtocol, property.name.UTF8String, attributes, count, isRequired, YES); free(attributes); } - (void)addMethod:(SEL)selector typeEncoding:(NSString *)typeEncoding isRequired:(BOOL)isRequired isInstanceMethod:(BOOL)isInstanceMethod { MutationAssertion(@"Methods cannot be added once a protocol has been registered"); protocol_addMethodDescription(self.workingProtocol, selector, typeEncoding.UTF8String, isRequired, isInstanceMethod); } - (void)addProtocol:(Protocol *)protocol { MutationAssertion(@"Protocols cannot be added once a protocol has been registered"); protocol_addProtocol(self.workingProtocol, protocol); } - (FLEXProtocol *)registerProtocol { MutationAssertion(@"Protocol is already registered"); _isRegistered = YES; objc_registerProtocol(self.workingProtocol); return [FLEXProtocol protocol:self.workingProtocol]; } @end ================================================ FILE: Classes/Utility/Runtime/flex_fishhook.c ================================================ // Copyright (c) 2013, Facebook, Inc. // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name Facebook nor the names of its contributors may be used to // endorse or promote products derived from this software without specific // prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "flex_fishhook.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __LP64__ typedef struct mach_header_64 mach_header_t; typedef struct segment_command_64 segment_command_t; typedef struct section_64 section_t; typedef struct nlist_64 nlist_t; #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 #else typedef struct mach_header mach_header_t; typedef struct segment_command segment_command_t; typedef struct section section_t; typedef struct nlist nlist_t; #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT #endif #ifndef SEG_DATA_CONST #define SEG_DATA_CONST "__DATA_CONST" #endif struct rebindings_entry { struct rebinding *rebindings; size_t rebindings_nel; struct rebindings_entry *next; }; static struct rebindings_entry *_flex_rebindings_head; /// @return 0 on success static int flex_prepend_rebindings(struct rebindings_entry **rebindings_head, struct rebinding rebindings[], size_t nel) { struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); if (!new_entry) { return -1; } new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); if (!new_entry->rebindings) { free(new_entry); return -1; } memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); new_entry->rebindings_nel = nel; new_entry->next = *rebindings_head; *rebindings_head = new_entry; return 0; } static vm_prot_t flex_get_protection(void *sectionStart) { mach_port_t task = mach_task_self(); vm_size_t size = 0; vm_address_t address = (vm_address_t)sectionStart; memory_object_name_t object; #if __LP64__ mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; vm_region_basic_info_data_64_t info; kern_return_t info_ret = vm_region_64( task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object ); #else mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT; vm_region_basic_info_data_t info; kern_return_t info_ret = vm_region( task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object ); #endif if (info_ret == KERN_SUCCESS) { return info.protection; } else { return VM_PROT_READ; } } static void flex_perform_rebinding_with_section(struct rebindings_entry *rebindings, section_t *section, intptr_t slide, nlist_t *symtab, char *strtab, uint32_t *indirect_symtab) { const bool isDataConst = strcmp(section->segname, "__DATA_CONST") == 0; uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); vm_prot_t oldProtection = VM_PROT_READ; if (isDataConst) { oldProtection = flex_get_protection(rebindings); mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE); } for (uint i = 0; i < section->size / sizeof(void *); i++) { uint32_t symtab_index = indirect_symbol_indices[i]; if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { continue; } uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; char *symbol_name = strtab + strtab_offset; bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; struct rebindings_entry *cur = rebindings; while (cur) { for (uint j = 0; j < cur->rebindings_nel; j++) { if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; } indirect_symbol_bindings[i] = cur->rebindings[j].replacement; goto symbol_loop; } } cur = cur->next; } symbol_loop:; } if (isDataConst) { int protection = 0; if (oldProtection & VM_PROT_READ) { protection |= PROT_READ; } if (oldProtection & VM_PROT_WRITE) { protection |= PROT_WRITE; } if (oldProtection & VM_PROT_EXECUTE) { protection |= PROT_EXEC; } mprotect(indirect_symbol_bindings, section->size, protection); } } static void flex_rebind_symbols_for_image(struct rebindings_entry *rebindings, const struct mach_header *header, intptr_t slide) { Dl_info info; if (dladdr(header, &info) == 0) { return; } segment_command_t *cur_seg_cmd; segment_command_t *linkedit_segment = NULL; struct symtab_command* symtab_cmd = NULL; struct dysymtab_command* dysymtab_cmd = NULL; uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { cur_seg_cmd = (segment_command_t *)cur; if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { linkedit_segment = cur_seg_cmd; } } else if (cur_seg_cmd->cmd == LC_SYMTAB) { symtab_cmd = (struct symtab_command*)cur_seg_cmd; } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; } } if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || !dysymtab_cmd->nindirectsyms) { return; } // Find base symbol/string table addresses uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); // Get indirect symbol table (array of uint32_t indices into symbol table) uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); cur = (uintptr_t)header + sizeof(mach_header_t); for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { cur_seg_cmd = (segment_command_t *)cur; if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { continue; } for (uint j = 0; j < cur_seg_cmd->nsects; j++) { section_t *sect = (section_t *)(cur + sizeof(segment_command_t)) + j; if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { flex_perform_rebinding_with_section( rebindings, sect, slide, symtab, strtab, indirect_symtab ); } if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { flex_perform_rebinding_with_section( rebindings, sect, slide, symtab, strtab, indirect_symtab ); } } } } } static void _flex_rebind_symbols_for_image(const struct mach_header *header, intptr_t slide) { flex_rebind_symbols_for_image(_flex_rebindings_head, header, slide); } int flex_rebind_symbols_image(void *header, intptr_t slide, struct rebinding rebindings[], size_t rebindings_nel) { struct rebindings_entry *rebindings_head = NULL; int retval = flex_prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); flex_rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); if (rebindings_head) { free(rebindings_head->rebindings); } free(rebindings_head); return retval; } /// @return 0 on success int flex_rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { int retval = flex_prepend_rebindings(&_flex_rebindings_head, rebindings, rebindings_nel); if (retval < 0) { return retval; } // If this was the first call, register callback for image additions (which is also invoked for // existing images, otherwise, just run on existing images if (!_flex_rebindings_head->next) { _dyld_register_func_for_add_image(_flex_rebind_symbols_for_image); } else { uint32_t c = _dyld_image_count(); for (uint32_t i = 0; i < c; i++) { _flex_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); } } return retval; } ================================================ FILE: Classes/Utility/Runtime/flex_fishhook.h ================================================ // Copyright (c) 2013, Facebook, Inc. // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name Facebook nor the names of its contributors may be used to // endorse or promote products derived from this software without specific // prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef fishhook_h #define fishhook_h #include #include #if !defined(FISHHOOK_EXPORT) #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) #else #define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) #endif #ifdef __cplusplus extern "C" { #endif //__cplusplus /** * A structure representing a particular intended rebinding from a symbol * name to its replacement */ struct rebinding { const char *name; void *replacement; void **replaced; }; /** * For each rebinding in rebindings, rebinds references to external, indirect * symbols with the specified name to instead point at replacement for each * image in the calling process as well as for all future images that are loaded * by the process. If rebind_functions is called more than once, the symbols to * rebind are added to the existing list of rebindings, and if a given symbol * is rebound more than once, the later rebinding will take precedence. * @return 0 on success */ FISHHOOK_VISIBILITY int flex_rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); /** * Rebinds as above, but only in the specified image. The header should point * to the mach-o header, the slide should be the slide offset. Others as above. * @return 0 on success */ FISHHOOK_VISIBILITY int flex_rebind_symbols_image(void *header, intptr_t slide, struct rebinding rebindings[], size_t rebindings_nel); #ifdef __cplusplus } #endif //__cplusplus #endif //fishhook_h ================================================ FILE: Classes/ViewHierarchy/FLEXHierarchyViewController.h ================================================ // // FLEXHierarchyViewController.h // FLEX // // Created by Tanner Bennett on 1/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXNavigationController.h" @protocol FLEXHierarchyDelegate - (void)viewHierarchyDidDismiss:(UIView *)selectedView; @end /// A navigation controller which manages two child view controllers: /// a 3D Reveal-like hierarchy explorer, and a 2D tree-list hierarchy explorer. @interface FLEXHierarchyViewController : FLEXNavigationController + (instancetype)delegate:(id)delegate; + (instancetype)delegate:(id)delegate viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)selectedView; - (void)toggleHierarchyMode; @end ================================================ FILE: Classes/ViewHierarchy/FLEXHierarchyViewController.m ================================================ // // FLEXHierarchyViewController.m // FLEX // // Created by Tanner Bennett on 1/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FLEXHierarchyViewController.h" #import "FLEXHierarchyTableViewController.h" #import "FHSViewController.h" #import "FLEXUtility.h" #import "FLEXTabList.h" #import "FLEXResources.h" #import "UIBarButtonItem+FLEX.h" typedef NS_ENUM(NSUInteger, FLEXHierarchyViewMode) { FLEXHierarchyViewModeTree = 1, FLEXHierarchyViewMode3DSnapshot }; @interface FLEXHierarchyViewController () @property (nonatomic, readonly, weak) id hierarchyDelegate; @property (nonatomic, readonly) FHSViewController *snapshotViewController; @property (nonatomic, readonly) FLEXHierarchyTableViewController *treeViewController; @property (nonatomic) FLEXHierarchyViewMode mode; @property (nonatomic, readonly) UIView *selectedView; @end @implementation FLEXHierarchyViewController #pragma mark - Initialization + (instancetype)delegate:(id)delegate { return [self delegate:delegate viewsAtTap:nil selectedView:nil]; } + (instancetype)delegate:(id)delegate viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)selectedView { return [[self alloc] initWithDelegate:delegate viewsAtTap:viewsAtTap selectedView:selectedView]; } - (id)initWithDelegate:(id)delegate viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)view { self = [super init]; if (self) { NSArray *allWindows = FLEXUtility.allWindows; _hierarchyDelegate = delegate; _treeViewController = [FLEXHierarchyTableViewController windows:allWindows viewsAtTap:viewsAtTap selectedView:view ]; if (viewsAtTap) { _snapshotViewController = [FHSViewController snapshotViewsAtTap:viewsAtTap selectedView:view]; } else { _snapshotViewController = [FHSViewController snapshotWindows:allWindows]; } self.modalPresentationStyle = UIModalPresentationFullScreen; } return self; } #pragma mark - Lifecycle - (void)viewDidLoad { [super viewDidLoad]; // 3D toggle button self.treeViewController.navigationItem.leftBarButtonItem = [UIBarButtonItem flex_itemWithImage:FLEXResources.toggle3DIcon target:self action:@selector(toggleHierarchyMode) ]; // Dismiss when tree view row is selected __weak id delegate = self.hierarchyDelegate; self.treeViewController.didSelectRowAction = ^(UIView *selectedView) { [delegate viewHierarchyDidDismiss:selectedView]; }; // Start of in tree view _mode = FLEXHierarchyViewModeTree; [self pushViewController:self.treeViewController animated:NO]; } - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { // Done button: manually added here because the hierarhcy screens need to actually pass // data back to the explorer view controller so that it can highlight selected views viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed) ]; [super pushViewController:viewController animated:animated]; } #pragma mark - Private - (void)donePressed { // We need to manually close ourselves here because // FLEXNavigationController doesn't ever close tabs itself [FLEXTabList.sharedList closeTab:self]; [self.hierarchyDelegate viewHierarchyDidDismiss:self.selectedView]; } - (void)toggleHierarchyMode { switch (self.mode) { case FLEXHierarchyViewModeTree: self.mode = FLEXHierarchyViewMode3DSnapshot; break; case FLEXHierarchyViewMode3DSnapshot: self.mode = FLEXHierarchyViewModeTree; break; } } - (void)setMode:(FLEXHierarchyViewMode)mode { if (mode != _mode) { // The tree view controller is our top stack view controller, and // changing the mode simply pushes the snapshot view. In the future, // I would like to have the 3D toggle button transparently switch // between two views instead of pushing a new view controller. // This way the views should share the search controller somehow. switch (mode) { case FLEXHierarchyViewModeTree: [self popViewControllerAnimated:NO]; self.toolbarHidden = YES; self.treeViewController.selectedView = self.selectedView; break; case FLEXHierarchyViewMode3DSnapshot: [self pushViewController:self.snapshotViewController animated:NO]; self.toolbarHidden = NO; self.snapshotViewController.selectedView = self.selectedView; break; } // Change this last so that self.selectedView works right above _mode = mode; } } - (UIView *)selectedView { switch (self.mode) { case FLEXHierarchyViewModeTree: return self.treeViewController.selectedView; case FLEXHierarchyViewMode3DSnapshot: return self.snapshotViewController.selectedView; } } @end ================================================ FILE: Classes/ViewHierarchy/FLEXImagePreviewViewController.h ================================================ // // FLEXImagePreviewViewController.h // Flipboard // // Created by Ryan Olson on 6/12/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @interface FLEXImagePreviewViewController : UIViewController + (instancetype)previewForView:(UIView *)view; + (instancetype)previewForLayer:(CALayer *)layer; + (instancetype)forImage:(UIImage *)image; @end ================================================ FILE: Classes/ViewHierarchy/FLEXImagePreviewViewController.m ================================================ // // FLEXImagePreviewViewController.m // Flipboard // // Created by Ryan Olson on 6/12/14. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXImagePreviewViewController.h" #import "FLEXActivityViewController.h" #import "FLEXUtility.h" #import "FLEXColor.h" #import "FLEXResources.h" @interface FLEXImagePreviewViewController () @property (nonatomic) UIImage *image; @property (nonatomic) UIScrollView *scrollView; @property (nonatomic) UIImageView *imageView; @property (nonatomic) UITapGestureRecognizer *bgColorTapGesture; @property (nonatomic) NSInteger backgroundColorIndex; @property (nonatomic, readonly) NSArray *backgroundColors; @end #pragma mark - @implementation FLEXImagePreviewViewController #pragma mark Initialization + (instancetype)previewForView:(UIView *)view { return [self forImage:[FLEXUtility previewImageForView:view]]; } + (instancetype)previewForLayer:(CALayer *)layer { return [self forImage:[FLEXUtility previewImageForLayer:layer]]; } + (instancetype)forImage:(UIImage *)image { return [[self alloc] initWithImage:image]; } - (id)initWithImage:(UIImage *)image { NSParameterAssert(image); self = [super init]; if (self) { self.title = @"Preview"; self.image = image; _backgroundColors = @[FLEXResources.checkerPatternColor, UIColor.whiteColor, UIColor.blackColor]; } return self; } - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { [super dismissViewControllerAnimated:flag completion:completion]; } #pragma mark Lifecycle - (void)viewDidLoad { [super viewDidLoad]; self.imageView = [[UIImageView alloc] initWithImage:self.image]; self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; self.scrollView.delegate = self; self.scrollView.backgroundColor = self.backgroundColors.firstObject; self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.scrollView addSubview:self.imageView]; self.scrollView.contentSize = self.imageView.frame.size; self.scrollView.minimumZoomScale = 1.0; self.scrollView.maximumZoomScale = 2.0; [self.view addSubview:self.scrollView]; self.bgColorTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(changeBackground)]; [self.scrollView addGestureRecognizer:self.bgColorTapGesture]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionButtonPressed:) ]; } - (void)viewDidLayoutSubviews { [self centerContentInScrollViewIfNeeded]; } #pragma mark UIScrollViewDelegate - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.imageView; } - (void)scrollViewDidZoom:(UIScrollView *)scrollView { [self centerContentInScrollViewIfNeeded]; } #pragma mark Private - (void)centerContentInScrollViewIfNeeded { CGFloat horizontalInset = 0.0; CGFloat verticalInset = 0.0; if (self.scrollView.contentSize.width < self.scrollView.bounds.size.width) { horizontalInset = (self.scrollView.bounds.size.width - self.scrollView.contentSize.width) / 2.0; } if (self.scrollView.contentSize.height < self.scrollView.bounds.size.height) { verticalInset = (self.scrollView.bounds.size.height - self.scrollView.contentSize.height) / 2.0; } self.scrollView.contentInset = UIEdgeInsetsMake(verticalInset, horizontalInset, verticalInset, horizontalInset); } - (void)changeBackground { self.backgroundColorIndex++; self.backgroundColorIndex %= self.backgroundColors.count; self.scrollView.backgroundColor = self.backgroundColors[self.backgroundColorIndex]; } - (void)actionButtonPressed:(id)sender { static BOOL canSaveToCameraRoll = NO, didShowWarning = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (UIDevice.currentDevice.systemVersion.floatValue < 10) { canSaveToCameraRoll = YES; return; } NSBundle *mainBundle = NSBundle.mainBundle; if ([mainBundle.infoDictionary.allKeys containsObject:@"NSPhotoLibraryUsageDescription"]) { canSaveToCameraRoll = YES; } }); UIViewController *activityVC = [FLEXActivityViewController sharing:@[self.image] source:sender]; if (!canSaveToCameraRoll && !didShowWarning) { didShowWarning = YES; NSString *msg = @"Add 'NSPhotoLibraryUsageDescription' to this app's Info.plist to save images."; [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Reminder").message(msg); make.button(@"OK").handler(^(NSArray *strings) { [self presentViewController:activityVC animated:YES completion:nil]; }); } showFrom:self]; } else { [self presentViewController:activityVC animated:YES completion:nil]; } } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSRangeSlider.h ================================================ // // FHSRangeSlider.h // FLEX // // Created by Tanner Bennett on 1/7/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface FHSRangeSlider : UIControl @property (nonatomic) CGFloat allowedMinValue; @property (nonatomic) CGFloat allowedMaxValue; @property (nonatomic) CGFloat minValue; @property (nonatomic) CGFloat maxValue; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSRangeSlider.m ================================================ // // FHSRangeSlider.m // FLEX // // Created by Tanner Bennett on 1/7/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FHSRangeSlider.h" #import "FLEXResources.h" #import "FLEXUtility.h" @interface FHSRangeSlider () @property (nonatomic, readonly) UIImageView *track; @property (nonatomic, readonly) UIImageView *fill; @property (nonatomic, readonly) UIImageView *leftHandle; @property (nonatomic, readonly) UIImageView *rightHandle; @property (nonatomic, getter=isTrackingLeftHandle) BOOL trackingLeftHandle; @property (nonatomic, getter=isTrackingRightHandle) BOOL trackingRightHandle; @end @implementation FHSRangeSlider #pragma mark - Initialization - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { _allowedMaxValue = 1.f; _maxValue = 1.f; [self initSubviews]; } return self; } - (void)initSubviews { self.userInteractionEnabled = YES; UIImageView * (^newSubviewImageView)(UIImage *) = ^UIImageView *(UIImage *image) { UIImageView *iv = [UIImageView new]; iv.image = image; // iv.userInteractionEnabled = YES; [self addSubview:iv]; return iv; }; _track = newSubviewImageView(FLEXResources.rangeSliderTrack); _fill = newSubviewImageView(FLEXResources.rangeSliderFill); _leftHandle = newSubviewImageView(FLEXResources.rangeSliderLeftHandle); _rightHandle = newSubviewImageView(FLEXResources.rangeSliderRightHandle); } #pragma mark - Setters / Private - (CGFloat)valueAt:(CGFloat)x { CGFloat minX = self.leftHandle.image.size.width; CGFloat maxX = self.bounds.size.width - self.rightHandle.image.size.width; CGFloat cappedX = MIN(MAX(x, minX), maxX); CGFloat delta = maxX - minX; CGFloat maxDelta = self.allowedMaxValue - self.allowedMinValue; return ((delta > 0) ? (cappedX - minX) / delta : 0) * maxDelta + self.allowedMinValue; } - (void)setAllowedMinValue:(CGFloat)allowedMinValue { _allowedMinValue = allowedMinValue; if (self.minValue < self.allowedMaxValue) { self.minValue = self.allowedMaxValue; } else { [self setNeedsLayout]; } } - (void)setAllowedMaxValue:(CGFloat)allowedMaxValue { _allowedMaxValue = allowedMaxValue; if (self.maxValue > self.allowedMaxValue) { self.maxValue = self.allowedMaxValue; } else { [self valuesChanged:NO]; } } - (void)setMinValue:(CGFloat)minValue { _minValue = minValue; [self valuesChanged:YES]; } - (void)setMaxValue:(CGFloat)maxValue { _maxValue = maxValue; [self valuesChanged:YES]; } - (void)valuesChanged:(BOOL)sendActions { if (NSThread.isMainThread) { if (sendActions) { [self sendActionsForControlEvents:UIControlEventValueChanged]; } [self setNeedsLayout]; } } #pragma mark - Overrides - (CGSize)intrinsicContentSize { return CGSizeMake(UIViewNoIntrinsicMetric, self.leftHandle.image.size.height); } - (void)layoutSubviews { [super layoutSubviews]; CGSize lhs = self.leftHandle.image.size; CGSize rhs = self.rightHandle.image.size; CGSize trackSize = self.track.image.size; CGFloat delta = self.allowedMaxValue - self.allowedMinValue; CGFloat minPercent, maxPercent; if (delta <= 0) { minPercent = maxPercent = 0; } else { minPercent = MAX(0, (self.minValue - self.allowedMinValue) / delta); maxPercent = MAX(minPercent, (self.maxValue - self.allowedMinValue) / delta); } CGFloat rangeSliderWidth = self.bounds.size.width - lhs.width - rhs.width; self.leftHandle.frame = FLEXRectMake( rangeSliderWidth * minPercent, CGRectGetMidY(self.bounds) - (lhs.height / 2.f) + 3.f, lhs.width, lhs.height ); self.rightHandle.frame = FLEXRectMake( lhs.width + (rangeSliderWidth * maxPercent), CGRectGetMidY(self.bounds) - (rhs.height / 2.f) + 3.f, rhs.width, rhs.height ); self.track.frame = FLEXRectMake( lhs.width / 2.f, CGRectGetMidY(self.bounds) - trackSize.height / 2.f, self.bounds.size.width - (lhs.width / 2.f) - (rhs.width / 2.f), trackSize.height ); self.fill.frame = FLEXRectMake( CGRectGetMidX(self.leftHandle.frame), CGRectGetMinY(self.track.frame), CGRectGetMidX(self.rightHandle.frame) - CGRectGetMidX(self.leftHandle.frame), self.track.frame.size.height ); } - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint loc = [touch locationInView:self]; if (CGRectContainsPoint(self.leftHandle.frame, loc)) { self.trackingLeftHandle = YES; self.trackingRightHandle = NO; } else if (CGRectContainsPoint(self.rightHandle.frame, loc)) { self.trackingLeftHandle = NO; self.trackingRightHandle = YES; } else { return NO; } return YES; } - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint loc = [touch locationInView:self]; if (self.isTrackingLeftHandle) { self.minValue = MIN(MAX(self.allowedMinValue, [self valueAt:loc.x]), self.maxValue); } else if (self.isTrackingRightHandle) { self.maxValue = MAX(MIN(self.allowedMaxValue, [self valueAt:loc.x]), self.minValue); } else { return NO; } [self setNeedsLayout]; [self layoutIfNeeded]; return YES; } - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { self.trackingLeftHandle = NO; self.trackingRightHandle = NO; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return NO; } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSSnapshotView.h ================================================ // // FHSSnapshotView.h // FLEX // // Created by Tanner Bennett on 1/7/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FHSViewSnapshot.h" #import "FHSRangeSlider.h" NS_ASSUME_NONNULL_BEGIN @protocol FHSSnapshotViewDelegate - (void)didSelectView:(FHSViewSnapshot *)snapshot; - (void)didDeselectView:(FHSViewSnapshot *)snapshot; - (void)didLongPressView:(FHSViewSnapshot *)snapshot; @end @interface FHSSnapshotView : UIView + (instancetype)delegate:(id)delegate; @property (nonatomic, weak) id delegate; @property (nonatomic) NSArray *snapshots; @property (nonatomic, nullable) FHSViewSnapshot *selectedView; /// Views of these classes will have their headers hidden @property (nonatomic) NSArray *headerExclusions; @property (nonatomic, readonly) UISlider *spacingSlider; @property (nonatomic, readonly) FHSRangeSlider *depthSlider; - (void)emphasizeViews:(NSArray *)emphasizedViews; - (void)toggleShowHeaders; - (void)toggleShowBorders; - (void)hideView:(FHSViewSnapshot *)view; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSSnapshotView.m ================================================ // // FHSSnapshotView.m // FLEX // // Created by Tanner Bennett on 1/7/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FHSSnapshotView.h" #import "FHSSnapshotNodes.h" #import "SceneKit+Snapshot.h" #import "FLEXColor.h" @interface FHSSnapshotView () @property (nonatomic, readonly) SCNView *sceneView; @property (nonatomic) NSString *currentSummary; /// Maps nodes by snapshot IDs @property (nonatomic) NSDictionary *nodesMap; @property (nonatomic) NSInteger maxDepth; @property (nonatomic) FHSSnapshotNodes *highlightedNodes; @property (nonatomic, getter=wantsHideHeaders) BOOL hideHeaders; @property (nonatomic, getter=wantsHideBorders) BOOL hideBorders; @property (nonatomic) BOOL suppressSelectionEvents; @property (nonatomic, readonly) BOOL mustHideHeaders; @end @implementation FHSSnapshotView #pragma mark - Initialization + (instancetype)delegate:(id)delegate { FHSSnapshotView *view = [self new]; view.delegate = delegate; return view; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:CGRectZero]; if (self) { [self initSpacingSlider]; [self initDepthSlider]; [self initSceneView]; // Must be last; calls setMaxDepth // self.hideHeaders = YES; // Self self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // Scene self.sceneView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:) ]]; } return self; } - (void)initSceneView { _sceneView = [SCNView new]; self.sceneView.allowsCameraControl = YES; [self addSubview:self.sceneView]; } - (void)initSpacingSlider { _spacingSlider = [UISlider new]; self.spacingSlider.minimumValue = 0; self.spacingSlider.maximumValue = 100; self.spacingSlider.continuous = YES; [self.spacingSlider addTarget:self action:@selector(spacingSliderDidChange:) forControlEvents:UIControlEventValueChanged ]; self.spacingSlider.value = 50; } - (void)initDepthSlider { _depthSlider = [FHSRangeSlider new]; [self.depthSlider addTarget:self action:@selector(depthSliderDidChange:) forControlEvents:UIControlEventValueChanged ]; } #pragma mark - Public - (void)setSelectedView:(FHSViewSnapshot *)view { // Ivar set in selectSnapshot: [self selectSnapshot:view ? self.nodesMap[view.view.identifier] : nil]; } - (void)setSnapshots:(NSArray *)snapshots { _snapshots = snapshots; // Create new scene (possibly discarding old scene) SCNScene *scene = [SCNScene new]; scene.background.contents = FLEXColor.primaryBackgroundColor; self.sceneView.scene = scene; NSInteger depth = 0; NSMutableDictionary *nodesMap = [NSMutableDictionary new]; // Add every root snapshot to the root scene node with increasing depths SCNNode *root = scene.rootNode; for (FHSViewSnapshot *snapshot in self.snapshots) { [SCNNode snapshot:snapshot parent:nil parentNode:nil root:root depth:&depth nodesMap:nodesMap hideHeaders:_hideHeaders ]; } self.maxDepth = depth; self.nodesMap = nodesMap; } - (void)setHeaderExclusions:(NSArray *)headerExclusions { _headerExclusions = headerExclusions; if (headerExclusions.count) { for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) { if ([headerExclusions containsObject:nodes.snapshotItem.view.view.class]) { nodes.forceHideHeader = YES; } else { nodes.forceHideHeader = NO; } } } } - (void)emphasizeViews:(NSArray *)emphasizedViews { if (emphasizedViews.count) { [self emphasizeViews:emphasizedViews inSnapshots:self.snapshots]; [self setNeedsLayout]; } } - (void)emphasizeViews:(NSArray *)emphasizedViews inSnapshots:(NSArray *)snapshots { for (FHSViewSnapshot *snapshot in snapshots) { FHSSnapshotNodes *nodes = self.nodesMap[snapshot.view.identifier]; nodes.dimmed = ![emphasizedViews containsObject:snapshot.view.view]; [self emphasizeViews:emphasizedViews inSnapshots:snapshot.children]; } } - (void)toggleShowHeaders { self.hideHeaders = !self.hideHeaders; } - (void)toggleShowBorders { self.hideBorders = !self.hideBorders; } - (void)hideView:(FHSViewSnapshot *)view { NSParameterAssert(view); FHSSnapshotNodes *nodes = self.nodesMap[view.view.identifier]; [nodes.snapshot removeFromParentNode]; } #pragma mark - Helper - (BOOL)mustHideHeaders { return self.spacingSlider.value <= kFHSSmallZOffset; } - (void)setMaxDepth:(NSInteger)maxDepth { _maxDepth = maxDepth; self.depthSlider.allowedMinValue = 0; self.depthSlider.allowedMaxValue = maxDepth; self.depthSlider.maxValue = maxDepth; self.depthSlider.minValue = 0; } - (void)setHideHeaders:(BOOL)hideHeaders { if (_hideHeaders != hideHeaders) { _hideHeaders = hideHeaders; if (!self.mustHideHeaders) { if (hideHeaders) { [self hideHeaders]; } else { [self unhideHeaders]; } } } } - (void)setHideBorders:(BOOL)hideBorders { if (_hideBorders != hideBorders) { _hideBorders = hideBorders; for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) { nodes.border.hidden = hideBorders; } } } - (FHSSnapshotNodes *)nodesAtPoint:(CGPoint)point { NSArray *results = [self.sceneView hitTest:point options:nil]; for (SCNHitTestResult *result in results) { SCNNode *nearestSnapshot = result.node.nearestAncestorSnapshot; if (nearestSnapshot) { return self.nodesMap[nearestSnapshot.name]; } } return nil; } - (void)selectSnapshot:(FHSSnapshotNodes *)selected { // Notify delegate of de-select if (!selected && self.selectedView) { [self.delegate didDeselectView:self.selectedView]; } _selectedView = selected.snapshotItem; // Case: selected the currently selected node if (selected == self.highlightedNodes) { return; } // No-op if nothng is selected (yay objc!) self.highlightedNodes.highlighted = NO; self.highlightedNodes = nil; // No node means we tapped the background if (selected) { selected.highlighted = YES; // TODO: update description text here self.highlightedNodes = selected; } // Notify delegate [self.delegate didSelectView:selected.snapshotItem]; [self setNeedsLayout]; } - (void)hideHeaders { for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) { nodes.header.hidden = YES; } } - (void)unhideHeaders { for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) { if (!nodes.forceHideHeader) { nodes.header.hidden = NO; } } } #pragma mark - Event Handlers - (void)handleTap:(UITapGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateRecognized) { CGPoint tap = [gesture locationInView:self.sceneView]; [self selectSnapshot:[self nodesAtPoint:tap]]; } } - (void)spacingSliderDidChange:(UISlider *)slider { // TODO: hiding the header when flat logic for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) { nodes.snapshot.position = ({ SCNVector3 pos = nodes.snapshot.position; pos.z = MAX(slider.value, kFHSSmallZOffset) * nodes.depth; pos; }); if (!self.wantsHideHeaders) { if (self.mustHideHeaders) { [self hideHeaders]; } else { [self unhideHeaders]; } } } } - (void)depthSliderDidChange:(FHSRangeSlider *)slider { CGFloat min = slider.minValue, max = slider.maxValue; for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) { CGFloat depth = nodes.depth; nodes.snapshot.hidden = depth < min || max < depth; } } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSView.h ================================================ // // FHSView.h // FLEX // // Created by Tanner Bennett on 1/6/20. // #import @interface FHSView : NSObject { @private BOOL _inScrollView; } + (instancetype)forView:(UIView *)view isInScrollView:(BOOL)inScrollView; /// Intentionally not weak @property (nonatomic, readonly) UIView *view; @property (nonatomic, readonly) NSString *identifier; @property (nonatomic, readonly) NSString *title; /// Whether or not this view item should be visually distinguished @property (nonatomic, readwrite) BOOL important; @property (nonatomic, readonly) CGRect frame; @property (nonatomic, readonly) BOOL hidden; @property (nonatomic, readonly) UIImage *snapshotImage; @property (nonatomic, readonly) NSArray *children; @property (nonatomic, readonly) NSString *summary; /// @return importantAttr if .important, otherwise normalAttr //- (id)ifImportant:(id)importantAttr ifNormal:(id)normalAttr; @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSView.m ================================================ // // FHSView.m // FLEX // // Created by Tanner Bennett on 1/6/20. // #import "FHSView.h" #import "FLEXUtility.h" #import "NSArray+FLEX.h" @interface FHSView (Snapshotting) + (UIImage *)_snapshotView:(UIView *)view; @end @implementation FHSView + (instancetype)forView:(UIView *)view isInScrollView:(BOOL)inScrollView { return [[self alloc] initWithView:view isInScrollView:inScrollView]; } - (id)initWithView:(UIView *)view isInScrollView:(BOOL)inScrollView { self = [super init]; if (self) { _view = view; _inScrollView = inScrollView; _identifier = NSUUID.UUID.UUIDString; UIViewController *controller = [FLEXUtility viewControllerForView:view]; if (controller) { _important = YES; _title = [NSString stringWithFormat: @"%@ (for %@)", NSStringFromClass([controller class]), NSStringFromClass([view class]) ]; } else { _title = NSStringFromClass([view class]); } } return self; } - (CGRect)frame { if (_inScrollView) { CGPoint offset = [(UIScrollView *)self.view.superview contentOffset]; return CGRectOffset(self.view.frame, -offset.x, -offset.y); } else { return self.view.frame; } } - (BOOL)hidden { return self.view.isHidden; } - (UIImage *)snapshotImage { return [FHSView _snapshotView:self.view]; } - (NSArray *)children { BOOL isScrollView = [self.view isKindOfClass:[UIScrollView class]]; return [self.view.subviews flex_mapped:^id(UIView *subview, NSUInteger idx) { return [FHSView forView:subview isInScrollView:isScrollView]; }]; } - (NSString *)summary { CGRect f = self.frame; return [NSString stringWithFormat: @"%@ (%.1f, %.1f, %.1f, %.1f)", NSStringFromClass([self.view class]), f.origin.x, f.origin.y, f.size.width, f.size.height ]; } - (NSString *)description{ return self.view.description; } - (id)ifImportant:(id)importantAttr ifNormal:(id)normalAttr { return self.important ? importantAttr : normalAttr; } @end @implementation FHSView (Snapshotting) + (UIImage *)drawView:(UIView *)view { if (CGRectIsEmpty(view.bounds)) { return [UIImage new]; } CGSize size = view.bounds.size; CGFloat minUnit = 1.f / UIScreen.mainScreen.scale; // Every drawn view must not have 0 width or height CGSize minsize = CGSizeMake(MAX(size.width, minUnit), MAX(size.height, minUnit)); CGRect minBounds = CGRectMake(0, 0, minsize.width, minsize.height); UIGraphicsBeginImageContextWithOptions(minsize, NO, 0); [view drawViewHierarchyInRect:minBounds afterScreenUpdates:YES]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } /// Recursively hides all views that may be obscuring the given view and collects them /// in the given array. You should unhide them all when you are done. + (BOOL)_hideViewsCoveringView:(UIView *)view root:(UIView *)rootView hiddenViews:(NSMutableArray *)hiddenViews { // Stop when we reach this view if (view == rootView) { return YES; } for (UIView *subview in rootView.subviews.reverseObjectEnumerator.allObjects) { if ([self _hideViewsCoveringView:view root:subview hiddenViews:hiddenViews]) { return YES; } } if (!rootView.isHidden) { [self _setHidden:YES forView:rootView]; [hiddenViews addObject:rootView]; } return NO; } /// Recursively hides all views that may be obscuring the given view and collects them /// in the given array. You should unhide them all when you are done. + (void)hideViewsCoveringView:(UIView *)view doWhileHidden:(void(^)(void))block { NSMutableArray *viewsToUnhide = [NSMutableArray new]; if ([self _hideViewsCoveringView:view root:view.window hiddenViews:viewsToUnhide]) { block(); } for (UIView *v in viewsToUnhide) { [self _setHidden:NO forView:v]; } } + (UIImage *)_snapshotVisualEffectBackdropView:(UIView *)view { NSParameterAssert(view.window); // UIVisualEffectView is a special case that cannot be snapshotted // the same way as any other view. From Apple docs: // // Many effects require support from the window that hosts the // UIVisualEffectView. Attempting to take a snapshot of only the // UIVisualEffectView will result in a snapshot that does not // contain the effect. To take a snapshot of a view hierarchy // that contains a UIVisualEffectView, you must take a snapshot // of the entire UIWindow or UIScreen that contains it. // // To snapshot this view, we traverse the view hierarchy starting // from the window and hide any views that are on top of the // _UIVisualEffectBackdropView so that it is visible in a snapshot // of the window. We then take a snapshot of the window and crop // it to the part that contains the backdrop view. This appears to // be the same technique that Xcode's own view debugger uses to // snapshot visual effect views. __block UIImage *image = nil; [self hideViewsCoveringView:view doWhileHidden:^{ image = [self drawView:view]; CGRect cropRect = [view.window convertRect:view.bounds fromView:view]; image = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(image.CGImage, cropRect)]; }]; return image; } + (UIImage *)_snapshotView:(UIView *)view { UIView *superview = view.superview; // Is this view inside a UIVisualEffectView? if ([superview isKindOfClass:[UIVisualEffectView class]]) { // Is it (probably) the "backdrop" view of this UIVisualEffectView? if (superview.subviews.firstObject == view) { return [self _snapshotVisualEffectBackdropView:view]; } } // Hide the view's subviews before we snapshot it NSMutableIndexSet *toUnhide = [NSMutableIndexSet new]; [view.subviews flex_forEach:^(UIView *v, NSUInteger idx) { if (!v.isHidden) { [self _setHidden:YES forView:v]; [toUnhide addIndex:idx]; } }]; // Snapshot the view, then unhide the previously-unhidden views UIImage *snapshot = [self drawView:view]; for (UIView *v in [view.subviews objectsAtIndexes:toUnhide]) { [self _setHidden:NO forView:v]; } return snapshot; } + (void)_setHidden:(BOOL)hidden forView:(UIView *)view { // SpringBoard's SBHomeGrabberView responds to setHidden: but raises an exception // if you try to use it. // Catching the exception is less fragile than hardcoding classes to ignore @try { view.hidden = hidden; } @catch (NSException *exception) { NSString *hidingOrUnhiding = hidden ? @"hiding" : @"unhiding"; NSLog(@"Exception raised when %@ view %@: %@", hidingOrUnhiding, view, exception); } } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSViewController.h ================================================ // // FHSViewController.h // FLEX // // Created by Tanner Bennett on 1/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN /// The view controller /// "FHS" stands for "FLEX (view) hierarchy snapshot" @interface FHSViewController : UIViewController /// Use this when you want to snapshot a set of windows. + (instancetype)snapshotWindows:(NSArray *)windows; /// Use this when you want to snapshot a specific slice of the view hierarchy. + (instancetype)snapshotView:(UIView *)view; /// Use this when you want to emphasize specific views on the screen. /// These views must all be in the same window as the selected view. + (instancetype)snapshotViewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)view; @property (nonatomic, nullable) UIView *selectedView; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSViewController.m ================================================ // // FHSViewController.m // FLEX // // Created by Tanner Bennett on 1/6/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FHSViewController.h" #import "FHSSnapshotView.h" #import "FLEXHierarchyViewController.h" #import "FLEXColor.h" #import "FLEXAlert.h" #import "FLEXWindow.h" #import "FLEXResources.h" #import "NSArray+FLEX.h" #import "UIBarButtonItem+FLEX.h" BOOL const kFHSViewControllerExcludeFLEXWindows = YES; @interface FHSViewController () /// An array of only the target views whose hierarchies /// we wish to snapshot, not every view in the snapshot. @property (nonatomic, readonly) NSArray *targetViews; @property (nonatomic, readonly) NSArray *views; @property (nonatomic ) NSArray *snapshots; @property (nonatomic, ) FHSSnapshotView *snapshotView; @property (nonatomic, readonly) UIView *containerView; @property (nonatomic, readonly) NSArray *viewsAtTap; @property (nonatomic, readonly) NSMutableSet *forceHideHeaders; @end @implementation FHSViewController @synthesize views = _views; @synthesize snapshotView = _snapshotView; #pragma mark - Initialization + (instancetype)snapshotWindows:(NSArray *)windows { return [[self alloc] initWithViews:windows viewsAtTap:nil selectedView:nil]; } + (instancetype)snapshotView:(UIView *)view { return [[self alloc] initWithViews:@[view] viewsAtTap:nil selectedView:nil]; } + (instancetype)snapshotViewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)view { NSParameterAssert(viewsAtTap.count); NSParameterAssert(view.window); return [[self alloc] initWithViews:@[view.window] viewsAtTap:viewsAtTap selectedView:view]; } - (id)initWithViews:(NSArray *)views viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)view { NSParameterAssert(views.count); self = [super init]; if (self) { _forceHideHeaders = [NSMutableSet setWithObject:NSClassFromString(@"_UITableViewCellSeparatorView")]; _selectedView = view; _viewsAtTap = viewsAtTap; if (!viewsAtTap && kFHSViewControllerExcludeFLEXWindows) { Class flexwindow = [FLEXWindow class]; views = [views flex_filtered:^BOOL(UIView *view, NSUInteger idx) { return [view class] != flexwindow; }]; } _targetViews = views; _views = [views flex_mapped:^id(UIView *view, NSUInteger idx) { BOOL isScrollView = [view.superview isKindOfClass:[UIScrollView class]]; return [FHSView forView:view isInScrollView:isScrollView]; }]; } return self; } - (void)refreshSnapshotView { // Alert view to block interaction while we load everything UIAlertController *loading = [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Please Wait").message(@"Generating snapshot…"); }]; [self presentViewController:loading animated:YES completion:^{ self.snapshots = [self.views flex_mapped:^id(FHSView *view, NSUInteger idx) { return [FHSViewSnapshot snapshotWithView:view]; }]; FHSSnapshotView *newSnapshotView = [FHSSnapshotView delegate:self]; // This work is highly intensive so we do it on a background thread first dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // Setting the snapshots computes lots of SCNNodes, takes several seconds newSnapshotView.snapshots = self.snapshots; // After we finish generating all the model objects and scene nodes, display the view dispatch_async(dispatch_get_main_queue(), ^{ // Dismiss alert [loading dismissViewControllerAnimated:YES completion:nil]; self.snapshotView = newSnapshotView; }); }); }]; } #pragma mark - View Controller Lifecycle - (void)loadView { [super loadView]; self.view.backgroundColor = FLEXColor.primaryBackgroundColor; } - (void)viewDidLoad { [super viewDidLoad]; // Initialize back bar button item for 3D view to look like a button self.navigationItem.hidesBackButton = YES; self.navigationItem.leftBarButtonItem = [UIBarButtonItem flex_itemWithImage:FLEXResources.toggle2DIcon target:self.navigationController action:@selector(toggleHierarchyMode) ]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (!_snapshotView) { [self refreshSnapshotView]; } } #pragma mark - Public - (void)setSelectedView:(UIView *)view { _selectedView = view; self.snapshotView.selectedView = view ? [self snapshotForView:view] : nil; } #pragma mark - Private #pragma mark Properties - (FHSSnapshotView *)snapshotView { return self.isViewLoaded ? _snapshotView : nil; } - (void)setSnapshotView:(FHSSnapshotView *)snapshotView { NSParameterAssert(snapshotView); _snapshotView = snapshotView; // Initialize our toolbar items self.toolbarItems = @[ [UIBarButtonItem flex_itemWithCustomView:snapshotView.spacingSlider], UIBarButtonItem.flex_flexibleSpace, [UIBarButtonItem flex_itemWithImage:FLEXResources.moreIcon target:self action:@selector(didPressOptionsButton:) ], UIBarButtonItem.flex_flexibleSpace, [UIBarButtonItem flex_itemWithCustomView:snapshotView.depthSlider] ]; [self resizeToolbarItems:self.view.frame.size]; // If we have views-at-tap, dim the other views [snapshotView emphasizeViews:self.viewsAtTap]; // Set the selected view, if any snapshotView.selectedView = [self snapshotForView:self.selectedView]; snapshotView.headerExclusions = self.forceHideHeaders.allObjects; [snapshotView setNeedsLayout]; // Remove old snapshot, if any, and add the new one [_snapshotView removeFromSuperview]; snapshotView.frame = self.containerView.bounds; [self.containerView addSubview:snapshotView]; } - (UIView *)containerView { return self.view; } #pragma mark Helper - (FHSViewSnapshot *)snapshotForView:(UIView *)view { if (!view || !self.snapshots.count) return nil; for (FHSViewSnapshot *snapshot in self.snapshots) { FHSViewSnapshot *found = [snapshot snapshotForView:view]; if (found) { return found; } } // Error: we have snapshots but the view we requested is not in one @throw NSInternalInconsistencyException; return nil; } #pragma mark Events - (void)didPressOptionsButton:(UIBarButtonItem *)sender { [FLEXAlert makeSheet:^(FLEXAlert *make) { if (self.selectedView) { make.button(@"Hide selected view").handler(^(NSArray *strings) { [self.snapshotView hideView:[self snapshotForView:self.selectedView]]; }); make.button(@"Hide headers for views like this").handler(^(NSArray *strings) { Class cls = [self.selectedView class]; if (![self.forceHideHeaders containsObject:cls]) { [self.forceHideHeaders addObject:[self.selectedView class]]; self.snapshotView.headerExclusions = self.forceHideHeaders.allObjects; } }); } make.title(@"Options"); make.button(@"Toggle headers").handler(^(NSArray *strings) { [self.snapshotView toggleShowHeaders]; }); make.button(@"Toggle outlines").handler(^(NSArray *strings) { [self.snapshotView toggleShowBorders]; }); make.button(@"Cancel").cancelStyle(); } showFrom:self source:sender]; } - (void)resizeToolbarItems:(CGSize)viewSize { CGFloat sliderHeights = self.snapshotView.spacingSlider.bounds.size.height; CGFloat sliderWidths = viewSize.width / 3.f; CGRect frame = CGRectMake(0, 0, sliderWidths, sliderHeights); self.snapshotView.spacingSlider.frame = frame; self.snapshotView.depthSlider.frame = frame; [self.navigationController.toolbar setNeedsLayout]; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:^(id _Nonnull context) { [self resizeToolbarItems:self.view.frame.size]; } completion:nil]; } #pragma mark FHSSnapshotViewDelegate - (void)didDeselectView:(FHSViewSnapshot *)snapshot { // Our setter would also call the setter for the snapshot view, // which we don't need to do here since it is already selected _selectedView = nil; } - (void)didLongPressView:(FHSViewSnapshot *)snapshot { } - (void)didSelectView:(FHSViewSnapshot *)snapshot { // Our setter would also call the setter for the snapshot view, // which we don't need to do here since it is already selected _selectedView = snapshot.view.view; } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSViewSnapshot.h ================================================ // // FHSViewSnapshot.h // FLEX // // Created by Tanner Bennett on 1/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FHSView.h" NS_ASSUME_NONNULL_BEGIN @interface FHSViewSnapshot : NSObject + (instancetype)snapshotWithView:(FHSView *)view; @property (nonatomic, readonly) FHSView *view; @property (nonatomic, readonly) NSString *title; /// Whether or not this view item should be visually distinguished @property (nonatomic, readwrite) BOOL important; @property (nonatomic, readonly) CGRect frame; @property (nonatomic, readonly) BOOL hidden; @property (nonatomic, readonly) UIImage *snapshotImage; @property (nonatomic, readonly) NSArray *children; @property (nonatomic, readonly) NSString *summary; /// Returns a different color based on whether or not the view is important @property (nonatomic, readonly) UIColor *headerColor; - (FHSViewSnapshot *)snapshotForView:(UIView *)view; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/FHSViewSnapshot.m ================================================ // // FHSViewSnapshot.m // FLEX // // Created by Tanner Bennett on 1/9/20. // Copyright © 2020 FLEX Team. All rights reserved. // #import "FHSViewSnapshot.h" #import "NSArray+FLEX.h" @implementation FHSViewSnapshot + (instancetype)snapshotWithView:(FHSView *)view { NSArray *children = [view.children flex_mapped:^id(FHSView *v, NSUInteger idx) { return [self snapshotWithView:v]; }]; return [[self alloc] initWithView:view children:children]; } - (id)initWithView:(FHSView *)view children:(NSArray *)children { NSParameterAssert(view); NSParameterAssert(children); self = [super init]; if (self) { _view = view; _title = view.title; _important = view.important; _frame = view.frame; _hidden = view.hidden; _snapshotImage = view.snapshotImage; _children = children; _summary = view.summary; } return self; } - (UIColor *)headerColor { if (self.important) { return [UIColor colorWithRed: 0.000 green: 0.533 blue: 1.000 alpha: 0.900]; } else { return [UIColor colorWithRed:0.961 green: 0.651 blue: 0.137 alpha: 0.900]; } } - (FHSViewSnapshot *)snapshotForView:(UIView *)view { if (view == self.view.view) { return self; } for (FHSViewSnapshot *child in self.children) { FHSViewSnapshot *snapshot = [child snapshotForView:view]; if (snapshot) { return snapshot; } } return nil; } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/Scene/FHSSnapshotNodes.h ================================================ // // FHSSnapshotNodes.h // FLEX // // Created by Tanner Bennett on 1/7/20. // #import "FHSViewSnapshot.h" #import NS_ASSUME_NONNULL_BEGIN /// Container that holds references to the SceneKit nodes associated with a snapshot. @interface FHSSnapshotNodes : NSObject + (instancetype)snapshot:(FHSViewSnapshot *)snapshot depth:(NSInteger)depth; @property (nonatomic, readonly) FHSViewSnapshot *snapshotItem; @property (nonatomic, readonly) NSInteger depth; /// The view image itself @property (nonatomic, nullable) SCNNode *snapshot; /// Goes on top of the snapshot, has rounded top corners @property (nonatomic, nullable) SCNNode *header; /// The bounding box drawn around the snapshot @property (nonatomic, nullable) SCNNode *border; /// Used to indicate when a view is selected @property (nonatomic, getter=isHighlighted) BOOL highlighted; /// Used to indicate when a view is de-emphasized @property (nonatomic, getter=isDimmed) BOOL dimmed; @property (nonatomic) BOOL forceHideHeader; @end NS_ASSUME_NONNULL_END ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/Scene/FHSSnapshotNodes.m ================================================ // // FHSSnapshotNodes.m // FLEX // // Created by Tanner Bennett on 1/7/20. // #import "FHSSnapshotNodes.h" #import "SceneKit+Snapshot.h" @interface FHSSnapshotNodes () @property (nonatomic, nullable) SCNNode *highlight; @property (nonatomic, nullable) SCNNode *dimming; @end @implementation FHSSnapshotNodes + (instancetype)snapshot:(FHSViewSnapshot *)snapshot depth:(NSInteger)depth { FHSSnapshotNodes *nodes = [self new]; nodes->_snapshotItem = snapshot; nodes->_depth = depth; return nodes; } - (void)setHighlighted:(BOOL)highlighted { if (_highlighted != highlighted) { _highlighted = highlighted; if (highlighted) { if (!self.highlight) { // Create highlight node self.highlight = [SCNNode highlight:self.snapshotItem color:[UIColor.blueColor colorWithAlphaComponent:0.5] ]; } // Add add highlight node, remove dimming node if dimmed [self.snapshot addChildNode:self.highlight]; if (self.isDimmed) { [self.dimming removeFromParentNode]; } } else { // Remove highlight node, add back dimming node if dimmed [self.highlight removeFromParentNode]; if (self.isDimmed) { [self.snapshot addChildNode:self.dimming]; } } } } - (void)setDimmed:(BOOL)dimmed { if (_dimmed != dimmed) { _dimmed = dimmed; if (dimmed) { if (!self.dimming) { // Create dimming node self.dimming = [SCNNode highlight:self.snapshotItem color:[UIColor.blackColor colorWithAlphaComponent:0.5] ]; } // Add add dimming node if not highlighted if (!self.isHighlighted) { [self.snapshot addChildNode:self.dimming]; } } else { // Remove dimming node (if not already highlighted) if (!self.isHighlighted) { [self.dimming removeFromParentNode]; } } } } - (void)setForceHideHeader:(BOOL)forceHideHeader { if (_forceHideHeader != forceHideHeader) { _forceHideHeader = forceHideHeader; if (self.header.parentNode) { self.header.hidden = YES; [self.header removeFromParentNode]; } else { self.header.hidden = NO; [self.snapshot addChildNode:self.header]; } } } @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/Scene/SceneKit+Snapshot.h ================================================ // // SceneKit+Snapshot.h // FLEX // // Created by Tanner Bennett on 1/8/20. // #import #import "FHSViewSnapshot.h" @class FHSSnapshotNodes; extern CGFloat const kFHSSmallZOffset; #pragma mark SCNNode @interface SCNNode (Snapshot) /// @return the nearest ancestor snapshot node starting at this node @property (nonatomic, readonly) SCNNode *nearestAncestorSnapshot; /// @return a node that renders a highlight overlay over a specified snapshot + (instancetype)highlight:(FHSViewSnapshot *)view color:(UIColor *)color; /// @return a node that renders a snapshot image + (instancetype)snapshot:(FHSViewSnapshot *)view; /// @return a node that draws a line between two vertices + (instancetype)lineFrom:(SCNVector3)v1 to:(SCNVector3)v2 color:(UIColor *)lineColor; /// @return a node that can be used to render a colored border around the specified node - (instancetype)borderWithColor:(UIColor *)color; /// @return a node that renders a header above a snapshot node /// using the title text from the view, if specified + (instancetype)header:(FHSViewSnapshot *)view; /// @return a SceneKit node that recursively renders a hierarchy /// of UI elements starting at the specified snapshot + (instancetype)snapshot:(FHSViewSnapshot *)view parent:(FHSViewSnapshot *)parentView parentNode:(SCNNode *)parentNode root:(SCNNode *)rootNode depth:(NSInteger *)depthOut nodesMap:(NSMutableDictionary *)nodesMap hideHeaders:(BOOL)hideHeaders; @end #pragma mark SCNShape @interface SCNShape (Snapshot) /// @return a shape with the given path, 0 extrusion depth, and a double-sided /// material with the given diffuse contents inserted at index 0 + (instancetype)shapeWithPath:(UIBezierPath *)path materialDiffuse:(id)contents; /// @return a shape that is used to render the background of the snapshot header + (instancetype)nameHeader:(UIColor *)color frame:(CGRect)frame corners:(CGFloat)cornerRadius; @end #pragma mark SCNText @interface SCNText (Snapshot) /// @return text geometry used to render text inside the snapshot header + (instancetype)labelGeometry:(NSString *)text font:(UIFont *)font; @end ================================================ FILE: Classes/ViewHierarchy/SnapshotExplorer/Scene/SceneKit+Snapshot.m ================================================ // // SceneKit+Snapshot.m // FLEX // // Created by Tanner Bennett on 1/8/20. // #import "SceneKit+Snapshot.h" #import "FHSSnapshotNodes.h" /// This value is chosen such that this offset can be applied to avoid /// z-fighting amongst nodes at the same z-position, but small enough /// that they appear to visually be on the same plane. CGFloat const kFHSSmallZOffset = 0.05; CGFloat const kHeaderVerticalInset = 8.0; #pragma mark SCNGeometry @interface SCNGeometry (SnapshotPrivate) @end @implementation SCNGeometry (SnapshotPrivate) - (void)addDoubleSidedMaterialWithDiffuseContents:(id)contents { SCNMaterial *material = [SCNMaterial new]; material.doubleSided = YES; material.diffuse.contents = contents; [self insertMaterial:material atIndex:0]; } @end #pragma mark SCNNode @implementation SCNNode (Snapshot) - (SCNNode *)nearestAncestorSnapshot { SCNNode *node = self; while (!node.name && node) { node = node.parentNode; } return node; } + (instancetype)shapeNodeWithSize:(CGSize)size materialDiffuse:(id)contents offsetZ:(BOOL)offsetZ { UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake( 0, 0, size.width, size.height )]; SCNShape *shape = [SCNShape shapeWithPath:path materialDiffuse:contents]; SCNNode *node = [SCNNode nodeWithGeometry:shape]; if (offsetZ) { node.position = SCNVector3Make(0, 0, kFHSSmallZOffset); } return node; } + (instancetype)highlight:(FHSViewSnapshot *)view color:(UIColor *)color { return [self shapeNodeWithSize:view.frame.size materialDiffuse:color offsetZ:YES]; } + (instancetype)snapshot:(FHSViewSnapshot *)view { id image = view.snapshotImage; return [self shapeNodeWithSize:view.frame.size materialDiffuse:image offsetZ:NO]; } + (instancetype)lineFrom:(SCNVector3)v1 to:(SCNVector3)v2 color:(UIColor *)lineColor { SCNVector3 vertices[2] = { v1, v2 }; int32_t _indices[2] = { 0, 1 }; NSData *indices = [NSData dataWithBytes:_indices length:sizeof(_indices)]; SCNGeometrySource *source = [SCNGeometrySource geometrySourceWithVertices:vertices count:2]; SCNGeometryElement *element = [SCNGeometryElement geometryElementWithData:indices primitiveType:SCNGeometryPrimitiveTypeLine primitiveCount:2 bytesPerIndex:sizeof(int32_t) ]; SCNGeometry *geometry = [SCNGeometry geometryWithSources:@[source] elements:@[element]]; [geometry addDoubleSidedMaterialWithDiffuseContents:lineColor]; return [SCNNode nodeWithGeometry:geometry]; } - (instancetype)borderWithColor:(UIColor *)color { struct { SCNVector3 min, max; } bb; [self getBoundingBoxMin:&bb.min max:&bb.max]; SCNVector3 topLeft = SCNVector3Make(bb.min.x, bb.max.y, kFHSSmallZOffset); SCNVector3 bottomLeft = SCNVector3Make(bb.min.x, bb.min.y, kFHSSmallZOffset); SCNVector3 topRight = SCNVector3Make(bb.max.x, bb.max.y, kFHSSmallZOffset); SCNVector3 bottomRight = SCNVector3Make(bb.max.x, bb.min.y, kFHSSmallZOffset); SCNNode *top = [SCNNode lineFrom:topLeft to:topRight color:color]; SCNNode *left = [SCNNode lineFrom:bottomLeft to:topLeft color:color]; SCNNode *bottom = [SCNNode lineFrom:bottomLeft to:bottomRight color:color]; SCNNode *right = [SCNNode lineFrom:bottomRight to:topRight color:color]; SCNNode *border = [SCNNode new]; [border addChildNode:top]; [border addChildNode:left]; [border addChildNode:bottom]; [border addChildNode:right]; return border; } + (instancetype)header:(FHSViewSnapshot *)view { SCNText *text = [SCNText labelGeometry:view.title font:[UIFont boldSystemFontOfSize:13.0]]; SCNNode *textNode = [SCNNode nodeWithGeometry:text]; struct { SCNVector3 min, max; } bb; [textNode getBoundingBoxMin:&bb.min max:&bb.max]; CGFloat textWidth = bb.max.x - bb.min.x; CGFloat textHeight = bb.max.y - bb.min.y; CGFloat snapshotWidth = view.frame.size.width; CGFloat headerWidth = MAX(snapshotWidth, textWidth); CGRect frame = CGRectMake(0, 0, headerWidth, textHeight + (kHeaderVerticalInset * 2)); SCNNode *headerNode = [SCNNode nodeWithGeometry:[SCNShape nameHeader:view.headerColor frame:frame corners:8 ]]; [headerNode addChildNode:textNode]; textNode.position = SCNVector3Make( (frame.size.width / 2.f) - (textWidth / 2.f), (frame.size.height / 2.f) - (textHeight / 2.f), kFHSSmallZOffset ); headerNode.position = SCNVector3Make( (snapshotWidth / 2.f) - (headerWidth / 2.f), view.frame.size.height, kFHSSmallZOffset ); return headerNode; } + (instancetype)snapshot:(FHSViewSnapshot *)view parent:(FHSViewSnapshot *)parent parentNode:(SCNNode *)parentNode root:(SCNNode *)rootNode depth:(NSInteger *)depthOut nodesMap:(NSMutableDictionary *)nodesMap hideHeaders:(BOOL)hideHeaders { NSInteger const depth = *depthOut; // Ignore elements that are not visible. // These should appear in the list, but not in the 3D view. if (view.hidden || CGSizeEqualToSize(view.frame.size, CGSizeZero)) { return nil; } // Create a node whose contents are the snapshot of the element SCNNode *node = [self snapshot:view]; node.name = view.view.identifier; // Begin building node tree FHSSnapshotNodes *nodes = [FHSSnapshotNodes snapshot:view depth:depth]; nodes.snapshot = node; // The node must be added to the root node // for the coordinate space calculations below to work [rootNode addChildNode:node]; node.position = ({ // Flip the y-coordinate since SceneKit has a // flipped version of the UIKit coordinate system CGRect pframe = parent ? parent.frame : CGRectZero; CGFloat y = parent ? pframe.size.height - CGRectGetMaxY(view.frame) : 0; // To simplify calculating the z-axis spacing between the layers, we make // each snapshot node a direct child of the root rather than embedding // the nodes in their parent nodes in the same structure as the UI elements // themselves. With this flattened hierarchy, the z-position can be // calculated for every node simply by multiplying the spacing by the depth. // // `parentSnapshotNode` as referenced here is NOT the actual parent node // of `node`, it is the node corresponding to the parent of the UI element. // It is used to convert from frame coordinates, which are relative to // the bounds of the parent, to coordinates relative to the root node. SCNVector3 positionRelativeToParent = SCNVector3Make(view.frame.origin.x, y, 0); SCNVector3 positionRelativeToRoot; if (parent) { positionRelativeToRoot = [rootNode convertPosition:positionRelativeToParent fromNode:parentNode]; } else { positionRelativeToRoot = positionRelativeToParent; } positionRelativeToRoot.z = 50 * depth; positionRelativeToRoot; }); // Make border node nodes.border = [node borderWithColor:view.headerColor]; [node addChildNode:nodes.border]; // Make header node nodes.header = [SCNNode header:view]; [node addChildNode:nodes.header]; if (hideHeaders) { nodes.header.hidden = YES; } nodesMap[view.view.identifier] = nodes; NSMutableArray *checkForIntersect = [NSMutableArray new]; NSInteger maxChildDepth = depth; // Recurse to child nodes; overlapping children have higher depths for (FHSViewSnapshot *child in view.children) { NSInteger childDepth = depth + 1; // Children that intersect a sibling are rendered // in a separate layer above the previous siblings for (FHSViewSnapshot *sibling in checkForIntersect) { if (CGRectIntersectsRect(sibling.frame, child.frame)) { childDepth = maxChildDepth + 1; break; } } id didMakeNode = [SCNNode snapshot:child parent:view parentNode:node root:rootNode depth:&childDepth nodesMap:nodesMap hideHeaders:hideHeaders ]; if (didMakeNode) { maxChildDepth = MAX(childDepth, maxChildDepth); [checkForIntersect addObject:child]; } } *depthOut = maxChildDepth; return node; } @end #pragma mark SCNShape @implementation SCNShape (Snapshot) + (instancetype)shapeWithPath:(UIBezierPath *)path materialDiffuse:(id)contents { SCNShape *shape = [SCNShape shapeWithPath:path extrusionDepth:0]; [shape addDoubleSidedMaterialWithDiffuseContents:contents]; return shape; } + (instancetype)nameHeader:(UIColor *)color frame:(CGRect)frame corners:(CGFloat)radius { UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(radius, radius) ]; return [SCNShape shapeWithPath:path materialDiffuse:color]; } @end #pragma mark SCNText @implementation SCNText (Snapshot) + (instancetype)labelGeometry:(NSString *)text font:(UIFont *)font { NSParameterAssert(text); SCNText *label = [self new]; label.string = text; label.font = font; label.alignmentMode = kCAAlignmentCenter; label.truncationMode = kCATruncationEnd; return label; } @end ================================================ FILE: Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewCell.h ================================================ // // FLEXHierarchyTableViewCell.h // Flipboard // // Created by Ryan Olson on 2014-05-02. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import @interface FLEXHierarchyTableViewCell : UITableViewCell - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier; @property (nonatomic) NSInteger viewDepth; @property (nonatomic) UIColor *randomColorTag; @property (nonatomic) UIColor *indicatedViewColor; @end ================================================ FILE: Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewCell.m ================================================ // // FLEXHierarchyTableViewCell.m // Flipboard // // Created by Ryan Olson on 2014-05-02. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXHierarchyTableViewCell.h" #import "FLEXUtility.h" #import "FLEXResources.h" #import "FLEXColor.h" @interface FLEXHierarchyTableViewCell () /// Indicates how deep the view is in the hierarchy @property (nonatomic) UIView *depthIndicatorView; /// Holds the color that visually distinguishes views from one another @property (nonatomic) UIImageView *colorCircleImageView; /// A checker-patterned view, used to help show the color of a view, like a photoshop canvas @property (nonatomic) UIView *backgroundColorCheckerPatternView; /// The subview of the checker pattern view which holds the actual color of the view @property (nonatomic) UIView *viewBackgroundColorView; @end @implementation FLEXHierarchyTableViewCell - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier { return [self initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; } - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { self.depthIndicatorView = [UIView new]; self.depthIndicatorView.backgroundColor = FLEXUtility.hierarchyIndentPatternColor; [self.contentView addSubview:self.depthIndicatorView]; UIImage *defaultCircleImage = [FLEXUtility circularImageWithColor:UIColor.blackColor radius:5]; self.colorCircleImageView = [[UIImageView alloc] initWithImage:defaultCircleImage]; [self.contentView addSubview:self.colorCircleImageView]; self.textLabel.font = UIFont.flex_defaultTableCellFont; self.detailTextLabel.font = UIFont.flex_defaultTableCellFont; self.accessoryType = UITableViewCellAccessoryDetailButton; // Use a pattern-based color to simplify application of the checker pattern static UIColor *checkerPatternColor = nil; static dispatch_once_t once; dispatch_once(&once, ^{ checkerPatternColor = [UIColor colorWithPatternImage:FLEXResources.checkerPattern]; }); self.backgroundColorCheckerPatternView = [UIView new]; self.backgroundColorCheckerPatternView.clipsToBounds = YES; self.backgroundColorCheckerPatternView.layer.borderColor = FLEXColor.tertiaryBackgroundColor.CGColor; self.backgroundColorCheckerPatternView.layer.borderWidth = 2.f / UIScreen.mainScreen.scale; self.backgroundColorCheckerPatternView.backgroundColor = checkerPatternColor; [self.contentView addSubview:self.backgroundColorCheckerPatternView]; self.viewBackgroundColorView = [UIView new]; [self.backgroundColorCheckerPatternView addSubview:self.viewBackgroundColorView]; } return self; } - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { UIColor *originalColour = self.viewBackgroundColorView.backgroundColor; [super setHighlighted:highlighted animated:animated]; // UITableViewCell changes all subviews in the contentView to backgroundColor = clearColor. // We want to preserve the hierarchy background color when highlighted. self.depthIndicatorView.backgroundColor = FLEXUtility.hierarchyIndentPatternColor; self.viewBackgroundColorView.backgroundColor = originalColour; } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { UIColor *originalColour = self.viewBackgroundColorView.backgroundColor; [super setSelected:selected animated:animated]; // See setHighlighted above. self.depthIndicatorView.backgroundColor = FLEXUtility.hierarchyIndentPatternColor; self.viewBackgroundColorView.backgroundColor = originalColour; } - (void)layoutSubviews { [super layoutSubviews]; const CGFloat kContentPadding = 6; const CGFloat kDepthIndicatorWidthMultiplier = 4; const CGFloat kViewColorIndicatorSize = 22; const CGRect bounds = self.contentView.bounds; const CGFloat centerY = CGRectGetMidY(bounds); const CGFloat textLabelCenterY = CGRectGetMidY(self.textLabel.frame); BOOL hideCheckerView = self.backgroundColorCheckerPatternView.hidden; CGFloat maxWidth = CGRectGetMaxX(bounds); maxWidth -= (hideCheckerView ? kContentPadding : (kViewColorIndicatorSize + kContentPadding * 2)); CGRect depthIndicatorFrame = self.depthIndicatorView.frame = CGRectMake( kContentPadding, 0, self.viewDepth * kDepthIndicatorWidthMultiplier, CGRectGetHeight(bounds) ); // Circle goes after depth, and its center Y = textLabel's center Y CGRect circleFrame = self.colorCircleImageView.frame; circleFrame.origin.x = CGRectGetMaxX(depthIndicatorFrame) + kContentPadding; circleFrame.origin.y = FLEXFloor(textLabelCenterY - CGRectGetHeight(circleFrame) / 2.f); self.colorCircleImageView.frame = circleFrame; // Text label goes after random color circle, width extends to the edge // of the contentView or to the padding before the color indicator view CGRect textLabelFrame = self.textLabel.frame; CGFloat textOriginX = CGRectGetMaxX(circleFrame) + kContentPadding; textLabelFrame.origin.x = textOriginX; textLabelFrame.size.width = maxWidth - textOriginX; self.textLabel.frame = textLabelFrame; // detailTextLabel leading edge lines up with the circle, and the // width extends to the same max X as the same max X as the textLabel CGRect detailTextLabelFrame = self.detailTextLabel.frame; CGFloat detailOriginX = circleFrame.origin.x; detailTextLabelFrame.origin.x = detailOriginX; detailTextLabelFrame.size.width = maxWidth - detailOriginX; self.detailTextLabel.frame = detailTextLabelFrame; // Checker pattern view starts after the padding after the max X of textLabel, // and is centered vertically within the entire contentView self.backgroundColorCheckerPatternView.frame = CGRectMake( CGRectGetMaxX(self.textLabel.frame) + kContentPadding, centerY - kViewColorIndicatorSize / 2.f, kViewColorIndicatorSize, kViewColorIndicatorSize ); // Background color view fills it's superview self.viewBackgroundColorView.frame = self.backgroundColorCheckerPatternView.bounds; self.backgroundColorCheckerPatternView.layer.cornerRadius = kViewColorIndicatorSize / 2.f; } - (void)setRandomColorTag:(UIColor *)randomColorTag { if (![_randomColorTag isEqual:randomColorTag]) { _randomColorTag = randomColorTag; self.colorCircleImageView.image = [FLEXUtility circularImageWithColor:randomColorTag radius:6]; } } - (void)setViewDepth:(NSInteger)viewDepth { if (_viewDepth != viewDepth) { _viewDepth = viewDepth; [self setNeedsLayout]; } } - (UIColor *)indicatedViewColor { return self.viewBackgroundColorView.backgroundColor; } - (void)setIndicatedViewColor:(UIColor *)color { self.viewBackgroundColorView.backgroundColor = color; // Hide the checker pattern view if there is no background color self.backgroundColorCheckerPatternView.hidden = color == nil; [self setNeedsLayout]; } @end ================================================ FILE: Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewController.h ================================================ // // FLEXHierarchyTableViewController.h // Flipboard // // Created by Ryan Olson on 2014-05-01. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXTableViewController.h" @interface FLEXHierarchyTableViewController : FLEXTableViewController + (instancetype)windows:(NSArray *)allWindows viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)selectedView; @property (nonatomic) UIView *selectedView; @property (nonatomic) void(^didSelectRowAction)(UIView *selectedView); @end ================================================ FILE: Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewController.m ================================================ // // FLEXHierarchyTableViewController.m // Flipboard // // Created by Ryan Olson on 2014-05-01. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXColor.h" #import "FLEXHierarchyTableViewController.h" #import "NSMapTable+FLEX_Subscripting.h" #import "FLEXUtility.h" #import "FLEXHierarchyTableViewCell.h" #import "FLEXObjectExplorerViewController.h" #import "FLEXObjectExplorerFactory.h" #import "FLEXResources.h" #import "FLEXWindow.h" typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) { FLEXHierarchyScopeFullHierarchy, FLEXHierarchyScopeViewsAtTap }; @interface FLEXHierarchyTableViewController () @property (nonatomic) NSArray *allViews; /// Map of view's address to its depth /// @((uintptr_t)(__bridge void *)view) -> depth @property (nonatomic) NSMapTable *depthsForViews; @property (nonatomic) NSArray *viewsAtTap; @property (nonatomic) NSArray *displayedViews; @property (nonatomic, readonly) BOOL showScopeBar; @end @implementation FLEXHierarchyTableViewController + (instancetype)windows:(NSArray *)allWindows viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)selected { NSParameterAssert(allWindows.count); NSArray *allViews = [self allViewsInHierarchy:allWindows]; NSMapTable *depths = [self hierarchyDepthsForViews:allViews]; return [[self alloc] initWithViews:allViews viewsAtTap:viewsAtTap selectedView:selected depths:depths]; } - (instancetype)initWithViews:(NSArray *)allViews viewsAtTap:(NSArray *)viewsAtTap selectedView:(UIView *)selectedView depths:(NSMapTable *)depthsForViews { NSParameterAssert(allViews); NSParameterAssert(depthsForViews.count == allViews.count); self = [super initWithStyle:UITableViewStylePlain]; if (self) { self.allViews = allViews; self.depthsForViews = depthsForViews; self.viewsAtTap = viewsAtTap; self.selectedView = selectedView; self.title = @"View Hierarchy Tree"; } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Preserve selection between presentations self.clearsSelectionOnViewWillAppear = NO; // A little more breathing room self.tableView.rowHeight = 50.0; self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // Separator inset clashes with persistent cell selection [self.tableView setSeparatorInset:UIEdgeInsetsZero]; self.showsSearchBar = YES; self.showSearchBarInitially = YES; // Using pinSearchBar on this screen causes a weird visual // thing on the next view controller that gets pushed. // // self.pinSearchBar = YES; self.searchBarDebounceInterval = kFLEXDebounceInstant; self.automaticallyShowsSearchBarCancelButton = NO; if (self.showScopeBar) { self.searchController.searchBar.showsScopeBar = YES; self.searchController.searchBar.scopeButtonTitles = @[@"Full Hierarchy", @"Views at Tap"]; self.selectedScope = FLEXHierarchyScopeViewsAtTap; } [self updateDisplayedViews]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self disableToolbar]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self trySelectCellForSelectedView]; } #pragma mark - Hierarchy helpers + (NSArray *)allViewsInHierarchy:(NSArray *)windows { return [windows flex_flatmapped:^id(UIWindow *window, NSUInteger idx) { if (![window isKindOfClass:[FLEXWindow class]]) { return [self viewWithRecursiveSubviews:window]; } return nil; }]; } + (NSArray *)viewWithRecursiveSubviews:(UIView *)view { NSMutableArray *subviews = [NSMutableArray arrayWithObject:view]; for (UIView *subview in view.subviews) { [subviews addObjectsFromArray:[self viewWithRecursiveSubviews:subview]]; } return subviews; } + (NSMapTable *)hierarchyDepthsForViews:(NSArray *)views { NSMapTable *depths = [NSMapTable strongToStrongObjectsMapTable]; for (UIView *view in views) { NSInteger depth = 0; UIView *tryView = view; while (tryView.superview) { tryView = tryView.superview; depth++; } depths[@((uintptr_t)(__bridge void *)view)] = @(depth); } return depths; } #pragma mark Selection and Filtering Helpers - (void)trySelectCellForSelectedView { NSUInteger selectedViewIndex = [self.displayedViews indexOfObject:self.selectedView]; if (selectedViewIndex != NSNotFound) { UITableViewScrollPosition scrollPosition = UITableViewScrollPositionMiddle; NSIndexPath *selectedViewIndexPath = [NSIndexPath indexPathForRow:selectedViewIndex inSection:0]; [self.tableView selectRowAtIndexPath:selectedViewIndexPath animated:YES scrollPosition:scrollPosition]; } } - (void)updateDisplayedViews { NSArray *candidateViews = nil; if (self.showScopeBar) { if (self.selectedScope == FLEXHierarchyScopeViewsAtTap) { candidateViews = self.viewsAtTap; } else if (self.selectedScope == FLEXHierarchyScopeFullHierarchy) { candidateViews = self.allViews; } } else { candidateViews = self.allViews; } if (self.searchText.length) { self.displayedViews = [candidateViews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *candidateView, NSDictionary *bindings) { NSString *title = [FLEXUtility descriptionForView:candidateView includingFrame:NO]; NSString *candidateViewPointerAddress = [NSString stringWithFormat:@"%p", candidateView]; BOOL matchedViewPointerAddress = [candidateViewPointerAddress rangeOfString:self.searchText options:NSCaseInsensitiveSearch].location != NSNotFound; BOOL matchedViewTitle = [title rangeOfString:self.searchText options:NSCaseInsensitiveSearch].location != NSNotFound; return matchedViewPointerAddress || matchedViewTitle; }]]; } else { self.displayedViews = candidateViews; } [self.tableView reloadData]; } - (void)setSelectedView:(UIView *)selectedView { _selectedView = selectedView; if (self.isViewLoaded) { [self trySelectCellForSelectedView]; } } #pragma mark - Search Bar / Scope Bar - (BOOL)showScopeBar { return self.viewsAtTap.count > 0; } - (void)updateSearchResults:(NSString *)newText { [self updateDisplayedViews]; // If the search bar text field is active, don't scroll on selection because we may want // to continue typing. Otherwise, scroll so that the selected cell is visible. if (!self.searchController.searchBar.isFirstResponder) { [self trySelectCellForSelectedView]; } } #pragma mark - Table View Data Source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.displayedViews.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; FLEXHierarchyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[FLEXHierarchyTableViewCell alloc] initWithReuseIdentifier:CellIdentifier]; } UIView *view = self.displayedViews[indexPath.row]; cell.textLabel.text = [FLEXUtility descriptionForView:view includingFrame:NO]; cell.detailTextLabel.text = [FLEXUtility detailDescriptionForView:view]; cell.randomColorTag = [FLEXUtility consistentRandomColorForObject:view]; cell.viewDepth = self.depthsForViews[@((uintptr_t)(__bridge void *)view)].integerValue; cell.indicatedViewColor = view.backgroundColor; if (view.isHidden || view.alpha < 0.01) { cell.textLabel.textColor = FLEXColor.deemphasizedTextColor; cell.detailTextLabel.textColor = FLEXColor.deemphasizedTextColor; } else { cell.textLabel.textColor = FLEXColor.primaryTextColor; cell.detailTextLabel.textColor = FLEXColor.primaryTextColor; } return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { _selectedView = self.displayedViews[indexPath.row]; // Don't scroll, avoid setter if (self.didSelectRowAction) { self.didSelectRowAction(_selectedView); } } - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { UIView *drillInView = self.displayedViews[indexPath.row]; FLEXObjectExplorerViewController *viewExplorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:drillInView]; [self.navigationController pushViewController:viewExplorer animated:YES]; } @end ================================================ FILE: Example/FLEXample/App/Commit.swift ================================================ // // Commit.swift // FLEXample // // Created by Tanner on 3/12/20. // Copyright © 2020 Flipboard. All rights reserved. // import Foundation infix operator ~ : ComparisonPrecedence func ~ (a: String, b: String) -> Bool { return a.localizedCaseInsensitiveContains(b) } func ~ (a: NSString, b: String) -> Bool { return a.localizedCaseInsensitiveContains(b) } /// Used for both commit details and the outer committer @objcMembers public class CommitIdentity: NSObject, Codable { // These actually come from the "root[committer]" part public let login: String? public let id: Int? public let avatarUrl: String? public let gravatarUrl: String? // These actually come from the // "root[commit][author/committer]" part public let name: String? public let email: String? public let date: Date? public func matches(query: String) -> Bool { if let login = self.login { return login ~ query } else if let name = self.name, let email = self.email { return name ~ query || email ~ query } return false } } //@objcMembers public struct CommitDetails: Codable { public let message: String public let url: String public let author: CommitIdentity public let committer: CommitIdentity public func matches(query: String) -> Bool { return message ~ query || author.matches(query: query) || committer.matches(query: query) } } @objcMembers public class Commit: NSObject, Codable { static var formatter: DateFormatter = { var f = DateFormatter() f.dateFormat = "dd MMM yyyy h:mm a" return f }() /// Turn some response data into a list of commits static func commits(from data: Data) -> [Commit] { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase decoder.dateDecodingStrategy = .iso8601 _ = try! decoder.decode([Commit].self, from: data) if let commits = try? decoder.decode([Commit].self, from: data) { return commits } return [] } enum CodingKeys: String, CodingKey { case sha, htmlUrl, committer case details = "commit" } public private(set) var sha: String = "" public private(set) var htmlUrl: String = "" /// Details does not contain avi URLs for users public private(set) var details: CommitDetails /// This does have the (g)avatar URL public private(set) var committer: CommitIdentity public func matches(query: String) -> Bool { return sha ~ query || details.matches(query: query) || committer.matches(query: query) } // You're crazy if you think I'm going to slice strings with Swift.String public lazy var shortHash: String = NSString(string: self.sha).substring(to: 8) public lazy var date: String = { if let date = details.committer.date ?? details.author.date { return Commit.formatter.string(from: date) } return "no date found" }() public lazy var firstLine: String = { let name = details.committer.name ?? details.author.name ?? "Anonymous" return name + " — " + self.date }() public lazy var secondLine: String = { return self.shortHash + " " + self.details.message }() public lazy var identifier: Int = self.sha.hashValue } ================================================ FILE: Example/FLEXample/App/CommitListViewController.h ================================================ // // CommitListViewController.h // FLEXample // // Created by Tanner on 3/11/20. // Copyright © 2020 Flipboard. All rights reserved. // #import "FLEXFilteringTableViewController.h" NS_ASSUME_NONNULL_BEGIN @interface CommitListViewController : FLEXFilteringTableViewController @end NS_ASSUME_NONNULL_END ================================================ FILE: Example/FLEXample/App/CommitListViewController.m ================================================ // // CommitListViewController.m // FLEXample // // Created by Tanner on 3/11/20. // Copyright © 2020 Flipboard. All rights reserved. // #import "CommitListViewController.h" #import "FLEXample-Swift.h" #import "Person.h" #import @interface CommitListViewController () @property (nonatomic) FLEXMutableListSection *commits; @property (nonatomic, readonly) NSMutableDictionary *avatars; @end @interface UIAlertController (Private) @property (nonatomic) UIViewController *contentViewController; @end @interface ContentViewController : UIViewController + (instancetype)customView:(UIView *)view; @end @implementation ContentViewController + (instancetype)customView:(UIView *)view { ContentViewController *vc = [self new]; vc.view = view; return vc; } @end @implementation CommitListViewController - (id)init { // Default style is grouped return [self initWithStyle:UITableViewStylePlain]; } - (void)showCustomView { UIAlertController *alert = [FLEXAlert makeAlert:^(FLEXAlert *make) { make.title(@"Custom View").button(@"Dismiss").cancelStyle(); }]; // I like to use this to easily display and interact with custom views I'm working on alert.contentViewController = [ContentViewController customView:[UIView new]]; [self presentViewController:alert animated:YES completion:nil]; } - (void)viewDidLoad { [super viewDidLoad]; _avatars = [NSMutableDictionary new]; self.title = @"FLEX Commit History"; self.showsSearchBar = YES; self.navigationItem.rightBarButtonItem = [UIBarButtonItem flex_itemWithTitle:@"FLEX" target:FLEXManager.sharedManager action:@selector(toggleExplorer) ]; self.navigationItem.rightBarButtonItem.accessibilityIdentifier = @"toggle-explorer"; self.navigationItem.leftBarButtonItem = [UIBarButtonItem flex_itemWithTitle:@"Test" target:self action:@selector(showCustomView) ]; // Load and process commits NSString *commitsURL = @"https://api.github.com/repos/Flipboard/FLEX/commits"; [self startDataTask:commitsURL completion:^(NSData *data, NSInteger statusCode, NSError *error) { if (statusCode == 200) { self.commits.list = [Commit commitsFrom:data]; [self fadeInNewRows]; } else { [FLEXAlert showAlert:@"Error" message:error.localizedDescription ?: @(statusCode).stringValue from:self ]; } }]; FLEXManager *flex = FLEXManager.sharedManager; // Register 't' for testing: quickly present an object explorer for debugging [flex registerSimulatorShortcutWithKey:@"t" modifiers:0 action:^{ [flex showExplorer]; [flex presentTool:^UINavigationController *{ return [FLEXNavigationController withRootViewController:[FLEXObjectExplorerFactory explorerViewControllerForObject:Person.bob ]]; } completion:nil]; } description:@"Present an object explorer for debugging"]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self disableToolbar]; } - (NSArray *)makeSections { _commits = [FLEXMutableListSection list:@[] cellConfiguration:^(__kindof UITableViewCell *cell, Commit *commit, NSInteger row) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = commit.firstLine; cell.detailTextLabel.text = commit.secondLine; cell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingTail; // cell.textLabel.numberOfLines = 2; // cell.detailTextLabel.numberOfLines = 3; UIImage *avi = self.avatars[commit.committer.login]; if (avi) { cell.imageView.image = avi; } else { cell.tag = commit.identifier; [self loadImage:commit.committer.avatarUrl completion:^(UIImage *image) { self.avatars[commit.committer.login] = image; if (cell.tag == commit.identifier) { cell.imageView.image = image; } else { [self.tableView reloadRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:row inSection:0] ] withRowAnimation:UITableViewRowAnimationAutomatic]; } }]; } } filterMatcher:^BOOL(NSString *filterText, Commit *commit) { return [commit matchesWithQuery:filterText]; } ]; self.commits.selectionHandler = ^(__kindof UIViewController *host, Commit *commit) { [host.navigationController pushViewController:[ FLEXObjectExplorerFactory explorerViewControllerForObject:commit ] animated:YES]; }; return @[self.commits]; } // Empty sections are removed by default and we always want this one section - (NSArray *)nonemptySections { return @[_commits]; } - (void)fadeInNewRows { NSIndexSet *sections = [NSIndexSet indexSetWithIndex:0]; [self.tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationAutomatic]; } - (void)loadImage:(NSString *)imageURL completion:(void(^)(UIImage *image))callback { [self startDataTask:imageURL completion:^(NSData *data, NSInteger statusCode, NSError *error) { if (statusCode == 200) { callback([UIImage imageWithData:data]); } }]; } - (void)startDataTask:(NSString *)urlString completion:(void (^)(NSData *data, NSInteger statusCode, NSError *error))completionHandler { // return; NSURL *url = [NSURL URLWithString:urlString]; [[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ NSInteger code = [(NSHTTPURLResponse *)response statusCode]; completionHandler(data, code, error); }); }] resume]; } @end ================================================ FILE: Example/FLEXample/App/Person.h ================================================ // // Person.h // UICatalog // // Created by Tanner on 4/17/19. // Copyright © 2019 . All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject + (instancetype)bob; @property (nonatomic, readonly) NSString *name; @property (nonatomic, readonly) NSInteger age; @property (nonatomic, readonly) CGFloat height; @property (nonatomic, readonly) NSNumber *numberOfKids; @property (nonatomic) NSDecimalNumber *netWorth; @end NS_ASSUME_NONNULL_END ================================================ FILE: Example/FLEXample/App/Person.m ================================================ // // Person.m // UICatalog // // Created by Tanner on 4/17/19. // Copyright © 2019 f. All rights reserved. // #import "Person.h" @implementation Person + (id)bob { Person *bob = [Person new]; bob->_name = @"Bob"; bob->_age = 50; bob->_height = 5.8; bob->_numberOfKids = @3; bob->_netWorth = [NSDecimalNumber decimalNumberWithString:@"12345.67"]; return bob; } - (void)encodeWithCoder:(nonnull NSCoder *)coder { [coder encodeObject:self.name forKey:@"name"]; [coder encodeInteger:self.age forKey:@"age"]; [coder encodeDouble:self.height forKey:@"height"]; [coder encodeObject:self.numberOfKids forKey:@"numberOfKids"]; [coder encodeObject:self.netWorth forKey:@"netWorth"]; } - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { self->_name = [coder decodeObjectForKey:@"name"]; self->_age = [coder decodeIntegerForKey:@"age"]; self->_height = [coder decodeDoubleForKey:@"height"]; self->_numberOfKids = [coder decodeObjectForKey:@"numberOfKids"]; self->_netWorth = [coder decodeObjectForKey:@"netWorth"]; return self; } - (void)setNetWorth:(NSDecimalNumber *)netWorth { _netWorth = netWorth; } - (NSUInteger)hash { return self.name.hash ^ @(self.age).hash ^ self.numberOfKids.hash ^ self.netWorth.hash; } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[Person class]]) return [self isEqualToPerson:object]; return [super isEqual:object]; } - (BOOL)isEqualToPerson:(Person *)person { return [self.name isEqualToString:person.name]; } + (NSInteger)version { return 2; } @end ================================================ FILE: Example/FLEXample/AppDelegate.swift ================================================ // // AppDelegate.swift // FLEXample // // Created by Tanner on 3/11/20. // Copyright © 2020 Flipboard. All rights reserved. // import UIKit @UIApplicationMain @objcMembers class AppDelegate: UIResponder, UIApplicationDelegate { var repeatingLogExampleTimer: Timer! var window: UIWindow? @available(iOS 13.0, *) func application(_ application: UIApplication, configurationForConnecting session: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: nil, sessionRole: session.role) } func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FLEXManager.shared.isNetworkDebuggingEnabled = true // Add at least oen custom user defaults key to explore UserDefaults.standard.set("foo", forKey: "FLEXamplePrefFoo") // To show off the system log viewer, send 10 example log messages at 3 second intervals self.repeatingLogExampleTimer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [weak self] (_) in if let self = self { NSLog("Example log \(self.exampleLogSent)") self.exampleLogSent += 1 if self.exampleLogSent > self.exampleLogLimit { self.repeatingLogExampleTimer.invalidate() } } } // To show off the network logger, send several misc network requests MiscNetworkRequests.sendExampleRequests() // For testing unarchiving of objects self.archiveBob() // For < iOS 13, set up the window here self.setupWindow() return true } func setupWindow() { guard ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 13 else { return } let window = UIWindow(frame: UIScreen.main.bounds) window.rootViewController = FLEXNavigationController( rootViewController: CommitListViewController() ) self.window = window window.makeKeyAndVisible() FLEXManager.shared.showExplorer() } func archiveBob() { let documents = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true ).first! as NSString let whereToSaveBob = documents.appendingPathComponent("Bob.plist") try! NSKeyedArchiver.archivedData( withRootObject: Person.bob(), requiringSecureCoding: false ).write(to: URL(fileURLWithPath: whereToSaveBob), options: []) } let exampleLogLimit = 10 var exampleLogSent = 0 } ================================================ FILE: Example/FLEXample/MiscNetworkRequests.h ================================================ // // MiscNetworkRequests.h // FLEXample // // Created by Tanner on 3/12/20. // Copyright © 2020 Flipboard. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface MiscNetworkRequests : NSObject + (void)sendExampleRequests; @property (nonatomic, nullable) NSMutableArray *connections; @end NS_ASSUME_NONNULL_END ================================================ FILE: Example/FLEXample/MiscNetworkRequests.m ================================================ // // MiscNetworkRequests.m // FLEXample // // Created by Tanner on 3/12/20. // Copyright © 2020 Flipboard. All rights reserved. // #import "MiscNetworkRequests.h" @implementation MiscNetworkRequests + (void)sendExampleRequests { MiscNetworkRequests *misc = [self new]; NSURLSessionConfiguration *config = NSURLSessionConfiguration.defaultSessionConfiguration; config.timeoutIntervalForRequest = 10.0; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:misc delegateQueue:nil]; [misc sendExampleNetworkRequests:session]; [misc sendExampleWebsocketTraffic:session]; } - (NSMutableURLRequest *)request:(NSString *)url { return [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; } - (void)sendExampleNetworkRequests:(NSURLSession *)sessionWithDelegate { NSString *kFlipboardIcon = @"https://cdn.flipboard.com/serviceIcons/v2/social-icon-flipboard-96.png"; NSString *kRandomAnimal = @"https://lorempixel.com/248/250/animals/"; NSString *kSnowLeopard = @"https://lorempixel.com/248/250/animals/4/"; NSString *kRateLimit = @"https://api.github.com/rate_limit"; NSString *kImgurUpload = @"https://api.imgur.com/3/upload"; //####################### // # // NSURLSession # // # //####################### NSMutableArray *pendingTasks = [NSMutableArray array]; // With delegate // // NSURLSessionDataTask [pendingTasks addObject:[sessionWithDelegate dataTaskWithURL:[NSURL URLWithString:kFlipboardIcon]]]; // NSURLSessionDownloadTask [pendingTasks addObject:[sessionWithDelegate downloadTaskWithURL:[NSURL URLWithString:kRandomAnimal]]]; // Without delegate // // NSURLSessionDownloadTask [pendingTasks addObject:[NSURLSession.sharedSession downloadTaskWithURL:[NSURL URLWithString:kSnowLeopard] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { NSInteger status = [(NSHTTPURLResponse *)response statusCode]; if (status == 200) { NSLog(@"Image downloaded to %@", location.absoluteString); } else { NSLog(@"Image failed to download with status %@ (error: %@)", @(status), error.localizedDescription ); } } ]]; // NSURLSessionDataTask [pendingTasks addObject:[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:kRateLimit] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }]]; // NSURLSessionUploadTask NSMutableURLRequest *upload = [self request:kImgurUpload]; upload.HTTPMethod = @"POST"; [upload setValue:@"Client-ID 0e8a1cb2eb594ef" forHTTPHeaderField:@"Authorization"]; [pendingTasks addObject:[sessionWithDelegate uploadTaskWithRequest:upload fromFile:[NSBundle.mainBundle URLForResource:@"image" withExtension:@"jpg"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSInteger status = [(NSHTTPURLResponse *)response statusCode]; if (status == 200) { NSError *jsonError = nil; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; if (json) { NSLog(@"Image uploaded to %@", json[@"data"][@"link"]); } else { NSLog(@"Error decoding JSON after uploading image: %@", jsonError.localizedDescription); } } else if (data) { NSError *jsonError = nil; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; if (json) { NSLog(@"Image failed to upload with error: %@", json[@"data"][@"error"]); } else { NSLog(@"Error decoding JSON after failing to upload image: %@", jsonError.localizedDescription); } } else { NSLog(@"Error uploading image: %@", error.localizedDescription); } } ]]; NSTimeInterval delayTime = 5; const NSTimeInterval stagger = 1; // Stagger each task for (NSURLSessionTask *task in pendingTasks) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [task resume]; }); delayTime += stagger; } //######################## // # // NSURLConnection # // # //######################## #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // Remaining requests made through NSURLConnection with a delegate NSArray *requestURLStrings = @[ @"https://lorempixel.com/400/400/", @"https://google.com", @"https://api.github.com/users/Flipboard/repos", @"https://api.github.com/repos/Flipboard/FLEX/issues", @"https://cloud.githubusercontent.com/assets/516562/3971767/e4e21f58-27d6-11e4-9b07-4d1fe82b80ca.png", @"https://lorempixel.com/750/1334/" ]; // Async NSURLConnection [NSURLConnection sendAsynchronousRequest:[self request:@"https://api.github.com/repos/Flipboard/FLEX/issues"] queue:NSOperationQueue.mainQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { }]; // Begin staggering NSURLConnection requests self.connections = [NSMutableArray array]; for (NSString *urlString in requestURLStrings) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.connections addObject:[NSURLConnection connectionWithRequest:[self request:urlString] delegate:self]]; }); delayTime += stagger; } #pragma clang diagnostic pop } - (void)sendExampleWebsocketTraffic:(NSURLSession *)sessionWithDelegate { NSString *APIKey = @"VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV"; NSString *wsurl = [NSString stringWithFormat:@"wss://demo.piesocket.com/v3/channel_1?api_key=%@¬ify_self", APIKey]; NSURLSessionWebSocketTask *task = [sessionWithDelegate webSocketTaskWithURL:[NSURL URLWithString:wsurl]]; [task resume]; } - (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)task didOpenWithProtocol:(NSString *)protocol { [task receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage *message, NSError *error) { if (!error) { NSLog(@"Received WS message: %@", message.string); id message = [[NSURLSessionWebSocketMessage alloc] initWithString:@"Hello"]; [task sendMessage:message completionHandler:^(NSError *error) { if (error) { NSLog(@"Error sending WS message: %@", error.localizedDescription); } else { NSLog(@"WS message sent."); } }]; } }]; } - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { NSLog(@"URLSession didBecomeInvalidWithError: %@", error.localizedDescription); } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { completionHandler(NSURLSessionResponseAllow); } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"didFinishDownloadingToURL: %@", location.absoluteString); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [self.connections removeObject:connection]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { [self.connections removeObject:connection]; } @end ================================================ FILE: Example/FLEXample/SceneDelegate.swift ================================================ // // SceneDelegate.swift // FLEXample // // Created by Tanner on 3/11/20. // Copyright © 2020 Flipboard. All rights reserved. // import UIKit import SwiftUI @objcMembers @available(iOS 13.0, *) class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = FLEXNavigationController(rootViewController: CommitListViewController()) self.window = window window.makeKeyAndVisible() } } func sceneDidBecomeActive(_ scene: UIScene) { FLEXManager.shared.showExplorer() } } ================================================ FILE: Example/FLEXample/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/FLEXample/Supporting Files/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/FLEXample/Supporting Files/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Example/FLEXample/Supporting Files/FLEXample-Bridging-Header.h ================================================ // // Use this file to import your target's public headers that you would like to expose to Swift. // #import #import "MiscNetworkRequests.h" #import "CommitListViewController.h" #import "Person.h" ================================================ FILE: Example/FLEXample/Supporting Files/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads NSPhotoLibraryAddUsageDescription NSPhotoLibraryAddUsageDescription NSPhotoLibraryUsageDescription Save images UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UISceneConfigurations UIWindowSceneSessionRoleApplication UISceneConfigurationName Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Example/FLEXample/Supporting Files/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/FLEXample-Cocoapods.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ A9D70EC640E633FE829EF695 /* libPods-FLEXample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */; }; C376F92B28DBE503004E88BA /* dogs.realm in Resources */ = {isa = PBXBuildFile; fileRef = C386D706241AA66100699085 /* dogs.realm */; }; C386D6D02419975A00699085 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6CF2419975A00699085 /* AppDelegate.swift */; }; C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6D12419975A00699085 /* SceneDelegate.swift */; }; C386D6D62419975B00699085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D52419975B00699085 /* Assets.xcassets */; }; C386D6D92419975B00699085 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D82419975B00699085 /* Preview Assets.xcassets */; }; C386D6DC2419975B00699085 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C386D6DA2419975B00699085 /* LaunchScreen.storyboard */; }; C386D6E62419984700699085 /* CommitListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D6E52419984700699085 /* CommitListViewController.m */; }; C386D6FD241AA4B700699085 /* image.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = C386D6F8241AA4B700699085 /* image.jpeg */; }; C386D6FE241AA4B700699085 /* image in Resources */ = {isa = PBXBuildFile; fileRef = C386D6F9241AA4B700699085 /* image */; }; C386D6FF241AA4B700699085 /* image.png in Resources */ = {isa = PBXBuildFile; fileRef = C386D6FA241AA4B700699085 /* image.png */; }; C386D701241AA4B700699085 /* image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = C386D6FC241AA4B700699085 /* image.jpg */; }; C386D704241AA61600699085 /* music_library.db in Resources */ = {isa = PBXBuildFile; fileRef = C386D702241AA61600699085 /* music_library.db */; }; C386D705241AA61600699085 /* music_library_schema.jpg in Resources */ = {isa = PBXBuildFile; fileRef = C386D703241AA61600699085 /* music_library_schema.jpg */; }; C386D70B241AA67800699085 /* Dog.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D709241AA67800699085 /* Dog.m */; }; C386D70C241AA67800699085 /* Owner.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D70A241AA67800699085 /* Owner.m */; }; C3A67856241AB8AD005A4681 /* MiscNetworkRequests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */; }; C3A67858241ADDF7005A4681 /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A67857241ADDF7005A4681 /* Commit.swift */; }; C3B3760025B8CDA300AD43AB /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = C3B375FF25B8CDA300AD43AB /* Person.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 92E1E1EECA3F2533E1B8BF35 /* Pods-FLEXample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FLEXample.debug.xcconfig"; path = "Target Support Files/Pods-FLEXample/Pods-FLEXample.debug.xcconfig"; sourceTree = ""; }; BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FLEXample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BE1780244AEC542DDB894B08 /* Pods-FLEXample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FLEXample.release.xcconfig"; path = "Target Support Files/Pods-FLEXample/Pods-FLEXample.release.xcconfig"; sourceTree = ""; }; C386D6CC2419975A00699085 /* FLEXample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FLEXample.app; sourceTree = BUILT_PRODUCTS_DIR; }; C386D6CF2419975A00699085 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C386D6D12419975A00699085 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; C386D6D52419975B00699085 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C386D6D82419975B00699085 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; C386D6DB2419975B00699085 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C386D6DD2419975B00699085 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C386D6E32419984700699085 /* FLEXample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXample-Bridging-Header.h"; sourceTree = ""; }; C386D6E42419984700699085 /* CommitListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommitListViewController.h; sourceTree = ""; }; C386D6E52419984700699085 /* CommitListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CommitListViewController.m; sourceTree = ""; }; C386D6F8241AA4B700699085 /* image.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image.jpeg; sourceTree = ""; }; C386D6F9241AA4B700699085 /* image */ = {isa = PBXFileReference; lastKnownFileType = file; path = image; sourceTree = ""; }; C386D6FA241AA4B700699085 /* image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = image.png; sourceTree = ""; }; C386D6FC241AA4B700699085 /* image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image.jpg; sourceTree = ""; }; C386D702241AA61600699085 /* music_library.db */ = {isa = PBXFileReference; lastKnownFileType = file; path = music_library.db; sourceTree = ""; }; C386D703241AA61600699085 /* music_library_schema.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = music_library_schema.jpg; sourceTree = ""; }; C386D706241AA66100699085 /* dogs.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = dogs.realm; sourceTree = ""; }; C386D707241AA67800699085 /* Dog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dog.h; sourceTree = ""; }; C386D708241AA67800699085 /* Owner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Owner.h; sourceTree = ""; }; C386D709241AA67800699085 /* Dog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Dog.m; sourceTree = ""; }; C386D70A241AA67800699085 /* Owner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Owner.m; sourceTree = ""; }; C3A67854241AB8AD005A4681 /* MiscNetworkRequests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MiscNetworkRequests.h; sourceTree = ""; }; C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MiscNetworkRequests.m; sourceTree = ""; }; C3A67857241ADDF7005A4681 /* Commit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Commit.swift; sourceTree = ""; }; C3B375FE25B8CDA300AD43AB /* Person.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = ""; }; C3B375FF25B8CDA300AD43AB /* Person.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C386D6C92419975A00699085 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( A9D70EC640E633FE829EF695 /* libPods-FLEXample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C386D6C32419975A00699085 = { isa = PBXGroup; children = ( C386D6CE2419975A00699085 /* FLEXample */, C3A67853241AB88F005A4681 /* Realm */, C386D6CD2419975A00699085 /* Products */, DCEB5037DA3D5C01CB9CEE93 /* Frameworks */, EB0852B7D285AB31D7532049 /* Pods */, ); sourceTree = ""; }; C386D6CD2419975A00699085 /* Products */ = { isa = PBXGroup; children = ( C386D6CC2419975A00699085 /* FLEXample.app */, ); name = Products; sourceTree = ""; }; C386D6CE2419975A00699085 /* FLEXample */ = { isa = PBXGroup; children = ( C386D6E72419985E00699085 /* Supporting Files */, C386D6CF2419975A00699085 /* AppDelegate.swift */, C386D6D12419975A00699085 /* SceneDelegate.swift */, C3A67854241AB8AD005A4681 /* MiscNetworkRequests.h */, C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */, C3A67852241AB86D005A4681 /* App */, ); path = FLEXample; sourceTree = ""; }; C386D6D72419975B00699085 /* Preview Content */ = { isa = PBXGroup; children = ( C386D6D82419975B00699085 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; C386D6E72419985E00699085 /* Supporting Files */ = { isa = PBXGroup; children = ( C386D6D52419975B00699085 /* Assets.xcassets */, C386D6D72419975B00699085 /* Preview Content */, C386D6F7241AA4AA00699085 /* Bundle */, C386D6DA2419975B00699085 /* LaunchScreen.storyboard */, C386D6DD2419975B00699085 /* Info.plist */, C386D6E32419984700699085 /* FLEXample-Bridging-Header.h */, ); path = "Supporting Files"; sourceTree = ""; }; C386D6F7241AA4AA00699085 /* Bundle */ = { isa = PBXGroup; children = ( C386D702241AA61600699085 /* music_library.db */, C386D703241AA61600699085 /* music_library_schema.jpg */, C386D706241AA66100699085 /* dogs.realm */, C386D6F9241AA4B700699085 /* image */, C386D6F8241AA4B700699085 /* image.jpeg */, C386D6FC241AA4B700699085 /* image.jpg */, C386D6FA241AA4B700699085 /* image.png */, ); path = Bundle; sourceTree = ""; }; C3A67852241AB86D005A4681 /* App */ = { isa = PBXGroup; children = ( C3B375FE25B8CDA300AD43AB /* Person.h */, C3B375FF25B8CDA300AD43AB /* Person.m */, C3A67857241ADDF7005A4681 /* Commit.swift */, C386D6E42419984700699085 /* CommitListViewController.h */, C386D6E52419984700699085 /* CommitListViewController.m */, ); path = App; sourceTree = ""; }; C3A67853241AB88F005A4681 /* Realm */ = { isa = PBXGroup; children = ( C386D707241AA67800699085 /* Dog.h */, C386D709241AA67800699085 /* Dog.m */, C386D708241AA67800699085 /* Owner.h */, C386D70A241AA67800699085 /* Owner.m */, ); path = Realm; sourceTree = ""; }; DCEB5037DA3D5C01CB9CEE93 /* Frameworks */ = { isa = PBXGroup; children = ( BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */, ); name = Frameworks; sourceTree = ""; }; EB0852B7D285AB31D7532049 /* Pods */ = { isa = PBXGroup; children = ( 92E1E1EECA3F2533E1B8BF35 /* Pods-FLEXample.debug.xcconfig */, BE1780244AEC542DDB894B08 /* Pods-FLEXample.release.xcconfig */, ); path = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C386D6CB2419975A00699085 /* FLEXample */ = { isa = PBXNativeTarget; buildConfigurationList = C386D6E02419975B00699085 /* Build configuration list for PBXNativeTarget "FLEXample" */; buildPhases = ( FC7B42D14DC20FF97D095393 /* [CP] Check Pods Manifest.lock */, C386D6C82419975A00699085 /* Sources */, C386D6C92419975A00699085 /* Frameworks */, C386D6CA2419975A00699085 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = FLEXample; productName = FLEXample; productReference = C386D6CC2419975A00699085 /* FLEXample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C386D6C42419975A00699085 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1130; LastUpgradeCheck = 1200; ORGANIZATIONNAME = Flipboard; TargetAttributes = { C386D6CB2419975A00699085 = { CreatedOnToolsVersion = 11.3.1; LastSwiftMigration = 1130; }; }; }; buildConfigurationList = C386D6C72419975A00699085 /* Build configuration list for PBXProject "FLEXample-Cocoapods" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = C386D6C32419975A00699085; productRefGroup = C386D6CD2419975A00699085 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C386D6CB2419975A00699085 /* FLEXample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C386D6CA2419975A00699085 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( C376F92B28DBE503004E88BA /* dogs.realm in Resources */, C386D6DC2419975B00699085 /* LaunchScreen.storyboard in Resources */, C386D6FF241AA4B700699085 /* image.png in Resources */, C386D704241AA61600699085 /* music_library.db in Resources */, C386D6FE241AA4B700699085 /* image in Resources */, C386D6FD241AA4B700699085 /* image.jpeg in Resources */, C386D701241AA4B700699085 /* image.jpg in Resources */, C386D6D92419975B00699085 /* Preview Assets.xcassets in Resources */, C386D6D62419975B00699085 /* Assets.xcassets in Resources */, C386D705241AA61600699085 /* music_library_schema.jpg in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ FC7B42D14DC20FF97D095393 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-FLEXample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C386D6C82419975A00699085 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C3A67858241ADDF7005A4681 /* Commit.swift in Sources */, C386D6D02419975A00699085 /* AppDelegate.swift in Sources */, C3B3760025B8CDA300AD43AB /* Person.m in Sources */, C386D6E62419984700699085 /* CommitListViewController.m in Sources */, C386D70B241AA67800699085 /* Dog.m in Sources */, C3A67856241AB8AD005A4681 /* MiscNetworkRequests.m in Sources */, C386D70C241AA67800699085 /* Owner.m in Sources */, C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ C386D6DA2419975B00699085 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( C386D6DB2419975B00699085 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C386D6DE2419975B00699085 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; C386D6DF2419975B00699085 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; C386D6E12419975B00699085 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 92E1E1EECA3F2533E1B8BF35 /* Pods-FLEXample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"FLEXample/Supporting Files/Preview Content\""; DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "FLEXample/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.flex.FLEXample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "FLEXample/Supporting Files/FLEXample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; C386D6E22419975B00699085 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = BE1780244AEC542DDB894B08 /* Pods-FLEXample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"FLEXample/Supporting Files/Preview Content\""; DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "FLEXample/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.flex.FLEXample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "FLEXample/Supporting Files/FLEXample-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C386D6C72419975A00699085 /* Build configuration list for PBXProject "FLEXample-Cocoapods" */ = { isa = XCConfigurationList; buildConfigurations = ( C386D6DE2419975B00699085 /* Debug */, C386D6DF2419975B00699085 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C386D6E02419975B00699085 /* Build configuration list for PBXNativeTarget "FLEXample" */ = { isa = XCConfigurationList; buildConfigurations = ( C386D6E12419975B00699085 /* Debug */, C386D6E22419975B00699085 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = C386D6C42419975A00699085 /* Project object */; } ================================================ FILE: Example/FLEXample-Cocoapods.xcodeproj/xcshareddata/xcschemes/FLEXample.xcscheme ================================================ ================================================ FILE: Example/FLEXample-SPM.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 52; objects = { /* Begin PBXBuildFile section */ C35F0EF4292CC5CA00B728BC /* Reflex in Frameworks */ = {isa = PBXBuildFile; productRef = C35F0EF3292CC5CA00B728BC /* Reflex */; }; C386D6D02419975A00699085 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6CF2419975A00699085 /* AppDelegate.swift */; }; C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C386D6D12419975A00699085 /* SceneDelegate.swift */; }; C386D6D62419975B00699085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D52419975B00699085 /* Assets.xcassets */; }; C386D6D92419975B00699085 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C386D6D82419975B00699085 /* Preview Assets.xcassets */; }; C386D6DC2419975B00699085 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C386D6DA2419975B00699085 /* LaunchScreen.storyboard */; }; C386D6E62419984700699085 /* CommitListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D6E52419984700699085 /* CommitListViewController.m */; }; C386D6FD241AA4B700699085 /* image.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = C386D6F8241AA4B700699085 /* image.jpeg */; }; C386D6FE241AA4B700699085 /* image in Resources */ = {isa = PBXBuildFile; fileRef = C386D6F9241AA4B700699085 /* image */; }; C386D6FF241AA4B700699085 /* image.png in Resources */ = {isa = PBXBuildFile; fileRef = C386D6FA241AA4B700699085 /* image.png */; }; C386D701241AA4B700699085 /* image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = C386D6FC241AA4B700699085 /* image.jpg */; }; C386D704241AA61600699085 /* music_library.db in Resources */ = {isa = PBXBuildFile; fileRef = C386D702241AA61600699085 /* music_library.db */; }; C386D705241AA61600699085 /* music_library_schema.jpg in Resources */ = {isa = PBXBuildFile; fileRef = C386D703241AA61600699085 /* music_library_schema.jpg */; }; C386D70B241AA67800699085 /* Dog.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D709241AA67800699085 /* Dog.m */; }; C386D70C241AA67800699085 /* Owner.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D70A241AA67800699085 /* Owner.m */; }; C3A67856241AB8AD005A4681 /* MiscNetworkRequests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */; }; C3A67858241ADDF7005A4681 /* Commit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A67857241ADDF7005A4681 /* Commit.swift */; }; C3B3760025B8CDA300AD43AB /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = C3B375FF25B8CDA300AD43AB /* Person.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FLEXample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C386D6CC2419975A00699085 /* FLEXample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FLEXample.app; sourceTree = BUILT_PRODUCTS_DIR; }; C386D6CF2419975A00699085 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C386D6D12419975A00699085 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; C386D6D52419975B00699085 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C386D6D82419975B00699085 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; C386D6DB2419975B00699085 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C386D6DD2419975B00699085 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C386D6E32419984700699085 /* FLEXample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXample-Bridging-Header.h"; sourceTree = ""; }; C386D6E42419984700699085 /* CommitListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommitListViewController.h; sourceTree = ""; }; C386D6E52419984700699085 /* CommitListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CommitListViewController.m; sourceTree = ""; }; C386D6F8241AA4B700699085 /* image.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image.jpeg; sourceTree = ""; }; C386D6F9241AA4B700699085 /* image */ = {isa = PBXFileReference; lastKnownFileType = file; path = image; sourceTree = ""; }; C386D6FA241AA4B700699085 /* image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = image.png; sourceTree = ""; }; C386D6FC241AA4B700699085 /* image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image.jpg; sourceTree = ""; }; C386D702241AA61600699085 /* music_library.db */ = {isa = PBXFileReference; lastKnownFileType = file; path = music_library.db; sourceTree = ""; }; C386D703241AA61600699085 /* music_library_schema.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = music_library_schema.jpg; sourceTree = ""; }; C386D706241AA66100699085 /* dogs.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = dogs.realm; sourceTree = ""; }; C386D707241AA67800699085 /* Dog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dog.h; sourceTree = ""; }; C386D708241AA67800699085 /* Owner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Owner.h; sourceTree = ""; }; C386D709241AA67800699085 /* Dog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Dog.m; sourceTree = ""; }; C386D70A241AA67800699085 /* Owner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Owner.m; sourceTree = ""; }; C3A67854241AB8AD005A4681 /* MiscNetworkRequests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MiscNetworkRequests.h; sourceTree = ""; }; C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MiscNetworkRequests.m; sourceTree = ""; }; C3A67857241ADDF7005A4681 /* Commit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Commit.swift; sourceTree = ""; }; C3B375FE25B8CDA300AD43AB /* Person.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = ""; }; C3B375FF25B8CDA300AD43AB /* Person.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C386D6C92419975A00699085 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( C35F0EF4292CC5CA00B728BC /* Reflex in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C386D6C32419975A00699085 = { isa = PBXGroup; children = ( C386D6CE2419975A00699085 /* FLEXample */, C3A67853241AB88F005A4681 /* Realm */, C386D6CD2419975A00699085 /* Products */, DCEB5037DA3D5C01CB9CEE93 /* Frameworks */, ); sourceTree = ""; }; C386D6CD2419975A00699085 /* Products */ = { isa = PBXGroup; children = ( C386D6CC2419975A00699085 /* FLEXample.app */, ); name = Products; sourceTree = ""; }; C386D6CE2419975A00699085 /* FLEXample */ = { isa = PBXGroup; children = ( C386D6E72419985E00699085 /* Supporting Files */, C386D6CF2419975A00699085 /* AppDelegate.swift */, C386D6D12419975A00699085 /* SceneDelegate.swift */, C3A67854241AB8AD005A4681 /* MiscNetworkRequests.h */, C3A67855241AB8AD005A4681 /* MiscNetworkRequests.m */, C3A67852241AB86D005A4681 /* App */, ); path = FLEXample; sourceTree = ""; }; C386D6D72419975B00699085 /* Preview Content */ = { isa = PBXGroup; children = ( C386D6D82419975B00699085 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; C386D6E72419985E00699085 /* Supporting Files */ = { isa = PBXGroup; children = ( C386D6D52419975B00699085 /* Assets.xcassets */, C386D6D72419975B00699085 /* Preview Content */, C386D6F7241AA4AA00699085 /* Bundle */, C386D6DA2419975B00699085 /* LaunchScreen.storyboard */, C386D6DD2419975B00699085 /* Info.plist */, C386D6E32419984700699085 /* FLEXample-Bridging-Header.h */, ); path = "Supporting Files"; sourceTree = ""; }; C386D6F7241AA4AA00699085 /* Bundle */ = { isa = PBXGroup; children = ( C386D702241AA61600699085 /* music_library.db */, C386D703241AA61600699085 /* music_library_schema.jpg */, C386D706241AA66100699085 /* dogs.realm */, C386D6F9241AA4B700699085 /* image */, C386D6F8241AA4B700699085 /* image.jpeg */, C386D6FC241AA4B700699085 /* image.jpg */, C386D6FA241AA4B700699085 /* image.png */, ); path = Bundle; sourceTree = ""; }; C3A67852241AB86D005A4681 /* App */ = { isa = PBXGroup; children = ( C3B375FE25B8CDA300AD43AB /* Person.h */, C3B375FF25B8CDA300AD43AB /* Person.m */, C3A67857241ADDF7005A4681 /* Commit.swift */, C386D6E42419984700699085 /* CommitListViewController.h */, C386D6E52419984700699085 /* CommitListViewController.m */, ); path = App; sourceTree = ""; }; C3A67853241AB88F005A4681 /* Realm */ = { isa = PBXGroup; children = ( C386D707241AA67800699085 /* Dog.h */, C386D709241AA67800699085 /* Dog.m */, C386D708241AA67800699085 /* Owner.h */, C386D70A241AA67800699085 /* Owner.m */, ); path = Realm; sourceTree = ""; }; DCEB5037DA3D5C01CB9CEE93 /* Frameworks */ = { isa = PBXGroup; children = ( BBD699DDBAC5A16D8CFD39AC /* libPods-FLEXample.a */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C386D6CB2419975A00699085 /* FLEXample */ = { isa = PBXNativeTarget; buildConfigurationList = C386D6E02419975B00699085 /* Build configuration list for PBXNativeTarget "FLEXample" */; buildPhases = ( C386D6C82419975A00699085 /* Sources */, C386D6C92419975A00699085 /* Frameworks */, C386D6CA2419975A00699085 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = FLEXample; packageProductDependencies = ( C35F0EF3292CC5CA00B728BC /* Reflex */, ); productName = FLEXample; productReference = C386D6CC2419975A00699085 /* FLEXample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C386D6C42419975A00699085 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1130; LastUpgradeCheck = 1200; ORGANIZATIONNAME = Flipboard; TargetAttributes = { C386D6CB2419975A00699085 = { CreatedOnToolsVersion = 11.3.1; LastSwiftMigration = 1130; }; }; }; buildConfigurationList = C386D6C72419975A00699085 /* Build configuration list for PBXProject "FLEXample-SPM" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = C386D6C32419975A00699085; packageReferences = ( C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */, ); productRefGroup = C386D6CD2419975A00699085 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C386D6CB2419975A00699085 /* FLEXample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C386D6CA2419975A00699085 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( C386D6DC2419975B00699085 /* LaunchScreen.storyboard in Resources */, C386D6FF241AA4B700699085 /* image.png in Resources */, C386D704241AA61600699085 /* music_library.db in Resources */, C386D6FE241AA4B700699085 /* image in Resources */, C386D6FD241AA4B700699085 /* image.jpeg in Resources */, C386D701241AA4B700699085 /* image.jpg in Resources */, C386D6D92419975B00699085 /* Preview Assets.xcassets in Resources */, C386D6D62419975B00699085 /* Assets.xcassets in Resources */, C386D705241AA61600699085 /* music_library_schema.jpg in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C386D6C82419975A00699085 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C3A67858241ADDF7005A4681 /* Commit.swift in Sources */, C386D6D02419975A00699085 /* AppDelegate.swift in Sources */, C3B3760025B8CDA300AD43AB /* Person.m in Sources */, C386D6E62419984700699085 /* CommitListViewController.m in Sources */, C386D70B241AA67800699085 /* Dog.m in Sources */, C3A67856241AB8AD005A4681 /* MiscNetworkRequests.m in Sources */, C386D70C241AA67800699085 /* Owner.m in Sources */, C386D6D22419975A00699085 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ C386D6DA2419975B00699085 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( C386D6DB2419975B00699085 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C386D6DE2419975B00699085 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; C386D6DF2419975B00699085 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; C386D6E12419975B00699085 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"FLEXample/Supporting Files/Preview Content\""; DEVELOPMENT_TEAM = S6N2F22V2Z; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "FLEXample/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.flex.FLEXample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "FLEXample/Supporting Files/FLEXample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; C386D6E22419975B00699085 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"FLEXample/Supporting Files/Preview Content\""; DEVELOPMENT_TEAM = S6N2F22V2Z; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "FLEXample/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.flex.FLEXample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "FLEXample/Supporting Files/FLEXample-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C386D6C72419975A00699085 /* Build configuration list for PBXProject "FLEXample-SPM" */ = { isa = XCConfigurationList; buildConfigurations = ( C386D6DE2419975B00699085 /* Debug */, C386D6DF2419975B00699085 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C386D6E02419975B00699085 /* Build configuration list for PBXNativeTarget "FLEXample" */ = { isa = XCConfigurationList; buildConfigurations = ( C386D6E12419975B00699085 /* Debug */, C386D6E22419975B00699085 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/FLEXTool/Reflex"; requirement = { branch = master; kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ C35F0EF3292CC5CA00B728BC /* Reflex */ = { isa = XCSwiftPackageProductDependency; package = C35F0EF2292CC5CA00B728BC /* XCRemoteSwiftPackageReference "Reflex" */; productName = Reflex; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = C386D6C42419975A00699085 /* Project object */; } ================================================ FILE: Example/FLEXample-SPM.xcodeproj/xcshareddata/xcschemes/FLEXample.xcscheme ================================================ ================================================ FILE: Example/Podfile ================================================ platform :ios, '13.0' target 'FLEXample' do project 'FLEXample-Cocoapods' pod 'FLEX', :path => '../' end ================================================ FILE: Example/Realm/Dog.h ================================================ // // Dog.h // UICatalog // // Created by Tim Oliver on 17/02/2016. // Copyright © 2016 Realm. All rights reserved. // #if __has_include() #import #import "Owner.h" @interface Dog : RLMObject @property NSString *name; @property CGFloat height; @property NSDate *birthdate; @property BOOL vaccinated; @property Owner *owner; @end #endif ================================================ FILE: Example/Realm/Dog.m ================================================ // // Dog.m // UICatalog // // Created by Tim Oliver on 17/02/2016. // Copyright © 2016 Realm. All rights reserved. // #import "Dog.h" #if __has_include() @implementation Dog @end #endif ================================================ FILE: Example/Realm/Owner.h ================================================ // // Owner.h // UICatalog // // Created by Tim Oliver on 17/02/2016. // Copyright © 2016 Realm. All rights reserved. // #if __has_include() #import @interface Owner : RLMObject @property NSString *name; @end #endif ================================================ FILE: Example/Realm/Owner.m ================================================ // // Owner.m // UICatalog // // Created by Tim Oliver on 17/02/2016. // Copyright © 2016 Realm. All rights reserved. // #import "Owner.h" #if __has_include() @implementation Owner @end #endif ================================================ FILE: FLEX.podspec ================================================ Pod::Spec.new do |spec| spec.name = "FLEX" spec.version = "5.22.10" spec.summary = "A set of in-app debugging and exploration tools for iOS" spec.description = <<-DESC - Inspect and modify views in the hierarchy. - View Detailed network request history. - See the properties and ivars on any object. - Dynamically modify many properties and ivars. - Dynamically call instance and class methods. - Access any live object via a scan of the heap. - See system log messages (i.e. from `NSLog()`). - View the file system within your app's sandbox. - Explore all classes in your app and linked systems frameworks (public and private). - Quickly access useful objects such as `[UIApplication sharedApplication]`, the app delegate, the root view controller on the key window, and more. - Dynamically view and modify `NSUserDefaults` values. DESC spec.homepage = "https://github.com/FLEXTool/FLEX" spec.screenshots = [ "http://engineering.flipboard.com/assets/flex/basic-view-exploration.gif", "http://engineering.flipboard.com/assets/flex/advanced-view-editing.gif", "http://engineering.flipboard.com/assets/flex/heap-browser.gif", "http://engineering.flipboard.com/assets/flex/file-browser.gif", "http://engineering.flipboard.com/assets/flex/nsuserdefaults-editor.gif", "http://engineering.flipboard.com/assets/flex/system-libraries-browser.gif", "http://engineering.flipboard.com/assets/flex/flex-readme-reverse-1.png", "http://engineering.flipboard.com/assets/flex/flex-readme-reverse-2.png" ] spec.license = { :type => "BSD", :file => "LICENSE" } spec.author = { "Tanner Bennett" => "tannerbennett@me.com" } spec.platform = :ios, "9.0" spec.source = { :git => "https://github.com/FLEXTool/FLEX.git", :tag => "#{spec.version}" } spec.source_files = "Classes/**/*.{h,c,m,mm}" spec.exclude_files = "Classes/Headers/*.{h,c,m,mm}" spec.frameworks = [ "Foundation", "UIKit", "CoreGraphics", "ImageIO", "QuartzCore", "WebKit", "Security", "SceneKit" ] spec.libraries = [ "z", "sqlite3" ] spec.requires_arc = true spec.library = 'stdc++' spec.pod_target_xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'gnu++11', } spec.compiler_flags = "-Wno-unsupported-availability-guard", "-Wno-deprecated-declarations" spec.public_header_files = [ "Classes/*.h", "Classes/Manager/*.h", "Classes/Toolbar/*.h", "Classes/Core/Controllers/*.h", "Classes/Core/Views/*.h", "Classes/Core/Views/Cells/*.h", "Classes/Core/*.h", "Classes/Utility/Categories/*.h", "Classes/Utility/Runtime/Objc/**/*.h", "Classes/ObjectExplorers/*.h", "Classes/ObjectExplorers/Sections/*.h", "Classes/Utility/FLEXMacros.h", "Classes/Utility/FLEXAlert.h", "Classes/Utility/FLEXResources.h", "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h", "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h", "Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h", "Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h" ] end ================================================ FILE: FLEX.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 03ED813D24B7D91200831CA0 /* FLEXTableRowDataViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 03ED813B24B7D91200831CA0 /* FLEXTableRowDataViewController.m */; }; 03ED813E24B7D91200831CA0 /* FLEXTableRowDataViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 03ED813C24B7D91200831CA0 /* FLEXTableRowDataViewController.h */; }; 04F1CA191C137CF1000A52B0 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 04F1CA181C137CF1000A52B0 /* LICENSE */; }; 1C27A8B91F0E5A0400F0D02D /* FLEXTestsMethodsList.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C27A8B81F0E5A0400F0D02D /* FLEXTestsMethodsList.m */; }; 1C27A8BB1F0E5A0400F0D02D /* FLEX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4C941F1B5B20570088C3F2 /* FLEX.framework */; }; 222C88221C7339DC007CA15F /* FLEXRealmDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 222C88211C7339DC007CA15F /* FLEXRealmDefines.h */; }; 224D49A81C673AB5000EAB86 /* FLEXRealmDatabaseManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 224D49A41C673AB5000EAB86 /* FLEXRealmDatabaseManager.h */; }; 224D49A91C673AB5000EAB86 /* FLEXRealmDatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 224D49A51C673AB5000EAB86 /* FLEXRealmDatabaseManager.m */; }; 224D49AA1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 224D49A61C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.h */; }; 224D49AB1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 224D49A71C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m */; }; 2EF6B04D1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EF6B04B1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m */; }; 2EF6B04E1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EF6B04C1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h */; }; 3A4C94251B5B20570088C3F2 /* FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94241B5B20570088C3F2 /* FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A4C94CD1B5B21410088C3F2 /* FLEXGlobalsEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94441B5B21410088C3F2 /* FLEXGlobalsEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A4C94CE1B5B21410088C3F2 /* FLEXGlobalsEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94451B5B21410088C3F2 /* FLEXGlobalsEntry.m */; }; 3A4C94D31B5B21410088C3F2 /* FLEXObjectExplorerFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C944A1B5B21410088C3F2 /* FLEXObjectExplorerFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A4C94D41B5B21410088C3F2 /* FLEXObjectExplorerFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C944B1B5B21410088C3F2 /* FLEXObjectExplorerFactory.m */; }; 3A4C94D51B5B21410088C3F2 /* FLEXObjectExplorerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C944C1B5B21410088C3F2 /* FLEXObjectExplorerViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A4C94D61B5B21410088C3F2 /* FLEXObjectExplorerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C944D1B5B21410088C3F2 /* FLEXObjectExplorerViewController.m */; }; 3A4C94DD1B5B21410088C3F2 /* FLEXHeapEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94551B5B21410088C3F2 /* FLEXHeapEnumerator.h */; }; 3A4C94DE1B5B21410088C3F2 /* FLEXHeapEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94561B5B21410088C3F2 /* FLEXHeapEnumerator.m */; }; 3A4C94E11B5B21410088C3F2 /* FLEXResources.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94591B5B21410088C3F2 /* FLEXResources.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A4C94E21B5B21410088C3F2 /* FLEXResources.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C945A1B5B21410088C3F2 /* FLEXResources.m */; }; 3A4C94E31B5B21410088C3F2 /* FLEXRuntimeUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C945B1B5B21410088C3F2 /* FLEXRuntimeUtility.h */; }; 3A4C94E41B5B21410088C3F2 /* FLEXRuntimeUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C945C1B5B21410088C3F2 /* FLEXRuntimeUtility.m */; }; 3A4C94E51B5B21410088C3F2 /* FLEXUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C945D1B5B21410088C3F2 /* FLEXUtility.h */; }; 3A4C94E61B5B21410088C3F2 /* FLEXUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C945E1B5B21410088C3F2 /* FLEXUtility.m */; }; 3A4C94E71B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94601B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94E81B5B21410088C3F2 /* FLEXHierarchyTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94611B5B21410088C3F2 /* FLEXHierarchyTableViewCell.m */; }; 3A4C94E91B5B21410088C3F2 /* FLEXHierarchyTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94621B5B21410088C3F2 /* FLEXHierarchyTableViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94EA1B5B21410088C3F2 /* FLEXHierarchyTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94631B5B21410088C3F2 /* FLEXHierarchyTableViewController.m */; }; 3A4C94EB1B5B21410088C3F2 /* FLEXImagePreviewViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94641B5B21410088C3F2 /* FLEXImagePreviewViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94EC1B5B21410088C3F2 /* FLEXImagePreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94651B5B21410088C3F2 /* FLEXImagePreviewViewController.m */; }; 3A4C94ED1B5B21410088C3F2 /* FLEXArgumentInputColorView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94681B5B21410088C3F2 /* FLEXArgumentInputColorView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94EE1B5B21410088C3F2 /* FLEXArgumentInputColorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94691B5B21410088C3F2 /* FLEXArgumentInputColorView.m */; }; 3A4C94EF1B5B21410088C3F2 /* FLEXArgumentInputDateView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C946A1B5B21410088C3F2 /* FLEXArgumentInputDateView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94F01B5B21410088C3F2 /* FLEXArgumentInputDateView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C946B1B5B21410088C3F2 /* FLEXArgumentInputDateView.m */; }; 3A4C94F11B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C946C1B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94F21B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C946D1B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.m */; }; 3A4C94F31B5B21410088C3F2 /* FLEXArgumentInputFontView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C946E1B5B21410088C3F2 /* FLEXArgumentInputFontView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94F41B5B21410088C3F2 /* FLEXArgumentInputFontView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C946F1B5B21410088C3F2 /* FLEXArgumentInputFontView.m */; }; 3A4C94F51B5B21410088C3F2 /* FLEXArgumentInputObjectView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94701B5B21410088C3F2 /* FLEXArgumentInputObjectView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94F61B5B21410088C3F2 /* FLEXArgumentInputObjectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94711B5B21410088C3F2 /* FLEXArgumentInputObjectView.m */; }; 3A4C94F71B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94721B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94F81B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94731B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m */; }; 3A4C94F91B5B21410088C3F2 /* FLEXArgumentInputNumberView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94741B5B21410088C3F2 /* FLEXArgumentInputNumberView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94FA1B5B21410088C3F2 /* FLEXArgumentInputNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94751B5B21410088C3F2 /* FLEXArgumentInputNumberView.m */; }; 3A4C94FB1B5B21410088C3F2 /* FLEXArgumentInputStringView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94761B5B21410088C3F2 /* FLEXArgumentInputStringView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94FC1B5B21410088C3F2 /* FLEXArgumentInputStringView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94771B5B21410088C3F2 /* FLEXArgumentInputStringView.m */; }; 3A4C94FD1B5B21410088C3F2 /* FLEXArgumentInputStructView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94781B5B21410088C3F2 /* FLEXArgumentInputStructView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C94FE1B5B21410088C3F2 /* FLEXArgumentInputStructView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94791B5B21410088C3F2 /* FLEXArgumentInputStructView.m */; }; 3A4C94FF1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C947A1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95001B5B21410088C3F2 /* FLEXArgumentInputSwitchView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C947B1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.m */; }; 3A4C95011B5B21410088C3F2 /* FLEXArgumentInputTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C947C1B5B21410088C3F2 /* FLEXArgumentInputTextView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95021B5B21410088C3F2 /* FLEXArgumentInputTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C947D1B5B21410088C3F2 /* FLEXArgumentInputTextView.m */; }; 3A4C95031B5B21410088C3F2 /* FLEXArgumentInputView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C947E1B5B21410088C3F2 /* FLEXArgumentInputView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95041B5B21410088C3F2 /* FLEXArgumentInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C947F1B5B21410088C3F2 /* FLEXArgumentInputView.m */; }; 3A4C95051B5B21410088C3F2 /* FLEXArgumentInputViewFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94801B5B21410088C3F2 /* FLEXArgumentInputViewFactory.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95061B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94811B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m */; }; 3A4C95071B5B21410088C3F2 /* FLEXDefaultEditorViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94821B5B21410088C3F2 /* FLEXDefaultEditorViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95081B5B21410088C3F2 /* FLEXDefaultEditorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94831B5B21410088C3F2 /* FLEXDefaultEditorViewController.m */; }; 3A4C95091B5B21410088C3F2 /* FLEXFieldEditorView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94841B5B21410088C3F2 /* FLEXFieldEditorView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C950A1B5B21410088C3F2 /* FLEXFieldEditorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94851B5B21410088C3F2 /* FLEXFieldEditorView.m */; }; 3A4C950F1B5B21410088C3F2 /* FLEXMethodCallingViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C948A1B5B21410088C3F2 /* FLEXMethodCallingViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95101B5B21410088C3F2 /* FLEXMethodCallingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C948B1B5B21410088C3F2 /* FLEXMethodCallingViewController.m */; }; 3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C949F1B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95231B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */; }; 3A4C95241B5B21410088C3F2 /* FLEXFileBrowserController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95251B5B21410088C3F2 /* FLEXFileBrowserController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserController.m */; }; 3A4C95261B5B21410088C3F2 /* FLEXGlobalsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A31B5B21410088C3F2 /* FLEXGlobalsViewController.h */; }; 3A4C95271B5B21410088C3F2 /* FLEXGlobalsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A41B5B21410088C3F2 /* FLEXGlobalsViewController.m */; }; 3A4C95281B5B21410088C3F2 /* FLEXObjectListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A51B5B21410088C3F2 /* FLEXObjectListViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95291B5B21410088C3F2 /* FLEXObjectListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A61B5B21410088C3F2 /* FLEXObjectListViewController.m */; }; 3A4C952C1B5B21410088C3F2 /* FLEXLiveObjectsController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A91B5B21410088C3F2 /* FLEXLiveObjectsController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C952D1B5B21410088C3F2 /* FLEXLiveObjectsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94AA1B5B21410088C3F2 /* FLEXLiveObjectsController.m */; }; 3A4C952E1B5B21410088C3F2 /* FLEXWebViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94AB1B5B21410088C3F2 /* FLEXWebViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C952F1B5B21410088C3F2 /* FLEXWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94AC1B5B21410088C3F2 /* FLEXWebViewController.m */; }; 3A4C95301B5B21410088C3F2 /* FLEXSystemLogMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94AE1B5B21410088C3F2 /* FLEXSystemLogMessage.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95311B5B21410088C3F2 /* FLEXSystemLogMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94AF1B5B21410088C3F2 /* FLEXSystemLogMessage.m */; }; 3A4C95321B5B21410088C3F2 /* FLEXSystemLogCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B01B5B21410088C3F2 /* FLEXSystemLogCell.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95331B5B21410088C3F2 /* FLEXSystemLogCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B11B5B21410088C3F2 /* FLEXSystemLogCell.m */; }; 3A4C95341B5B21410088C3F2 /* FLEXSystemLogViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B21B5B21410088C3F2 /* FLEXSystemLogViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95351B5B21410088C3F2 /* FLEXSystemLogViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B31B5B21410088C3F2 /* FLEXSystemLogViewController.m */; }; 3A4C95361B5B21410088C3F2 /* FLEXNetworkMITMViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B51B5B21410088C3F2 /* FLEXNetworkMITMViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95371B5B21410088C3F2 /* FLEXNetworkMITMViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B61B5B21410088C3F2 /* FLEXNetworkMITMViewController.m */; }; 3A4C95381B5B21410088C3F2 /* FLEXNetworkRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B71B5B21410088C3F2 /* FLEXNetworkRecorder.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C953A1B5B21410088C3F2 /* FLEXNetworkSettingsController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94B91B5B21410088C3F2 /* FLEXNetworkSettingsController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C953B1B5B21410088C3F2 /* FLEXNetworkSettingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94BA1B5B21410088C3F2 /* FLEXNetworkSettingsController.m */; }; 3A4C953C1B5B21410088C3F2 /* FLEXNetworkTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94BB1B5B21410088C3F2 /* FLEXNetworkTransaction.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C953D1B5B21410088C3F2 /* FLEXNetworkTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94BC1B5B21410088C3F2 /* FLEXNetworkTransaction.m */; }; 3A4C95401B5B21410088C3F2 /* FLEXNetworkTransactionCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94BF1B5B21410088C3F2 /* FLEXNetworkTransactionCell.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95411B5B21410088C3F2 /* FLEXNetworkTransactionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94C01B5B21410088C3F2 /* FLEXNetworkTransactionCell.m */; }; 3A4C95421B5B21410088C3F2 /* FLEXNetworkObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94C21B5B21410088C3F2 /* FLEXNetworkObserver.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95431B5B21410088C3F2 /* FLEXNetworkObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94C31B5B21410088C3F2 /* FLEXNetworkObserver.m */; }; 3A4C95471B5B217D0088C3F2 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4C95461B5B217D0088C3F2 /* libz.dylib */; }; 679F64861BD53B7B00A8C94C /* FLEXCookiesViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 679F64841BD53B7B00A8C94C /* FLEXCookiesViewController.h */; }; 679F64871BD53B7B00A8C94C /* FLEXCookiesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 679F64851BD53B7B00A8C94C /* FLEXCookiesViewController.m */; }; 71E1C2132307FBB800F5032A /* FLEXKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20B2307FBB700F5032A /* FLEXKeychain.h */; }; 71E1C2142307FBB800F5032A /* FLEXKeychainViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20C2307FBB700F5032A /* FLEXKeychainViewController.h */; }; 71E1C2152307FBB800F5032A /* FLEXKeychainQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20D2307FBB700F5032A /* FLEXKeychainQuery.h */; }; 71E1C2172307FBB800F5032A /* FLEXKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C20F2307FBB700F5032A /* FLEXKeychain.m */; }; 71E1C2182307FBB800F5032A /* FLEXKeychainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C2102307FBB700F5032A /* FLEXKeychainViewController.m */; }; 71E1C2192307FBB800F5032A /* FLEXKeychainQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C2112307FBB700F5032A /* FLEXKeychainQuery.m */; }; 7349FD6A22B93CDF00051810 /* FLEXColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7349FD6822B93CDF00051810 /* FLEXColor.h */; }; 7349FD6B22B93CDF00051810 /* FLEXColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 7349FD6922B93CDF00051810 /* FLEXColor.m */; }; 779B1ECE1C0C4D7C001F5E49 /* FLEXDatabaseManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1EC01C0C4D7C001F5E49 /* FLEXDatabaseManager.h */; }; 779B1ED01C0C4D7C001F5E49 /* FLEXMultiColumnTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1EC21C0C4D7C001F5E49 /* FLEXMultiColumnTableView.h */; }; 779B1ED11C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 779B1EC31C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m */; }; 779B1ED21C0C4D7C001F5E49 /* FLEXTableColumnHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1EC41C0C4D7C001F5E49 /* FLEXTableColumnHeader.h */; }; 779B1ED31C0C4D7C001F5E49 /* FLEXTableColumnHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 779B1EC51C0C4D7C001F5E49 /* FLEXTableColumnHeader.m */; }; 779B1ED41C0C4D7C001F5E49 /* FLEXDBQueryRowCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1EC61C0C4D7C001F5E49 /* FLEXDBQueryRowCell.h */; }; 779B1ED51C0C4D7C001F5E49 /* FLEXDBQueryRowCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 779B1EC71C0C4D7C001F5E49 /* FLEXDBQueryRowCell.m */; }; 779B1ED61C0C4D7C001F5E49 /* FLEXTableContentViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1EC81C0C4D7C001F5E49 /* FLEXTableContentViewController.h */; }; 779B1ED71C0C4D7C001F5E49 /* FLEXTableContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 779B1EC91C0C4D7C001F5E49 /* FLEXTableContentViewController.m */; }; 779B1ED81C0C4D7C001F5E49 /* FLEXTableLeftCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1ECA1C0C4D7C001F5E49 /* FLEXTableLeftCell.h */; }; 779B1ED91C0C4D7C001F5E49 /* FLEXTableLeftCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 779B1ECB1C0C4D7C001F5E49 /* FLEXTableLeftCell.m */; }; 779B1EDA1C0C4D7C001F5E49 /* FLEXTableListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 779B1ECC1C0C4D7C001F5E49 /* FLEXTableListViewController.h */; }; 779B1EDB1C0C4D7C001F5E49 /* FLEXTableListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 779B1ECD1C0C4D7C001F5E49 /* FLEXTableListViewController.m */; }; 779B1EDD1C0C4EAD001F5E49 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 779B1EDC1C0C4EAD001F5E49 /* libsqlite3.dylib */; }; 942DCD871BAE0CA300DB5DC2 /* FLEXKeyboardShortcutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 942DCD831BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.m */; }; 94A515141C4CA1C00063292F /* FLEXManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 94A515131C4CA1C00063292F /* FLEXManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 94A515171C4CA1D70063292F /* FLEXManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 94A515151C4CA1D70063292F /* FLEXManager.m */; }; 94A515181C4CA1D70063292F /* FLEXManager+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 94A515161C4CA1D70063292F /* FLEXManager+Private.h */; }; 94A5151D1C4CA1F10063292F /* FLEXExplorerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 94A515191C4CA1F10063292F /* FLEXExplorerViewController.h */; }; 94A5151E1C4CA1F10063292F /* FLEXExplorerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94A5151A1C4CA1F10063292F /* FLEXExplorerViewController.m */; }; 94A5151F1C4CA1F10063292F /* FLEXWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 94A5151B1C4CA1F10063292F /* FLEXWindow.h */; }; 94A515201C4CA1F10063292F /* FLEXWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 94A5151C1C4CA1F10063292F /* FLEXWindow.m */; }; 94A515251C4CA2080063292F /* FLEXExplorerToolbar.h in Headers */ = {isa = PBXBuildFile; fileRef = 94A515211C4CA2080063292F /* FLEXExplorerToolbar.h */; settings = {ATTRIBUTES = (Public, ); }; }; 94A515261C4CA2080063292F /* FLEXExplorerToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = 94A515221C4CA2080063292F /* FLEXExplorerToolbar.m */; }; 94A515271C4CA2080063292F /* FLEXExplorerToolbarItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 94A515231C4CA2080063292F /* FLEXExplorerToolbarItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 94A515281C4CA2080063292F /* FLEXExplorerToolbarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 94A515241C4CA2080063292F /* FLEXExplorerToolbarItem.m */; }; 94AAF0381BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */; }; 94AAF03A1BAF2F0300DE8760 /* FLEXKeyboardShortcutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */; settings = {ATTRIBUTES = (Private, ); }; }; A42069CF2809377F00EBCAEA /* FLEXFirebaseTransaction.mm in Sources */ = {isa = PBXBuildFile; fileRef = A42069CE2809377F00EBCAEA /* FLEXFirebaseTransaction.mm */; }; C301994A2409B38A00759E8E /* CALayer+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C30199482409B38A00759E8E /* CALayer+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C301994B2409B38A00759E8E /* CALayer+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C30199492409B38A00759E8E /* CALayer+FLEX.m */; }; C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C309B82D223ED64400B228EC /* FLEXLogController.h */; }; C30D2960261FAE9E00D89649 /* FLEXNSStringShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D295C261FAE9E00D89649 /* FLEXNSStringShortcuts.h */; }; C30D2961261FAE9E00D89649 /* FLEXNSDataShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C30D295D261FAE9E00D89649 /* FLEXNSDataShortcuts.m */; }; C30D2962261FAE9E00D89649 /* FLEXNSDataShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D295E261FAE9E00D89649 /* FLEXNSDataShortcuts.h */; }; C30D2963261FAE9E00D89649 /* FLEXNSStringShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C30D295F261FAE9E00D89649 /* FLEXNSStringShortcuts.m */; }; C30D2968261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D2966261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h */; }; C30D2969261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C30D2967261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m */; }; C312A13023ECB5D300E38049 /* FLEXBookmarkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */; }; C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */; }; C312A13423ECBE5800E38049 /* FLEXBookmarksViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A13223ECBE5800E38049 /* FLEXBookmarksViewController.h */; }; C312A13523ECBE5800E38049 /* FLEXBookmarksViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A13323ECBE5800E38049 /* FLEXBookmarksViewController.m */; }; C312A13C23ECE79000E38049 /* FLEXWindowManagerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C312A13A23ECE79000E38049 /* FLEXWindowManagerController.m */; }; C312A13D23ECE79000E38049 /* FLEXWindowManagerController.h in Headers */ = {isa = PBXBuildFile; fileRef = C312A13B23ECE79000E38049 /* FLEXWindowManagerController.h */; }; C313853F23F5C1A10046E63C /* FLEXViewControllersViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C313853D23F5C1A10046E63C /* FLEXViewControllersViewController.h */; }; C313854023F5C1A10046E63C /* FLEXViewControllersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C313853E23F5C1A10046E63C /* FLEXViewControllersViewController.m */; }; C313854823F5F7D50046E63C /* flex_fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = C313854623F5F7D40046E63C /* flex_fishhook.c */; }; C313854923F5F7D50046E63C /* flex_fishhook.h in Headers */ = {isa = PBXBuildFile; fileRef = C313854723F5F7D50046E63C /* flex_fishhook.h */; }; C31C4A6923342A2200C35F12 /* FLEXMetadataSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C31C4A6723342A2200C35F12 /* FLEXMetadataSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C31C4A6A23342A2200C35F12 /* FLEXMetadataSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C31C4A6823342A2200C35F12 /* FLEXMetadataSection.m */; }; C31D93E423E38CBE005517BF /* FLEXBlockShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */; }; C31D93E523E38CBE005517BF /* FLEXBlockShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C31D93E323E38CBE005517BF /* FLEXBlockShortcuts.m */; }; C31D93E823E38E97005517BF /* FLEXBlockDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = C31D93E623E38E97005517BF /* FLEXBlockDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; C31D93E923E38E97005517BF /* FLEXBlockDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = C31D93E723E38E97005517BF /* FLEXBlockDescription.m */; }; C32A195E231732E800EB02AC /* FLEXCollectionContentSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C32A195C231732E800EB02AC /* FLEXCollectionContentSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C32A195F231732E800EB02AC /* FLEXCollectionContentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C32A195D231732E800EB02AC /* FLEXCollectionContentSection.m */; }; C32A19622317378C00EB02AC /* FLEXDefaultsContentSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C32A19602317378C00EB02AC /* FLEXDefaultsContentSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C32A19632317378C00EB02AC /* FLEXDefaultsContentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C32A19612317378C00EB02AC /* FLEXDefaultsContentSection.m */; }; C32F3A18247C6B3E0063542D /* FLEXUIAppShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C32F3A16247C6B3E0063542D /* FLEXUIAppShortcuts.h */; }; C32F3A19247C6B3E0063542D /* FLEXUIAppShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */; }; C33C825B23159EAF00DD2451 /* FLEXTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C33C825A23159EAF00DD2451 /* FLEXTests.m */; }; C33C825E2316DC8600DD2451 /* FLEXObjectExplorer.h in Headers */ = {isa = PBXBuildFile; fileRef = C33C825C2316DC8600DD2451 /* FLEXObjectExplorer.h */; settings = {ATTRIBUTES = (Public, ); }; }; C33C825F2316DC8600DD2451 /* FLEXObjectExplorer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */; }; C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */; }; C33E46B0223B02CD004BD0E6 /* FLEXASLLogController.m in Sources */ = {isa = PBXBuildFile; fileRef = C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */; }; C34063B528400A26007B46D3 /* FLEXActivityViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C34063B328400A26007B46D3 /* FLEXActivityViewController.h */; }; C34063B628400A26007B46D3 /* FLEXActivityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C34063B428400A26007B46D3 /* FLEXActivityViewController.m */; }; C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3474C4123DA496400466532 /* FLEXKeyValueTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */; }; C3490E1F233BDD73002AE200 /* FLEXSingleRowSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3490E20233BDD73002AE200 /* FLEXSingleRowSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3490E1E233BDD73002AE200 /* FLEXSingleRowSection.m */; }; C34C9BDD23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = C34C9BDB23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; C34C9BDE23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = C34C9BDC23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.m */; }; C34D4EB023A2ABD900C1F903 /* FLEXLayerShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C34D4EAE23A2ABD900C1F903 /* FLEXLayerShortcuts.h */; }; C34D4EB123A2ABD900C1F903 /* FLEXLayerShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C34D4EAF23A2ABD900C1F903 /* FLEXLayerShortcuts.m */; }; C34D4EB423A2AF2A00C1F903 /* FLEXColorPreviewSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C34D4EB223A2AF2A00C1F903 /* FLEXColorPreviewSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C34D4EB523A2AF2A00C1F903 /* FLEXColorPreviewSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C34D4EB323A2AF2A00C1F903 /* FLEXColorPreviewSection.m */; }; C34D4EB823A2B17900C1F903 /* FLEXBundleShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C34D4EB623A2B17900C1F903 /* FLEXBundleShortcuts.h */; }; C34D4EB923A2B17900C1F903 /* FLEXBundleShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C34D4EB723A2B17900C1F903 /* FLEXBundleShortcuts.m */; }; C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */ = {isa = PBXBuildFile; fileRef = C34EE30621CB23CC00BD3A7C /* FLEXOSLogController.h */; }; C3511B9122D7C99E0057BAB7 /* FLEXGlobalsSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3511B8F22D7C99E0057BAB7 /* FLEXGlobalsSection.h */; }; C3511B9222D7C99E0057BAB7 /* FLEXGlobalsSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3511B9022D7C99E0057BAB7 /* FLEXGlobalsSection.m */; }; C3531B9D23E69BB200A184AD /* FLEXManager+Networking.h in Headers */ = {isa = PBXBuildFile; fileRef = C3531B9B23E69BB200A184AD /* FLEXManager+Networking.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3531B9E23E69BB200A184AD /* FLEXManager+Networking.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531B9C23E69BB200A184AD /* FLEXManager+Networking.m */; }; C3531BA123E796BD00A184AD /* FLEXManager+Extensibility.h in Headers */ = {isa = PBXBuildFile; fileRef = C3531B9F23E796BD00A184AD /* FLEXManager+Extensibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3531BA223E796BD00A184AD /* FLEXManager+Extensibility.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531BA023E796BD00A184AD /* FLEXManager+Extensibility.m */; }; C3531BA523E88A2100A184AD /* FLEXNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3531BA323E88A2100A184AD /* FLEXNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3531BA623E88A2100A184AD /* FLEXNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531BA423E88A2100A184AD /* FLEXNavigationController.m */; }; C3531BAA23E88FAC00A184AD /* FLEXTabList.h in Headers */ = {isa = PBXBuildFile; fileRef = C3531BA823E88FAC00A184AD /* FLEXTabList.h */; }; C3531BAB23E88FAC00A184AD /* FLEXTabList.m in Sources */ = {isa = PBXBuildFile; fileRef = C3531BA923E88FAC00A184AD /* FLEXTabList.m */; }; C35DAD822709140700AA95E6 /* FLEXHTTPTransactionDetailController.h in Headers */ = {isa = PBXBuildFile; fileRef = C35DAD7E2709140700AA95E6 /* FLEXHTTPTransactionDetailController.h */; }; C35DAD832709140700AA95E6 /* FLEXHTTPTransactionDetailController.m in Sources */ = {isa = PBXBuildFile; fileRef = C35DAD7F2709140700AA95E6 /* FLEXHTTPTransactionDetailController.m */; }; C35DAD8E2709143000AA95E6 /* FLEXMITMDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = C35DAD8C2709143000AA95E6 /* FLEXMITMDataSource.h */; }; C35DAD8F2709143000AA95E6 /* FLEXMITMDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = C35DAD8D2709143000AA95E6 /* FLEXMITMDataSource.m */; }; C362AE8123C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h in Headers */ = {isa = PBXBuildFile; fileRef = C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */; settings = {ATTRIBUTES = (Private, ); }; }; C362AE8223C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m in Sources */ = {isa = PBXBuildFile; fileRef = C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */; }; C3694DBA23EA1096006625D7 /* FLEXTabsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3694DB823EA1096006625D7 /* FLEXTabsViewController.h */; }; C3694DBB23EA1096006625D7 /* FLEXTabsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3694DB923EA1096006625D7 /* FLEXTabsViewController.m */; }; C3694DC223EA147F006625D7 /* UIBarButtonItem+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3694DC323EA147F006625D7 /* UIBarButtonItem+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */; }; C36B096523E0D4A1008F5D47 /* UIMenu+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C36B096323E0D4A1008F5D47 /* UIMenu+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36B096623E0D4A1008F5D47 /* UIMenu+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */; }; C36B097023E1EDCD008F5D47 /* FLEXTableViewSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C36B096E23E1EDCD008F5D47 /* FLEXTableViewSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36B097123E1EDCD008F5D47 /* FLEXTableViewSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C36B096F23E1EDCD008F5D47 /* FLEXTableViewSection.m */; }; C36E1B26259D64CC00FEFEF6 /* FLEXNewRootClass.m in Sources */ = {isa = PBXBuildFile; fileRef = C36E1B25259D64CC00FEFEF6 /* FLEXNewRootClass.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; C36FBFCB230F3B98008D95D5 /* FLEXMirror.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFB9230F3B97008D95D5 /* FLEXMirror.m */; }; C36FBFCC230F3B98008D95D5 /* FLEXProtocolBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFBA230F3B97008D95D5 /* FLEXProtocolBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFCD230F3B98008D95D5 /* FLEXMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFBB230F3B97008D95D5 /* FLEXMethod.m */; }; C36FBFCE230F3B98008D95D5 /* FLEXProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFBC230F3B97008D95D5 /* FLEXProperty.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFCF230F3B98008D95D5 /* FLEXProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFBD230F3B97008D95D5 /* FLEXProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFD0230F3B98008D95D5 /* FLEXProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFBE230F3B97008D95D5 /* FLEXProperty.m */; }; C36FBFD1230F3B98008D95D5 /* FLEXClassBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFBF230F3B98008D95D5 /* FLEXClassBuilder.m */; }; C36FBFD2230F3B98008D95D5 /* FLEXMethodBase.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFC0230F3B98008D95D5 /* FLEXMethodBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFD3230F3B98008D95D5 /* FLEXMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFC1230F3B98008D95D5 /* FLEXMethod.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFD4230F3B98008D95D5 /* FLEXProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFC2230F3B98008D95D5 /* FLEXProtocol.m */; }; C36FBFD5230F3B98008D95D5 /* FLEXMethodBase.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFC3230F3B98008D95D5 /* FLEXMethodBase.m */; }; C36FBFD6230F3B98008D95D5 /* FLEXIvar.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFC4230F3B98008D95D5 /* FLEXIvar.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFD7230F3B98008D95D5 /* FLEXMirror.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFC5230F3B98008D95D5 /* FLEXMirror.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFD8230F3B98008D95D5 /* FLEXPropertyAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFC6230F3B98008D95D5 /* FLEXPropertyAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFD9230F3B98008D95D5 /* FLEXProtocolBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFC7230F3B98008D95D5 /* FLEXProtocolBuilder.m */; }; C36FBFDA230F3B98008D95D5 /* FLEXPropertyAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFC8230F3B98008D95D5 /* FLEXPropertyAttributes.m */; }; C36FBFDB230F3B98008D95D5 /* FLEXClassBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C36FBFC9230F3B98008D95D5 /* FLEXClassBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; C36FBFDC230F3B98008D95D5 /* FLEXIvar.m in Sources */ = {isa = PBXBuildFile; fileRef = C36FBFCA230F3B98008D95D5 /* FLEXIvar.m */; }; C37A0C93218BAC9600848CA7 /* FLEXObjcInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = C37A0C91218BAC9600848CA7 /* FLEXObjcInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; C37A0C94218BAC9600848CA7 /* FLEXObjcInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = C37A0C92218BAC9600848CA7 /* FLEXObjcInternal.mm */; }; C381FDC3271CCB0500401081 /* Firestore.h in Headers */ = {isa = PBXBuildFile; fileRef = C381FDC2271CCB0500401081 /* Firestore.h */; }; C383C3B923B6A62A007A321B /* FLEXRuntimeSafety.h in Headers */ = {isa = PBXBuildFile; fileRef = C383C3B723B6A62A007A321B /* FLEXRuntimeSafety.h */; settings = {ATTRIBUTES = (Public, ); }; }; C383C3BA23B6A62A007A321B /* FLEXRuntimeSafety.m in Sources */ = {isa = PBXBuildFile; fileRef = C383C3B823B6A62A007A321B /* FLEXRuntimeSafety.m */; }; C383C3BD23B6B398007A321B /* UITextField+Range.m in Sources */ = {isa = PBXBuildFile; fileRef = C383C3BB23B6B398007A321B /* UITextField+Range.m */; }; C383C3BE23B6B398007A321B /* UITextField+Range.h in Headers */ = {isa = PBXBuildFile; fileRef = C383C3BC23B6B398007A321B /* UITextField+Range.h */; settings = {ATTRIBUTES = (Public, ); }; }; C383C3C123B6B429007A321B /* NSTimer+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C383C3BF23B6B429007A321B /* NSTimer+FLEX.m */; }; C383C3C223B6B429007A321B /* NSTimer+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C383C3C023B6B429007A321B /* NSTimer+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C383C3C523B6BB81007A321B /* FLEXCodeFontCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C383C3C323B6BB81007A321B /* FLEXCodeFontCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; C383C3C623B6BB81007A321B /* FLEXCodeFontCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C383C3C423B6BB81007A321B /* FLEXCodeFontCell.m */; }; C3854DF023F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C3854DEF23F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m */; }; C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38568FB272B3BFC00B1E37F /* FLEXSwiftInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */; }; C386D6A9241995A800699085 /* FLEXTypeEncodingParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; C386D6E924199C1B00699085 /* FLEX-Core.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6E824199C1B00699085 /* FLEX-Core.h */; settings = {ATTRIBUTES = (Public, ); }; }; C386D6EB24199E9600699085 /* FLEX-ObjectExploring.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6EA24199E9600699085 /* FLEX-ObjectExploring.h */; settings = {ATTRIBUTES = (Public, ); }; }; C386D6ED24199EC600699085 /* FLEX-Runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6EC24199EC600699085 /* FLEX-Runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; C386D6F02419A33F00699085 /* FLEXRuntimeConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = C386D6EF2419A33F00699085 /* FLEXRuntimeConstants.m */; }; C386D6F2241A96AD00699085 /* FLEXMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6F1241A96AD00699085 /* FLEXMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; C386D6F3241A976100699085 /* FLEXRuntimeConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6EE2419A2F400699085 /* FLEXRuntimeConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; C386D6F6241A9D6900699085 /* FLEX-Categories.h in Headers */ = {isa = PBXBuildFile; fileRef = C386D6F5241A9D6900699085 /* FLEX-Categories.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3878DBA23A749960038FDBE /* FLEXVariableEditorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3878DB823A749960038FDBE /* FLEXVariableEditorViewController.m */; }; C3878DBB23A749960038FDBE /* FLEXVariableEditorViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3878DB923A749960038FDBE /* FLEXVariableEditorViewController.h */; }; C3878DBC23A749F70038FDBE /* FLEXFieldEditorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94871B5B21410088C3F2 /* FLEXFieldEditorViewController.m */; }; C3878DBE23A74A8F0038FDBE /* FLEXNetworkRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94B81B5B21410088C3F2 /* FLEXNetworkRecorder.m */; }; C387C87A22DFCD6A00750E58 /* FLEXCarouselCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C387C87822DFCD6A00750E58 /* FLEXCarouselCell.h */; }; C387C87B22DFCD6A00750E58 /* FLEXCarouselCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C387C87922DFCD6A00750E58 /* FLEXCarouselCell.m */; }; C387C88322E0D24A00750E58 /* UIView+FLEX_Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */; settings = {ATTRIBUTES = (Private, ); }; }; C387C88422E0D24A00750E58 /* UIView+FLEX_Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */; }; C38D970228190C93008709D0 /* FLEXMetadataExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = C38D970028190C93008709D0 /* FLEXMetadataExtras.m */; }; C38D970328190C93008709D0 /* FLEXMetadataExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = C38D970128190C93008709D0 /* FLEXMetadataExtras.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38DF0EA22CFE4370077B4AD /* FLEXTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38DF0E822CFE4370077B4AD /* FLEXTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38DF0EB22CFE4370077B4AD /* FLEXTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C38DF0E922CFE4370077B4AD /* FLEXTableViewController.m */; }; C38EF26223A2FCD20047A7EC /* FLEXViewControllerShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */; }; C38EF26323A2FCD20047A7EC /* FLEXViewControllerShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF26123A2FCD20047A7EC /* FLEXViewControllerShortcuts.h */; }; C38F3F31230C958F004E3731 /* FLEXAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = C38F3F2F230C958F004E3731 /* FLEXAlert.h */; settings = {ATTRIBUTES = (Public, ); }; }; C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = C38F3F30230C958F004E3731 /* FLEXAlert.m */; }; C397E318240EC98F0091E4EC /* FLEXSQLResult.h in Headers */ = {isa = PBXBuildFile; fileRef = C397E316240EC98F0091E4EC /* FLEXSQLResult.h */; }; C397E319240EC98F0091E4EC /* FLEXSQLResult.m in Sources */ = {isa = PBXBuildFile; fileRef = C397E317240EC98F0091E4EC /* FLEXSQLResult.m */; }; C398624D23AD6C67007E6793 /* FLEXKeyPathSearchController.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624323AD6C67007E6793 /* FLEXKeyPathSearchController.h */; }; C398624E23AD6C67007E6793 /* FLEXRuntimeKeyPath.m in Sources */ = {isa = PBXBuildFile; fileRef = C398624423AD6C67007E6793 /* FLEXRuntimeKeyPath.m */; }; C398624F23AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624523AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.h */; }; C398625023AD6C67007E6793 /* FLEXObjcRuntimeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C398624623AD6C67007E6793 /* FLEXObjcRuntimeViewController.m */; }; C398625123AD6C67007E6793 /* FLEXRuntimeKeyPath.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624723AD6C67007E6793 /* FLEXRuntimeKeyPath.h */; }; C398625223AD6C67007E6793 /* FLEXKeyPathSearchController.m in Sources */ = {isa = PBXBuildFile; fileRef = C398624823AD6C67007E6793 /* FLEXKeyPathSearchController.m */; }; C398625323AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = C398624923AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.m */; }; C398625423AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624A23AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h */; }; C398625523AD6C67007E6793 /* FLEXObjcRuntimeViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624B23AD6C67007E6793 /* FLEXObjcRuntimeViewController.h */; }; C398625623AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = C398624C23AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.m */; }; C398625923AD6C88007E6793 /* FLEXSearchToken.h in Headers */ = {isa = PBXBuildFile; fileRef = C398625723AD6C88007E6793 /* FLEXSearchToken.h */; }; C398625A23AD6C88007E6793 /* FLEXSearchToken.m in Sources */ = {isa = PBXBuildFile; fileRef = C398625823AD6C88007E6793 /* FLEXSearchToken.m */; }; C398625D23AD6E90007E6793 /* UIFont+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C398625B23AD6E90007E6793 /* UIFont+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C398625E23AD6E90007E6793 /* UIFont+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C398625C23AD6E90007E6793 /* UIFont+FLEX.m */; }; C398626123AD70DF007E6793 /* FLEXKeyboardToolbar.h in Headers */ = {isa = PBXBuildFile; fileRef = C398625F23AD70DF007E6793 /* FLEXKeyboardToolbar.h */; }; C398626223AD70DF007E6793 /* FLEXKeyboardToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = C398626023AD70DF007E6793 /* FLEXKeyboardToolbar.m */; }; C398626523AD70F5007E6793 /* FLEXKBToolbarButton.h in Headers */ = {isa = PBXBuildFile; fileRef = C398626323AD70F5007E6793 /* FLEXKBToolbarButton.h */; }; C398626623AD70F5007E6793 /* FLEXKBToolbarButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C398626423AD70F5007E6793 /* FLEXKBToolbarButton.m */; }; C398626B23AD71C1007E6793 /* FLEXRuntimeClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C398626723AD71C1007E6793 /* FLEXRuntimeClient.h */; }; C398626C23AD71C1007E6793 /* FLEXRuntimeController.h in Headers */ = {isa = PBXBuildFile; fileRef = C398626823AD71C1007E6793 /* FLEXRuntimeController.h */; }; C398626D23AD71C1007E6793 /* FLEXRuntimeController.m in Sources */ = {isa = PBXBuildFile; fileRef = C398626923AD71C1007E6793 /* FLEXRuntimeController.m */; }; C398626E23AD71C1007E6793 /* FLEXRuntimeClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C398626A23AD71C1007E6793 /* FLEXRuntimeClient.m */; }; C398627223AD7951007E6793 /* UIGestureRecognizer+Blocks.h in Headers */ = {isa = PBXBuildFile; fileRef = C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */; settings = {ATTRIBUTES = (Public, ); }; }; C398627323AD7951007E6793 /* UIGestureRecognizer+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = C398627123AD7951007E6793 /* UIGestureRecognizer+Blocks.m */; }; C398627623AD79B7007E6793 /* NSString+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C398627423AD79B6007E6793 /* NSString+FLEX.h */; settings = {ATTRIBUTES = (Private, ); }; }; C398627723AD79B7007E6793 /* NSString+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C398627523AD79B7007E6793 /* NSString+FLEX.m */; }; C398682523AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m in Sources */ = {isa = PBXBuildFile; fileRef = C398682323AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m */; }; C398682623AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h in Headers */ = {isa = PBXBuildFile; fileRef = C398682423AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h */; }; C398682823AC36EC00E9E391 /* FLEXViewShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F527C4231891F6009CBA07 /* FLEXViewShortcuts.m */; }; C398682923AC370100E9E391 /* FLEXViewShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F527C3231891F6009CBA07 /* FLEXViewShortcuts.h */; }; C39EADC923F37B89005618BE /* FLEXTypeEncodingParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C3854DF123F36C9E00FCD1E2 /* FLEXTypeEncodingParser.m */; }; C39ED92822D63F3200B5773A /* FLEXAddressExplorerCoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */; }; C39ED92922D63F3200B5773A /* FLEXAddressExplorerCoordinator.m in Sources */ = {isa = PBXBuildFile; fileRef = C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */; }; C3A04B64288E007100F2C16D /* NSDateFormatter+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3A04B65288E007100F2C16D /* NSDateFormatter+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */; }; C3A9422C23C3DA39006871A3 /* FHSView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9422823C3DA39006871A3 /* FHSView.h */; }; C3A9422D23C3DA39006871A3 /* FHSView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9422923C3DA39006871A3 /* FHSView.m */; }; C3A9423423C3F98E006871A3 /* FHSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9423223C3F98E006871A3 /* FHSViewController.h */; }; C3A9423523C3F98E006871A3 /* FHSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9423323C3F98E006871A3 /* FHSViewController.m */; }; C3A9423823C51B8D006871A3 /* FHSRangeSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9423623C51B8D006871A3 /* FHSRangeSlider.h */; }; C3A9423923C51B8D006871A3 /* FHSRangeSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9423723C51B8D006871A3 /* FHSRangeSlider.m */; }; C3A9423C23C54010006871A3 /* FHSSnapshotView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9423A23C54010006871A3 /* FHSSnapshotView.h */; }; C3A9423D23C54010006871A3 /* FHSSnapshotView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9423B23C54010006871A3 /* FHSSnapshotView.m */; }; C3A9424023C5443A006871A3 /* FHSSnapshotNodes.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9423E23C5443A006871A3 /* FHSSnapshotNodes.h */; }; C3A9424123C5443A006871A3 /* FHSSnapshotNodes.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9423F23C5443A006871A3 /* FHSSnapshotNodes.m */; }; C3A9424523C641C1006871A3 /* SceneKit+Snapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9424323C641C1006871A3 /* SceneKit+Snapshot.h */; }; C3A9424623C641C1006871A3 /* SceneKit+Snapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9424423C641C1006871A3 /* SceneKit+Snapshot.m */; }; C3A9424923C78878006871A3 /* FLEXHierarchyViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9424723C78878006871A3 /* FLEXHierarchyViewController.h */; }; C3A9424A23C78878006871A3 /* FLEXHierarchyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9424823C78878006871A3 /* FLEXHierarchyViewController.m */; }; C3A9424D23C78CFF006871A3 /* FHSViewSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A9424B23C78CFF006871A3 /* FHSViewSnapshot.h */; }; C3A9424E23C78CFF006871A3 /* FHSViewSnapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A9424C23C78CFF006871A3 /* FHSViewSnapshot.m */; }; C3BFD070233C23ED0015FB82 /* NSArray+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3BFD06E233C23ED0015FB82 /* NSArray+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3BFD071233C23ED0015FB82 /* NSArray+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3BFD06F233C23ED0015FB82 /* NSArray+FLEX.m */; }; C3DB9F642107FC9600B46809 /* FLEXObjectRef.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */; }; C3DB9F652107FC9600B46809 /* FLEXObjectRef.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */; }; C3DBFD0C26CE2FAF00E0466A /* OSCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DBFD0A26CE2FAF00E0466A /* OSCache.m */; }; C3DBFD0D26CE2FAF00E0466A /* OSCache.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DBFD0B26CE2FAF00E0466A /* OSCache.h */; }; C3DC287C223ED5F200F48AA6 /* FLEXOSLogController.m in Sources */ = {isa = PBXBuildFile; fileRef = C34EE30721CB23CC00BD3A7C /* FLEXOSLogController.m */; }; C3DFCD942416BC6500BB7084 /* FLEXFilteringTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DFCD922416BC6500BB7084 /* FLEXFilteringTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3DFCD952416BC6500BB7084 /* FLEXFilteringTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */; }; C3DFCD982416E7DD00BB7084 /* FLEXMutableListSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3DFCD992416E7DD00BB7084 /* FLEXMutableListSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */; }; C3DFCDB82418336D00BB7084 /* NSUserDefaults+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3DFCDB92418336D00BB7084 /* NSUserDefaults+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */; }; C3E5D9FD2316E83700E655DB /* FLEXRuntime+Compare.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3E5D9FE2316E83700E655DB /* FLEXRuntime+Compare.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */; }; C3E5DA02231700EE00E655DB /* FLEXObjectInfoSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3EB6F8D242E9C83006EA386 /* FLEXRuntimeExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */; }; C3EB6F8E242E9C83006EA386 /* FLEXRuntimeExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */; }; C3EBE111287028D600702098 /* FLEXAPNSViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */; }; C3EBE112287028D600702098 /* FLEXAPNSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EBE110287028D600702098 /* FLEXAPNSViewController.m */; }; C3EE76BF22DFC63600EC0AA0 /* FLEXScopeCarousel.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */; }; C3EE76C022DFC63600EC0AA0 /* FLEXScopeCarousel.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */; }; C3EF290528EE8BC500B337B6 /* FLEXWindowShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */; }; C3EF290628EE8BC500B337B6 /* FLEXWindowShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */; }; C3F31D3D2267D883003C991A /* FLEXSubtitleTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F31D3E2267D883003C991A /* FLEXTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D352267D883003C991A /* FLEXTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F31D3F2267D883003C991A /* FLEXMultilineTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F31D402267D883003C991A /* FLEXSubtitleTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F31D372267D883003C991A /* FLEXSubtitleTableViewCell.m */; }; C3F31D412267D883003C991A /* FLEXMultilineTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F31D382267D883003C991A /* FLEXMultilineTableViewCell.m */; }; C3F31D422267D883003C991A /* FLEXTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F31D392267D883003C991A /* FLEXTableViewCell.m */; }; C3F31D432267D883003C991A /* FLEXTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F31D3B2267D883003C991A /* FLEXTableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F31D442267D883003C991A /* FLEXTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F31D3C2267D883003C991A /* FLEXTableView.m */; }; C3F527BD2318603F009CBA07 /* FLEXShortcutsSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F527BB2318603F009CBA07 /* FLEXShortcutsSection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F527BE2318603F009CBA07 /* FLEXShortcutsSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F527BC2318603F009CBA07 /* FLEXShortcutsSection.m */; }; C3F527C12318670F009CBA07 /* FLEXImageShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F527BF2318670F009CBA07 /* FLEXImageShortcuts.h */; }; C3F527C22318670F009CBA07 /* FLEXImageShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F527C02318670F009CBA07 /* FLEXImageShortcuts.m */; }; C3F646C1239EAA8F00D4A011 /* UIPasteboard+FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F646BF239EAA8F00D4A011 /* UIPasteboard+FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F646C2239EAA8F00D4A011 /* UIPasteboard+FLEX.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F646C0239EAA8F00D4A011 /* UIPasteboard+FLEX.m */; }; C3F646F223A045DB00D4A011 /* FLEXClassShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F646F023A045DB00D4A011 /* FLEXClassShortcuts.h */; }; C3F646F323A045DB00D4A011 /* FLEXClassShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F646F123A045DB00D4A011 /* FLEXClassShortcuts.m */; }; C3F646F623A04A7500D4A011 /* FLEXShortcut.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F646F423A04A7500D4A011 /* FLEXShortcut.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F646F723A04A7500D4A011 /* FLEXShortcut.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F646F523A04A7500D4A011 /* FLEXShortcut.m */; }; C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F9777D2311B38E0032776D /* NSString+ObjcRuntime.h */; settings = {ATTRIBUTES = (Private, ); }; }; C3F977842311B38F0032776D /* NSDictionary+ObjcRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F9777E2311B38E0032776D /* NSDictionary+ObjcRuntime.m */; }; C3F977852311B38F0032776D /* NSDictionary+ObjcRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F9777F2311B38F0032776D /* NSDictionary+ObjcRuntime.h */; settings = {ATTRIBUTES = (Private, ); }; }; C3F977862311B38F0032776D /* NSString+ObjcRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F977802311B38F0032776D /* NSString+ObjcRuntime.m */; }; C3F977872311B38F0032776D /* NSObject+FLEX_Reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = C3F977812311B38F0032776D /* NSObject+FLEX_Reflection.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3F977882311B38F0032776D /* NSObject+FLEX_Reflection.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F977822311B38F0032776D /* NSObject+FLEX_Reflection.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 1C27A8BC1F0E5A0400F0D02D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3A4C94161B5B20570088C3F2 /* Project object */; proxyType = 1; remoteGlobalIDString = 3A4C941E1B5B20570088C3F2; remoteInfo = FLEX; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 03ED813B24B7D91200831CA0 /* FLEXTableRowDataViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableRowDataViewController.m; sourceTree = ""; }; 03ED813C24B7D91200831CA0 /* FLEXTableRowDataViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableRowDataViewController.h; sourceTree = ""; }; 04F1CA181C137CF1000A52B0 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 1C27A8B61F0E5A0300F0D02D /* FLEXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FLEXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1C27A8B81F0E5A0400F0D02D /* FLEXTestsMethodsList.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTestsMethodsList.m; sourceTree = ""; }; 1C27A8BA1F0E5A0400F0D02D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 222C88211C7339DC007CA15F /* FLEXRealmDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRealmDefines.h; sourceTree = ""; }; 224D49A41C673AB5000EAB86 /* FLEXRealmDatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRealmDatabaseManager.h; sourceTree = ""; }; 224D49A51C673AB5000EAB86 /* FLEXRealmDatabaseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRealmDatabaseManager.m; sourceTree = ""; }; 224D49A61C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSQLiteDatabaseManager.h; sourceTree = ""; }; 224D49A71C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSQLiteDatabaseManager.m; sourceTree = ""; }; 2EF6B04B1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkCurlLogger.m; sourceTree = ""; }; 2EF6B04C1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkCurlLogger.h; sourceTree = ""; }; 3A4C941F1B5B20570088C3F2 /* FLEX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FLEX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3A4C94231B5B20570088C3F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3A4C94241B5B20570088C3F2 /* FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEX.h; sourceTree = ""; }; 3A4C94441B5B21410088C3F2 /* FLEXGlobalsEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXGlobalsEntry.h; sourceTree = ""; }; 3A4C94451B5B21410088C3F2 /* FLEXGlobalsEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXGlobalsEntry.m; sourceTree = ""; }; 3A4C944A1B5B21410088C3F2 /* FLEXObjectExplorerFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXObjectExplorerFactory.h; sourceTree = ""; }; 3A4C944B1B5B21410088C3F2 /* FLEXObjectExplorerFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXObjectExplorerFactory.m; sourceTree = ""; }; 3A4C944C1B5B21410088C3F2 /* FLEXObjectExplorerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXObjectExplorerViewController.h; sourceTree = ""; }; 3A4C944D1B5B21410088C3F2 /* FLEXObjectExplorerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXObjectExplorerViewController.m; sourceTree = ""; }; 3A4C94551B5B21410088C3F2 /* FLEXHeapEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXHeapEnumerator.h; sourceTree = ""; }; 3A4C94561B5B21410088C3F2 /* FLEXHeapEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXHeapEnumerator.m; sourceTree = ""; }; 3A4C94591B5B21410088C3F2 /* FLEXResources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXResources.h; sourceTree = ""; }; 3A4C945A1B5B21410088C3F2 /* FLEXResources.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXResources.m; sourceTree = ""; }; 3A4C945B1B5B21410088C3F2 /* FLEXRuntimeUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeUtility.h; sourceTree = ""; }; 3A4C945C1B5B21410088C3F2 /* FLEXRuntimeUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeUtility.m; sourceTree = ""; }; 3A4C945D1B5B21410088C3F2 /* FLEXUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXUtility.h; sourceTree = ""; }; 3A4C945E1B5B21410088C3F2 /* FLEXUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXUtility.m; sourceTree = ""; }; 3A4C94601B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXHierarchyTableViewCell.h; sourceTree = ""; }; 3A4C94611B5B21410088C3F2 /* FLEXHierarchyTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXHierarchyTableViewCell.m; sourceTree = ""; }; 3A4C94621B5B21410088C3F2 /* FLEXHierarchyTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXHierarchyTableViewController.h; sourceTree = ""; }; 3A4C94631B5B21410088C3F2 /* FLEXHierarchyTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXHierarchyTableViewController.m; sourceTree = ""; }; 3A4C94641B5B21410088C3F2 /* FLEXImagePreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXImagePreviewViewController.h; sourceTree = ""; }; 3A4C94651B5B21410088C3F2 /* FLEXImagePreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXImagePreviewViewController.m; sourceTree = ""; }; 3A4C94681B5B21410088C3F2 /* FLEXArgumentInputColorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputColorView.h; sourceTree = ""; }; 3A4C94691B5B21410088C3F2 /* FLEXArgumentInputColorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputColorView.m; sourceTree = ""; }; 3A4C946A1B5B21410088C3F2 /* FLEXArgumentInputDateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputDateView.h; sourceTree = ""; }; 3A4C946B1B5B21410088C3F2 /* FLEXArgumentInputDateView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputDateView.m; sourceTree = ""; }; 3A4C946C1B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputFontsPickerView.h; sourceTree = ""; }; 3A4C946D1B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputFontsPickerView.m; sourceTree = ""; }; 3A4C946E1B5B21410088C3F2 /* FLEXArgumentInputFontView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputFontView.h; sourceTree = ""; }; 3A4C946F1B5B21410088C3F2 /* FLEXArgumentInputFontView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputFontView.m; sourceTree = ""; }; 3A4C94701B5B21410088C3F2 /* FLEXArgumentInputObjectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputObjectView.h; sourceTree = ""; }; 3A4C94711B5B21410088C3F2 /* FLEXArgumentInputObjectView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputObjectView.m; sourceTree = ""; }; 3A4C94721B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputNotSupportedView.h; sourceTree = ""; }; 3A4C94731B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputNotSupportedView.m; sourceTree = ""; }; 3A4C94741B5B21410088C3F2 /* FLEXArgumentInputNumberView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputNumberView.h; sourceTree = ""; }; 3A4C94751B5B21410088C3F2 /* FLEXArgumentInputNumberView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputNumberView.m; sourceTree = ""; }; 3A4C94761B5B21410088C3F2 /* FLEXArgumentInputStringView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputStringView.h; sourceTree = ""; }; 3A4C94771B5B21410088C3F2 /* FLEXArgumentInputStringView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputStringView.m; sourceTree = ""; }; 3A4C94781B5B21410088C3F2 /* FLEXArgumentInputStructView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputStructView.h; sourceTree = ""; }; 3A4C94791B5B21410088C3F2 /* FLEXArgumentInputStructView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputStructView.m; sourceTree = ""; }; 3A4C947A1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputSwitchView.h; sourceTree = ""; }; 3A4C947B1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputSwitchView.m; sourceTree = ""; }; 3A4C947C1B5B21410088C3F2 /* FLEXArgumentInputTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputTextView.h; sourceTree = ""; }; 3A4C947D1B5B21410088C3F2 /* FLEXArgumentInputTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputTextView.m; sourceTree = ""; }; 3A4C947E1B5B21410088C3F2 /* FLEXArgumentInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputView.h; sourceTree = ""; }; 3A4C947F1B5B21410088C3F2 /* FLEXArgumentInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputView.m; sourceTree = ""; }; 3A4C94801B5B21410088C3F2 /* FLEXArgumentInputViewFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputViewFactory.h; sourceTree = ""; }; 3A4C94811B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputViewFactory.m; sourceTree = ""; }; 3A4C94821B5B21410088C3F2 /* FLEXDefaultEditorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXDefaultEditorViewController.h; sourceTree = ""; }; 3A4C94831B5B21410088C3F2 /* FLEXDefaultEditorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXDefaultEditorViewController.m; sourceTree = ""; }; 3A4C94841B5B21410088C3F2 /* FLEXFieldEditorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXFieldEditorView.h; sourceTree = ""; }; 3A4C94851B5B21410088C3F2 /* FLEXFieldEditorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXFieldEditorView.m; sourceTree = ""; }; 3A4C94861B5B21410088C3F2 /* FLEXFieldEditorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXFieldEditorViewController.h; sourceTree = ""; }; 3A4C94871B5B21410088C3F2 /* FLEXFieldEditorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXFieldEditorViewController.m; sourceTree = ""; }; 3A4C948A1B5B21410088C3F2 /* FLEXMethodCallingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMethodCallingViewController.h; sourceTree = ""; }; 3A4C948B1B5B21410088C3F2 /* FLEXMethodCallingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMethodCallingViewController.m; sourceTree = ""; }; 3A4C949F1B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXFileBrowserSearchOperation.h; sourceTree = ""; }; 3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXFileBrowserSearchOperation.m; sourceTree = ""; }; 3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXFileBrowserController.h; sourceTree = ""; }; 3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXFileBrowserController.m; sourceTree = ""; }; 3A4C94A31B5B21410088C3F2 /* FLEXGlobalsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXGlobalsViewController.h; sourceTree = ""; }; 3A4C94A41B5B21410088C3F2 /* FLEXGlobalsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXGlobalsViewController.m; sourceTree = ""; }; 3A4C94A51B5B21410088C3F2 /* FLEXObjectListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXObjectListViewController.h; sourceTree = ""; }; 3A4C94A61B5B21410088C3F2 /* FLEXObjectListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXObjectListViewController.m; sourceTree = ""; }; 3A4C94A91B5B21410088C3F2 /* FLEXLiveObjectsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXLiveObjectsController.h; sourceTree = ""; }; 3A4C94AA1B5B21410088C3F2 /* FLEXLiveObjectsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXLiveObjectsController.m; sourceTree = ""; }; 3A4C94AB1B5B21410088C3F2 /* FLEXWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWebViewController.h; sourceTree = ""; }; 3A4C94AC1B5B21410088C3F2 /* FLEXWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWebViewController.m; sourceTree = ""; }; 3A4C94AE1B5B21410088C3F2 /* FLEXSystemLogMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSystemLogMessage.h; sourceTree = ""; }; 3A4C94AF1B5B21410088C3F2 /* FLEXSystemLogMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSystemLogMessage.m; sourceTree = ""; }; 3A4C94B01B5B21410088C3F2 /* FLEXSystemLogCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSystemLogCell.h; sourceTree = ""; }; 3A4C94B11B5B21410088C3F2 /* FLEXSystemLogCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSystemLogCell.m; sourceTree = ""; }; 3A4C94B21B5B21410088C3F2 /* FLEXSystemLogViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSystemLogViewController.h; sourceTree = ""; }; 3A4C94B31B5B21410088C3F2 /* FLEXSystemLogViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSystemLogViewController.m; sourceTree = ""; }; 3A4C94B51B5B21410088C3F2 /* FLEXNetworkMITMViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkMITMViewController.h; sourceTree = ""; }; 3A4C94B61B5B21410088C3F2 /* FLEXNetworkMITMViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkMITMViewController.m; sourceTree = ""; }; 3A4C94B71B5B21410088C3F2 /* FLEXNetworkRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkRecorder.h; sourceTree = ""; }; 3A4C94B81B5B21410088C3F2 /* FLEXNetworkRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkRecorder.m; sourceTree = ""; }; 3A4C94B91B5B21410088C3F2 /* FLEXNetworkSettingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkSettingsController.h; sourceTree = ""; }; 3A4C94BA1B5B21410088C3F2 /* FLEXNetworkSettingsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkSettingsController.m; sourceTree = ""; }; 3A4C94BB1B5B21410088C3F2 /* FLEXNetworkTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkTransaction.h; sourceTree = ""; }; 3A4C94BC1B5B21410088C3F2 /* FLEXNetworkTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkTransaction.m; sourceTree = ""; }; 3A4C94BF1B5B21410088C3F2 /* FLEXNetworkTransactionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkTransactionCell.h; sourceTree = ""; }; 3A4C94C01B5B21410088C3F2 /* FLEXNetworkTransactionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkTransactionCell.m; sourceTree = ""; }; 3A4C94C21B5B21410088C3F2 /* FLEXNetworkObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkObserver.h; sourceTree = ""; }; 3A4C94C31B5B21410088C3F2 /* FLEXNetworkObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkObserver.m; sourceTree = ""; }; 3A4C94C41B5B21410088C3F2 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 3A4C95461B5B217D0088C3F2 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 679F64841BD53B7B00A8C94C /* FLEXCookiesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXCookiesViewController.h; sourceTree = ""; }; 679F64851BD53B7B00A8C94C /* FLEXCookiesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXCookiesViewController.m; sourceTree = ""; }; 71E1C20B2307FBB700F5032A /* FLEXKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeychain.h; sourceTree = ""; }; 71E1C20C2307FBB700F5032A /* FLEXKeychainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeychainViewController.h; sourceTree = ""; }; 71E1C20D2307FBB700F5032A /* FLEXKeychainQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeychainQuery.h; sourceTree = ""; }; 71E1C20F2307FBB700F5032A /* FLEXKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeychain.m; sourceTree = ""; }; 71E1C2102307FBB700F5032A /* FLEXKeychainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeychainViewController.m; sourceTree = ""; }; 71E1C2112307FBB700F5032A /* FLEXKeychainQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeychainQuery.m; sourceTree = ""; }; 7349FD6822B93CDF00051810 /* FLEXColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXColor.h; sourceTree = ""; }; 7349FD6922B93CDF00051810 /* FLEXColor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXColor.m; sourceTree = ""; }; 779B1EC01C0C4D7C001F5E49 /* FLEXDatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXDatabaseManager.h; sourceTree = ""; }; 779B1EC21C0C4D7C001F5E49 /* FLEXMultiColumnTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMultiColumnTableView.h; sourceTree = ""; }; 779B1EC31C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMultiColumnTableView.m; sourceTree = ""; }; 779B1EC41C0C4D7C001F5E49 /* FLEXTableColumnHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableColumnHeader.h; sourceTree = ""; }; 779B1EC51C0C4D7C001F5E49 /* FLEXTableColumnHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableColumnHeader.m; sourceTree = ""; }; 779B1EC61C0C4D7C001F5E49 /* FLEXDBQueryRowCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXDBQueryRowCell.h; sourceTree = ""; }; 779B1EC71C0C4D7C001F5E49 /* FLEXDBQueryRowCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXDBQueryRowCell.m; sourceTree = ""; }; 779B1EC81C0C4D7C001F5E49 /* FLEXTableContentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableContentViewController.h; sourceTree = ""; }; 779B1EC91C0C4D7C001F5E49 /* FLEXTableContentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableContentViewController.m; sourceTree = ""; }; 779B1ECA1C0C4D7C001F5E49 /* FLEXTableLeftCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableLeftCell.h; sourceTree = ""; }; 779B1ECB1C0C4D7C001F5E49 /* FLEXTableLeftCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableLeftCell.m; sourceTree = ""; }; 779B1ECC1C0C4D7C001F5E49 /* FLEXTableListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableListViewController.h; sourceTree = ""; }; 779B1ECD1C0C4D7C001F5E49 /* FLEXTableListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableListViewController.m; sourceTree = ""; }; 779B1EDC1C0C4EAD001F5E49 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = ../../../../../usr/lib/libsqlite3.dylib; sourceTree = ""; }; 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardShortcutManager.h; sourceTree = ""; }; 942DCD831BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardShortcutManager.m; sourceTree = ""; }; 94A515131C4CA1C00063292F /* FLEXManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXManager.h; sourceTree = ""; }; 94A515151C4CA1D70063292F /* FLEXManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXManager.m; sourceTree = ""; }; 94A515161C4CA1D70063292F /* FLEXManager+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FLEXManager+Private.h"; sourceTree = ""; }; 94A515191C4CA1F10063292F /* FLEXExplorerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXExplorerViewController.h; sourceTree = ""; }; 94A5151A1C4CA1F10063292F /* FLEXExplorerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXExplorerViewController.m; sourceTree = ""; }; 94A5151B1C4CA1F10063292F /* FLEXWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWindow.h; sourceTree = ""; }; 94A5151C1C4CA1F10063292F /* FLEXWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWindow.m; sourceTree = ""; }; 94A515211C4CA2080063292F /* FLEXExplorerToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FLEXExplorerToolbar.h; path = Classes/Toolbar/FLEXExplorerToolbar.h; sourceTree = SOURCE_ROOT; }; 94A515221C4CA2080063292F /* FLEXExplorerToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLEXExplorerToolbar.m; path = Classes/Toolbar/FLEXExplorerToolbar.m; sourceTree = SOURCE_ROOT; }; 94A515231C4CA2080063292F /* FLEXExplorerToolbarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FLEXExplorerToolbarItem.h; path = Classes/Toolbar/FLEXExplorerToolbarItem.h; sourceTree = SOURCE_ROOT; }; 94A515241C4CA2080063292F /* FLEXExplorerToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLEXExplorerToolbarItem.m; path = Classes/Toolbar/FLEXExplorerToolbarItem.m; sourceTree = SOURCE_ROOT; }; 94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardHelpViewController.h; sourceTree = ""; }; 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardHelpViewController.m; sourceTree = ""; }; A42069CE2809377F00EBCAEA /* FLEXFirebaseTransaction.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FLEXFirebaseTransaction.mm; sourceTree = ""; }; C30199482409B38A00759E8E /* CALayer+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CALayer+FLEX.h"; sourceTree = ""; }; C30199492409B38A00759E8E /* CALayer+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CALayer+FLEX.m"; sourceTree = ""; }; C309B82D223ED64400B228EC /* FLEXLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXLogController.h; sourceTree = ""; }; C30D295C261FAE9E00D89649 /* FLEXNSStringShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNSStringShortcuts.h; sourceTree = ""; }; C30D295D261FAE9E00D89649 /* FLEXNSDataShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNSDataShortcuts.m; sourceTree = ""; }; C30D295E261FAE9E00D89649 /* FLEXNSDataShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNSDataShortcuts.h; sourceTree = ""; }; C30D295F261FAE9E00D89649 /* FLEXNSStringShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNSStringShortcuts.m; sourceTree = ""; }; C30D2966261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Cocoa+FLEXShortcuts.h"; sourceTree = ""; }; C30D2967261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Cocoa+FLEXShortcuts.m"; sourceTree = ""; }; C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBookmarkManager.h; sourceTree = ""; }; C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBookmarkManager.m; sourceTree = ""; }; C312A13223ECBE5800E38049 /* FLEXBookmarksViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBookmarksViewController.h; sourceTree = ""; }; C312A13323ECBE5800E38049 /* FLEXBookmarksViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBookmarksViewController.m; sourceTree = ""; }; C312A13A23ECE79000E38049 /* FLEXWindowManagerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWindowManagerController.m; sourceTree = ""; }; C312A13B23ECE79000E38049 /* FLEXWindowManagerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWindowManagerController.h; sourceTree = ""; }; C313853D23F5C1A10046E63C /* FLEXViewControllersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXViewControllersViewController.h; sourceTree = ""; }; C313853E23F5C1A10046E63C /* FLEXViewControllersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXViewControllersViewController.m; sourceTree = ""; }; C313854623F5F7D40046E63C /* flex_fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flex_fishhook.c; sourceTree = ""; }; C313854723F5F7D50046E63C /* flex_fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = flex_fishhook.h; sourceTree = ""; }; C31C4A6723342A2200C35F12 /* FLEXMetadataSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXMetadataSection.h; sourceTree = ""; }; C31C4A6823342A2200C35F12 /* FLEXMetadataSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXMetadataSection.m; sourceTree = ""; }; C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBlockShortcuts.h; sourceTree = ""; }; C31D93E323E38CBE005517BF /* FLEXBlockShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBlockShortcuts.m; sourceTree = ""; }; C31D93E623E38E97005517BF /* FLEXBlockDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXBlockDescription.h; sourceTree = ""; }; C31D93E723E38E97005517BF /* FLEXBlockDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXBlockDescription.m; sourceTree = ""; }; C32A195C231732E800EB02AC /* FLEXCollectionContentSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXCollectionContentSection.h; sourceTree = ""; }; C32A195D231732E800EB02AC /* FLEXCollectionContentSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXCollectionContentSection.m; sourceTree = ""; }; C32A19602317378C00EB02AC /* FLEXDefaultsContentSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXDefaultsContentSection.h; sourceTree = ""; }; C32A19612317378C00EB02AC /* FLEXDefaultsContentSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXDefaultsContentSection.m; sourceTree = ""; }; C32F3A16247C6B3E0063542D /* FLEXUIAppShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXUIAppShortcuts.h; sourceTree = ""; }; C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXUIAppShortcuts.m; sourceTree = ""; }; C33C825A23159EAF00DD2451 /* FLEXTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTests.m; sourceTree = ""; }; C33C825C2316DC8600DD2451 /* FLEXObjectExplorer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FLEXObjectExplorer.h; path = Classes/ObjectExplorers/FLEXObjectExplorer.h; sourceTree = SOURCE_ROOT; }; C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FLEXObjectExplorer.m; path = Classes/ObjectExplorers/FLEXObjectExplorer.m; sourceTree = SOURCE_ROOT; }; C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXASLLogController.h; sourceTree = ""; }; C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXASLLogController.m; sourceTree = ""; }; C34063B328400A26007B46D3 /* FLEXActivityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXActivityViewController.h; sourceTree = ""; }; C34063B428400A26007B46D3 /* FLEXActivityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXActivityViewController.m; sourceTree = ""; }; C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXKeyValueTableViewCell.h; sourceTree = ""; }; C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyValueTableViewCell.m; sourceTree = ""; }; C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSingleRowSection.h; sourceTree = ""; }; C3490E1E233BDD73002AE200 /* FLEXSingleRowSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXSingleRowSection.m; sourceTree = ""; }; C34C9BDB23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXRuntime+UIKitHelpers.h"; sourceTree = ""; }; C34C9BDC23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXRuntime+UIKitHelpers.m"; sourceTree = ""; }; C34D4EAE23A2ABD900C1F903 /* FLEXLayerShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXLayerShortcuts.h; sourceTree = ""; }; C34D4EAF23A2ABD900C1F903 /* FLEXLayerShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXLayerShortcuts.m; sourceTree = ""; }; C34D4EB223A2AF2A00C1F903 /* FLEXColorPreviewSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXColorPreviewSection.h; sourceTree = ""; }; C34D4EB323A2AF2A00C1F903 /* FLEXColorPreviewSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXColorPreviewSection.m; sourceTree = ""; }; C34D4EB623A2B17900C1F903 /* FLEXBundleShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXBundleShortcuts.h; sourceTree = ""; }; C34D4EB723A2B17900C1F903 /* FLEXBundleShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXBundleShortcuts.m; sourceTree = ""; }; C34EE30621CB23CC00BD3A7C /* FLEXOSLogController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXOSLogController.h; sourceTree = ""; }; C34EE30721CB23CC00BD3A7C /* FLEXOSLogController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXOSLogController.m; sourceTree = ""; }; C34EE30A21CB249E00BD3A7C /* ActivityStreamAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStreamAPI.h; sourceTree = ""; }; C3511B8F22D7C99E0057BAB7 /* FLEXGlobalsSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXGlobalsSection.h; sourceTree = ""; }; C3511B9022D7C99E0057BAB7 /* FLEXGlobalsSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXGlobalsSection.m; sourceTree = ""; }; C3531B9B23E69BB200A184AD /* FLEXManager+Networking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXManager+Networking.h"; sourceTree = ""; }; C3531B9C23E69BB200A184AD /* FLEXManager+Networking.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXManager+Networking.m"; sourceTree = ""; }; C3531B9F23E796BD00A184AD /* FLEXManager+Extensibility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXManager+Extensibility.h"; sourceTree = ""; }; C3531BA023E796BD00A184AD /* FLEXManager+Extensibility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXManager+Extensibility.m"; sourceTree = ""; }; C3531BA323E88A2100A184AD /* FLEXNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNavigationController.h; sourceTree = ""; }; C3531BA423E88A2100A184AD /* FLEXNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNavigationController.m; sourceTree = ""; }; C3531BA823E88FAC00A184AD /* FLEXTabList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTabList.h; sourceTree = ""; }; C3531BA923E88FAC00A184AD /* FLEXTabList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTabList.m; sourceTree = ""; }; C35DAD7E2709140700AA95E6 /* FLEXHTTPTransactionDetailController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXHTTPTransactionDetailController.h; sourceTree = ""; }; C35DAD7F2709140700AA95E6 /* FLEXHTTPTransactionDetailController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXHTTPTransactionDetailController.m; sourceTree = ""; }; C35DAD8C2709143000AA95E6 /* FLEXMITMDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMITMDataSource.h; sourceTree = ""; }; C35DAD8D2709143000AA95E6 /* FLEXMITMDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMITMDataSource.m; sourceTree = ""; }; C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMapTable+FLEX_Subscripting.h"; sourceTree = ""; }; C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMapTable+FLEX_Subscripting.m"; sourceTree = ""; }; C3694DB823EA1096006625D7 /* FLEXTabsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXTabsViewController.h; sourceTree = ""; }; C3694DB923EA1096006625D7 /* FLEXTabsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTabsViewController.m; sourceTree = ""; }; C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+FLEX.h"; sourceTree = ""; }; C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+FLEX.m"; sourceTree = ""; }; C36B096323E0D4A1008F5D47 /* UIMenu+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIMenu+FLEX.h"; sourceTree = ""; }; C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIMenu+FLEX.m"; sourceTree = ""; }; C36B096E23E1EDCD008F5D47 /* FLEXTableViewSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewSection.h; sourceTree = ""; }; C36B096F23E1EDCD008F5D47 /* FLEXTableViewSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTableViewSection.m; sourceTree = ""; }; C36E1B24259D64CC00FEFEF6 /* FLEXNewRootClass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXNewRootClass.h; sourceTree = ""; }; C36E1B25259D64CC00FEFEF6 /* FLEXNewRootClass.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXNewRootClass.m; sourceTree = ""; }; C36FBFB9230F3B97008D95D5 /* FLEXMirror.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMirror.m; sourceTree = ""; }; C36FBFBA230F3B97008D95D5 /* FLEXProtocolBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXProtocolBuilder.h; sourceTree = ""; }; C36FBFBB230F3B97008D95D5 /* FLEXMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMethod.m; sourceTree = ""; }; C36FBFBC230F3B97008D95D5 /* FLEXProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXProperty.h; sourceTree = ""; }; C36FBFBD230F3B97008D95D5 /* FLEXProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXProtocol.h; sourceTree = ""; }; C36FBFBE230F3B97008D95D5 /* FLEXProperty.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXProperty.m; sourceTree = ""; }; C36FBFBF230F3B98008D95D5 /* FLEXClassBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXClassBuilder.m; sourceTree = ""; }; C36FBFC0230F3B98008D95D5 /* FLEXMethodBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMethodBase.h; sourceTree = ""; }; C36FBFC1230F3B98008D95D5 /* FLEXMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMethod.h; sourceTree = ""; }; C36FBFC2230F3B98008D95D5 /* FLEXProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXProtocol.m; sourceTree = ""; }; C36FBFC3230F3B98008D95D5 /* FLEXMethodBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMethodBase.m; sourceTree = ""; }; C36FBFC4230F3B98008D95D5 /* FLEXIvar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXIvar.h; sourceTree = ""; }; C36FBFC5230F3B98008D95D5 /* FLEXMirror.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMirror.h; sourceTree = ""; }; C36FBFC6230F3B98008D95D5 /* FLEXPropertyAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXPropertyAttributes.h; sourceTree = ""; }; C36FBFC7230F3B98008D95D5 /* FLEXProtocolBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXProtocolBuilder.m; sourceTree = ""; }; C36FBFC8230F3B98008D95D5 /* FLEXPropertyAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXPropertyAttributes.m; sourceTree = ""; }; C36FBFC9230F3B98008D95D5 /* FLEXClassBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXClassBuilder.h; sourceTree = ""; }; C36FBFCA230F3B98008D95D5 /* FLEXIvar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXIvar.m; sourceTree = ""; }; C37A0C91218BAC9600848CA7 /* FLEXObjcInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjcInternal.h; sourceTree = ""; }; C37A0C92218BAC9600848CA7 /* FLEXObjcInternal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FLEXObjcInternal.mm; sourceTree = ""; }; C381FDC2271CCB0500401081 /* Firestore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Firestore.h; sourceTree = ""; }; C383C3B723B6A62A007A321B /* FLEXRuntimeSafety.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeSafety.h; sourceTree = ""; }; C383C3B823B6A62A007A321B /* FLEXRuntimeSafety.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeSafety.m; sourceTree = ""; }; C383C3BB23B6B398007A321B /* UITextField+Range.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+Range.m"; sourceTree = ""; }; C383C3BC23B6B398007A321B /* UITextField+Range.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+Range.h"; sourceTree = ""; }; C383C3BF23B6B429007A321B /* NSTimer+FLEX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+FLEX.m"; sourceTree = ""; }; C383C3C023B6B429007A321B /* NSTimer+FLEX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTimer+FLEX.h"; sourceTree = ""; }; C383C3C323B6BB81007A321B /* FLEXCodeFontCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXCodeFontCell.h; sourceTree = ""; }; C383C3C423B6BB81007A321B /* FLEXCodeFontCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXCodeFontCell.m; sourceTree = ""; }; C3854DEF23F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTypeEncodingParserTests.m; sourceTree = ""; }; C3854DF123F36C9E00FCD1E2 /* FLEXTypeEncodingParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTypeEncodingParser.m; sourceTree = ""; }; C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTypeEncodingParser.h; sourceTree = ""; }; C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSwiftInternal.h; sourceTree = ""; }; C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FLEXSwiftInternal.mm; sourceTree = ""; }; C386D6E824199C1B00699085 /* FLEX-Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-Core.h"; sourceTree = ""; }; C386D6EA24199E9600699085 /* FLEX-ObjectExploring.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-ObjectExploring.h"; sourceTree = ""; }; C386D6EC24199EC600699085 /* FLEX-Runtime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-Runtime.h"; sourceTree = ""; }; C386D6EE2419A2F400699085 /* FLEXRuntimeConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeConstants.h; sourceTree = ""; }; C386D6EF2419A33F00699085 /* FLEXRuntimeConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeConstants.m; sourceTree = ""; }; C386D6F1241A96AD00699085 /* FLEXMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXMacros.h; sourceTree = ""; }; C386D6F5241A9D6900699085 /* FLEX-Categories.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEX-Categories.h"; sourceTree = ""; }; C3878DB823A749960038FDBE /* FLEXVariableEditorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXVariableEditorViewController.m; sourceTree = ""; }; C3878DB923A749960038FDBE /* FLEXVariableEditorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXVariableEditorViewController.h; sourceTree = ""; }; C387C87822DFCD6A00750E58 /* FLEXCarouselCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXCarouselCell.h; sourceTree = ""; }; C387C87922DFCD6A00750E58 /* FLEXCarouselCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXCarouselCell.m; sourceTree = ""; }; C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+FLEX_Layout.h"; sourceTree = ""; }; C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+FLEX_Layout.m"; sourceTree = ""; }; C38D970028190C93008709D0 /* FLEXMetadataExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMetadataExtras.m; sourceTree = ""; }; C38D970128190C93008709D0 /* FLEXMetadataExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMetadataExtras.h; sourceTree = ""; }; C38DF0E822CFE4370077B4AD /* FLEXTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewController.h; sourceTree = ""; }; C38DF0E922CFE4370077B4AD /* FLEXTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTableViewController.m; sourceTree = ""; }; C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXViewControllerShortcuts.m; sourceTree = ""; }; C38EF26123A2FCD20047A7EC /* FLEXViewControllerShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXViewControllerShortcuts.h; sourceTree = ""; }; C38F3F2F230C958F004E3731 /* FLEXAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXAlert.h; sourceTree = ""; }; C38F3F30230C958F004E3731 /* FLEXAlert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXAlert.m; sourceTree = ""; }; C397E316240EC98F0091E4EC /* FLEXSQLResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSQLResult.h; sourceTree = ""; }; C397E317240EC98F0091E4EC /* FLEXSQLResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXSQLResult.m; sourceTree = ""; }; C398624323AD6C67007E6793 /* FLEXKeyPathSearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyPathSearchController.h; sourceTree = ""; }; C398624423AD6C67007E6793 /* FLEXRuntimeKeyPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeKeyPath.m; sourceTree = ""; }; C398624523AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeKeyPathTokenizer.h; sourceTree = ""; }; C398624623AD6C67007E6793 /* FLEXObjcRuntimeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXObjcRuntimeViewController.m; sourceTree = ""; }; C398624723AD6C67007E6793 /* FLEXRuntimeKeyPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeKeyPath.h; sourceTree = ""; }; C398624823AD6C67007E6793 /* FLEXKeyPathSearchController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyPathSearchController.m; sourceTree = ""; }; C398624923AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeKeyPathTokenizer.m; sourceTree = ""; }; C398624A23AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeBrowserToolbar.h; sourceTree = ""; }; C398624B23AD6C67007E6793 /* FLEXObjcRuntimeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXObjcRuntimeViewController.h; sourceTree = ""; }; C398624C23AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeBrowserToolbar.m; sourceTree = ""; }; C398625723AD6C88007E6793 /* FLEXSearchToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSearchToken.h; sourceTree = ""; }; C398625823AD6C88007E6793 /* FLEXSearchToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSearchToken.m; sourceTree = ""; }; C398625B23AD6E90007E6793 /* UIFont+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+FLEX.h"; sourceTree = ""; }; C398625C23AD6E90007E6793 /* UIFont+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+FLEX.m"; sourceTree = ""; }; C398625F23AD70DF007E6793 /* FLEXKeyboardToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyboardToolbar.h; sourceTree = ""; }; C398626023AD70DF007E6793 /* FLEXKeyboardToolbar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyboardToolbar.m; sourceTree = ""; }; C398626323AD70F5007E6793 /* FLEXKBToolbarButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKBToolbarButton.h; sourceTree = ""; }; C398626423AD70F5007E6793 /* FLEXKBToolbarButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKBToolbarButton.m; sourceTree = ""; }; C398626723AD71C1007E6793 /* FLEXRuntimeClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeClient.h; sourceTree = ""; }; C398626823AD71C1007E6793 /* FLEXRuntimeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeController.h; sourceTree = ""; }; C398626923AD71C1007E6793 /* FLEXRuntimeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeController.m; sourceTree = ""; }; C398626A23AD71C1007E6793 /* FLEXRuntimeClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeClient.m; sourceTree = ""; }; C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+Blocks.h"; sourceTree = ""; }; C398627123AD7951007E6793 /* UIGestureRecognizer+Blocks.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+Blocks.m"; sourceTree = ""; }; C398627423AD79B6007E6793 /* NSString+FLEX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+FLEX.h"; sourceTree = ""; }; C398627523AD79B7007E6793 /* NSString+FLEX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+FLEX.m"; sourceTree = ""; }; C398682323AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "FLEXShortcutsFactory+Defaults.m"; sourceTree = ""; }; C398682423AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FLEXShortcutsFactory+Defaults.h"; sourceTree = ""; }; C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXAddressExplorerCoordinator.h; sourceTree = ""; }; C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXAddressExplorerCoordinator.m; sourceTree = ""; }; C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+FLEX.h"; sourceTree = ""; }; C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+FLEX.m"; sourceTree = ""; }; C3A9422823C3DA39006871A3 /* FHSView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FHSView.h; sourceTree = ""; }; C3A9422923C3DA39006871A3 /* FHSView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FHSView.m; sourceTree = ""; }; C3A9423223C3F98E006871A3 /* FHSViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSViewController.h; sourceTree = ""; }; C3A9423323C3F98E006871A3 /* FHSViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FHSViewController.m; sourceTree = ""; }; C3A9423623C51B8D006871A3 /* FHSRangeSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSRangeSlider.h; sourceTree = ""; }; C3A9423723C51B8D006871A3 /* FHSRangeSlider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FHSRangeSlider.m; sourceTree = ""; }; C3A9423A23C54010006871A3 /* FHSSnapshotView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSSnapshotView.h; sourceTree = ""; }; C3A9423B23C54010006871A3 /* FHSSnapshotView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FHSSnapshotView.m; sourceTree = ""; }; C3A9423E23C5443A006871A3 /* FHSSnapshotNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSSnapshotNodes.h; sourceTree = ""; }; C3A9423F23C5443A006871A3 /* FHSSnapshotNodes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FHSSnapshotNodes.m; sourceTree = ""; }; C3A9424323C641C1006871A3 /* SceneKit+Snapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SceneKit+Snapshot.h"; sourceTree = ""; }; C3A9424423C641C1006871A3 /* SceneKit+Snapshot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SceneKit+Snapshot.m"; sourceTree = ""; }; C3A9424723C78878006871A3 /* FLEXHierarchyViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXHierarchyViewController.h; sourceTree = ""; }; C3A9424823C78878006871A3 /* FLEXHierarchyViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXHierarchyViewController.m; sourceTree = ""; }; C3A9424B23C78CFF006871A3 /* FHSViewSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FHSViewSnapshot.h; sourceTree = ""; }; C3A9424C23C78CFF006871A3 /* FHSViewSnapshot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FHSViewSnapshot.m; sourceTree = ""; }; C3BFD06E233C23ED0015FB82 /* NSArray+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSArray+FLEX.h"; sourceTree = ""; }; C3BFD06F233C23ED0015FB82 /* NSArray+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSArray+FLEX.m"; sourceTree = ""; }; C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectRef.h; sourceTree = ""; }; C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXObjectRef.m; sourceTree = ""; }; C3DBFD0A26CE2FAF00E0466A /* OSCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSCache.m; sourceTree = ""; }; C3DBFD0B26CE2FAF00E0466A /* OSCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSCache.h; sourceTree = ""; }; C3DFCD922416BC6500BB7084 /* FLEXFilteringTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXFilteringTableViewController.h; sourceTree = ""; }; C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXFilteringTableViewController.m; sourceTree = ""; }; C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXMutableListSection.h; sourceTree = ""; }; C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXMutableListSection.m; sourceTree = ""; }; C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+FLEX.h"; sourceTree = ""; }; C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+FLEX.m"; sourceTree = ""; }; C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEXRuntime+Compare.h"; sourceTree = ""; }; C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FLEXRuntime+Compare.m"; sourceTree = ""; }; C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXObjectInfoSection.h; sourceTree = ""; }; C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXRuntimeExporter.m; sourceTree = ""; }; C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXRuntimeExporter.h; sourceTree = ""; }; C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXAPNSViewController.h; sourceTree = ""; }; C3EBE110287028D600702098 /* FLEXAPNSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXAPNSViewController.m; sourceTree = ""; }; C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXScopeCarousel.h; sourceTree = ""; }; C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXScopeCarousel.m; sourceTree = ""; }; C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXWindowShortcuts.h; sourceTree = ""; }; C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXWindowShortcuts.m; sourceTree = ""; }; C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXSubtitleTableViewCell.h; sourceTree = ""; }; C3F31D352267D883003C991A /* FLEXTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableViewCell.h; sourceTree = ""; }; C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXMultilineTableViewCell.h; sourceTree = ""; }; C3F31D372267D883003C991A /* FLEXSubtitleTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSubtitleTableViewCell.m; sourceTree = ""; }; C3F31D382267D883003C991A /* FLEXMultilineTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXMultilineTableViewCell.m; sourceTree = ""; }; C3F31D392267D883003C991A /* FLEXTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableViewCell.m; sourceTree = ""; }; C3F31D3B2267D883003C991A /* FLEXTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXTableView.h; sourceTree = ""; }; C3F31D3C2267D883003C991A /* FLEXTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXTableView.m; sourceTree = ""; }; C3F527BB2318603F009CBA07 /* FLEXShortcutsSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXShortcutsSection.h; sourceTree = ""; }; C3F527BC2318603F009CBA07 /* FLEXShortcutsSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXShortcutsSection.m; sourceTree = ""; }; C3F527BF2318670F009CBA07 /* FLEXImageShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXImageShortcuts.h; sourceTree = ""; }; C3F527C02318670F009CBA07 /* FLEXImageShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXImageShortcuts.m; sourceTree = ""; }; C3F527C3231891F6009CBA07 /* FLEXViewShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXViewShortcuts.h; sourceTree = ""; }; C3F527C4231891F6009CBA07 /* FLEXViewShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXViewShortcuts.m; sourceTree = ""; }; C3F646BF239EAA8F00D4A011 /* UIPasteboard+FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIPasteboard+FLEX.h"; sourceTree = ""; }; C3F646C0239EAA8F00D4A011 /* UIPasteboard+FLEX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIPasteboard+FLEX.m"; sourceTree = ""; }; C3F646F023A045DB00D4A011 /* FLEXClassShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXClassShortcuts.h; sourceTree = ""; }; C3F646F123A045DB00D4A011 /* FLEXClassShortcuts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXClassShortcuts.m; sourceTree = ""; }; C3F646F423A04A7500D4A011 /* FLEXShortcut.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXShortcut.h; sourceTree = ""; }; C3F646F523A04A7500D4A011 /* FLEXShortcut.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXShortcut.m; sourceTree = ""; }; C3F9777D2311B38E0032776D /* NSString+ObjcRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+ObjcRuntime.h"; sourceTree = ""; }; C3F9777E2311B38E0032776D /* NSDictionary+ObjcRuntime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+ObjcRuntime.m"; sourceTree = ""; }; C3F9777F2311B38F0032776D /* NSDictionary+ObjcRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+ObjcRuntime.h"; sourceTree = ""; }; C3F977802311B38F0032776D /* NSString+ObjcRuntime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+ObjcRuntime.m"; sourceTree = ""; }; C3F977812311B38F0032776D /* NSObject+FLEX_Reflection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+FLEX_Reflection.h"; sourceTree = ""; }; C3F977822311B38F0032776D /* NSObject+FLEX_Reflection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+FLEX_Reflection.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 1C27A8B31F0E5A0300F0D02D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1C27A8BB1F0E5A0400F0D02D /* FLEX.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 3A4C941B1B5B20570088C3F2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 779B1EDD1C0C4EAD001F5E49 /* libsqlite3.dylib in Frameworks */, 3A4C95471B5B217D0088C3F2 /* libz.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1C27A8B71F0E5A0400F0D02D /* FLEXTests */ = { isa = PBXGroup; children = ( C33C825A23159EAF00DD2451 /* FLEXTests.m */, 1C27A8B81F0E5A0400F0D02D /* FLEXTestsMethodsList.m */, C3854DEF23F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m */, 1C27A8BA1F0E5A0400F0D02D /* Info.plist */, C36E1B27259D64D300FEFEF6 /* Supporting Files */, ); path = FLEXTests; sourceTree = ""; }; 3A4C94151B5B20570088C3F2 = { isa = PBXGroup; children = ( 3A4C94211B5B20570088C3F2 /* FLEX */, 1C27A8B71F0E5A0400F0D02D /* FLEXTests */, 3A4C95451B5B216C0088C3F2 /* Frameworks */, 3A4C94201B5B20570088C3F2 /* Products */, ); sourceTree = ""; }; 3A4C94201B5B20570088C3F2 /* Products */ = { isa = PBXGroup; children = ( 3A4C941F1B5B20570088C3F2 /* FLEX.framework */, 1C27A8B61F0E5A0300F0D02D /* FLEXTests.xctest */, ); name = Products; sourceTree = ""; }; 3A4C94211B5B20570088C3F2 /* FLEX */ = { isa = PBXGroup; children = ( 3A4C94241B5B20570088C3F2 /* FLEX.h */, C386D6E824199C1B00699085 /* FLEX-Core.h */, C386D6EC24199EC600699085 /* FLEX-Runtime.h */, C386D6F5241A9D6900699085 /* FLEX-Categories.h */, C386D6EA24199E9600699085 /* FLEX-ObjectExploring.h */, 94A515111C4C9C1B0063292F /* Manager */, C38DF0E722CFE4140077B4AD /* Core */, 94A515121C4C9E7B0063292F /* ExplorerInterface */, 3A4C94661B5B21410088C3F2 /* Editing */, 3A4C948E1B5B21410088C3F2 /* Toolbar */, 3A4C949A1B5B21410088C3F2 /* GlobalStateExplorers */, 3A4C94B41B5B21410088C3F2 /* Network */, 3A4C943B1B5B21410088C3F2 /* ObjectExplorers */, 3A4C94541B5B21410088C3F2 /* Utility */, 3A4C945F1B5B21410088C3F2 /* ViewHierarchy */, 3A4C94221B5B20570088C3F2 /* Supporting Files */, ); name = FLEX; path = Classes; sourceTree = ""; }; 3A4C94221B5B20570088C3F2 /* Supporting Files */ = { isa = PBXGroup; children = ( 3A4C94231B5B20570088C3F2 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 3A4C943B1B5B21410088C3F2 /* ObjectExplorers */ = { isa = PBXGroup; children = ( C3E5D9FF2317007F00E655DB /* Sections */, C33C825C2316DC8600DD2451 /* FLEXObjectExplorer.h */, C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */, 3A4C944A1B5B21410088C3F2 /* FLEXObjectExplorerFactory.h */, 3A4C944B1B5B21410088C3F2 /* FLEXObjectExplorerFactory.m */, 3A4C944C1B5B21410088C3F2 /* FLEXObjectExplorerViewController.h */, 3A4C944D1B5B21410088C3F2 /* FLEXObjectExplorerViewController.m */, ); path = ObjectExplorers; sourceTree = ""; }; 3A4C94541B5B21410088C3F2 /* Utility */ = { isa = PBXGroup; children = ( C36FBFB8230F3B52008D95D5 /* Runtime */, C387C88022E0D22600750E58 /* Categories */, C3854DF423F36CB300FCD1E2 /* Keyboard */, 3A4C94551B5B21410088C3F2 /* FLEXHeapEnumerator.h */, 3A4C94561B5B21410088C3F2 /* FLEXHeapEnumerator.m */, 3A4C94591B5B21410088C3F2 /* FLEXResources.h */, 3A4C945A1B5B21410088C3F2 /* FLEXResources.m */, 3A4C945D1B5B21410088C3F2 /* FLEXUtility.h */, 3A4C945E1B5B21410088C3F2 /* FLEXUtility.m */, C38F3F2F230C958F004E3731 /* FLEXAlert.h */, C38F3F30230C958F004E3731 /* FLEXAlert.m */, 7349FD6822B93CDF00051810 /* FLEXColor.h */, 7349FD6922B93CDF00051810 /* FLEXColor.m */, C386D6F1241A96AD00699085 /* FLEXMacros.h */, ); path = Utility; sourceTree = ""; }; 3A4C945F1B5B21410088C3F2 /* ViewHierarchy */ = { isa = PBXGroup; children = ( C3A9424F23C7ABB2006871A3 /* TreeExplorer */, C3A9422523C3DA1E006871A3 /* SnapshotExplorer */, C3A9424723C78878006871A3 /* FLEXHierarchyViewController.h */, C3A9424823C78878006871A3 /* FLEXHierarchyViewController.m */, 3A4C94641B5B21410088C3F2 /* FLEXImagePreviewViewController.h */, 3A4C94651B5B21410088C3F2 /* FLEXImagePreviewViewController.m */, ); path = ViewHierarchy; sourceTree = ""; }; 3A4C94661B5B21410088C3F2 /* Editing */ = { isa = PBXGroup; children = ( 3A4C94671B5B21410088C3F2 /* ArgumentInputViews */, 3A4C94801B5B21410088C3F2 /* FLEXArgumentInputViewFactory.h */, 3A4C94811B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m */, 3A4C94841B5B21410088C3F2 /* FLEXFieldEditorView.h */, 3A4C94851B5B21410088C3F2 /* FLEXFieldEditorView.m */, C3878DB923A749960038FDBE /* FLEXVariableEditorViewController.h */, C3878DB823A749960038FDBE /* FLEXVariableEditorViewController.m */, 3A4C94861B5B21410088C3F2 /* FLEXFieldEditorViewController.h */, 3A4C94871B5B21410088C3F2 /* FLEXFieldEditorViewController.m */, 3A4C94821B5B21410088C3F2 /* FLEXDefaultEditorViewController.h */, 3A4C94831B5B21410088C3F2 /* FLEXDefaultEditorViewController.m */, 3A4C948A1B5B21410088C3F2 /* FLEXMethodCallingViewController.h */, 3A4C948B1B5B21410088C3F2 /* FLEXMethodCallingViewController.m */, ); path = Editing; sourceTree = ""; }; 3A4C94671B5B21410088C3F2 /* ArgumentInputViews */ = { isa = PBXGroup; children = ( 3A4C94681B5B21410088C3F2 /* FLEXArgumentInputColorView.h */, 3A4C94691B5B21410088C3F2 /* FLEXArgumentInputColorView.m */, 3A4C946A1B5B21410088C3F2 /* FLEXArgumentInputDateView.h */, 3A4C946B1B5B21410088C3F2 /* FLEXArgumentInputDateView.m */, 3A4C946C1B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.h */, 3A4C946D1B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.m */, 3A4C946E1B5B21410088C3F2 /* FLEXArgumentInputFontView.h */, 3A4C946F1B5B21410088C3F2 /* FLEXArgumentInputFontView.m */, 3A4C94701B5B21410088C3F2 /* FLEXArgumentInputObjectView.h */, 3A4C94711B5B21410088C3F2 /* FLEXArgumentInputObjectView.m */, 3A4C94721B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.h */, 3A4C94731B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m */, 3A4C94741B5B21410088C3F2 /* FLEXArgumentInputNumberView.h */, 3A4C94751B5B21410088C3F2 /* FLEXArgumentInputNumberView.m */, 3A4C94761B5B21410088C3F2 /* FLEXArgumentInputStringView.h */, 3A4C94771B5B21410088C3F2 /* FLEXArgumentInputStringView.m */, 3A4C94781B5B21410088C3F2 /* FLEXArgumentInputStructView.h */, 3A4C94791B5B21410088C3F2 /* FLEXArgumentInputStructView.m */, 3A4C947A1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h */, 3A4C947B1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.m */, 3A4C947C1B5B21410088C3F2 /* FLEXArgumentInputTextView.h */, 3A4C947D1B5B21410088C3F2 /* FLEXArgumentInputTextView.m */, 3A4C947E1B5B21410088C3F2 /* FLEXArgumentInputView.h */, 3A4C947F1B5B21410088C3F2 /* FLEXArgumentInputView.m */, ); path = ArgumentInputViews; sourceTree = ""; }; 3A4C948E1B5B21410088C3F2 /* Toolbar */ = { isa = PBXGroup; children = ( 94A515211C4CA2080063292F /* FLEXExplorerToolbar.h */, 94A515221C4CA2080063292F /* FLEXExplorerToolbar.m */, 94A515231C4CA2080063292F /* FLEXExplorerToolbarItem.h */, 94A515241C4CA2080063292F /* FLEXExplorerToolbarItem.m */, ); path = Toolbar; sourceTree = ""; }; 3A4C949A1B5B21410088C3F2 /* GlobalStateExplorers */ = { isa = PBXGroup; children = ( C398624223AD6B92007E6793 /* RuntimeBrowser */, 71E1C2092307FBB700F5032A /* Keychain */, C3511B8E22D7C9740057BAB7 /* Globals */, 779B1EBF1C0C4D7C001F5E49 /* DatabaseBrowser */, 3A4C94AD1B5B21410088C3F2 /* SystemLog */, C33CF16B22D664E600F9C6C0 /* FileBrowser */, 3A4C94A51B5B21410088C3F2 /* FLEXObjectListViewController.h */, 3A4C94A61B5B21410088C3F2 /* FLEXObjectListViewController.m */, C3DB9F622107FC9600B46809 /* FLEXObjectRef.h */, C3DB9F632107FC9600B46809 /* FLEXObjectRef.m */, 3A4C94A91B5B21410088C3F2 /* FLEXLiveObjectsController.h */, 3A4C94AA1B5B21410088C3F2 /* FLEXLiveObjectsController.m */, C3EBE10F287028D600702098 /* FLEXAPNSViewController.h */, C3EBE110287028D600702098 /* FLEXAPNSViewController.m */, 679F64841BD53B7B00A8C94C /* FLEXCookiesViewController.h */, 679F64851BD53B7B00A8C94C /* FLEXCookiesViewController.m */, 3A4C94AB1B5B21410088C3F2 /* FLEXWebViewController.h */, 3A4C94AC1B5B21410088C3F2 /* FLEXWebViewController.m */, C39ED92622D63F3200B5773A /* FLEXAddressExplorerCoordinator.h */, C39ED92722D63F3200B5773A /* FLEXAddressExplorerCoordinator.m */, ); path = GlobalStateExplorers; sourceTree = ""; }; 3A4C94AD1B5B21410088C3F2 /* SystemLog */ = { isa = PBXGroup; children = ( 3A4C94AE1B5B21410088C3F2 /* FLEXSystemLogMessage.h */, 3A4C94AF1B5B21410088C3F2 /* FLEXSystemLogMessage.m */, 3A4C94B01B5B21410088C3F2 /* FLEXSystemLogCell.h */, 3A4C94B11B5B21410088C3F2 /* FLEXSystemLogCell.m */, 3A4C94B21B5B21410088C3F2 /* FLEXSystemLogViewController.h */, 3A4C94B31B5B21410088C3F2 /* FLEXSystemLogViewController.m */, C309B82D223ED64400B228EC /* FLEXLogController.h */, C33E46AD223B02CD004BD0E6 /* FLEXASLLogController.h */, C33E46AE223B02CD004BD0E6 /* FLEXASLLogController.m */, C34EE30621CB23CC00BD3A7C /* FLEXOSLogController.h */, C34EE30721CB23CC00BD3A7C /* FLEXOSLogController.m */, C34EE30A21CB249E00BD3A7C /* ActivityStreamAPI.h */, ); path = SystemLog; sourceTree = ""; }; 3A4C94B41B5B21410088C3F2 /* Network */ = { isa = PBXGroup; children = ( C381FDC2271CCB0500401081 /* Firestore.h */, 3A4C94B51B5B21410088C3F2 /* FLEXNetworkMITMViewController.h */, 3A4C94B61B5B21410088C3F2 /* FLEXNetworkMITMViewController.m */, 3A4C94B71B5B21410088C3F2 /* FLEXNetworkRecorder.h */, 3A4C94B81B5B21410088C3F2 /* FLEXNetworkRecorder.m */, 3A4C94B91B5B21410088C3F2 /* FLEXNetworkSettingsController.h */, 3A4C94BA1B5B21410088C3F2 /* FLEXNetworkSettingsController.m */, 3A4C94BB1B5B21410088C3F2 /* FLEXNetworkTransaction.h */, 3A4C94BC1B5B21410088C3F2 /* FLEXNetworkTransaction.m */, A42069CE2809377F00EBCAEA /* FLEXFirebaseTransaction.mm */, C35DAD7E2709140700AA95E6 /* FLEXHTTPTransactionDetailController.h */, C35DAD7F2709140700AA95E6 /* FLEXHTTPTransactionDetailController.m */, C35DAD8C2709143000AA95E6 /* FLEXMITMDataSource.h */, C35DAD8D2709143000AA95E6 /* FLEXMITMDataSource.m */, 3A4C94BF1B5B21410088C3F2 /* FLEXNetworkTransactionCell.h */, 3A4C94C01B5B21410088C3F2 /* FLEXNetworkTransactionCell.m */, 2EF6B04C1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h */, 2EF6B04B1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m */, C3DBFD0926CE2FAF00E0466A /* OSCache */, 3A4C94C11B5B21410088C3F2 /* PonyDebugger */, ); path = Network; sourceTree = ""; }; 3A4C94C11B5B21410088C3F2 /* PonyDebugger */ = { isa = PBXGroup; children = ( 3A4C94C21B5B21410088C3F2 /* FLEXNetworkObserver.h */, 3A4C94C31B5B21410088C3F2 /* FLEXNetworkObserver.m */, 3A4C94C41B5B21410088C3F2 /* LICENSE */, ); path = PonyDebugger; sourceTree = ""; }; 3A4C95451B5B216C0088C3F2 /* Frameworks */ = { isa = PBXGroup; children = ( 779B1EDC1C0C4EAD001F5E49 /* libsqlite3.dylib */, 3A4C95461B5B217D0088C3F2 /* libz.dylib */, ); name = Frameworks; sourceTree = ""; }; 71E1C2092307FBB700F5032A /* Keychain */ = { isa = PBXGroup; children = ( 71E1C20B2307FBB700F5032A /* FLEXKeychain.h */, 71E1C20F2307FBB700F5032A /* FLEXKeychain.m */, 71E1C20D2307FBB700F5032A /* FLEXKeychainQuery.h */, 71E1C2112307FBB700F5032A /* FLEXKeychainQuery.m */, 71E1C20C2307FBB700F5032A /* FLEXKeychainViewController.h */, 71E1C2102307FBB700F5032A /* FLEXKeychainViewController.m */, ); path = Keychain; sourceTree = ""; }; 779B1EBF1C0C4D7C001F5E49 /* DatabaseBrowser */ = { isa = PBXGroup; children = ( C397E316240EC98F0091E4EC /* FLEXSQLResult.h */, C397E317240EC98F0091E4EC /* FLEXSQLResult.m */, 222C88211C7339DC007CA15F /* FLEXRealmDefines.h */, 779B1EC01C0C4D7C001F5E49 /* FLEXDatabaseManager.h */, 224D49A41C673AB5000EAB86 /* FLEXRealmDatabaseManager.h */, 224D49A51C673AB5000EAB86 /* FLEXRealmDatabaseManager.m */, 224D49A61C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.h */, 224D49A71C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m */, 779B1EC21C0C4D7C001F5E49 /* FLEXMultiColumnTableView.h */, 779B1EC31C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m */, 779B1EC41C0C4D7C001F5E49 /* FLEXTableColumnHeader.h */, 779B1EC51C0C4D7C001F5E49 /* FLEXTableColumnHeader.m */, 779B1EC61C0C4D7C001F5E49 /* FLEXDBQueryRowCell.h */, 779B1EC71C0C4D7C001F5E49 /* FLEXDBQueryRowCell.m */, 779B1EC81C0C4D7C001F5E49 /* FLEXTableContentViewController.h */, 779B1EC91C0C4D7C001F5E49 /* FLEXTableContentViewController.m */, 779B1ECA1C0C4D7C001F5E49 /* FLEXTableLeftCell.h */, 779B1ECB1C0C4D7C001F5E49 /* FLEXTableLeftCell.m */, 779B1ECC1C0C4D7C001F5E49 /* FLEXTableListViewController.h */, 779B1ECD1C0C4D7C001F5E49 /* FLEXTableListViewController.m */, 03ED813C24B7D91200831CA0 /* FLEXTableRowDataViewController.h */, 03ED813B24B7D91200831CA0 /* FLEXTableRowDataViewController.m */, 04F1CA181C137CF1000A52B0 /* LICENSE */, ); path = DatabaseBrowser; sourceTree = ""; }; 94A515111C4C9C1B0063292F /* Manager */ = { isa = PBXGroup; children = ( 94A515131C4CA1C00063292F /* FLEXManager.h */, 94A515151C4CA1D70063292F /* FLEXManager.m */, C3F3417F242276D200A597D7 /* Private */, C3531B9F23E796BD00A184AD /* FLEXManager+Extensibility.h */, C3531BA023E796BD00A184AD /* FLEXManager+Extensibility.m */, C3531B9B23E69BB200A184AD /* FLEXManager+Networking.h */, C3531B9C23E69BB200A184AD /* FLEXManager+Networking.m */, ); path = Manager; sourceTree = ""; }; 94A515121C4C9E7B0063292F /* ExplorerInterface */ = { isa = PBXGroup; children = ( C312A12D23ECB55B00E38049 /* Bookmarks */, C3531BA723E88FAC00A184AD /* Tabs */, 94A5151B1C4CA1F10063292F /* FLEXWindow.h */, 94A5151C1C4CA1F10063292F /* FLEXWindow.m */, 94A515191C4CA1F10063292F /* FLEXExplorerViewController.h */, 94A5151A1C4CA1F10063292F /* FLEXExplorerViewController.m */, C312A13B23ECE79000E38049 /* FLEXWindowManagerController.h */, C312A13A23ECE79000E38049 /* FLEXWindowManagerController.m */, C313853D23F5C1A10046E63C /* FLEXViewControllersViewController.h */, C313853E23F5C1A10046E63C /* FLEXViewControllersViewController.m */, ); path = ExplorerInterface; sourceTree = ""; }; C312A12D23ECB55B00E38049 /* Bookmarks */ = { isa = PBXGroup; children = ( C312A12E23ECB5D300E38049 /* FLEXBookmarkManager.h */, C312A12F23ECB5D300E38049 /* FLEXBookmarkManager.m */, C312A13223ECBE5800E38049 /* FLEXBookmarksViewController.h */, C312A13323ECBE5800E38049 /* FLEXBookmarksViewController.m */, ); path = Bookmarks; sourceTree = ""; }; C313854523F5F6120046E63C /* Objc */ = { isa = PBXGroup; children = ( C386D6F4241A9B5300699085 /* Reflection */, C383C3B723B6A62A007A321B /* FLEXRuntimeSafety.h */, C383C3B823B6A62A007A321B /* FLEXRuntimeSafety.m */, C37A0C91218BAC9600848CA7 /* FLEXObjcInternal.h */, C37A0C92218BAC9600848CA7 /* FLEXObjcInternal.mm */, C38568F8272B3BFC00B1E37F /* FLEXSwiftInternal.h */, C38568F9272B3BFC00B1E37F /* FLEXSwiftInternal.mm */, C386D6EE2419A2F400699085 /* FLEXRuntimeConstants.h */, C386D6EF2419A33F00699085 /* FLEXRuntimeConstants.m */, C3854DF223F36C9E00FCD1E2 /* FLEXTypeEncodingParser.h */, C3854DF123F36C9E00FCD1E2 /* FLEXTypeEncodingParser.m */, ); path = Objc; sourceTree = ""; }; C31E4E53259D4A4100712288 /* Private */ = { isa = PBXGroup; children = ( C30D2966261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h */, C30D2967261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m */, C362AE7F23C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h */, C362AE8023C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m */, C387C88122E0D24A00750E58 /* UIView+FLEX_Layout.h */, C387C88222E0D24A00750E58 /* UIView+FLEX_Layout.m */, C3F9777F2311B38F0032776D /* NSDictionary+ObjcRuntime.h */, C3F9777E2311B38E0032776D /* NSDictionary+ObjcRuntime.m */, C3F9777D2311B38E0032776D /* NSString+ObjcRuntime.h */, C3F977802311B38F0032776D /* NSString+ObjcRuntime.m */, C398627423AD79B6007E6793 /* NSString+FLEX.h */, C398627523AD79B7007E6793 /* NSString+FLEX.m */, ); path = Private; sourceTree = ""; }; C33CF16B22D664E600F9C6C0 /* FileBrowser */ = { isa = PBXGroup; children = ( 3A4C949F1B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h */, 3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */, 3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */, 3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserController.m */, C34063B328400A26007B46D3 /* FLEXActivityViewController.h */, C34063B428400A26007B46D3 /* FLEXActivityViewController.m */, ); path = FileBrowser; sourceTree = ""; }; C3511B8E22D7C9740057BAB7 /* Globals */ = { isa = PBXGroup; children = ( 3A4C94A31B5B21410088C3F2 /* FLEXGlobalsViewController.h */, 3A4C94A41B5B21410088C3F2 /* FLEXGlobalsViewController.m */, C3511B8F22D7C99E0057BAB7 /* FLEXGlobalsSection.h */, C3511B9022D7C99E0057BAB7 /* FLEXGlobalsSection.m */, 3A4C94441B5B21410088C3F2 /* FLEXGlobalsEntry.h */, 3A4C94451B5B21410088C3F2 /* FLEXGlobalsEntry.m */, ); path = Globals; sourceTree = ""; }; C3531BA723E88FAC00A184AD /* Tabs */ = { isa = PBXGroup; children = ( C3531BA823E88FAC00A184AD /* FLEXTabList.h */, C3531BA923E88FAC00A184AD /* FLEXTabList.m */, C3694DB823EA1096006625D7 /* FLEXTabsViewController.h */, C3694DB923EA1096006625D7 /* FLEXTabsViewController.m */, ); path = Tabs; sourceTree = ""; }; C36B096723E1E25F008F5D47 /* Carousel */ = { isa = PBXGroup; children = ( C3EE76BD22DFC63600EC0AA0 /* FLEXScopeCarousel.h */, C3EE76BE22DFC63600EC0AA0 /* FLEXScopeCarousel.m */, C387C87822DFCD6A00750E58 /* FLEXCarouselCell.h */, C387C87922DFCD6A00750E58 /* FLEXCarouselCell.m */, ); path = Carousel; sourceTree = ""; }; C36B096823E1E26C008F5D47 /* Controllers */ = { isa = PBXGroup; children = ( C3531BA323E88A2100A184AD /* FLEXNavigationController.h */, C3531BA423E88A2100A184AD /* FLEXNavigationController.m */, C38DF0E822CFE4370077B4AD /* FLEXTableViewController.h */, C38DF0E922CFE4370077B4AD /* FLEXTableViewController.m */, C3DFCD922416BC6500BB7084 /* FLEXFilteringTableViewController.h */, C3DFCD932416BC6500BB7084 /* FLEXFilteringTableViewController.m */, ); path = Controllers; sourceTree = ""; }; C36B096923E1E810008F5D47 /* Cells */ = { isa = PBXGroup; children = ( C3F31D352267D883003C991A /* FLEXTableViewCell.h */, C3F31D392267D883003C991A /* FLEXTableViewCell.m */, C3F31D342267D883003C991A /* FLEXSubtitleTableViewCell.h */, C3F31D372267D883003C991A /* FLEXSubtitleTableViewCell.m */, C3F31D362267D883003C991A /* FLEXMultilineTableViewCell.h */, C3F31D382267D883003C991A /* FLEXMultilineTableViewCell.m */, C3474C3E23DA496400466532 /* FLEXKeyValueTableViewCell.h */, C3474C3F23DA496400466532 /* FLEXKeyValueTableViewCell.m */, C383C3C323B6BB81007A321B /* FLEXCodeFontCell.h */, C383C3C423B6BB81007A321B /* FLEXCodeFontCell.m */, ); path = Cells; sourceTree = ""; }; C36E1B27259D64D300FEFEF6 /* Supporting Files */ = { isa = PBXGroup; children = ( C36E1B24259D64CC00FEFEF6 /* FLEXNewRootClass.h */, C36E1B25259D64CC00FEFEF6 /* FLEXNewRootClass.m */, ); path = "Supporting Files"; sourceTree = ""; }; C36FBFB8230F3B52008D95D5 /* Runtime */ = { isa = PBXGroup; children = ( C313854523F5F6120046E63C /* Objc */, C313854723F5F7D50046E63C /* flex_fishhook.h */, C313854623F5F7D40046E63C /* flex_fishhook.c */, 3A4C945B1B5B21410088C3F2 /* FLEXRuntimeUtility.h */, 3A4C945C1B5B21410088C3F2 /* FLEXRuntimeUtility.m */, ); path = Runtime; sourceTree = ""; }; C3854DF423F36CB300FCD1E2 /* Keyboard */ = { isa = PBXGroup; children = ( 942DCD821BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.h */, 942DCD831BAE0AD300DB5DC2 /* FLEXKeyboardShortcutManager.m */, 94AAF0361BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h */, 94AAF0371BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m */, ); path = Keyboard; sourceTree = ""; }; C386D6F4241A9B5300699085 /* Reflection */ = { isa = PBXGroup; children = ( C31D93E623E38E97005517BF /* FLEXBlockDescription.h */, C31D93E723E38E97005517BF /* FLEXBlockDescription.m */, C36FBFC5230F3B98008D95D5 /* FLEXMirror.h */, C36FBFB9230F3B97008D95D5 /* FLEXMirror.m */, C36FBFC0230F3B98008D95D5 /* FLEXMethodBase.h */, C36FBFC3230F3B98008D95D5 /* FLEXMethodBase.m */, C36FBFC1230F3B98008D95D5 /* FLEXMethod.h */, C36FBFBB230F3B97008D95D5 /* FLEXMethod.m */, C36FBFC4230F3B98008D95D5 /* FLEXIvar.h */, C36FBFCA230F3B98008D95D5 /* FLEXIvar.m */, C36FBFBC230F3B97008D95D5 /* FLEXProperty.h */, C36FBFBE230F3B97008D95D5 /* FLEXProperty.m */, C36FBFC6230F3B98008D95D5 /* FLEXPropertyAttributes.h */, C36FBFC8230F3B98008D95D5 /* FLEXPropertyAttributes.m */, C36FBFBD230F3B97008D95D5 /* FLEXProtocol.h */, C36FBFC2230F3B98008D95D5 /* FLEXProtocol.m */, C36FBFBA230F3B97008D95D5 /* FLEXProtocolBuilder.h */, C36FBFC7230F3B98008D95D5 /* FLEXProtocolBuilder.m */, C36FBFC9230F3B98008D95D5 /* FLEXClassBuilder.h */, C36FBFBF230F3B98008D95D5 /* FLEXClassBuilder.m */, C38D970128190C93008709D0 /* FLEXMetadataExtras.h */, C38D970028190C93008709D0 /* FLEXMetadataExtras.m */, ); path = Reflection; sourceTree = ""; }; C387C88022E0D22600750E58 /* Categories */ = { isa = PBXGroup; children = ( C31E4E53259D4A4100712288 /* Private */, C3DFCDB62418336D00BB7084 /* NSUserDefaults+FLEX.h */, C3DFCDB72418336D00BB7084 /* NSUserDefaults+FLEX.m */, C3A04B62288E007100F2C16D /* NSDateFormatter+FLEX.h */, C3A04B63288E007100F2C16D /* NSDateFormatter+FLEX.m */, C398627023AD7951007E6793 /* UIGestureRecognizer+Blocks.h */, C398627123AD7951007E6793 /* UIGestureRecognizer+Blocks.m */, C3F646BF239EAA8F00D4A011 /* UIPasteboard+FLEX.h */, C3F646C0239EAA8F00D4A011 /* UIPasteboard+FLEX.m */, C398625B23AD6E90007E6793 /* UIFont+FLEX.h */, C398625C23AD6E90007E6793 /* UIFont+FLEX.m */, C383C3BC23B6B398007A321B /* UITextField+Range.h */, C383C3BB23B6B398007A321B /* UITextField+Range.m */, C383C3C023B6B429007A321B /* NSTimer+FLEX.h */, C383C3BF23B6B429007A321B /* NSTimer+FLEX.m */, C3BFD06E233C23ED0015FB82 /* NSArray+FLEX.h */, C3BFD06F233C23ED0015FB82 /* NSArray+FLEX.m */, C3F977812311B38F0032776D /* NSObject+FLEX_Reflection.h */, C3F977822311B38F0032776D /* NSObject+FLEX_Reflection.m */, C3E5D9FB2316E83700E655DB /* FLEXRuntime+Compare.h */, C3E5D9FC2316E83700E655DB /* FLEXRuntime+Compare.m */, C34C9BDB23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.h */, C34C9BDC23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.m */, C36B096323E0D4A1008F5D47 /* UIMenu+FLEX.h */, C36B096423E0D4A1008F5D47 /* UIMenu+FLEX.m */, C3694DC023EA147F006625D7 /* UIBarButtonItem+FLEX.h */, C3694DC123EA147F006625D7 /* UIBarButtonItem+FLEX.m */, C30199482409B38A00759E8E /* CALayer+FLEX.h */, C30199492409B38A00759E8E /* CALayer+FLEX.m */, ); path = Categories; sourceTree = ""; }; C38DF0E722CFE4140077B4AD /* Core */ = { isa = PBXGroup; children = ( C36B096823E1E26C008F5D47 /* Controllers */, C3F31D3A2267D883003C991A /* Views */, C36B096E23E1EDCD008F5D47 /* FLEXTableViewSection.h */, C36B096F23E1EDCD008F5D47 /* FLEXTableViewSection.m */, C3490E1D233BDD73002AE200 /* FLEXSingleRowSection.h */, C3490E1E233BDD73002AE200 /* FLEXSingleRowSection.m */, ); path = Core; sourceTree = ""; }; C398624223AD6B92007E6793 /* RuntimeBrowser */ = { isa = PBXGroup; children = ( C398626F23AD71C6007E6793 /* DataSources */, C398625723AD6C88007E6793 /* FLEXSearchToken.h */, C398625823AD6C88007E6793 /* FLEXSearchToken.m */, C398624723AD6C67007E6793 /* FLEXRuntimeKeyPath.h */, C398624423AD6C67007E6793 /* FLEXRuntimeKeyPath.m */, C398624323AD6C67007E6793 /* FLEXKeyPathSearchController.h */, C398624823AD6C67007E6793 /* FLEXKeyPathSearchController.m */, C398624523AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.h */, C398624923AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.m */, C398624A23AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h */, C398624C23AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.m */, C398624B23AD6C67007E6793 /* FLEXObjcRuntimeViewController.h */, C398624623AD6C67007E6793 /* FLEXObjcRuntimeViewController.m */, C398625F23AD70DF007E6793 /* FLEXKeyboardToolbar.h */, C398626023AD70DF007E6793 /* FLEXKeyboardToolbar.m */, C398626323AD70F5007E6793 /* FLEXKBToolbarButton.h */, C398626423AD70F5007E6793 /* FLEXKBToolbarButton.m */, ); path = RuntimeBrowser; sourceTree = ""; }; C398626F23AD71C6007E6793 /* DataSources */ = { isa = PBXGroup; children = ( C3EB6F8C242E9C83006EA386 /* FLEXRuntimeExporter.h */, C3EB6F8B242E9C83006EA386 /* FLEXRuntimeExporter.m */, C398626723AD71C1007E6793 /* FLEXRuntimeClient.h */, C398626A23AD71C1007E6793 /* FLEXRuntimeClient.m */, C398626823AD71C1007E6793 /* FLEXRuntimeController.h */, C398626923AD71C1007E6793 /* FLEXRuntimeController.m */, ); path = DataSources; sourceTree = ""; }; C3A9422523C3DA1E006871A3 /* SnapshotExplorer */ = { isa = PBXGroup; children = ( C3A9424223C64190006871A3 /* Scene */, C3A9423223C3F98E006871A3 /* FHSViewController.h */, C3A9423323C3F98E006871A3 /* FHSViewController.m */, C3A9423A23C54010006871A3 /* FHSSnapshotView.h */, C3A9423B23C54010006871A3 /* FHSSnapshotView.m */, C3A9422823C3DA39006871A3 /* FHSView.h */, C3A9422923C3DA39006871A3 /* FHSView.m */, C3A9424B23C78CFF006871A3 /* FHSViewSnapshot.h */, C3A9424C23C78CFF006871A3 /* FHSViewSnapshot.m */, C3A9423623C51B8D006871A3 /* FHSRangeSlider.h */, C3A9423723C51B8D006871A3 /* FHSRangeSlider.m */, ); path = SnapshotExplorer; sourceTree = ""; }; C3A9424223C64190006871A3 /* Scene */ = { isa = PBXGroup; children = ( C3A9423E23C5443A006871A3 /* FHSSnapshotNodes.h */, C3A9423F23C5443A006871A3 /* FHSSnapshotNodes.m */, C3A9424323C641C1006871A3 /* SceneKit+Snapshot.h */, C3A9424423C641C1006871A3 /* SceneKit+Snapshot.m */, ); path = Scene; sourceTree = ""; }; C3A9424F23C7ABB2006871A3 /* TreeExplorer */ = { isa = PBXGroup; children = ( 3A4C94621B5B21410088C3F2 /* FLEXHierarchyTableViewController.h */, 3A4C94631B5B21410088C3F2 /* FLEXHierarchyTableViewController.m */, 3A4C94601B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h */, 3A4C94611B5B21410088C3F2 /* FLEXHierarchyTableViewCell.m */, ); path = TreeExplorer; sourceTree = ""; }; C3D43E9F231D79F60079F6A8 /* Shortcuts */ = { isa = PBXGroup; children = ( C398682423AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h */, C398682323AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m */, C3F646F423A04A7500D4A011 /* FLEXShortcut.h */, C3F646F523A04A7500D4A011 /* FLEXShortcut.m */, C3F527BB2318603F009CBA07 /* FLEXShortcutsSection.h */, C3F527BC2318603F009CBA07 /* FLEXShortcutsSection.m */, C3F527BF2318670F009CBA07 /* FLEXImageShortcuts.h */, C3F527C02318670F009CBA07 /* FLEXImageShortcuts.m */, C3F527C3231891F6009CBA07 /* FLEXViewShortcuts.h */, C3F527C4231891F6009CBA07 /* FLEXViewShortcuts.m */, C3EF290328EE8BC500B337B6 /* FLEXWindowShortcuts.h */, C3EF290428EE8BC500B337B6 /* FLEXWindowShortcuts.m */, C3F646F023A045DB00D4A011 /* FLEXClassShortcuts.h */, C3F646F123A045DB00D4A011 /* FLEXClassShortcuts.m */, C34D4EAE23A2ABD900C1F903 /* FLEXLayerShortcuts.h */, C34D4EAF23A2ABD900C1F903 /* FLEXLayerShortcuts.m */, C34D4EB623A2B17900C1F903 /* FLEXBundleShortcuts.h */, C34D4EB723A2B17900C1F903 /* FLEXBundleShortcuts.m */, C38EF26123A2FCD20047A7EC /* FLEXViewControllerShortcuts.h */, C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */, C32F3A16247C6B3E0063542D /* FLEXUIAppShortcuts.h */, C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */, C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */, C31D93E323E38CBE005517BF /* FLEXBlockShortcuts.m */, C30D295E261FAE9E00D89649 /* FLEXNSDataShortcuts.h */, C30D295D261FAE9E00D89649 /* FLEXNSDataShortcuts.m */, C30D295C261FAE9E00D89649 /* FLEXNSStringShortcuts.h */, C30D295F261FAE9E00D89649 /* FLEXNSStringShortcuts.m */, ); path = Shortcuts; sourceTree = ""; }; C3DBFD0926CE2FAF00E0466A /* OSCache */ = { isa = PBXGroup; children = ( C3DBFD0A26CE2FAF00E0466A /* OSCache.m */, C3DBFD0B26CE2FAF00E0466A /* OSCache.h */, ); path = OSCache; sourceTree = ""; }; C3E5D9FF2317007F00E655DB /* Sections */ = { isa = PBXGroup; children = ( C3E5DA00231700EE00E655DB /* FLEXObjectInfoSection.h */, C31C4A6723342A2200C35F12 /* FLEXMetadataSection.h */, C31C4A6823342A2200C35F12 /* FLEXMetadataSection.m */, C32A195C231732E800EB02AC /* FLEXCollectionContentSection.h */, C32A195D231732E800EB02AC /* FLEXCollectionContentSection.m */, C3DFCD962416E7DD00BB7084 /* FLEXMutableListSection.h */, C3DFCD972416E7DD00BB7084 /* FLEXMutableListSection.m */, C32A19602317378C00EB02AC /* FLEXDefaultsContentSection.h */, C32A19612317378C00EB02AC /* FLEXDefaultsContentSection.m */, C34D4EB223A2AF2A00C1F903 /* FLEXColorPreviewSection.h */, C34D4EB323A2AF2A00C1F903 /* FLEXColorPreviewSection.m */, C3D43E9F231D79F60079F6A8 /* Shortcuts */, ); path = Sections; sourceTree = ""; }; C3F31D3A2267D883003C991A /* Views */ = { isa = PBXGroup; children = ( C36B096723E1E25F008F5D47 /* Carousel */, C36B096923E1E810008F5D47 /* Cells */, C3F31D3B2267D883003C991A /* FLEXTableView.h */, C3F31D3C2267D883003C991A /* FLEXTableView.m */, ); path = Views; sourceTree = ""; }; C3F3417F242276D200A597D7 /* Private */ = { isa = PBXGroup; children = ( 94A515161C4CA1D70063292F /* FLEXManager+Private.h */, ); path = Private; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 3A4C941C1B5B20570088C3F2 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 779B1EDA1C0C4D7C001F5E49 /* FLEXTableListViewController.h in Headers */, C3531BA523E88A2100A184AD /* FLEXNavigationController.h in Headers */, 3A4C94ED1B5B21410088C3F2 /* FLEXArgumentInputColorView.h in Headers */, C3A9424523C641C1006871A3 /* SceneKit+Snapshot.h in Headers */, 3A4C94EB1B5B21410088C3F2 /* FLEXImagePreviewViewController.h in Headers */, C38D970328190C93008709D0 /* FLEXMetadataExtras.h in Headers */, C34D4EB823A2B17900C1F903 /* FLEXBundleShortcuts.h in Headers */, C3694DC223EA147F006625D7 /* UIBarButtonItem+FLEX.h in Headers */, C38F3F31230C958F004E3731 /* FLEXAlert.h in Headers */, C398624D23AD6C67007E6793 /* FLEXKeyPathSearchController.h in Headers */, 3A4C95381B5B21410088C3F2 /* FLEXNetworkRecorder.h in Headers */, C381FDC3271CCB0500401081 /* Firestore.h in Headers */, 3A4C94251B5B20570088C3F2 /* FLEX.h in Headers */, C383C3C523B6BB81007A321B /* FLEXCodeFontCell.h in Headers */, C3F527C12318670F009CBA07 /* FLEXImageShortcuts.h in Headers */, 3A4C95051B5B21410088C3F2 /* FLEXArgumentInputViewFactory.h in Headers */, C312A13423ECBE5800E38049 /* FLEXBookmarksViewController.h in Headers */, 222C88221C7339DC007CA15F /* FLEXRealmDefines.h in Headers */, 779B1ED21C0C4D7C001F5E49 /* FLEXTableColumnHeader.h in Headers */, 3A4C94FD1B5B21410088C3F2 /* FLEXArgumentInputStructView.h in Headers */, C3F646C1239EAA8F00D4A011 /* UIPasteboard+FLEX.h in Headers */, 94A515141C4CA1C00063292F /* FLEXManager.h in Headers */, C37A0C93218BAC9600848CA7 /* FLEXObjcInternal.h in Headers */, C3A9422C23C3DA39006871A3 /* FHSView.h in Headers */, 3A4C95301B5B21410088C3F2 /* FLEXSystemLogMessage.h in Headers */, C398625523AD6C67007E6793 /* FLEXObjcRuntimeViewController.h in Headers */, C3E5DA02231700EE00E655DB /* FLEXObjectInfoSection.h in Headers */, 03ED813E24B7D91200831CA0 /* FLEXTableRowDataViewController.h in Headers */, C34D4EB023A2ABD900C1F903 /* FLEXLayerShortcuts.h in Headers */, C3F646F623A04A7500D4A011 /* FLEXShortcut.h in Headers */, C3531B9D23E69BB200A184AD /* FLEXManager+Networking.h in Headers */, C3878DBB23A749960038FDBE /* FLEXVariableEditorViewController.h in Headers */, 3A4C95361B5B21410088C3F2 /* FLEXNetworkMITMViewController.h in Headers */, 3A4C94DD1B5B21410088C3F2 /* FLEXHeapEnumerator.h in Headers */, C3F527BD2318603F009CBA07 /* FLEXShortcutsSection.h in Headers */, C398626B23AD71C1007E6793 /* FLEXRuntimeClient.h in Headers */, 3A4C95321B5B21410088C3F2 /* FLEXSystemLogCell.h in Headers */, C3F977852311B38F0032776D /* NSDictionary+ObjcRuntime.h in Headers */, C3DFCD982416E7DD00BB7084 /* FLEXMutableListSection.h in Headers */, C386D6F6241A9D6900699085 /* FLEX-Categories.h in Headers */, C398625123AD6C67007E6793 /* FLEXRuntimeKeyPath.h in Headers */, 3A4C94F91B5B21410088C3F2 /* FLEXArgumentInputNumberView.h in Headers */, C3F646F223A045DB00D4A011 /* FLEXClassShortcuts.h in Headers */, C387C87A22DFCD6A00750E58 /* FLEXCarouselCell.h in Headers */, C3511B9122D7C99E0057BAB7 /* FLEXGlobalsSection.h in Headers */, C398625D23AD6E90007E6793 /* UIFont+FLEX.h in Headers */, 3A4C953A1B5B21410088C3F2 /* FLEXNetworkSettingsController.h in Headers */, C398626C23AD71C1007E6793 /* FLEXRuntimeController.h in Headers */, 779B1ED01C0C4D7C001F5E49 /* FLEXMultiColumnTableView.h in Headers */, 3A4C94D31B5B21410088C3F2 /* FLEXObjectExplorerFactory.h in Headers */, 3A4C94CD1B5B21410088C3F2 /* FLEXGlobalsEntry.h in Headers */, C3A9423423C3F98E006871A3 /* FHSViewController.h in Headers */, 3A4C94E31B5B21410088C3F2 /* FLEXRuntimeUtility.h in Headers */, C38DF0EA22CFE4370077B4AD /* FLEXTableViewController.h in Headers */, C31D93E823E38E97005517BF /* FLEXBlockDescription.h in Headers */, C3531BA123E796BD00A184AD /* FLEXManager+Extensibility.h in Headers */, C36FBFCF230F3B98008D95D5 /* FLEXProtocol.h in Headers */, 94A515271C4CA2080063292F /* FLEXExplorerToolbarItem.h in Headers */, C312A13023ECB5D300E38049 /* FLEXBookmarkManager.h in Headers */, 3A4C95341B5B21410088C3F2 /* FLEXSystemLogViewController.h in Headers */, C34C9BDD23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.h in Headers */, C35DAD822709140700AA95E6 /* FLEXHTTPTransactionDetailController.h in Headers */, C398624F23AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.h in Headers */, C362AE8123C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.h in Headers */, 3A4C95091B5B21410088C3F2 /* FLEXFieldEditorView.h in Headers */, C3E5D9FD2316E83700E655DB /* FLEXRuntime+Compare.h in Headers */, C3490E1F233BDD73002AE200 /* FLEXSingleRowSection.h in Headers */, 3A4C95281B5B21410088C3F2 /* FLEXObjectListViewController.h in Headers */, C36B097023E1EDCD008F5D47 /* FLEXTableViewSection.h in Headers */, C3531BAA23E88FAC00A184AD /* FLEXTabList.h in Headers */, 3A4C950F1B5B21410088C3F2 /* FLEXMethodCallingViewController.h in Headers */, 3A4C94F51B5B21410088C3F2 /* FLEXArgumentInputObjectView.h in Headers */, C3A9424923C78878006871A3 /* FLEXHierarchyViewController.h in Headers */, C3DFCD942416BC6500BB7084 /* FLEXFilteringTableViewController.h in Headers */, 3A4C94F11B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.h in Headers */, C3BFD070233C23ED0015FB82 /* NSArray+FLEX.h in Headers */, C398682923AC370100E9E391 /* FLEXViewShortcuts.h in Headers */, C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */, C32F3A18247C6B3E0063542D /* FLEXUIAppShortcuts.h in Headers */, 779B1ED61C0C4D7C001F5E49 /* FLEXTableContentViewController.h in Headers */, C3DFCDB82418336D00BB7084 /* NSUserDefaults+FLEX.h in Headers */, C30D2968261FAEEE00D89649 /* Cocoa+FLEXShortcuts.h in Headers */, 3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */, C33E46AF223B02CD004BD0E6 /* FLEXASLLogController.h in Headers */, C34EE30821CB23CC00BD3A7C /* FLEXOSLogController.h in Headers */, C38568FA272B3BFC00B1E37F /* FLEXSwiftInternal.h in Headers */, C3EBE111287028D600702098 /* FLEXAPNSViewController.h in Headers */, 3A4C94FF1B5B21410088C3F2 /* FLEXArgumentInputSwitchView.h in Headers */, C398625423AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.h in Headers */, 3A4C94E71B5B21410088C3F2 /* FLEXHierarchyTableViewCell.h in Headers */, C3DBFD0D26CE2FAF00E0466A /* OSCache.h in Headers */, 224D49AA1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.h in Headers */, C386D6A9241995A800699085 /* FLEXTypeEncodingParser.h in Headers */, C30D2962261FAE9E00D89649 /* FLEXNSDataShortcuts.h in Headers */, 3A4C95031B5B21410088C3F2 /* FLEXArgumentInputView.h in Headers */, C398627623AD79B7007E6793 /* NSString+FLEX.h in Headers */, 94A5151D1C4CA1F10063292F /* FLEXExplorerViewController.h in Headers */, C3F31D3F2267D883003C991A /* FLEXMultilineTableViewCell.h in Headers */, 71E1C2132307FBB800F5032A /* FLEXKeychain.h in Headers */, C36FBFD7230F3B98008D95D5 /* FLEXMirror.h in Headers */, C386D6F3241A976100699085 /* FLEXRuntimeConstants.h in Headers */, C3F31D432267D883003C991A /* FLEXTableView.h in Headers */, 3A4C95071B5B21410088C3F2 /* FLEXDefaultEditorViewController.h in Headers */, C309B82F223ED64400B228EC /* FLEXLogController.h in Headers */, C34063B528400A26007B46D3 /* FLEXActivityViewController.h in Headers */, C3694DBA23EA1096006625D7 /* FLEXTabsViewController.h in Headers */, C31C4A6923342A2200C35F12 /* FLEXMetadataSection.h in Headers */, C3F977832311B38F0032776D /* NSString+ObjcRuntime.h in Headers */, 94A5151F1C4CA1F10063292F /* FLEXWindow.h in Headers */, C312A13D23ECE79000E38049 /* FLEXWindowManagerController.h in Headers */, 779B1ECE1C0C4D7C001F5E49 /* FLEXDatabaseManager.h in Headers */, C32A19622317378C00EB02AC /* FLEXDefaultsContentSection.h in Headers */, 3A4C94D51B5B21410088C3F2 /* FLEXObjectExplorerViewController.h in Headers */, 3A4C95011B5B21410088C3F2 /* FLEXArgumentInputTextView.h in Headers */, C36FBFCC230F3B98008D95D5 /* FLEXProtocolBuilder.h in Headers */, C36FBFDB230F3B98008D95D5 /* FLEXClassBuilder.h in Headers */, 779B1ED41C0C4D7C001F5E49 /* FLEXDBQueryRowCell.h in Headers */, C301994A2409B38A00759E8E /* CALayer+FLEX.h in Headers */, 3A4C952C1B5B21410088C3F2 /* FLEXLiveObjectsController.h in Headers */, 3A4C94EF1B5B21410088C3F2 /* FLEXArgumentInputDateView.h in Headers */, C36FBFCE230F3B98008D95D5 /* FLEXProperty.h in Headers */, C36FBFD2230F3B98008D95D5 /* FLEXMethodBase.h in Headers */, 71E1C2142307FBB800F5032A /* FLEXKeychainViewController.h in Headers */, 3A4C94F71B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.h in Headers */, C3F31D3E2267D883003C991A /* FLEXTableViewCell.h in Headers */, 3A4C94E51B5B21410088C3F2 /* FLEXUtility.h in Headers */, 2EF6B04E1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h in Headers */, C386D6E924199C1B00699085 /* FLEX-Core.h in Headers */, C3F31D3D2267D883003C991A /* FLEXSubtitleTableViewCell.h in Headers */, 94A515251C4CA2080063292F /* FLEXExplorerToolbar.h in Headers */, C387C88322E0D24A00750E58 /* UIView+FLEX_Layout.h in Headers */, 3A4C953C1B5B21410088C3F2 /* FLEXNetworkTransaction.h in Headers */, C383C3C223B6B429007A321B /* NSTimer+FLEX.h in Headers */, 3A4C952E1B5B21410088C3F2 /* FLEXWebViewController.h in Headers */, C3A9424D23C78CFF006871A3 /* FHSViewSnapshot.h in Headers */, C3EE76BF22DFC63600EC0AA0 /* FLEXScopeCarousel.h in Headers */, C398627223AD7951007E6793 /* UIGestureRecognizer+Blocks.h in Headers */, 3A4C94E91B5B21410088C3F2 /* FLEXHierarchyTableViewController.h in Headers */, 3A4C94F31B5B21410088C3F2 /* FLEXArgumentInputFontView.h in Headers */, C3A9423C23C54010006871A3 /* FHSSnapshotView.h in Headers */, 3A4C95261B5B21410088C3F2 /* FLEXGlobalsViewController.h in Headers */, C398625923AD6C88007E6793 /* FLEXSearchToken.h in Headers */, C33C825E2316DC8600DD2451 /* FLEXObjectExplorer.h in Headers */, C34D4EB423A2AF2A00C1F903 /* FLEXColorPreviewSection.h in Headers */, C383C3BE23B6B398007A321B /* UITextField+Range.h in Headers */, C35DAD8E2709143000AA95E6 /* FLEXMITMDataSource.h in Headers */, 224D49A81C673AB5000EAB86 /* FLEXRealmDatabaseManager.h in Headers */, C313853F23F5C1A10046E63C /* FLEXViewControllersViewController.h in Headers */, C386D6ED24199EC600699085 /* FLEX-Runtime.h in Headers */, C398682623AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h in Headers */, C397E318240EC98F0091E4EC /* FLEXSQLResult.h in Headers */, 7349FD6A22B93CDF00051810 /* FLEXColor.h in Headers */, C36FBFD3230F3B98008D95D5 /* FLEXMethod.h in Headers */, C3EF290528EE8BC500B337B6 /* FLEXWindowShortcuts.h in Headers */, C36FBFD8230F3B98008D95D5 /* FLEXPropertyAttributes.h in Headers */, 3A4C94FB1B5B21410088C3F2 /* FLEXArgumentInputStringView.h in Headers */, C38EF26323A2FCD20047A7EC /* FLEXViewControllerShortcuts.h in Headers */, 3A4C95421B5B21410088C3F2 /* FLEXNetworkObserver.h in Headers */, C386D6EB24199E9600699085 /* FLEX-ObjectExploring.h in Headers */, 679F64861BD53B7B00A8C94C /* FLEXCookiesViewController.h in Headers */, C3F977872311B38F0032776D /* NSObject+FLEX_Reflection.h in Headers */, C3DB9F642107FC9600B46809 /* FLEXObjectRef.h in Headers */, 3A4C95401B5B21410088C3F2 /* FLEXNetworkTransactionCell.h in Headers */, C30D2960261FAE9E00D89649 /* FLEXNSStringShortcuts.h in Headers */, C398626523AD70F5007E6793 /* FLEXKBToolbarButton.h in Headers */, 3A4C95241B5B21410088C3F2 /* FLEXFileBrowserController.h in Headers */, C31D93E423E38CBE005517BF /* FLEXBlockShortcuts.h in Headers */, 94AAF0381BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.h in Headers */, 71E1C2152307FBB800F5032A /* FLEXKeychainQuery.h in Headers */, C36B096523E0D4A1008F5D47 /* UIMenu+FLEX.h in Headers */, C36FBFD6230F3B98008D95D5 /* FLEXIvar.h in Headers */, C3A9423823C51B8D006871A3 /* FHSRangeSlider.h in Headers */, C398626123AD70DF007E6793 /* FLEXKeyboardToolbar.h in Headers */, C386D6F2241A96AD00699085 /* FLEXMacros.h in Headers */, 94AAF03A1BAF2F0300DE8760 /* FLEXKeyboardShortcutManager.h in Headers */, 94A515181C4CA1D70063292F /* FLEXManager+Private.h in Headers */, C383C3B923B6A62A007A321B /* FLEXRuntimeSafety.h in Headers */, C39ED92822D63F3200B5773A /* FLEXAddressExplorerCoordinator.h in Headers */, C3A04B64288E007100F2C16D /* NSDateFormatter+FLEX.h in Headers */, C313854923F5F7D50046E63C /* flex_fishhook.h in Headers */, C3A9424023C5443A006871A3 /* FHSSnapshotNodes.h in Headers */, C32A195E231732E800EB02AC /* FLEXCollectionContentSection.h in Headers */, 3A4C94E11B5B21410088C3F2 /* FLEXResources.h in Headers */, C3EB6F8E242E9C83006EA386 /* FLEXRuntimeExporter.h in Headers */, 779B1ED81C0C4D7C001F5E49 /* FLEXTableLeftCell.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 1C27A8B51F0E5A0300F0D02D /* FLEXTests */ = { isa = PBXNativeTarget; buildConfigurationList = 1C27A8C01F0E5A0400F0D02D /* Build configuration list for PBXNativeTarget "FLEXTests" */; buildPhases = ( 1C27A8B21F0E5A0300F0D02D /* Sources */, 1C27A8B31F0E5A0300F0D02D /* Frameworks */, 1C27A8B41F0E5A0300F0D02D /* Resources */, ); buildRules = ( ); dependencies = ( 1C27A8BD1F0E5A0400F0D02D /* PBXTargetDependency */, ); name = FLEXTests; productName = FLEXTestsMethodsList; productReference = 1C27A8B61F0E5A0300F0D02D /* FLEXTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 3A4C941E1B5B20570088C3F2 /* FLEX */ = { isa = PBXNativeTarget; buildConfigurationList = 3A4C94351B5B20570088C3F2 /* Build configuration list for PBXNativeTarget "FLEX" */; buildPhases = ( 3A4C941A1B5B20570088C3F2 /* Sources */, 3A4C941B1B5B20570088C3F2 /* Frameworks */, 3A4C941C1B5B20570088C3F2 /* Headers */, 3A4C941D1B5B20570088C3F2 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = FLEX; productName = FLEX; productReference = 3A4C941F1B5B20570088C3F2 /* FLEX.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 3A4C94161B5B20570088C3F2 /* Project object */ = { isa = PBXProject; attributes = { CLASSPREFIX = FLEX; LastUpgradeCheck = 1400; ORGANIZATIONNAME = Flipboard; TargetAttributes = { 1C27A8B51F0E5A0300F0D02D = { DevelopmentTeam = U3LST7M92S; }; 3A4C941E1B5B20570088C3F2 = { CreatedOnToolsVersion = 6.4; LastSwiftMigration = 1130; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 3A4C94191B5B20570088C3F2 /* Build configuration list for PBXProject "FLEX" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 3A4C94151B5B20570088C3F2; productRefGroup = 3A4C94201B5B20570088C3F2 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 3A4C941E1B5B20570088C3F2 /* FLEX */, 1C27A8B51F0E5A0300F0D02D /* FLEXTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 1C27A8B41F0E5A0300F0D02D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 3A4C941D1B5B20570088C3F2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 04F1CA191C137CF1000A52B0 /* LICENSE in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 1C27A8B21F0E5A0300F0D02D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C36E1B26259D64CC00FEFEF6 /* FLEXNewRootClass.m in Sources */, C33C825B23159EAF00DD2451 /* FLEXTests.m in Sources */, 1C27A8B91F0E5A0400F0D02D /* FLEXTestsMethodsList.m in Sources */, C3854DF023F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 3A4C941A1B5B20570088C3F2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C398682823AC36EC00E9E391 /* FLEXViewShortcuts.m in Sources */, C3DFCDB92418336D00BB7084 /* NSUserDefaults+FLEX.m in Sources */, C3878DBE23A74A8F0038FDBE /* FLEXNetworkRecorder.m in Sources */, C3878DBC23A749F70038FDBE /* FLEXFieldEditorViewController.m in Sources */, C35DAD832709140700AA95E6 /* FLEXHTTPTransactionDetailController.m in Sources */, 942DCD871BAE0CA300DB5DC2 /* FLEXKeyboardShortcutManager.m in Sources */, C30D2961261FAE9E00D89649 /* FLEXNSDataShortcuts.m in Sources */, C3F977862311B38F0032776D /* NSString+ObjcRuntime.m in Sources */, C3A9424A23C78878006871A3 /* FLEXHierarchyViewController.m in Sources */, C31C4A6A23342A2200C35F12 /* FLEXMetadataSection.m in Sources */, 224D49A91C673AB5000EAB86 /* FLEXRealmDatabaseManager.m in Sources */, C39ED92922D63F3200B5773A /* FLEXAddressExplorerCoordinator.m in Sources */, C398626223AD70DF007E6793 /* FLEXKeyboardToolbar.m in Sources */, C34C9BDE23A7F2740031CA3E /* FLEXRuntime+UIKitHelpers.m in Sources */, 2EF6B04D1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m in Sources */, C398625323AD6C67007E6793 /* FLEXRuntimeKeyPathTokenizer.m in Sources */, 94A515201C4CA1F10063292F /* FLEXWindow.m in Sources */, C38EF26223A2FCD20047A7EC /* FLEXViewControllerShortcuts.m in Sources */, C398682523AC359600E9E391 /* FLEXShortcutsFactory+Defaults.m in Sources */, C3DC287C223ED5F200F48AA6 /* FLEXOSLogController.m in Sources */, 3A4C95101B5B21410088C3F2 /* FLEXMethodCallingViewController.m in Sources */, C362AE8223C7E9D1005A86AE /* NSMapTable+FLEX_Subscripting.m in Sources */, C3F646C2239EAA8F00D4A011 /* UIPasteboard+FLEX.m in Sources */, C38568FB272B3BFC00B1E37F /* FLEXSwiftInternal.mm in Sources */, C3EB6F8D242E9C83006EA386 /* FLEXRuntimeExporter.m in Sources */, 3A4C94F61B5B21410088C3F2 /* FLEXArgumentInputObjectView.m in Sources */, 3A4C94EC1B5B21410088C3F2 /* FLEXImagePreviewViewController.m in Sources */, C383C3BA23B6A62A007A321B /* FLEXRuntimeSafety.m in Sources */, 3A4C94F21B5B21410088C3F2 /* FLEXArgumentInputFontsPickerView.m in Sources */, C3BFD071233C23ED0015FB82 /* NSArray+FLEX.m in Sources */, 7349FD6B22B93CDF00051810 /* FLEXColor.m in Sources */, C3878DBA23A749960038FDBE /* FLEXVariableEditorViewController.m in Sources */, 94AAF0391BAF2E1F00DE8760 /* FLEXKeyboardHelpViewController.m in Sources */, C398624E23AD6C67007E6793 /* FLEXRuntimeKeyPath.m in Sources */, C3694DC323EA147F006625D7 /* UIBarButtonItem+FLEX.m in Sources */, C38D970228190C93008709D0 /* FLEXMetadataExtras.m in Sources */, C36FBFD4230F3B98008D95D5 /* FLEXProtocol.m in Sources */, C34D4EB123A2ABD900C1F903 /* FLEXLayerShortcuts.m in Sources */, C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */, C301994B2409B38A00759E8E /* CALayer+FLEX.m in Sources */, 3A4C95081B5B21410088C3F2 /* FLEXDefaultEditorViewController.m in Sources */, 779B1ED11C0C4D7C001F5E49 /* FLEXMultiColumnTableView.m in Sources */, C36FBFD0230F3B98008D95D5 /* FLEXProperty.m in Sources */, C3A9424E23C78CFF006871A3 /* FHSViewSnapshot.m in Sources */, C398625623AD6C67007E6793 /* FLEXRuntimeBrowserToolbar.m in Sources */, C3F31D412267D883003C991A /* FLEXMultilineTableViewCell.m in Sources */, 3A4C94EE1B5B21410088C3F2 /* FLEXArgumentInputColorView.m in Sources */, C38DF0EB22CFE4370077B4AD /* FLEXTableViewController.m in Sources */, C3A9423D23C54010006871A3 /* FHSSnapshotView.m in Sources */, C387C87B22DFCD6A00750E58 /* FLEXCarouselCell.m in Sources */, 3A4C94F01B5B21410088C3F2 /* FLEXArgumentInputDateView.m in Sources */, C398627723AD79B7007E6793 /* NSString+FLEX.m in Sources */, C36FBFDC230F3B98008D95D5 /* FLEXIvar.m in Sources */, C313854023F5C1A10046E63C /* FLEXViewControllersViewController.m in Sources */, C3F527C22318670F009CBA07 /* FLEXImageShortcuts.m in Sources */, 679F64871BD53B7B00A8C94C /* FLEXCookiesViewController.m in Sources */, C3A9422D23C3DA39006871A3 /* FHSView.m in Sources */, 3A4C94CE1B5B21410088C3F2 /* FLEXGlobalsEntry.m in Sources */, C398625223AD6C67007E6793 /* FLEXKeyPathSearchController.m in Sources */, C3E5D9FE2316E83700E655DB /* FLEXRuntime+Compare.m in Sources */, C32F3A19247C6B3E0063542D /* FLEXUIAppShortcuts.m in Sources */, 71E1C2192307FBB800F5032A /* FLEXKeychainQuery.m in Sources */, C398627323AD7951007E6793 /* UIGestureRecognizer+Blocks.m in Sources */, C3F646F723A04A7500D4A011 /* FLEXShortcut.m in Sources */, 3A4C94FE1B5B21410088C3F2 /* FLEXArgumentInputStructView.m in Sources */, C3A9424123C5443A006871A3 /* FHSSnapshotNodes.m in Sources */, C3F31D402267D883003C991A /* FLEXSubtitleTableViewCell.m in Sources */, C398626D23AD71C1007E6793 /* FLEXRuntimeController.m in Sources */, C36FBFD9230F3B98008D95D5 /* FLEXProtocolBuilder.m in Sources */, 3A4C95431B5B21410088C3F2 /* FLEXNetworkObserver.m in Sources */, 779B1EDB1C0C4D7C001F5E49 /* FLEXTableListViewController.m in Sources */, 3A4C94E41B5B21410088C3F2 /* FLEXRuntimeUtility.m in Sources */, 3A4C94D41B5B21410088C3F2 /* FLEXObjectExplorerFactory.m in Sources */, 94A515171C4CA1D70063292F /* FLEXManager.m in Sources */, C34063B628400A26007B46D3 /* FLEXActivityViewController.m in Sources */, C398626623AD70F5007E6793 /* FLEXKBToolbarButton.m in Sources */, 3A4C952F1B5B21410088C3F2 /* FLEXWebViewController.m in Sources */, 3A4C95041B5B21410088C3F2 /* FLEXArgumentInputView.m in Sources */, C3F977842311B38F0032776D /* NSDictionary+ObjcRuntime.m in Sources */, C312A13523ECBE5800E38049 /* FLEXBookmarksViewController.m in Sources */, 3A4C95411B5B21410088C3F2 /* FLEXNetworkTransactionCell.m in Sources */, C3DFCD952416BC6500BB7084 /* FLEXFilteringTableViewController.m in Sources */, C3F527BE2318603F009CBA07 /* FLEXShortcutsSection.m in Sources */, A42069CF2809377F00EBCAEA /* FLEXFirebaseTransaction.mm in Sources */, 3A4C94D61B5B21410088C3F2 /* FLEXObjectExplorerViewController.m in Sources */, C383C3BD23B6B398007A321B /* UITextField+Range.m in Sources */, 3A4C94DE1B5B21410088C3F2 /* FLEXHeapEnumerator.m in Sources */, 3A4C95251B5B21410088C3F2 /* FLEXFileBrowserController.m in Sources */, C36FBFD1230F3B98008D95D5 /* FLEXClassBuilder.m in Sources */, 779B1ED51C0C4D7C001F5E49 /* FLEXDBQueryRowCell.m in Sources */, 3A4C94E21B5B21410088C3F2 /* FLEXResources.m in Sources */, 94A515281C4CA2080063292F /* FLEXExplorerToolbarItem.m in Sources */, 3A4C95311B5B21410088C3F2 /* FLEXSystemLogMessage.m in Sources */, C3F31D422267D883003C991A /* FLEXTableViewCell.m in Sources */, 3A4C94F41B5B21410088C3F2 /* FLEXArgumentInputFontView.m in Sources */, C3EBE112287028D600702098 /* FLEXAPNSViewController.m in Sources */, C3694DBB23EA1096006625D7 /* FLEXTabsViewController.m in Sources */, C3A9424623C641C1006871A3 /* SceneKit+Snapshot.m in Sources */, C3A04B65288E007100F2C16D /* NSDateFormatter+FLEX.m in Sources */, 3A4C953B1B5B21410088C3F2 /* FLEXNetworkSettingsController.m in Sources */, 3A4C94FC1B5B21410088C3F2 /* FLEXArgumentInputStringView.m in Sources */, 3A4C94F81B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.m in Sources */, 3A4C95351B5B21410088C3F2 /* FLEXSystemLogViewController.m in Sources */, C3EF290628EE8BC500B337B6 /* FLEXWindowShortcuts.m in Sources */, C3DBFD0C26CE2FAF00E0466A /* OSCache.m in Sources */, C383C3C623B6BB81007A321B /* FLEXCodeFontCell.m in Sources */, 3A4C95271B5B21410088C3F2 /* FLEXGlobalsViewController.m in Sources */, C313854823F5F7D50046E63C /* flex_fishhook.c in Sources */, C3531BA223E796BD00A184AD /* FLEXManager+Extensibility.m in Sources */, 71E1C2172307FBB800F5032A /* FLEXKeychain.m in Sources */, C3A9423923C51B8D006871A3 /* FHSRangeSlider.m in Sources */, C3F646F323A045DB00D4A011 /* FLEXClassShortcuts.m in Sources */, C398625E23AD6E90007E6793 /* UIFont+FLEX.m in Sources */, C387C88422E0D24A00750E58 /* UIView+FLEX_Layout.m in Sources */, C32A195F231732E800EB02AC /* FLEXCollectionContentSection.m in Sources */, C36FBFCD230F3B98008D95D5 /* FLEXMethod.m in Sources */, 779B1ED31C0C4D7C001F5E49 /* FLEXTableColumnHeader.m in Sources */, C30D2963261FAE9E00D89649 /* FLEXNSStringShortcuts.m in Sources */, C37A0C94218BAC9600848CA7 /* FLEXObjcInternal.mm in Sources */, 03ED813D24B7D91200831CA0 /* FLEXTableRowDataViewController.m in Sources */, C31D93E523E38CBE005517BF /* FLEXBlockShortcuts.m in Sources */, C312A13123ECB5D300E38049 /* FLEXBookmarkManager.m in Sources */, C34D4EB523A2AF2A00C1F903 /* FLEXColorPreviewSection.m in Sources */, C36B097123E1EDCD008F5D47 /* FLEXTableViewSection.m in Sources */, C3531BA623E88A2100A184AD /* FLEXNavigationController.m in Sources */, 3A4C94EA1B5B21410088C3F2 /* FLEXHierarchyTableViewController.m in Sources */, 3A4C95331B5B21410088C3F2 /* FLEXSystemLogCell.m in Sources */, C3A9423523C3F98E006871A3 /* FHSViewController.m in Sources */, C3DB9F652107FC9600B46809 /* FLEXObjectRef.m in Sources */, 3A4C95021B5B21410088C3F2 /* FLEXArgumentInputTextView.m in Sources */, C3531B9E23E69BB200A184AD /* FLEXManager+Networking.m in Sources */, C39EADC923F37B89005618BE /* FLEXTypeEncodingParser.m in Sources */, C3F977882311B38F0032776D /* NSObject+FLEX_Reflection.m in Sources */, C36B096623E0D4A1008F5D47 /* UIMenu+FLEX.m in Sources */, 94A515261C4CA2080063292F /* FLEXExplorerToolbar.m in Sources */, C33C825F2316DC8600DD2451 /* FLEXObjectExplorer.m in Sources */, C383C3C123B6B429007A321B /* NSTimer+FLEX.m in Sources */, 3A4C94FA1B5B21410088C3F2 /* FLEXArgumentInputNumberView.m in Sources */, C3474C4123DA496400466532 /* FLEXKeyValueTableViewCell.m in Sources */, 779B1ED71C0C4D7C001F5E49 /* FLEXTableContentViewController.m in Sources */, C35DAD8F2709143000AA95E6 /* FLEXMITMDataSource.m in Sources */, C36FBFCB230F3B98008D95D5 /* FLEXMirror.m in Sources */, C36FBFD5230F3B98008D95D5 /* FLEXMethodBase.m in Sources */, 3A4C95001B5B21410088C3F2 /* FLEXArgumentInputSwitchView.m in Sources */, C386D6F02419A33F00699085 /* FLEXRuntimeConstants.m in Sources */, C3F31D442267D883003C991A /* FLEXTableView.m in Sources */, 224D49AB1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m in Sources */, C312A13C23ECE79000E38049 /* FLEXWindowManagerController.m in Sources */, C30D2969261FAEEE00D89649 /* Cocoa+FLEXShortcuts.m in Sources */, C36FBFDA230F3B98008D95D5 /* FLEXPropertyAttributes.m in Sources */, 779B1ED91C0C4D7C001F5E49 /* FLEXTableLeftCell.m in Sources */, 3A4C94E61B5B21410088C3F2 /* FLEXUtility.m in Sources */, 3A4C95231B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m in Sources */, C3490E20233BDD73002AE200 /* FLEXSingleRowSection.m in Sources */, C34D4EB923A2B17900C1F903 /* FLEXBundleShortcuts.m in Sources */, C3531BAB23E88FAC00A184AD /* FLEXTabList.m in Sources */, C3DFCD992416E7DD00BB7084 /* FLEXMutableListSection.m in Sources */, 3A4C952D1B5B21410088C3F2 /* FLEXLiveObjectsController.m in Sources */, C33E46B0223B02CD004BD0E6 /* FLEXASLLogController.m in Sources */, C398625023AD6C67007E6793 /* FLEXObjcRuntimeViewController.m in Sources */, C398626E23AD71C1007E6793 /* FLEXRuntimeClient.m in Sources */, 3A4C953D1B5B21410088C3F2 /* FLEXNetworkTransaction.m in Sources */, 3A4C94E81B5B21410088C3F2 /* FLEXHierarchyTableViewCell.m in Sources */, 3A4C950A1B5B21410088C3F2 /* FLEXFieldEditorView.m in Sources */, 3A4C95061B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m in Sources */, 3A4C95291B5B21410088C3F2 /* FLEXObjectListViewController.m in Sources */, C397E319240EC98F0091E4EC /* FLEXSQLResult.m in Sources */, C31D93E923E38E97005517BF /* FLEXBlockDescription.m in Sources */, 71E1C2182307FBB800F5032A /* FLEXKeychainViewController.m in Sources */, 94A5151E1C4CA1F10063292F /* FLEXExplorerViewController.m in Sources */, C398625A23AD6C88007E6793 /* FLEXSearchToken.m in Sources */, 3A4C95371B5B21410088C3F2 /* FLEXNetworkMITMViewController.m in Sources */, C3511B9222D7C99E0057BAB7 /* FLEXGlobalsSection.m in Sources */, C32A19632317378C00EB02AC /* FLEXDefaultsContentSection.m in Sources */, C3EE76C022DFC63600EC0AA0 /* FLEXScopeCarousel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 1C27A8BD1F0E5A0400F0D02D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3A4C941E1B5B20570088C3F2 /* FLEX */; targetProxy = 1C27A8BC1F0E5A0400F0D02D /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 1C27A8BE1F0E5A0400F0D02D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = U3LST7M92S; INFOPLIST_FILE = FLEXTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.FLEXTestsMethodsList; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 1C27A8BF1F0E5A0400F0D02D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; DEVELOPMENT_TEAM = U3LST7M92S; INFOPLIST_FILE = FLEXTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.flipboard.FLEXTestsMethodsList; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 3A4C94331B5B20570088C3F2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = "-Wno-unsupported-availability-guard"; }; name = Debug; }; 3A4C94341B5B20570088C3F2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = "-Wno-unsupported-availability-guard"; }; name = Release; }; 3A4C94361B5B20570088C3F2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = Classes/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; WARNING_CFLAGS = "-Wno-unsupported-availability-guard"; }; name = Debug; }; 3A4C94371B5B20570088C3F2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", ); INFOPLIST_FILE = Classes/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; WARNING_CFLAGS = "-Wno-unsupported-availability-guard"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1C27A8C01F0E5A0400F0D02D /* Build configuration list for PBXNativeTarget "FLEXTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 1C27A8BE1F0E5A0400F0D02D /* Debug */, 1C27A8BF1F0E5A0400F0D02D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 3A4C94191B5B20570088C3F2 /* Build configuration list for PBXProject "FLEX" */ = { isa = XCConfigurationList; buildConfigurations = ( 3A4C94331B5B20570088C3F2 /* Debug */, 3A4C94341B5B20570088C3F2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 3A4C94351B5B20570088C3F2 /* Build configuration list for PBXNativeTarget "FLEX" */ = { isa = XCConfigurationList; buildConfigurations = ( 3A4C94361B5B20570088C3F2 /* Debug */, 3A4C94371B5B20570088C3F2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 3A4C94161B5B20570088C3F2 /* Project object */; } ================================================ FILE: FLEX.xcodeproj/xcshareddata/xcschemes/FLEX.xcscheme ================================================ ================================================ FILE: FLEX.xcodeproj/xcshareddata/xcschemes/FLEXTests.xcscheme ================================================ ================================================ FILE: FLEXTests/FLEXTests.m ================================================ // // FLEXTests.m // FLEXTests // // Created by Tanner Bennett on 8/27/19. // Copyright © 2019 Flipboard. All rights reserved. // #import #import #import "NSObject+FLEX_Reflection.h" #import "FLEXObjcInternal.h" #import "FLEXHeapEnumerator.h" #import "FLEXObjectRef.h" #import "NSArray+FLEX.h" #import "FLEXPropertyAttributes.h" #import "FLEXProperty.h" #import "FLEXUtility.h" #import "FLEXRuntimeUtility.h" #import "FLEXMethod.h" #import "FLEXIvar.h" #import "FLEXNewRootClass.h" @interface Subclass : NSObject { @public NSUInteger *_indexes; } @end @implementation Subclass @end @interface NeverCreated : Subclass { NSInteger a, b, c; } @end @implementation NeverCreated @end @interface FLEXTests : XCTestCase @property (nonatomic, setter=setMyFoo:) id foo; @end @implementation FLEXTests - (void)testRuntimeAdditions { XCTAssertEqual(1, NSObject.flex_classHierarchy.count); XCTAssertEqual(4, FLEXTests.flex_classHierarchy.count); XCTAssertEqual(FLEXTests.flex_classHierarchy.firstObject, [self class]); XCTAssertEqual(FLEXTests.flex_classHierarchy.lastObject, [NSObject class]); } - (void)testAssumptionsAboutClasses { Class cls = [self class]; Class meta = objc_getMetaClass(NSStringFromClass(cls).UTF8String); Class rootMeta = object_getClass(meta); // Subsequent `class` calls yield self XCTAssertEqual(cls, [cls class]); XCTAssertEqual(meta, [meta class]); // A class's superclass is NOT a metaclass XCTAssertFalse(class_isMetaClass([cls superclass])); // A metaclass's superclass IS a metaclass XCTAssertTrue(class_isMetaClass([meta superclass])); // Subsequent object_getClass calls yield metaclass XCTAssertEqual(object_getClass(cls), meta); XCTAssertEqual(object_getClass(object_getClass(meta)), rootMeta); // Superclass of a root class is nil XCTAssertNil(NSObject.superclass); } - (void)testAssumptionsAboutMessageSending { // "instances respond to selector" works with metaclasses targeting class objects Class meta = object_getClass(NSBundle.class); XCTAssertTrue([meta instancesRespondToSelector:@selector(mainBundle)]); XCTAssertFalse([meta respondsToSelector:@selector(mainBundle)]); } - (void)testAssumptionsAboutRuntimeMethodFunctions { Class cls = [NSBundle class]; Class meta = object_getClass(cls); Method bundleID = class_getInstanceMethod(cls, @selector(bundleIdentifier)); Method mainBundle = class_getClassMethod(cls, @selector(mainBundle)); // Preconditions... XCTAssert(class_isMetaClass(meta)); XCTAssert(bundleID != nil); XCTAssert(mainBundle != nil); // Metaclasses cannot find instance methods XCTAssertEqual(nil, class_getInstanceMethod(meta, @selector(bundleIdentifier))); // Metaclasses can find class methods as both class methods and instance methods, // and the methods found by metaclasses are equal regardless of which function is used XCTAssertEqual(mainBundle, class_getClassMethod(meta, @selector(mainBundle))); // Metaclasses can find class methods as instance methods XCTAssertEqual(mainBundle, class_getInstanceMethod(meta, @selector(mainBundle))); } - (void)testAbilitiesOfKVC { [self setValue:@5 forKey:@"foo"]; XCTAssertEqualObjects(self.foo, @5); } - (void)testCPPTypeEncoding { const char *type = "{basic_string, " "std::__1::allocator >={__compressed_pair, std::__1::allocator >::__rep, " "std::__1::allocator >={__rep}}}"; XCTAssertThrows(NSGetSizeAndAlignment(type, nil, nil)); } - (void)testGetClassProperties { NSArray *props = NSBundle.flex_allClassProperties; props = [props flex_filtered:^BOOL(FLEXProperty *obj, NSUInteger idx) { return [obj.name isEqualToString:@"mainBundle"]; }]; XCTAssert(props.count == 1); } - (void)testIvarUnboxing { NSUInteger array[4] = { 0xaa, 0xbb, 0xcc, 0x00 }; Subclass *obj = [Subclass new]; obj->_indexes = array; FLEXIvar *ivar = [Subclass flex_ivarNamed:@"_indexes"]; NSValue *arrayValue = [ivar getPotentiallyUnboxedValue:obj]; NSUInteger *pointerValue = arrayValue.pointerValue; XCTAssert(pointerValue != nil); XCTAssertEqual(pointerValue, (NSUInteger *)&array); XCTAssertEqual(pointerValue[0], 0xaa); } - (void)testSafeRespondsToSelector { XCTAssertFalse([FLEXRuntimeUtility safeObject:[NSObject class] respondsToSelector:@selector(testSafeRespondsToSelector) ]); Class root = NSClassFromString(@"FLEXNewRootClass"); XCTAssertTrue([FLEXRuntimeUtility safeObject:root respondsToSelector:@selector(theOnlyMethod)]); XCTAssertFalse([FLEXRuntimeUtility safeObject:root respondsToSelector:@selector(class)]); } - (void)testSafeGetClassName { id instance = [NSClassFromString(@"FLEXNewRootClass") alloc]; NSString *className = [FLEXRuntimeUtility safeClassNameForObject:instance]; XCTAssertEqualObjects(@"FLEXNewRootClass", className); } - (void)testInvalidObjectFinding { // Create something that looks like an objc object uintptr_t *pointer = (uintptr_t *)calloc(1, sizeof(uintptr_t)); object_setClass((__bridge id)(void *)pointer, [NeverCreated class]); // Find that one object and assert that it is the object we just created NSArray *refs = [FLEXHeapEnumerator instancesOfClassWithName:@"NeverCreated" retained:NO ]; XCTAssertEqual(refs.count, 1); XCTAssertEqual((__bridge void *)refs.firstObject.object, pointer); // Find that the object isn't really an object XCTAssertFalse(FLEXPointerIsValidObjcObject(pointer)); free(pointer); } @end ================================================ FILE: FLEXTests/FLEXTestsMethodsList.m ================================================ // // FLEXTestMethodList.m // FLEXTestMethodList // // Created by Tigran Yesayan on 7/6/17. // Copyright © 2017 Flipboard. All rights reserved. // #import #import "FLEXRuntimeUtility.h" #import "FLEXManager.h" #import "FLEXWindow.h" #import "FLEXMultiColumnTableView.h" @interface FLEXRuntimeUtility (Testing) + (NSString *)readableTypeForEncoding:(NSString *)encodingString; @end @interface FLEXTestMethodList : XCTestCase @end @implementation FLEXTestMethodList - (void)testExample { NSArray *classesToTest = @[ [NSObject class], [NSArray class], [UIApplication class], [UIView class], [NSThread class], [CALayer class], [NSDictionary class], [NSProxy class], [NSData class], [FLEXManager class], [FLEXWindow class], [FLEXMultiColumnTableView class], [NSString class], [NSSet class], [NSUndoManager class], [NSMutableArray class], [NSMutableDictionary class], [NSException class], [UIImage class], [UIViewController class], [UIScreen class], [UIResponder class], [NSNumber class], [NSValue class], [NSError class], [NSNotificationCenter class], [NSUserActivity class], [NSUserDefaults class], [NSExpression class], [NSBundle class] ]; for (Class cls in classesToTest) { [self testMethodListForClass:cls]; } } - (void)testMethodListForClass:(Class)class { NSLog(@"class: %@", NSStringFromClass(class)); unsigned int methodCount = 0; Method *methods = class_copyMethodList(class, &methodCount); for (unsigned int i = 0; i < methodCount; ++i) { Method method = methods[i]; NSArray *prevWay = [self prettyArgumentComponentsForMethod:method]; NSArray *newWay = [FLEXRuntimeUtility prettyArgumentComponentsForMethod:method]; XCTAssertEqualObjects(prevWay, newWay); } free(methods); } #pragma mark - Method to test with - (NSArray *)prettyArgumentComponentsForMethod:(Method)method { NSMutableArray *components = [NSMutableArray new]; NSString *selectorName = NSStringFromSelector(method_getName(method)); NSArray *selectorComponents = [selectorName componentsSeparatedByString:@":"]; unsigned int numberOfArguments = method_getNumberOfArguments(method); for (unsigned int argIndex = kFLEXNumberOfImplicitArgs; argIndex < numberOfArguments; argIndex++) { char *argType = method_copyArgumentType(method, argIndex); NSString *readableArgType = [FLEXRuntimeUtility readableTypeForEncoding:@(argType)]; free(argType); NSString *prettyComponent = [NSString stringWithFormat:@"%@:(%@) ", [selectorComponents objectAtIndex:argIndex - kFLEXNumberOfImplicitArgs], readableArgType]; [components addObject:prettyComponent]; } return components; } @end ================================================ FILE: FLEXTests/FLEXTypeEncodingParserTests.m ================================================ // // FLEXTypeEncodingParserTests.m // FLEXTests // // Created by Tanner Bennett on 8/25/19. // Copyright © 2019 Flipboard. All rights reserved. // #import #import #import "FLEXTypeEncodingParser.h" #define Type(t) @(@encode(t)) #define TypeSizeAlignPair(t) Type(t): @[@(sizeof(t)), @(__alignof__(t))] @interface FLEXTypeEncodingParserTests : XCTestCase { struct { id __unsafe_unretained *bar; void (*foo[5])(void); SEL abc; Class cls; int baz; NSString * __unsafe_unretained str; id __unsafe_unretained proto; NSDictionary * __unsafe_unretained another; } _yikes; } @property (nonatomic, readonly) NSDictionary *> *typesToSizes; @end typedef struct Anon { NSString *bar; int baz; } Anon; typedef struct HasBitfield { int a: 1; } HasBitfield; typedef struct HasArray { int a[2]; } HasArray; typedef struct HasUnion { int a, b; union { float y, z; char a; } u; char c; } HasUnion; @implementation FLEXTypeEncodingParserTests - (void)setUp { memset(&_yikes.bar, 0x55, sizeof(id)); memset(&_yikes.foo, 0xff, sizeof(_yikes.foo)); memset(&_yikes.abc, 0x88, sizeof(SEL)); _typesToSizes = @{ TypeSizeAlignPair(__typeof__(_yikes)), @"{Anon=\"bar\"@\"NSString\"\"baz\"i}" : @[@(sizeof(Anon)), @(__alignof__(Anon))], Type(NSDecimal) : @[@-1, @0], // Real size is 16 on 64 bit machines TypeSizeAlignPair(char), TypeSizeAlignPair(short), TypeSizeAlignPair(int), TypeSizeAlignPair(long), TypeSizeAlignPair(long long), TypeSizeAlignPair(float), TypeSizeAlignPair(double), TypeSizeAlignPair(long double), TypeSizeAlignPair(Class), TypeSizeAlignPair(id), TypeSizeAlignPair(CGPoint), TypeSizeAlignPair(CGRect), TypeSizeAlignPair(char *), TypeSizeAlignPair(long *), TypeSizeAlignPair(Class *), TypeSizeAlignPair(CGRect *) }; } - (void)testTypeEncodingParser { [self.typesToSizes enumerateKeysAndObjectsUsingBlock:^(NSString *typeString, NSArray *sa, BOOL *stop) { if (sa[0].longLongValue >= 0) { XCTAssertNoThrow(NSGetSizeAndAlignment(typeString.UTF8String, nil, nil)); } else { XCTAssertThrows(NSGetSizeAndAlignment(typeString.UTF8String, nil, nil)); } ssize_t align = 0; ssize_t size = [FLEXTypeEncodingParser sizeForTypeEncoding:typeString alignment:&align]; XCTAssertEqual(size, sa[0].longValue); XCTAssertEqual(align, sa[1].longValue); }]; } - (void)testExpectedStructureSizes { typedef struct _FooBytes { uint8_t x: 3; struct { uint8_t a: 1; uint8_t b: 2; } y; uint8_t z: 5; } FooBytes; typedef struct _FooInts { unsigned int x: 3; struct { unsigned int a: 1; unsigned int b: 2; } y; unsigned int z: 5; } FooInts; typedef struct _Bar { unsigned int x: 3; unsigned int z: 5; struct { unsigned int a: 1; unsigned int b: 2; } y; } Bar; typedef struct _ArrayInMiddle { unsigned int x: 3; unsigned char c[2]; unsigned int z: 5; } ArrayInMiddle; typedef struct _ArrayAtEnd { unsigned int x: 3; unsigned int z: 5; unsigned char c[2]; } ArrayAtEnd; typedef struct _OneBit { uint8_t x: 1; } OneBit; typedef struct _OneByte { uint8_t x; } OneByte; typedef struct _TwoBytes { uint8_t x, y; } TwoBytes; typedef struct _TwoJoinedBytesAndOneByte { uint16_t x; uint8_t y; } TwoJoinedBytesAndOneByte; // Structs have the alignment of the size of their smallest member, recursively. // That is, a struct has the alignment of the greater of the size of its // largest direct member or the largest alignment of it's nested structs. XCTAssertEqual(__alignof__(FooBytes), 1); XCTAssertEqual(__alignof__(FooInts), 4); XCTAssertEqual(__alignof__(ArrayInMiddle), 4); XCTAssertEqual(__alignof__(ArrayAtEnd), 4); XCTAssertEqual(__alignof__(OneBit), 1); XCTAssertEqual(__alignof__(OneByte), 1); XCTAssertEqual(__alignof__(TwoBytes), 1); XCTAssertEqual(__alignof__(TwoJoinedBytesAndOneByte), 2); // Nested structs are aligned before and after, if between bitfields XCTAssertEqual(sizeof(FooBytes), 3); XCTAssertEqual(sizeof(FooInts), 12); // Bitfields are not aligned at all and they will pack if adjacent to one another XCTAssertEqual(sizeof(Bar), 8); // Structs are resized to match their alignment XCTAssertEqual(sizeof(OneBit), 1); XCTAssertEqual(sizeof(OneByte), 1); XCTAssertEqual(sizeof(TwoJoinedBytesAndOneByte), 4); // Arrays do not affect alignment like nested structs do XCTAssertEqual(sizeof(ArrayInMiddle), 4); XCTAssertEqual(sizeof(ArrayAtEnd), 4); XCTAssertEqual(sizeof(HasUnion), 16); // Test my method of converting calculated sizes to actual sizes // for FLEXTypeEncodingParser #define RoundUpToMultipleOf4(num) ((num + 3) & ~0x03) XCTAssertEqual(RoundUpToMultipleOf4(1), 4); XCTAssertEqual(RoundUpToMultipleOf4(2), 4); XCTAssertEqual(RoundUpToMultipleOf4(3), 4); XCTAssertEqual(RoundUpToMultipleOf4(4), 4); XCTAssertEqual(RoundUpToMultipleOf4(5), 8); XCTAssertEqual(RoundUpToMultipleOf4(6), 8); XCTAssertEqual(RoundUpToMultipleOf4(7), 8); XCTAssertEqual(RoundUpToMultipleOf4(8), 8); XCTAssertEqual(RoundUpToMultipleOf4(9), 12); XCTAssertEqual(RoundUpToMultipleOf4(10), 12); XCTAssertEqual(RoundUpToMultipleOf4(11), 12); XCTAssertEqual(RoundUpToMultipleOf4(12), 12); XCTAssertEqual(RoundUpToMultipleOf4(13), 16); } - (void)testUnsupportedMethodSignatures { NSArray *unsupported = @[ @"v40@0:8{?=}16d32", @"{?=[4]}16@0:8}", @"i48@0:8^{__CVBuffer=}16I24(pj_timestamp={?=II}Q)28i36B40B44", ]; for (NSString *signature in unsupported) { XCTAssertFalse([FLEXTypeEncodingParser methodTypeEncodingSupported:signature cleaned:nil]); } } - (void)testMethodSignatureCleaning { NSDictionary *uncleanToClean = @{ @"^{Layer=^^?{Atomic={?=i}}{Data={Vec4=ffff}b1{Vec2=dd}{Rect=dddd}}" "{Ref=^{Object}}{Ref >=" "^{TypedArray}}^{Layer}{Ref=^{Ext}}" "{Ref >=" "^{TypedArray}}{Ref=^{Handle}}}36@0:" "8^{Transaction=^{Shared}i^{HashTable}^{SpinLock}I" "^{Level}^{List}^{Command}^{Deleted}^{List}^{Context}" "^{HashTable}^{__CFRunLoop}^{__CFRunLoopObserver}" "^{LayoutList}^{List}{Atomic={?=i}}b1b1b1b1b1}16I24^I28": @"^{Layer=}36@0:8^{Transaction=}16I24^I28", @"{LSBinding=I^{LSBundleData=}I^{?}@@}16@0:8": @"{LSBinding=I^{LSBundleData=}I^{?=}@@}16@0:8", @"@40@0:8@16r^{?=BQ^{?}}24^@32": @"@40@0:8@16r^{?=BQ^{?=}}24^@32", @"@36@0:8@16^{mig_subsystem=^?iiIQ[1{routine_descriptor=^?^?II^{?}I}]}24B32": @"@36@0:8@16^{mig_subsystem=^?iiIQ[1{routine_descriptor=^?^?II^{?=}I}]}24B32", @"@28@0:8r^{basic_string, " "std::__1::allocator >={__compressed_pair, " "std::__1::allocator >::__rep, std::__1::allocator " ">={__rep=(?={__long=QQ*}{__short=(?=Cc)[23c]}{__raw=[3Q]})}}}16B24": @"@28@0:8r^{?=}16B24", @"^{nui_size_cache=^{pair}^{pair}" "{__compressed_pair *, " "std::__1::allocator > >=" "^{pair}}}16@0:8" : @"^{nui_size_cache=}16@0:8", @"^?32@0:8r^{_CAPropertyInfo=I[2:]b16b16*^{__CFString}}16" "r^{_CAPropertyInfo=I[2:]b16b16*^{__CFString}}24": @"^?32@0:8r^{_CAPropertyInfo=}16r^{_CAPropertyInfo=}24", // NSMethodSignature doesn't support unions for some reason @"^{?=(pj_timestamp={?=II}Q)Iii}20@0:8i16": @"^{?=}20@0:8i16", @"^{KeyValueArray=^^?{Atomic={?=i}}I[1^{Object}]}16@0:8": @"^{KeyValueArray=^^?{Atomic={?=i}}I[1^{Object=}]}16@0:8" }; [uncleanToClean enumerateKeysAndObjectsUsingBlock:^(NSString *needsCleaning, NSString *expected, BOOL *stop) { NSString *cleaned = nil; XCTAssertTrue([FLEXTypeEncodingParser methodTypeEncodingSupported:needsCleaning cleaned:&cleaned]); XCTAssertEqualObjects(cleaned, expected); }]; } - (void)testSupportedTypeEncodings { XCTAssertThrows(NSGetSizeAndAlignment(@encode(HasBitfield), nil, nil)); XCTAssertNoThrow(NSGetSizeAndAlignment(@encode(HasArray), nil, nil)); XCTAssertNoThrow(NSGetSizeAndAlignment(@encode(HasUnion), nil, nil)); } @end ================================================ FILE: FLEXTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: FLEXTests/Supporting Files/FLEXNewRootClass.h ================================================ // // FLEXNewRootClass.h // FLEXTests // // Created by Tanner on 12/30/20. // Copyright © 2020 Flipboard. All rights reserved. // #import /// Root class with one method OBJC_ROOT_CLASS @interface FLEXNewRootClass { Class isa OBJC_ISA_AVAILABILITY; } - (void)theOnlyMethod; @end ================================================ FILE: FLEXTests/Supporting Files/FLEXNewRootClass.m ================================================ // // FLEXNewRootClass.m // FLEXTests // // Created by Tanner on 12/30/20. // Copyright © 2020 Flipboard. All rights reserved. // #import "FLEXNewRootClass.h" #import @implementation FLEXNewRootClass + (id)alloc { FLEXNewRootClass *obj = (__bridge id)calloc(1, class_getInstanceSize(self)); object_setClass(obj, self); return obj; } - (void)theOnlyMethod { } - (void)retain { } - (void)release { } @end ================================================ FILE: Graphics/image_to_code.py ================================================ #!/usr/bin/python import os import sys import getopt headerTemplate = """/* * Copyright 2010-present Facebook. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file was built by running * * ./image_to_code.py %(args)s * * Thanks to the Facebook SDK for providing the base for this script */ """ def bytes_from_file(filename, chunksize=8192): with open(filename, "rb") as f: while True: chunk = f.read(chunksize) if chunk: for b in chunk: yield b else: break def write_header_file(header, className, outputFile): with open(outputFile, "w") as f: f.write(header) f.write(""" #import #import """) f.write("@interface " + className + " : NSObject\n\n") f.write("+ (UIImage *)image;\n\n") f.write("@end") def write_implementation_file(inputFile, header, className, outputFile): formattedBytes = ["0x{0:02x}".format(ord(x)) for x in bytes_from_file(inputFile)] with open(outputFile, "w") as f: f.write(header) f.write("\n") f.write("#import \"" + className + ".h\"\n") f.write("#import \"FLEXImageLoader.h\"\n\n") # Write standard bytes out f.write("const Byte " + className + "_standard[] = {\n") f.write(", ".join(formattedBytes)) f.write("\n") f.write("};\n") # Write retina if present useRetina = True try: (name, ext) = inputFile.rsplit(".", 1) name = name + "@2x" formattedBytes = ["0x{0:02x}".format(ord(x)) for x in bytes_from_file(name + "." + ext)] f.write("const Byte " + className + "_retina[] = {\n") f.write(", ".join(formattedBytes)) f.write("\n") f.write("};\n") except IOError: useRetina = False # Enter the bundle path override # strip of the PNG that was added for our class name for the resource name bundlepath = "@\"" + "FacebookSDKImages/" + className[0:-3] + ".png\"" f.write("\n") f.write("@implementation " + className + "\n\n") f.write("+ (UIImage *)image {\n") f.write(" return [FLEXImageLoader imageFromBytes:" + className + "_standard ") f.write("length:sizeof(" + className + "_standard)/sizeof(" + className + "_standard[0]) ") if useRetina: f.write("fromRetinaBytes:" + className + "_retina ") f.write("retinaLength:sizeof(" + className + "_retina)/sizeof(" + className + "_retina[0])];\n") else: f.write("fromRetinaBytes:NULL ") f.write("retinaLength:0];\n") f.write("}\n") f.write("@end\n") print(", ".join(formattedBytes)) def main(argv): inputFile = '' outputClass = 'ImageCode' outputDir = os.getcwd() try: opts, args = getopt.getopt(argv,"hi:c:o:") except getopt.GetoptError: print('image_to_code.py -i [-c ] [-o ]') sys.exit(2) for opt, arg in opts: if opt == '-h': print('image_to_code.py -i [-c ] [-o ]') sys.exit() elif opt == '-i': inputFile = arg elif opt == '-c': outputClass = arg elif opt in '-o': outputDir = arg # Build file headers header = headerTemplate % {"args" : " ".join(argv)} # outputClass needs to add PNG as part of it outputClass = outputClass + "PNG" # Build the output base filename outputFileBase = outputDir + "/" + outputClass # Build .h file outputFile = outputFileBase + ".h" write_header_file(header, outputClass, outputFile) # Build .m file outputFile = outputFileBase + ".m" write_implementation_file(inputFile, header, outputClass, outputFile) if __name__ == "__main__": main(sys.argv[1:]) ================================================ FILE: Graphics/images_to_hex.sh ================================================ #!/usr/bin/bash # Converts every image *.png to *.txt as an ASCII hex representation of the image. # # This script simply runs image_to_code.py on each of the folders containing # our images and writes the hex of each image to a text file. Instead of updating # the original script, I left it to be generic as it was originally intended # in case someone else finds it useful. The original python script outputs # header and implementation files, which this script removes after it is run. # # Run this command to remove the hex files: find . -type f -name "*.txt" | xargs rm # # Usage: ./images_to_hex.sh [one or more folders in a quoted string] # Examples: # bash images_to_hex.sh # bash images_to_hex.sh toolbar # bash images_to_hex.sh "toolbar filetypes" # if [[ $1 ]]; then imageFolders="$1" else imageFolders="filetypes range-slider toolbar misc" fi for dir in $imageFolders; do rm $dir/*.txt for image in `ls $dir`; do name=`basename $image .png` outfile=$dir/$name.txt echo "Output file: $outfile" ./image_to_code.py -i $dir/$image -c $name > "$outfile" done done rm -rf *PNG.[hm] ================================================ FILE: Graphics/iterate_hex_to_pasteboard.sh ================================================ #!/usr/bin/bash # I use this script to loop over the contents of each text file # produced by images_to_hex.sh and copy it to the clipboard. # This makes adding or updating graphics to the project faster. # # You can edit the $folder variable below to work in a certain # subdirectory, and you can edit the wildcard expression below # to only work with a certain image size, like "*@3x.txt" folder=. for file in `find $folder -type f -name "*.txt"`; do cat $file | pbcopy read -p "Copied $file, press any key to continue..." done ================================================ FILE: LICENSE ================================================ Copyright (c) 2014-2016, Flipboard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Flipboard nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * You must NOT include this project in an application to be submitted to the App Store™, as this project uses too many private APIs. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Package.swift ================================================ // swift-tools-version:5.3 import PackageDescription enum FLEXBuildOptions { /// Set this to `true` to use `.unsafeFlags` to silence warnings. static let silenceWarnings = false } #if swift(>=5.9) let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v12)] #elseif swift(>=5.7) let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v11)] #else let platforms: [PackageDescription.SupportedPlatform] = [.iOS(.v10)] #endif let package = Package( name: "FLEX", platforms: platforms, products: [ .library(name: "FLEX", targets: ["FLEX"]) ], targets: [ .target( name: "FLEX", path: "Classes", exclude: [ "Info.plist", "Utility/APPLE_LICENSE", "Network/OSCache/LICENSE.md", "Network/PonyDebugger/LICENSE", "GlobalStateExplorers/DatabaseBrowser/LICENSE", "GlobalStateExplorers/Keychain/SSKeychain_LICENSE", "GlobalStateExplorers/SystemLog/LLVM_LICENSE.TXT", ], publicHeadersPath: "Headers", cSettings: .headerSearchPaths + .warningFlags, linkerSettings: [ .linkedFramework("CoreGraphics"), .linkedLibrary("sqlite3"), .linkedLibrary("z"), ] ) ], // Required to compile FLEXSwiftInternal.mm cxxLanguageStandard: .gnucxx11 ) extension Array where Element == CSetting { static var warningFlags: [Element] { if FLEXBuildOptions.silenceWarnings { return [.unsafeFlags([ "-Wno-deprecated-declarations", "-Wno-strict-prototypes", "-Wno-unsupported-availability-guard", ])] } return [] } /// These are the header search paths needed for FLEX to compile, not /// the headers used by projects linking against FLEX. /// /// Do not modify the contents of this property by hand; /// Instead, run `bash generate-spm-headers.sh | grep headerSearchPath | pbcopy` /// and paste (and indent) the result below. Do this any time new folders are added. static var headerSearchPaths: [Element] { [ .headerSearchPath("Classes"), .headerSearchPath("Core"), .headerSearchPath("Core/Controllers"), .headerSearchPath("Core/Views"), .headerSearchPath("Core/Views/Cells"), .headerSearchPath("Core/Views/Carousel"), .headerSearchPath("ObjectExplorers"), .headerSearchPath("ObjectExplorers/Sections"), .headerSearchPath("ObjectExplorers/Sections/Shortcuts"), .headerSearchPath("Network"), .headerSearchPath("Network/PonyDebugger"), .headerSearchPath("Network/OSCache"), .headerSearchPath("Toolbar"), .headerSearchPath("Manager"), .headerSearchPath("Manager/Private"), .headerSearchPath("Editing"), .headerSearchPath("Editing/ArgumentInputViews"), .headerSearchPath("Headers"), .headerSearchPath("ExplorerInterface"), .headerSearchPath("ExplorerInterface/Tabs"), .headerSearchPath("ExplorerInterface/Bookmarks"), .headerSearchPath("GlobalStateExplorers"), .headerSearchPath("GlobalStateExplorers/Globals"), .headerSearchPath("GlobalStateExplorers/Keychain"), .headerSearchPath("GlobalStateExplorers/FileBrowser"), .headerSearchPath("GlobalStateExplorers/SystemLog"), .headerSearchPath("GlobalStateExplorers/DatabaseBrowser"), .headerSearchPath("GlobalStateExplorers/RuntimeBrowser"), .headerSearchPath("GlobalStateExplorers/RuntimeBrowser/DataSources"), .headerSearchPath("ViewHierarchy"), .headerSearchPath("ViewHierarchy/SnapshotExplorer"), .headerSearchPath("ViewHierarchy/SnapshotExplorer/Scene"), .headerSearchPath("ViewHierarchy/TreeExplorer"), .headerSearchPath("Utility"), .headerSearchPath("Utility/Runtime"), .headerSearchPath("Utility/Runtime/Objc"), .headerSearchPath("Utility/Runtime/Objc/Reflection"), .headerSearchPath("Utility/Categories"), .headerSearchPath("Utility/Categories/Private"), .headerSearchPath("Utility/Keyboard") ] } } ================================================ FILE: README.md ================================================ # FLEX [![CocoaPods](https://img.shields.io/cocoapods/v/FLEX.svg)](https://cocoapods.org/?q=FLEX) [![CocoaPods](https://img.shields.io/cocoapods/l/FLEX.svg)](https://github.com/Flipboard/FLEX/blob/master/LICENSE) [![CocoaPods](https://img.shields.io/cocoapods/p/FLEX.svg)]() [![Twitter: @ryanolsonk](https://img.shields.io/badge/contact-@ryanolsonk-blue.svg?style=flat)](https://twitter.com/ryanolsonk) [![Build Status](https://travis-ci.org/Flipboard/FLEX.svg?branch=master)](https://travis-ci.org/Flipboard/FLEX) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) FLEX (Flipboard Explorer) is a set of in-app debugging and exploration tools for iOS development. When presented, FLEX shows a toolbar that lives in a window above your application. From this toolbar, you can view and modify nearly every piece of state in your running application. Demo ## Give Yourself Debugging Superpowers - Inspect and modify views in the hierarchy. - See the properties and ivars on any object. - Dynamically modify many properties and ivars. - Dynamically call instance and class methods. - Observe detailed network request history with timing, headers, and full responses. - Add your own simulator keyboard shortcuts. - View system log messages (e.g. from `NSLog`). - Access any live object via a scan of the heap. - View the file system within your app's sandbox. - Browse SQLite/Realm databases in the file system. - Trigger 3D touch in the simulator using the control, shift, and command keys. - Explore all classes in your app and linked systems frameworks (public and private). - Quickly access useful objects such as `[UIApplication sharedApplication]`, the app delegate, the root view controller on the key window, and more. - Dynamically view and modify `NSUserDefaults` values. Unlike many other debugging tools, FLEX runs entirely inside your app, so you don't need to be connected to LLDB/Xcode or a different remote debugging server. It works well in the simulator and on physical devices. ## Usage In the iOS simulator, you can use keyboard shortcuts to activate FLEX. `f` will toggle the FLEX toolbar. Hit the `?` key for a full list of shortcuts. You can also show FLEX programmatically: Short version: ```objc // Objective-C [[FLEXManager sharedManager] showExplorer]; ``` ```swift // Swift FLEXManager.shared.showExplorer() ``` More complete version: ```objc #if DEBUG #import "FLEXManager.h" #endif ... - (void)handleSixFingerQuadrupleTap:(UITapGestureRecognizer *)tapRecognizer { #if DEBUG if (tapRecognizer.state == UIGestureRecognizerStateRecognized) { // This could also live in a handler for a keyboard shortcut, debug menu item, etc. [[FLEXManager sharedManager] showExplorer]; } #endif } ``` #### Aside: tvOS FLEX itself does not support tvOS out of the box. However, others have taken it upon themselves to port FLEX to tvOS. If you need tvOS support, seek out one of these forks. [Here is one such fork.](https://github.com/lechium/FLEX/tree/tvos) ## Feature Examples ### Modify Views Once a view is selected, you can tap on the info bar below the toolbar to present more details about the view. From there, you can modify properties and call methods. Modify Views ### Network History When enabled, network debugging allows you to view all requests made using NSURLConnection or NSURLSession. Settings allow you to adjust what kind of response bodies get cached and the maximum size limit of the response cache. You can choose to have network debugging enabled automatically on app launch. This setting is persisted across launches. Network History ### All Objects on the Heap FLEX queries malloc for all the live allocated memory blocks and searches for ones that look like objects. You can see everything from here. Heap/Live Objects Explorer ### Explore-at-address If you get your hands on an arbitrary address, you can try explore the object at that address, and FLEX will open it if it can verify the address points to a valid object. If FLEX isn't sure, it'll warn you and refuse to dereference the pointer. If you know better, however, you can choose to explore it anyway by choosing "Unsafe Explore" Address Explorer ### Simulator Keyboard Shortcuts Default keyboard shortcuts allow you to activate the FLEX tools, scroll with the arrow keys, and close modals using the escape key. You can also add custom keyboard shortcuts via `-[FLEXManager registerSimulatorShortcutWithKey:modifiers:action:description]` Simulator Keyboard Shortcuts ### File Browser View the file system within your app's bundle or sandbox container. FLEX shows file sizes, image previews, and pretty prints `.json` and `.plist` files. You can rename and delete files and folders. You can "share" any file if you want to inspect them outside of your app. File Browser ### SQLite Browser SQLite database files (with either `.db` or `.sqlite` extensions), or [Realm](https://realm.io) database files can be explored using FLEX. The database browser lets you view all tables, and individual tables can be sorted by tapping column headers. SQLite Browser ### 3D Touch in the Simulator Using a combination of the command, control, and shift keys, you can simulate different levels of 3D touch pressure in the simulator. Each key contributes 1/3 of maximum possible force. Note that you need to move the touch slightly to get pressure updates. Simulator 3D Touch ### Explore Loaded Libraries Go digging for all things public and private. To learn more about a class, you can create an instance of it and explore its default state. You can also type in a class name to jump to that class directly if you know which class you're looking for. Loaded Libraries Exploration ### NSUserDefaults Editing FLEX allows you to edit defaults that are any combination of strings, numbers, arrays, and dictionaries. The input is parsed as `JSON`. If other kinds of objects are set for a defaults key (i.e. `NSDate`), you can view them but not edit them. NSUserDefaults Editing ### Learning from Other Apps The code injection is left as an exercise for the reader. :innocent:

Springboard Lock Screen Springboard Home Screen

## Installation FLEX requires an app that targets iOS 9 or higher. To run the Example project, open a Terminal window in the Example/ folder and run `pod install`, then open the generated workspace. ### CocoaPods FLEX is available on [CocoaPods](https://cocoapods.org/pods/FLEX). Simply add the following line to your podfile: ```ruby pod 'FLEX', :configurations => ['Debug'] ``` ### Carthage Add the following to your Cartfile: ``` github "flipboard/FLEX" ``` ### Buck If you're using Buck, you may want to silence some of the warnings emitted by FLEX. You will need to build FLEX as an `apple_library` and pass the `-Wno-unsupported-availability-guard` flag, as well as the other warning flags below to disable any other warnings FLEX may have. ### Manual Manually add the files in `Classes/` to your Xcode project, or just drag in the entire `FLEX/` folder. Be sure to exclude FLEX from `Release` builds or your app will be rejected. ##### Silencing warnings Add the following flags to to **Other Warnings Flags** in **Build Settings:** - `-Wno-deprecated-declarations` - `-Wno-strict-prototypes` - `-Wno-unsupported-availability-guard` ### Swift Package Manager Include the dependency in the `dependencies` value of your `Package.swift` ``` swift dependencies: [ .package(url: "https://github.com/FLEXTool/FLEX.git", .upToNextMajor(from: "4.3.0")) ] ``` Next, include the library in your target: ```js .target( name: "YourDependency", dependencies: [ "FLEX" ] ) ``` ## Excluding FLEX from Release (App Store) Builds FLEX makes it easy to explore the internals of your app, so it is not something you should expose to your users. Fortunately, it is easy to exclude FLEX files from Release builds. The strategies differ depending on how you integrated FLEX in your project, and are described below. Wrap the places in your code where you integrate FLEX with an `#if DEBUG` statement to ensure the tool is only accessible in your `Debug` builds and to avoid errors in your `Release` builds. For more help with integrating FLEX, see the example project. ### CocoaPods CocoaPods automatically excludes FLEX from release builds if you only specify the Debug configuration for FLEX in your Podfile: ```ruby pod 'FLEX', :configurations => ['Debug'] ``` ### Carthage 1. Do NOT add `FLEX.framework` to the embedded binaries of your target, as it would otherwise be included in all builds (therefore also in release ones). 1. Instead, add `$(PROJECT_DIR)/Carthage/Build/iOS` to your target _Framework Search Paths_ (this setting might already be present if you already included other frameworks with Carthage). This makes it possible to import the FLEX framework from your source files. It does not harm if this setting is added for all configurations, but it should at least be added for the debug one. 1. Add a _Run Script Phase_ to your target (inserting it after the existing `Link Binary with Libraries` phase, for example), and which will embed `FLEX.framework` in debug builds only: ```shell if [ "$CONFIGURATION" == "Debug" ]; then /usr/local/bin/carthage copy-frameworks fi ``` Finally, add `$(SRCROOT)/Carthage/Build/iOS/FLEX.framework` as input file of this script phase. ### Swift Package Manager In Xcode, navigate to `Build Settings > Build Options > Excluded Source File Names`. For your `Release` configuration, set it to `FLEX*` like this to exclude all files with the `FLEX` prefix: ### FLEX files added manually to a project In Xcode, navigate to `Build Settings > Build Options > Excluded Source File Names`. For your `Release` configuration, set it to `FLEX*` like this to exclude all files with the `FLEX` prefix: ## Additional Notes - When setting fields of type `id` or values in `NSUserDefaults`, FLEX attempts to parse the input string as `JSON`. This allows you to use a combination of strings, numbers, arrays, and dictionaries. If you want to set a string value, it must be wrapped in quotes. For ivars or properties that are explicitly typed as `NSStrings`, quotes are not required. - You may want to disable the exception breakpoint while using FLEX. Certain functions that FLEX uses throw exceptions when they get input they can't handle (i.e. `NSGetSizeAndAlignment()`). FLEX catches these to avoid crashing, but your breakpoint will get hit if it is active. ## Thanks & Credits FLEX builds on ideas and inspiration from open source tools that came before it. The following resources have been particularly helpful: - [MirrorKit](https://github.com/NSExceptional/MirrorKit): an Objective-C wrapper around the Objective-C runtime. - [DCIntrospect](https://github.com/domesticcatsoftware/DCIntrospect): view hierarchy debugging for the iOS simulator. - [PonyDebugger](https://github.com/square/PonyDebugger): network, core data, and view hierarchy debugging using the Chrome Developer Tools interface. - [Mike Ash](https://www.mikeash.com/pyblog/): well written, informative blog posts on all things obj-c and more. The links below were very useful for this project: - [MAObjCRuntime](https://github.com/mikeash/MAObjCRuntime) - [Let's Build Key Value Coding](https://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html) - [ARM64 and You](https://www.mikeash.com/pyblog/friday-qa-2013-09-27-arm64-and-you.html) - [RHObjectiveBeagle](https://github.com/heardrwt/RHObjectiveBeagle): a tool for scanning the heap for live objects. It should be noted that the source code of RHObjectiveBeagle was not consulted due to licensing concerns. - [heap_find.cpp](https://www.opensource.apple.com/source/lldb/lldb-179.1/examples/darwin/heap_find/heap/heap_find.cpp): an example of enumerating malloc blocks for finding objects on the heap. - [Gist](https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396) from [@samdmarshall](https://github.com/samdmarshall): another example of enumerating malloc blocks. - [Non-pointer isa](http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html): an explanation of changes to the isa field on iOS for ARM64 and mention of the useful `objc_debug_isa_class_mask` variable. - [GZIP](https://github.com/nicklockwood/GZIP): A library for compressing/decompressing data on iOS using libz. - [FMDB](https://github.com/ccgus/fmdb): This is an Objective-C wrapper around SQLite. - [InAppViewDebugger](https://github.com/indragiek/InAppViewDebugger): The inspiration and reference implementation for FLEX 4's 3D view explorer, by @indragiek. ## Contributing Please see our [Contributing Guide](https://github.com/Flipboard/FLEX/blob/master/CONTRIBUTING.md). ## TODO - Swift runtime introspection (swift classes, swift objects on the heap, etc.) - Add new NSUserDefault key/value pairs on the fly ================================================ FILE: SM/Surfile ================================================ output_path build framework FLEX targets sdk iOS project FLEX.xcodeproj scheme FLEX sdk iOSSimulator project FLEX.xcodeproj scheme FLEX finalActions openDirectory ================================================ FILE: generate-spm-headers.sh ================================================ #!/bin/bash # Link a specific header makeheader() { /bin/rm -f "Classes/Headers/$(basename $1)" ln -s "../../$1" "Classes/Headers/$(basename $1)" } # Link all headers under the given path and in subfolders generate_headers_recursive() { for path in `find "Classes/$1" -not -path "*Headers*" -name "*.h"`; do makeheader "$path" done } # Link all headers directly under the given path, only generate_headers() { for path in `ls "Classes/$1" | grep '\.h'`; do if [[ $1 ]]; then makeheader "Classes/$1/$path" else makeheader "Classes/$path" fi done } # The code below MUST match what is public_header_files in the podspec. # When this file was last updated, this was the content of public_header_files: # # "Classes/*.h", "Classes/Manager/*.h", "Classes/Toolbar/*.h", # "Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h", # "Classes/Core/**/*.h", "Classes/Utility/Runtime/Objc/**/*.h", # "Classes/ObjectExplorers/**/*.h", "Classes/Editing/**/*.h", # "Classes/Utility/FLEXMacros.h", "Classes/Utility/Categories/*.h", # "Classes/Utility/FLEXAlert.h", "Classes/Utility/FLEXResources.h" # "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h", # "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h", # "Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h", # "Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h" rm -rf Classes/Headers mkdir -p Classes/Headers # Include all headers in these folders generate_headers "" # Top-level headers generate_headers "Manager" generate_headers "Toolbar" generate_headers "Utility/Categories" generate_headers "Core" generate_headers "Core/Controllers" generate_headers "Core/Views" generate_headers "Core/Views/Cells" generate_headers "ObjectExplorers" generate_headers "ObjectExplorers/Sections" generate_headers_recursive "Utility/Runtime/Objc" # Include only headers in these specific folders, # such as those with subfolders that should not be linked makeheader "Classes/Utility/FLEXMacros.h" makeheader "Classes/Utility/FLEXAlert.h" makeheader "Classes/Utility/FLEXResources.h" makeheader "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcut.h" makeheader "Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.h" makeheader "Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h" makeheader "Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.h" # Print all folders in Classes for use in Package.swift for folder in `find "Classes" -type d`; do echo ".headerSearchPath(\"${folder#Classes/}\")," done